Browse Source

feat(product): 优化商品编辑与属性管理功能

- 商品品牌选择改为下拉框并支持默认值设置
- 移除销量人气输入框和安装服务勾选组
- 重构商品主图上传组件,使用文件选择器替代原上传方式
- 简化完成步骤页面,仅显示成功图标和返回按钮
- 新增品牌编辑页面,支持品牌详细信息维护
- 新增属性编辑页面,支持商品属性配置管理
- 优化属性列表页查询条件和表格展示列
- 添加分类排除子节点查询接口和批量添加商品到商品池接口
- 注册新的路由组件包括品牌编辑、属性编辑、商品池管理等
- 完善商品属性值的序列化与反序列化处理逻辑
- 更新商品提交流程,验证通过后直接跳转至完成页面
- 引入文件选择器组件用于主图选择,提升用户体验
肖路 5 months ago
parent
commit
28d4496231

+ 15 - 10
src/api/product/attributes/types.ts

@@ -1,4 +1,4 @@
-export interface AttributesVO {
+  export interface AttributesVO {
   /**
    * 主键,自增ID
    */
@@ -9,6 +9,11 @@ export interface AttributesVO {
    */
   categoryId: string | number;
 
+  /**
+   * 产品分类名称
+   */
+  categoryName?: string;
+
   /**
    * 属性编码(用于系统识别)
    */
@@ -20,12 +25,12 @@ export interface AttributesVO {
   productAttributesName: string;
 
   /**
-   * 是否可选:1=是,0=否
+   * 是否可选:0=单选属性,1=唯一属性,2=复选属性
    */
-  isOptional: string;
+  isOptional: string; 
 
   /**
-   * 属性录入方式(1=下拉,2=文本输入,3=多选等
+   * 属性录入方式(1=手工录入,2=从列表中选择
    */
   entryMethod: string;
 
@@ -73,19 +78,19 @@ export interface AttributesForm extends BaseEntity {
   productAttributesName?: string;
 
   /**
-   * 是否可选:1=是,0=否
+   * 是否可选:0=单选属性,1=唯一属性,2=复选属性
    */
-  isOptional?: string;
+  isOptional?: string | number;
 
   /**
-   * 属性录入方式(1=下拉,2=文本输入,3=多选等
+   * 属性录入方式(manual=手工录入,select=从列表中选择
    */
   entryMethod?: string;
 
   /**
    * 是否用于商品筛选:1=是,0=否
    */
-  isFilter?: string;
+  isFilter?: string | number;
 
   /**
    * 预定义属性值列表(逗号分隔或JSON)
@@ -122,12 +127,12 @@ export interface AttributesQuery extends PageQuery {
   productAttributesName?: string;
 
   /**
-   * 是否可选:1=是,0=否
+   * 是否可选:0=单选属性,1=唯一属性,2=复选属性
    */
   isOptional?: string;
 
   /**
-   * 属性录入方式(1=下拉,2=文本输入,3=多选等
+   * 属性录入方式(manual=手工录入,select=从列表中选择
    */
   entryMethod?: string;
 

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

@@ -228,6 +228,11 @@ export interface BaseVO {
    */
   minOrderQuantity?: number;
 
+  /**
+   * 商品属性值(JSON字符串)
+   */
+  attributesList?: string;
+
 }
 
 export interface BaseForm extends BaseEntity {
@@ -541,6 +546,11 @@ export interface BaseForm extends BaseEntity {
    */
   salesVolume?: number;
 
+  /**
+   * 商品属性值(JSON字符串)
+   */
+  attributesList?: string;
+
 }
 
 export interface BaseQuery extends PageQuery {

+ 11 - 0
src/api/product/category/index.ts

@@ -61,3 +61,14 @@ export const delCategory = (id: string | number | Array<string | number>) => {
     method: 'delete'
   });
 };
+
+/**
+ * 查询产品分类列表(排除节点)
+ * @param id
+ */
+export const listCategoryExcludeChild = (id: string | number): AxiosPromise<CategoryVO[]> => {
+  return request({
+    url: '/product/category/list/exclude/' + id,
+    method: 'get'
+  });
+};

+ 15 - 0
src/api/product/category/types.ts

@@ -19,6 +19,11 @@ export interface CategoryVO {
    */
   parentId: string | number;
 
+  /**
+   * 父级分类名称
+   */
+  parentName?: string;
+
   /**
    * 祖籍列表
    */
@@ -119,6 +124,11 @@ export interface CategoryVO {
    */
   remark: string;
 
+  /**
+   * 子级分类
+   */
+  children?: CategoryVO[];
+
 }
 
 export interface CategoryForm extends BaseEntity {
@@ -142,6 +152,11 @@ export interface CategoryForm extends BaseEntity {
    */
   parentId?: string | number;
 
+  /**
+   * 父级分类名称
+   */
+  parentName?: string;
+
   /**
    * 祖籍列表
    */

+ 55 - 0
src/api/product/pool/types.ts

@@ -34,6 +34,26 @@ export interface PoolVO {
    */
   remark: string;
 
+  /**
+   * 商品数量
+   */
+  productCount?: number;
+
+  /**
+   * 创建时间
+   */
+  createTime?: string;
+
+  /**
+   * 创建人
+   */
+  createBy?: string;
+
+  /**
+   * 审核人
+   */
+  reviewBy?: string;
+
 }
 
 export interface PoolForm extends BaseEntity {
@@ -106,11 +126,46 @@ export interface PoolQuery extends PageQuery {
    */
   platformCode?: string;
 
+  /**
+   * 创建人
+   */
+  createBy?: string;
+
+  /**
+   * 审核人
+   */
+  reviewBy?: string;
+
     /**
      * 日期范围参数
      */
     params?: any;
 }
 
+/**
+ * 产品池审核表单类型
+ */
+export interface PoolReviewForm {
+  /**
+   * 主键ID
+   */
+  id?: string | number;
+
+  /**
+   * 池名称(显示用)
+   */
+  name?: string;
+
+  /**
+   * 产品审核状态 0=待提交,1=待审核,2=审核通过,3=审核驳回
+   */
+  productReviewStatus?: string;
+
+  /**
+   * 审核原因
+   */
+  reviewReason?: string;
+}
+
 
 

+ 20 - 0
src/api/product/poolLink/index.ts

@@ -61,3 +61,23 @@ export const delPoolLink = (id: string | number | Array<string | number>) => {
     method: 'delete'
   });
 };
+
+/**
+ * 批量添加商品到商品池
+ * @param data 批量添加参数
+ */
+export interface BatchAddProductData {
+  poolId: string | number;
+  products: Array<{
+    productId: string | number;
+    agreementPrice?: number;
+  }>;
+}
+
+export const batchAddProducts = (data: BatchAddProductData) => {
+  return request({
+    url: '/product/poolLink/batchAdd',
+    method: 'post',
+    data: data
+  });
+};

+ 110 - 4
src/api/product/poolLink/types.ts

@@ -44,6 +44,80 @@ export interface PoolLinkVO {
    */
   remark: string;
 
+  /**
+   * 创建时间
+   */
+  createTime?: string;
+
+  /**
+   * 商品编号
+   */
+  productNo?: string;
+
+  /**
+   * 商品名称
+   */
+  itemName?: string;
+
+  /**
+   * 商品图片URL
+   */
+  productImageUrl?: string;
+
+  /**
+   * 品牌名称
+   */
+  brandName?: string;
+
+  /**
+   * 分类名称
+   */
+  categoryName?: string;
+
+  /**
+   * 单位名称
+   */
+  unitName?: string;
+
+  /**
+   * 库存
+   */
+  stock?: number;
+
+  /**
+   * 市场价
+   */
+  marketPrice?: number;
+
+  /**
+   * 平台售价
+   */
+  platformPrice?: number;
+
+  /**
+   * 最低售价
+   */
+  minPrice?: number;
+
+  /**
+   * 采购价
+   */
+  purchasePrice?: number;
+
+  /**
+   * 暂估毛利率
+   */
+  grossMargin?: number;
+
+  /**
+   * 商品状态 1=上架,0=下架
+   */
+  productStatus?: string;
+
+  /**
+   * 供应商
+   */
+  supplier?: string;
 }
 
 export interface PoolLinkForm extends BaseEntity {
@@ -136,10 +210,42 @@ export interface PoolLinkQuery extends PageQuery {
    */
   platformCode?: string;
 
-    /**
-     * 日期范围参数
-     */
-    params?: any;
+  // ========== 查询扩展字段 ==========
+
+  /**
+   * 商品编号
+   */
+  productNo?: string;
+
+  /**
+   * 商品名称
+   */
+  itemName?: string;
+
+  /**
+   * 品牌ID
+   */
+  brandId?: string | number;
+
+  /**
+   * 分类ID
+   */
+  categoryId?: string | number;
+
+  /**
+   * 商品状态 1=上架,0=下架
+   */
+  productStatus?: string;
+
+  /**
+   * 供应商
+   */
+  supplier?: string;
+
+  /**
+   * 日期范围参数
+   */
+  params?: any;
 }
 
 

+ 30 - 0
src/router/index.ts

@@ -106,6 +106,36 @@ export const constantRoutes: RouteRecordRaw[] = [
         component: () => import('@/views/product/base/add.vue'),
         name: 'ProductEdit',
         meta: { title: '编辑商品', activeMenu: '/product/base' }
+      },
+      {
+        path: 'brand/edit',
+        component: () => import('@/views/product/brand/edit.vue'),
+        name: 'BrandEdit',
+        meta: { title: '品牌编辑', activeMenu: '/product/brand' }
+      },
+      {
+        path: 'attributes/edit',
+        component: () => import('@/views/product/attributes/edit.vue'),
+        name: 'AttributesEdit',
+        meta: { title: '属性编辑', activeMenu: '/product/attributes' }
+      },
+      {
+        path: 'poolLink',
+        component: () => import('@/views/product/poolLink/index.vue'),
+        name: 'PoolLink',
+        meta: { title: '商品池管理', activeMenu: '/product/pool' }
+      },
+      {
+        path: 'base/review',
+        component: () => import('@/views/product/base/review.vue'),
+        name: 'BaseReview',
+        meta: { title: '商品审核', activeMenu: '/product/base' }
+      },
+      {
+        path: 'base/detail/:id',
+        component: () => import('@/views/product/base/add.vue'),
+        name: 'BaseDetail',
+        meta: { title: '商品详情', activeMenu: '/product/base' }
       }
     ]
   }

+ 258 - 0
src/views/product/attributes/edit.vue

@@ -0,0 +1,258 @@
+<template>
+  <div class="p-4">
+    <el-card shadow="never">
+      <template #header>
+        <div class="flex justify-between items-center">
+          <span class="text-lg font-bold">{{ pageTitle }}</span>
+          <el-button link type="primary" @click="goBack">返回</el-button>
+        </div>
+      </template>
+
+      <el-form ref="attributesFormRef" :model="form" :rules="rules" label-width="140px" class="max-w-5xl">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="品牌名称" prop="productAttributesName">
+              <el-input v-model="form.productAttributesName" placeholder="请输入品牌名称" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="品牌首字母" prop="productAttributesCode">
+              <el-input v-model="form.productAttributesCode" placeholder="请输入品牌首字母" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="品牌名称(英文)" prop="categoryId">
+              <el-tree-select
+                v-model="form.categoryId"
+                :data="categoryOptions"
+                :props="{ value: 'id', label: 'label', children: 'children' }"
+                check-strictly
+                :render-after-expand="false"
+                clearable
+                placeholder="请选择关联类别(必须选择第三级)"
+              >
+                <template #default="{ node, data }">
+                  <span :style="{ color: !node.isLeaf && node.level < 3 ? '#999' : '' }">
+                    {{ getCategoryFullPath(data.id) }}
+                  </span>
+                </template>
+              </el-tree-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="品牌推荐系数" prop="recommendValue">
+              <el-input-number 
+                v-model="extendFormData.recommendValue" 
+                :min="0" 
+                :max="9999" 
+                controls-position="right" 
+                style="width: 100%" 
+                placeholder="请输入推荐系数"
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-form-item label="品牌标题" prop="brandTitle">
+          <el-input v-model="extendFormData.brandTitle" placeholder="请输入内容" />
+        </el-form-item>
+
+        <el-form-item label="品牌LOGO" prop="brandLogo">
+          <image-upload v-model="extendFormData.brandLogo" />
+        </el-form-item>
+
+        <el-form-item label="品牌故事" prop="brandStory">
+          <el-input v-model="extendFormData.brandStory" type="textarea" :rows="3" placeholder="请输入内容" />
+        </el-form-item>
+
+        <el-form-item label="是否显示" prop="isFilter">
+          <el-radio-group v-model="form.isFilter">
+            <el-radio :value="1">是</el-radio>
+            <el-radio :value="0">否</el-radio>
+          </el-radio-group>
+          <div class="text-gray-500 text-sm mt-2">
+            当品牌下还没有商品的时候。分类的品牌区不会不显示品牌。
+          </div>
+        </el-form-item>
+
+        <el-form-item label="品牌介绍" prop="brandDescribe">
+          <editor v-model="extendFormData.brandDescribe" :min-height="192" />
+        </el-form-item>
+
+        <el-form-item>
+          <el-button type="primary" :loading="buttonLoading" @click="submitForm">保存</el-button>
+          <el-button @click="goBack">取消</el-button>
+        </el-form-item>
+      </el-form>
+    </el-card>
+  </div>
+</template>
+
+<script setup lang="ts" name="AttributesEdit">
+import { getAttributes, addAttributes, updateAttributes } from '@/api/product/attributes';
+import { AttributesForm } from '@/api/product/attributes/types';
+import { categoryTree } from '@/api/product/base';
+import { categoryTreeVO } from '@/api/product/category/types';
+
+const route = useRoute();
+const router = useRouter();
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const attributesFormRef = ref<ElFormInstance>();
+const buttonLoading = ref(false);
+const categoryOptions = ref<categoryTreeVO[]>([]);
+
+const pageTitle = computed(() => {
+  return route.query.type === 'add' ? '新增属性' : '编辑属性';
+});
+
+const initFormData: AttributesForm = {
+  id: undefined,
+  categoryId: undefined,
+  productAttributesCode: undefined,
+  productAttributesName: undefined,
+  isOptional: 1,
+  entryMethod: '1',
+  isFilter: 1,
+  attributesList: undefined,
+  required: 0,
+  remark: undefined,
+}
+
+// 扩展表单字段(用于品牌风格的表单)
+const extendFormData = reactive({
+  brandTitle: undefined as string | undefined,
+  brandLogo: undefined as string | undefined,
+  brandStory: undefined as string | undefined,
+  brandDescribe: undefined as string | undefined,
+  recommendValue: undefined as number | undefined,
+})
+
+const form = ref<AttributesForm>({ ...initFormData });
+
+const rules = reactive({
+  categoryId: [
+    { required: true, message: "商品类别不能为空", trigger: "change" },
+    { 
+      validator: (rule: any, value: any, callback: any) => {
+        if (!value) {
+          callback();
+          return;
+        }
+        // 检查是否为第三级类别
+        const isThirdLevel = checkIsThirdLevel(value);
+        if (!isThirdLevel) {
+          callback(new Error('请选择第三级类别'));
+        } else {
+          callback();
+        }
+      },
+      trigger: "change"
+    }
+  ],
+  productAttributesCode: [
+    { required: true, message: "品牌首字母不能为空", trigger: "blur" }
+  ],
+  productAttributesName: [
+    { required: true, message: "品牌名称不能为空", trigger: "blur" }
+  ],
+  brandTitle: [
+    { required: false, message: "品牌标题不能为空", trigger: "blur" }
+  ],
+  recommendValue: [
+    { required: false, message: "推荐系数不能为空", trigger: "blur" }
+  ]
+});
+
+/** 检查是否为第三级类别 */
+const checkIsThirdLevel = (categoryId: string | number): boolean => {
+  const findLevel = (nodes: categoryTreeVO[], targetId: string | number, level: number = 1): number | null => {
+    for (const node of nodes) {
+      if (node.id === targetId) {
+        return level;
+      }
+      if (node.children && node.children.length > 0) {
+        const result = findLevel(node.children, targetId, level + 1);
+        if (result !== null) return result;
+      }
+    }
+    return null;
+  };
+
+  const level = findLevel(categoryOptions.value, categoryId);
+  return level === 3;
+};
+
+/** 获取分类完整路径 */
+const getCategoryFullPath = (categoryId: string | number): string => {
+  const findPath = (nodes: categoryTreeVO[], targetId: string | number, path: string[] = []): string[] | null => {
+    for (const node of nodes) {
+      const currentPath = [...path, node.label];
+      if (node.id === targetId) {
+        return currentPath;
+      }
+      if (node.children && node.children.length > 0) {
+        const result = findPath(node.children, targetId, currentPath);
+        if (result) return result;
+      }
+    }
+    return null;
+  };
+
+  const pathArray = findPath(categoryOptions.value, categoryId);
+  return pathArray ? pathArray.join(' / ') : '';
+}
+
+/** 查询分类树 */
+const getCategoryTree = async () => {
+  const res = await categoryTree();
+  categoryOptions.value = res.data || [];
+}
+
+/** 获取属性详情 */
+const getDetail = async () => {
+  const id = route.query.id as string;
+  if (id && route.query.type !== 'add') {
+    const res = await getAttributes(id);
+    Object.assign(form.value, {
+      ...res.data,
+      isOptional: typeof res.data.isOptional === 'string' ? Number(res.data.isOptional) : res.data.isOptional,
+      isFilter: typeof res.data.isFilter === 'string' ? Number(res.data.isFilter) : res.data.isFilter,
+      required: typeof res.data.required === 'string' ? Number(res.data.required) : res.data.required,
+    });
+  }
+};
+
+/** 提交表单 */
+const submitForm = () => {
+  attributesFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      try {
+        if (form.value.id) {
+          await updateAttributes(form.value);
+        } else {
+          await addAttributes(form.value);
+        }
+        proxy?.$modal.msgSuccess("操作成功");
+        goBack();
+      } finally {
+        buttonLoading.value = false;
+      }
+    }
+  });
+};
+
+/** 返回 */
+const goBack = () => {
+  router.back();
+};
+
+onMounted(() => {
+  getCategoryTree();
+  getDetail();
+});
+</script>

+ 209 - 87
src/views/product/attributes/index.vue

@@ -4,32 +4,26 @@
       <div v-show="showSearch" class="mb-[10px]">
         <el-card shadow="hover">
           <el-form ref="queryFormRef" :model="queryParams" :inline="true">
-            <el-form-item label="关联的产品分类id" prop="categoryId">
-              <el-input v-model="queryParams.categoryId" placeholder="请输入关联的产品分类id" clearable @keyup.enter="handleQuery" />
+            <el-form-item label="属性名称" prop="productAttributesName">
+              <el-input v-model="queryParams.productAttributesName" placeholder="请输入属性名称" clearable @keyup.enter="handleQuery" />
             </el-form-item>
-            <el-form-item label="属性编码" prop="productAttributesCode">
-              <el-input v-model="queryParams.productAttributesCode" placeholder="请输入属性编码" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="属性显示名称" prop="productAttributesName">
-              <el-input v-model="queryParams.productAttributesName" placeholder="请输入属性显示名称" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="是否可选:1=是,0=否" prop="isOptional">
-              <el-input v-model="queryParams.isOptional" placeholder="请输入是否可选:1=是,0=否" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="属性录入方式" prop="entryMethod">
-              <el-input v-model="queryParams.entryMethod" placeholder="请输入属性录入方式" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="是否用于商品筛选:1=是,0=否" prop="isFilter">
-              <el-input v-model="queryParams.isFilter" placeholder="请输入是否用于商品筛选:1=是,0=否" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="预定义属性值列表" prop="attributesList">
-              <el-input v-model="queryParams.attributesList" placeholder="请输入预定义属性值列表" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="是否必填: 1=是, 0=否" prop="required">
-              <el-input v-model="queryParams.required" placeholder="请输入是否必填: 1=是, 0=否" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="平台标识" prop="platformCode">
-              <el-input v-model="queryParams.platformCode" placeholder="请输入平台标识" clearable @keyup.enter="handleQuery" />
+            <el-form-item label="商品类别" prop="categoryId">
+              <el-tree-select
+                v-model="queryParams.categoryId"
+                :data="categoryOptions"
+                :props="{ value: 'id', label: 'label', children: 'children' }"
+                check-strictly
+                :render-after-expand="false"
+                show-checkbox
+                :check-on-click-node="true"
+                node-key="id"
+                clearable
+                placeholder="请选择商品类别"
+              >
+                <template #default="{ data }">
+                  <span>{{ getCategoryFullPath(data.id) }}</span>
+                </template>
+              </el-tree-select>
             </el-form-item>
             <el-form-item>
               <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
@@ -52,28 +46,51 @@
           <el-col :span="1.5">
             <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['product:attributes:remove']">删除</el-button>
           </el-col>
-          <el-col :span="1.5">
+          <!-- <el-col :span="1.5">
             <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['product:attributes:export']">导出</el-button>
-          </el-col>
+          </el-col> -->
           <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
         </el-row>
       </template>
 
       <el-table v-loading="loading" border :data="attributesList" @selection-change="handleSelectionChange">
         <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="主键,自增ID" align="center" prop="id" v-if="true" />
-        <el-table-column label="关联的产品分类id" align="center" prop="categoryId" />
-        <el-table-column label="属性编码" align="center" prop="productAttributesCode" />
-        <el-table-column label="属性显示名称" align="center" prop="productAttributesName" />
-        <el-table-column label="是否可选:1=是,0=否" align="center" prop="isOptional" />
-        <el-table-column label="属性录入方式" align="center" prop="entryMethod" />
-        <el-table-column label="是否用于商品筛选:1=是,0=否" align="center" prop="isFilter" />
-        <el-table-column label="预定义属性值列表" align="center" prop="attributesList" />
-        <el-table-column label="是否必填: 1=是, 0=否" align="center" prop="required" />
-        <el-table-column label="备注" align="center" prop="remark" />
-        <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <el-table-column label="编号" align="center" prop="id" width="120" />
+        <el-table-column label="属性名称" align="center" prop="productAttributesName" />
+        <el-table-column label="商品类别" align="center" prop="categoryId" min-width="200">
+          <template #default="scope">
+            <span>{{ getCategoryFullPath(scope.row.categoryId) }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="属性是否可选" align="center" prop="isOptional" width="120">
+          <template #default="scope">
+            <dict-tag :options="[
+              { label: '是', value: '1' },
+              { label: '否', value: '0' }
+            ]" :value="String(scope.row.isOptional)" />
+          </template>
+        </el-table-column>
+        <el-table-column label="属性值的输入方式" align="center" prop="entryMethod" width="140">
           <template #default="scope">
-            <el-tooltip content="修改" placement="top">
+            <dict-tag :options="[
+              { label: '唯一属性', value: '1' },
+              { label: '单选属性', value: '0' },
+              { label: '复选属性', value: '2' }
+            ]" :value="String(scope.row.entryMethod)" />
+          </template>
+        </el-table-column>
+        <el-table-column label="可选值列表" align="center" prop="attributesList" show-overflow-tooltip />
+        <el-table-column label="是否必填" align="center" prop="required" width="100">
+          <template #default="scope">
+            <dict-tag :options="[
+              { label: '是', value: '1' },
+              { label: '否', value: '0' }
+            ]" :value="String(scope.row.required)" />
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="150">
+          <template #default="scope">
+            <el-tooltip content="编辑" placement="top">
               <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['product:attributes:edit']"></el-button>
             </el-tooltip>
             <el-tooltip content="删除" placement="top">
@@ -85,35 +102,64 @@
 
       <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
     </el-card>
-    <!-- 添加或修改产品属性定义(用于动态属性配置)对话框 -->
-    <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
-      <el-form ref="attributesFormRef" :model="form" :rules="rules" label-width="80px">
-        <el-form-item label="关联的产品分类id" prop="categoryId">
-          <el-input v-model="form.categoryId" placeholder="请输入关联的产品分类id" />
-        </el-form-item>
-        <el-form-item label="属性编码" prop="productAttributesCode">
-          <el-input v-model="form.productAttributesCode" placeholder="请输入属性编码" />
-        </el-form-item>
-        <el-form-item label="属性显示名称" prop="productAttributesName">
-          <el-input v-model="form.productAttributesName" placeholder="请输入属性显示名称" />
+   <!-- 添加或修改产品属性定义(用于动态属性配置)对话框 -->
+    <el-dialog :title="dialog.title" v-model="dialog.visible" width="650px" append-to-body>
+      <el-form ref="attributesFormRef" :model="form" :rules="rules" label-width="100px">
+        <el-form-item label="属性名称" prop="productAttributesName">
+          <el-input v-model="form.productAttributesName" placeholder="请输入属性名称" />
         </el-form-item>
-        <el-form-item label="是否可选:1=是,0=否" prop="isOptional">
-          <el-input v-model="form.isOptional" placeholder="请输入是否可选:1=是,0=否" />
+        <el-form-item label="关联类别" prop="categoryId">
+          <el-tree-select
+            v-model="form.categoryId"
+            :data="categoryOptions"
+            :props="{ value: 'id', label: 'label', children: 'children' }"
+            check-strictly
+            :render-after-expand="false"
+            clearable
+            placeholder="请选择关联类别(必须选择第三级)"
+          >
+            <template #default="{ node, data }">
+              <span :style="{ color: !node.isLeaf && node.level < 3 ? '#999' : '' }">
+                {{ getCategoryFullPath(data.id) }}
+              </span>
+            </template>
+          </el-tree-select>
         </el-form-item>
-        <el-form-item label="属性录入方式" prop="entryMethod">
-          <el-input v-model="form.entryMethod" placeholder="请输入属性录入方式" />
+        <el-form-item label="是否可选" prop="isOptional">
+          <el-radio-group v-model="form.isOptional">
+            <el-radio :value="1">唯一属性</el-radio>
+            <el-radio :value="0">单选属性</el-radio>
+            <el-radio :value="2">复选属性</el-radio>
+          </el-radio-group>
+          <div class="text-gray-500 text-sm mt-2">
+            选择"单选/复选属性"时,可以对商品属性设置多个值,同时还能对不同属性值设定不同的价格,用户购买商品时需要选择具体的属性值后才能加入购物车。用户购买商品时需要先选择具体的属性值。选择"唯一属性"时,商品的该属性值只能是唯一一个值,用户只能查看具体的属性值。
+          </div>
         </el-form-item>
-        <el-form-item label="是否用于商品筛选:1=是,0=否" prop="isFilter">
-          <el-input v-model="form.isFilter" placeholder="请输入是否用于商品筛选:1=是,0=否" />
+        <el-form-item label="录入方式" prop="entryMethod">
+          <el-radio-group v-model="form.entryMethod">
+            <el-radio value="1">手工录入</el-radio>
+            <el-radio value="2">从下面的列表中选择</el-radio>
+          </el-radio-group>
         </el-form-item>
-        <el-form-item label="预定义属性值列表" prop="attributesList">
-          <el-input v-model="form.attributesList" placeholder="请输入预定义属性值列表" />
+        <el-form-item label="加入筛选" prop="isFilter">
+          <el-radio-group v-model="form.isFilter">
+            <el-radio :value="1">是</el-radio>
+            <el-radio :value="0">否</el-radio>
+          </el-radio-group>
         </el-form-item>
-        <el-form-item label="是否必填: 1=是, 0=否" prop="required">
-          <el-input v-model="form.required" placeholder="请输入是否必填: 1=是, 0=否" />
+        <el-form-item label="是否必填" prop="required">
+          <el-radio-group v-model="form.required">
+            <el-radio :value="1">是</el-radio>
+            <el-radio :value="0">否</el-radio>
+          </el-radio-group>
         </el-form-item>
-        <el-form-item label="备注" prop="remark">
-            <el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
+        <el-form-item label="可选值列表" prop="attributesList" v-if="form.entryMethod === 'select'">
+          <el-input 
+            v-model="form.attributesList" 
+            type="textarea" 
+            :rows="4" 
+            placeholder="请输入属性值,各属性值请用逗号隔开并来示意。例如:A4,70g,100g" 
+          />
         </el-form-item>
       </el-form>
       <template #footer>
@@ -129,8 +175,11 @@
 <script setup name="Attributes" lang="ts">
 import { listAttributes, getAttributes, delAttributes, addAttributes, updateAttributes } from '@/api/product/attributes';
 import { AttributesVO, AttributesQuery, AttributesForm } from '@/api/product/attributes/types';
+import { categoryTree } from '@/api/product/base';
+import { categoryTreeVO } from '@/api/product/category/types';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const router = useRouter();
 
 const attributesList = ref<AttributesVO[]>([]);
 const buttonLoading = ref(false);
@@ -140,6 +189,7 @@ const ids = ref<Array<string | number>>([]);
 const single = ref(true);
 const multiple = ref(true);
 const total = ref(0);
+const categoryOptions = ref<categoryTreeVO[]>([]);
 
 const queryFormRef = ref<ElFormInstance>();
 const attributesFormRef = ref<ElFormInstance>();
@@ -154,11 +204,11 @@ const initFormData: AttributesForm = {
   categoryId: undefined,
   productAttributesCode: undefined,
   productAttributesName: undefined,
-  isOptional: undefined,
-  entryMethod: undefined,
-  isFilter: undefined,
+  isOptional: 1,
+  entryMethod: '1',
+  isFilter: 1,
   attributesList: undefined,
-  required: undefined,
+  required: 0,
   remark: undefined,
 }
 const data = reactive<PageData<AttributesForm, AttributesQuery>>({
@@ -180,23 +230,30 @@ const data = reactive<PageData<AttributesForm, AttributesQuery>>({
   },
   rules: {
     categoryId: [
-      { required: true, message: "关联的产品分类id不能为空", trigger: "blur" }
+      { required: true, message: "商品类别不能为空", trigger: "change" },
+      { 
+        validator: (rule: any, value: any, callback: any) => {
+          if (!value) {
+            callback();
+            return;
+          }
+          // 检查是否为第三级类别
+          const isThirdLevel = checkIsThirdLevel(value);
+          if (!isThirdLevel) {
+            callback(new Error('请选择第三级类别'));
+          } else {
+            callback();
+          }
+        },
+        trigger: "change"
+      }
     ],
     productAttributesCode: [
       { required: true, message: "属性编码不能为空", trigger: "blur" }
     ],
     productAttributesName: [
-      { required: true, message: "属性显示名称不能为空", trigger: "blur" }
-    ],
-    attributesList: [
-      { required: true, message: "预定义属性值列表不能为空", trigger: "blur" }
-    ],
-    required: [
-      { required: true, message: "是否必填: 1=是, 0=否不能为空", trigger: "blur" }
-    ],
-    remark: [
-      { required: true, message: "备注不能为空", trigger: "blur" }
-    ],
+      { required: true, message: "属性名称不能为空", trigger: "blur" }
+    ]
   }
 });
 
@@ -211,6 +268,65 @@ const getList = async () => {
   loading.value = false;
 }
 
+/** 查询分类树 */
+const getCategoryTree = async () => {
+  const res = await categoryTree();
+  categoryOptions.value = res.data || [];
+  // 设置默认选中第一项
+  if (categoryOptions.value.length > 0) {
+    setDefaultCategory();
+  }
+}
+
+/** 设置默认类别(选择第一个一级分类) */
+const setDefaultCategory = () => {
+  if (categoryOptions.value.length > 0) {
+    const firstCategory = categoryOptions.value[0];
+    if (!form.value.id && !form.value.categoryId) {
+      form.value.categoryId = firstCategory.id;
+    }
+  }
+}
+
+/** 检查是否为第三级类别 */
+const checkIsThirdLevel = (categoryId: string | number): boolean => {
+  const findLevel = (nodes: categoryTreeVO[], targetId: string | number, level: number = 1): number | null => {
+    for (const node of nodes) {
+      if (node.id === targetId) {
+        return level;
+      }
+      if (node.children && node.children.length > 0) {
+        const result = findLevel(node.children, targetId, level + 1);
+        if (result !== null) return result;
+      }
+    }
+    return null;
+  };
+
+  const level = findLevel(categoryOptions.value, categoryId);
+  return level === 3;
+};
+
+/** 获取分类完整路径 */
+const getCategoryFullPath = (categoryId: string | number): string => {
+  const findPath = (nodes: categoryTreeVO[], targetId: string | number, path: string[] = []): string[] | null => {
+    for (const node of nodes) {
+      const currentPath = [...path, node.label];
+      if (node.id === targetId) {
+        return currentPath;
+      }
+      if (node.children && node.children.length > 0) {
+        const result = findPath(node.children, targetId, currentPath);
+        if (result) return result;
+      }
+    }
+    return null;
+  };
+
+  const pathArray = findPath(categoryOptions.value, categoryId);
+  return pathArray ? pathArray.join(' / ') : '';
+}
+
 /** 取消按钮 */
 const cancel = () => {
   reset();
@@ -244,19 +360,24 @@ const handleSelectionChange = (selection: AttributesVO[]) => {
 
 /** 新增按钮操作 */
 const handleAdd = () => {
-  reset();
-  dialog.visible = true;
-  dialog.title = "添加产品属性定义(用于动态属性配置)";
+  router.push({
+    path: '/product/attributes/edit',
+    query: {
+      type: 'add'
+    }
+  });
 }
 
 /** 修改按钮操作 */
 const handleUpdate = async (row?: AttributesVO) => {
-  reset();
-  const _id = row?.id || ids.value[0]
-  const res = await getAttributes(_id);
-  Object.assign(form.value, res.data);
-  dialog.visible = true;
-  dialog.title = "修改产品属性定义(用于动态属性配置)";
+  const _id = row?.id || ids.value[0];
+  router.push({
+    path: '/product/attributes/edit',
+    query: {
+      id: _id,
+      type: 'edit'
+    }
+  });
 }
 
 /** 提交按钮 */
@@ -294,5 +415,6 @@ const handleExport = () => {
 
 onMounted(() => {
   getList();
+  getCategoryTree();
 });
 </script>

+ 165 - 76
src/views/product/base/add.vue

@@ -242,10 +242,17 @@
               </el-col>
             </el-row>
             <el-row :gutter="20">
-              <!-- 品品牌 -->
+              <!-- 品品牌 -->
                <el-col :span="12">
-                <el-form-item label="商品品牌:">
-                  <el-input v-model="productForm.productBrand" placeholder="请输入商品品牌" />
+                <el-form-item label="商品品牌:" prop="brandId" required>
+                  <el-select v-model="productForm.brandId" placeholder="请选择商品品牌" clearable class="w-full">
+                    <el-option
+                      v-for="option in brandOptions"
+                      :key="option.id"
+                      :label="option.brandName"
+                      :value="option.id"
+                    />
+                  </el-select>
                 </el-form-item>
                </el-col>
 
@@ -397,12 +404,6 @@
                 <el-checkbox label="免费安装" value="freeInstallation" />
               </el-checkbox-group>
             </el-form-item>
-            <!-- 销量人气 -->
-            <el-form-item label="销量人气:">
-              <el-checkbox-group v-model="salesVolume">
-                <el-input v-model="productForm.salesVolume" placeholder="请输入销量人气" />
-              </el-checkbox-group>
-            </el-form-item>
           </el-form>
         </el-card>
 
@@ -595,18 +596,21 @@
           <el-form ref="detailFormRef" :model="productForm" label-width="120px" class="product-info-form">
             <!-- 商品主图 -->
             <el-form-item label="商品主图:">
-              <el-upload
-                class="avatar-uploader"
-                action="#"
-                :show-file-list="false"
-                :auto-upload="false"
-                :on-change="handleMainImageChange"
-              >
-                <img v-if="productForm.mainImage" :src="productForm.mainImage" class="avatar" />
-                <el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
-              </el-upload>
+              <div class="image-upload-container">
+                <div v-if="productForm.productImage" class="image-preview">
+                  <img :src="productForm.productImage" class="preview-image" />
+                  <div class="image-actions">
+                    <el-button size="small" @click="openMainImageSelector">重新选择</el-button>
+                    <el-button size="small" type="danger" @click="clearMainImage">删除</el-button>
+                  </div>
+                </div>
+                <div v-else class="image-upload-placeholder" @click="openMainImageSelector">
+                  <el-icon class="upload-icon"><Plus /></el-icon>
+                  <div class="upload-text">点击选择图片</div>
+                </div>
+              </div>
               <div class="form-item-tip">
-                从图片库选择 按住ctrl可同时选择多张图片上传,最多可上传图片数量,建议尺寸300*300px
+                从图片库选择,建议尺寸300*300px
               </div>
             </el-form-item>
 
@@ -723,36 +727,41 @@
         </el-card>
 
         <!-- 步骤3: 完成 -->
-        <el-card v-show="currentStep === 2" shadow="never" class="step-card">
-          <el-result icon="success" title="商品信息填写完成" sub-title="请确认以下信息无误后提交">
-            <template #extra>
-              <div class="confirm-info">
-                <el-descriptions :column="2" border>
-                  <el-descriptions-item label="一级分类">{{ selectedLevel1Name }}</el-descriptions-item>
-                  <el-descriptions-item label="二级分类">{{ selectedLevel2Name }}</el-descriptions-item>
-                  <el-descriptions-item label="三级分类">{{ selectedLevel3Name }}</el-descriptions-item>
-                  <el-descriptions-item label="商品编号">{{ productForm.productNo }}</el-descriptions-item>
-                  <el-descriptions-item label="商品名称">{{ productForm.itemName }}</el-descriptions-item>
-                  <el-descriptions-item label="是否自营">{{ productForm.isSelf === '1' ? '是' : '否' }}</el-descriptions-item>
-                  <el-descriptions-item label="商品状态">{{ productForm.productStatus === '1' ? '上架' : '下架' }}</el-descriptions-item>
-                  <el-descriptions-item label="是否热门">{{ productForm.isPopular === '1' ? '是' : '否' }}</el-descriptions-item>
-                </el-descriptions>
-              </div>
-            </template>
-          </el-result>
+        <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 shadow="never" class="mt-3">
+      <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 v-if="currentStep === 2" type="primary" :loading="submitLoading" @click="handleSubmit">提交</el-button>
           <el-button @click="handleBack">取消</el-button>
         </div>
       </el-card>
     </div>
+
+    <!-- 文件选择器组件 -->
+    <FileSelector
+      v-model="mainImageSelectorVisible"
+      :allowed-types="[1]"
+      :multiple="false"
+      title="选择商品主图"
+      @confirm="handleMainImageSelected"
+    />
   </div>
 </template>
 
@@ -760,8 +769,9 @@
 import { ref, reactive, computed, onMounted, watch } from 'vue';
 import { useRoute, useRouter } from 'vue-router';
 import { ElMessage } from 'element-plus';
-import { Warning, ArrowRight, Check, Plus } from '@element-plus/icons-vue';
+import { Warning, ArrowRight, Check, Plus, CircleCheck } from '@element-plus/icons-vue';
 import Editor from '@/components/Editor/index.vue';
+import FileSelector from '@/components/FileSelector/index.vue';
 import { categoryTreeVO } from '@/api/product/category/types';
 import { BrandVO } from '@/api/product/brand/types';
 import { BaseForm } from '@/api/product/base/types';
@@ -783,6 +793,9 @@ const installationServices = ref<string[]>([]);
 // 商品详情选项卡
 const activeDetailTab = ref('pc');
 
+// 文件选择器相关
+const mainImageSelectorVisible = ref(false);
+
 // 定制说明表单
 const customForm = reactive({
   customizable: false,
@@ -928,14 +941,11 @@ const productForm = reactive<BaseForm>({
   isNew: '0',
   productStatus: '0',
   remark: undefined,
-  styleNo: undefined,
   a10ProductName: undefined,
   specification: undefined,
   upcBarcode: undefined,
   invoiceName: undefined,
   invoiceSpec: undefined,
-  productBrand: undefined,
-  sectionNo: undefined,
   packagingSpec: undefined,
   referenceLink: undefined,
   weight: undefined,
@@ -949,14 +959,10 @@ const productForm = reactive<BaseForm>({
   midRangePrice: undefined,
   standardPrice: undefined,
   certificatePrice: undefined,
-  priceVerificationQuantity: undefined,
   purchasePrice: undefined,
   estimatedPurchasePrice: undefined,
   productNature: '1',
   purchasingPersonnel: '1',
-  oldAttributeType: undefined,
-  entrySetCount: undefined,
-  mainImage: undefined,
   pcDetail: undefined,
   mobileDetail: undefined,
   taxRate: undefined,
@@ -1123,17 +1129,19 @@ const nextStep = async () => {
     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;
     }
   }
-
-  currentStep.value++;
 };
 
 // 上一步
@@ -1153,6 +1161,8 @@ const handleSubmit = async () => {
       ...productForm,
       // 将服务保障ID数组转换为逗号分隔字符串
       serviceGuarantee: serviceGuarantees.value.map(id => String(id)).join(','),
+      // 将商品属性值转换为JSON字符串
+      attributesList: JSON.stringify(productAttributesValues.value),
       customizable: customForm.customizable,
       customizedStyle: customForm.selectedMethods.join(','),
       customizedCraft: customForm.selectedCrafts.join(','),
@@ -1167,7 +1177,8 @@ const handleSubmit = async () => {
       await addBase(submitData);
       ElMessage.success('新增成功');
     }
-    router.push('/product/base');
+    // 跳转到完成页面(步骤3)
+    currentStep.value = 2;
   } catch (error) {
     console.error('提交失败:', error);
   } finally {
@@ -1180,13 +1191,26 @@ const handleBack = () => {
   router.back();
 };
 
-// 处理主图上传
-const handleMainImageChange = (file: any) => {
-  const reader = new FileReader();
-  reader.onload = (e) => {
-    productForm.mainImage = e.target?.result as string;
-  };
-  reader.readAsDataURL(file.raw);
+// 返回列表
+const handleBackToList = () => {
+  router.push('/product/base');
+};
+
+// 打开主图选择器
+const openMainImageSelector = () => {
+  mainImageSelectorVisible.value = true;
+};
+
+// 处理主图选择
+const handleMainImageSelected = (files: any[]) => {
+  if (files && files.length > 0) {
+    productForm.productImage = files[0].url;
+  }
+};
+
+// 清除主图
+const clearMainImage = () => {
+  productForm.productImage = undefined;
 };
 
 // 获取分类树
@@ -1204,6 +1228,10 @@ const getBrandList = async () => {
   try {
     const res = await brandList();
     brandOptions.value = res.data || [];
+    // 如果是新增模式且有选项,设置第一个为默认值
+    if (!route.params.id && brandOptions.value.length > 0 && !productForm.brandId) {
+      productForm.brandId = brandOptions.value[0].id;
+    }
   } catch (error) {
     console.error('获取品牌列表失败:', error);
   }
@@ -1329,6 +1357,17 @@ const loadProductDetail = async () => {
       if (res.data.freeInstallation === '1') services.push('freeInstallation');
       installationServices.value = services;
 
+      // 回显商品属性值
+      if (res.data.attributesList) {
+        try {
+          const parsedAttributes = JSON.parse(res.data.attributesList);
+          productAttributesValues.value = parsedAttributes;
+        } catch (e) {
+          console.error('解析商品属性失败:', e);
+          productAttributesValues.value = {};
+        }
+      }
+
       // 回显分类名称
       if (categoryForm.topCategoryId) {
         const level1 = level1Categories.value.find(item => item.id === categoryForm.topCategoryId);
@@ -1453,34 +1492,56 @@ onMounted(async () => {
     }
   }
 
-  .avatar-uploader {
-    :deep(.el-upload) {
+  .image-upload-container {
+    width: 178px;
+
+    .image-preview {
+      position: relative;
+      
+      .preview-image {
+        width: 178px;
+        height: 178px;
+        display: block;
+        object-fit: cover;
+        border-radius: 6px;
+        border: 1px solid #dcdfe6;
+      }
+
+      .image-actions {
+        margin-top: 8px;
+        display: flex;
+        gap: 8px;
+      }
+    }
+
+    .image-upload-placeholder {
+      width: 178px;
+      height: 178px;
       border: 1px dashed #d9d9d9;
       border-radius: 6px;
       cursor: pointer;
-      position: relative;
-      overflow: hidden;
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      justify-content: center;
       transition: all 0.3s;
+      background-color: #fafafa;
 
       &:hover {
         border-color: #409eff;
+        background-color: #f5f7fa;
       }
-    }
 
-    .avatar {
-      width: 178px;
-      height: 178px;
-      display: block;
-      object-fit: cover;
-    }
+      .upload-icon {
+        font-size: 28px;
+        color: #8c939d;
+        margin-bottom: 8px;
+      }
 
-    .avatar-uploader-icon {
-      font-size: 28px;
-      color: #8c939d;
-      width: 178px;
-      height: 178px;
-      text-align: center;
-      line-height: 178px;
+      .upload-text {
+        color: #8c939d;
+        font-size: 14px;
+      }
     }
   }
 
@@ -1494,5 +1555,33 @@ onMounted(async () => {
     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>

+ 53 - 21
src/views/product/base/index.vue

@@ -58,9 +58,11 @@
               <el-col :span="6">
                 <el-form-item label="审核状态" prop="productReviewStatus">
                   <el-select v-model="queryParams.productReviewStatus" placeholder="请选择" clearable>
-                    <el-option label="待审核" :value="1" />
-                    <el-option label="审核通过" :value="2" />
-                    <el-option label="审核拒绝" :value="3" />
+                    <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>
@@ -94,6 +96,7 @@
         <span class="mx-2">,审核状态({{ statistics.reviewStatus }}),上架状态({{ statistics.shelfStatus }})</span>
         <div class="ml-auto flex gap-2">
           <el-button type="primary" icon="Plus" @click="handleAdd">商品新增</el-button>
+          <el-button type="warning" icon="Check" @click="handleGoReview">商品审核</el-button>
           <el-button plain>批量操作</el-button>
           <el-button plain icon="Download" @click="handleExport">导出</el-button>
           <el-button circle icon="Refresh" @click="getList"></el-button>
@@ -110,9 +113,9 @@
             <el-link type="primary" @click="handleView(scope.row)">{{ scope.row.productNo }}</el-link>
           </template>
         </el-table-column>
-        <el-table-column label="商品图片" align="center" prop="productImageUrl" width="100">
+        <el-table-column label="商品图片" align="center" prop="productImage" width="100">
           <template #default="scope">
-            <image-preview :src="scope.row.productImageUrl" :width="60" :height="60"/>
+            <image-preview :src="scope.row.productImage" :width="60" :height="60"/>
           </template>
         </el-table-column>
         <el-table-column label="商品信息" align="center" width="250" show-overflow-tooltip>
@@ -187,27 +190,38 @@
         </el-table-column>
         <el-table-column label="操作" align="center" width="200" fixed="right">
           <template #default="scope">
-            <div class="flex flex-col gap-1">
-              <div class="flex gap-1 justify-center">
-                <el-link 
-                  :type="scope.row.productStatus === '1' ? 'warning' : 'success'" 
-                  :underline="false" 
-                  @click="handleShelf(scope.row)">
-                  {{ scope.row.isSelf === '1' ? '取消自营' : '加入自营池' }}
-                </el-link>
+            <!-- 待审核状态:只显示编辑 -->
+            <div v-if="scope.row.productReviewStatus !== '2'" 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.productReviewStatus === '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-if="scope.row.productStatus === '0'" class="flex gap-1 justify-center">
+                <el-link type="primary" :underline="false" @click="handleSupply(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="scope.row.productStatus === '1' ? 'warning' : 'success'" 
-                  :underline="false" 
-                  @click="handleShelf(scope.row)">
-                  {{ scope.row.productStatus === '1' ? '下架' : '上架' }}
-                </el-link>
-                <el-link type="primary" :underline="false" @click="handlePrice(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 class="flex gap-1 justify-center">
+              <div v-else-if="scope.row.productStatus === '1'" class="flex gap-1 justify-center">
                 <el-link type="primary" :underline="false" @click="handleSupply(scope.row)">修改库存</el-link>
               </div>
             </div>
+            
+            <!-- 其他状态(待提交、审核驳回等):显示编辑 -->
+            <div v-else class="flex gap-1 justify-center">
+              <el-link type="primary" :underline="false" @click="handleUpdate(scope.row)">编辑</el-link>
+            </div>
           </template>
         </el-table-column>
       </el-table>
@@ -451,6 +465,24 @@ const handleSupply = (row: BaseVO) => {
   // TODO: 打开供货存管理对话框
 }
 
+/** 停售操作 */
+const handleDiscontinue = async (row: BaseVO) => {
+  await proxy?.$modal.confirm('确认停售该商品吗?');
+  // TODO: 调用停售API
+  proxy?.$modal.msgSuccess('停售成功');
+  await getList();
+}
+
+/** 跳转到商品审核页面 */
+const handleGoReview = () => {
+  router.push({
+    path: '/product/base/review',
+    query: {
+      productReviewStatus: '1' // 默认显示待审核的商品
+    }
+  });
+}
+
 /** 查询品牌列表 */
 const getBrandList = async () => {
   const res = await brandList();

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

@@ -0,0 +1,410 @@
+<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="100px">
+            <el-row :gutter="20">
+              <el-col :span="6">
+                <el-form-item label="输入搜索" prop="searchText">
+                  <el-input v-model="queryParams.searchText" 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="请选择商品品牌" clearable filterable>
+                    <el-option 
+                      v-for="brand in brandOptions" 
+                      :key="brand.id" 
+                      :label="brand.brandName" 
+                      :value="brand.id" 
+                    />
+                  </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="mb-[10px]">
+      <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" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="商品图片" align="center" prop="productImageUrl" width="100" fixed="left">
+          <template #default="scope">
+            <image-preview :src="scope.row.productImageUrl" :width="60" :height="60"/>
+          </template>
+        </el-table-column>
+        <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.productNo }}</el-link>
+          </template>
+        </el-table-column>
+        <el-table-column label="商品信息" align="center" 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.0000' }}%</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="warning">待审核</el-tag>
+            <el-tag v-else-if="scope.row.productReviewStatus === '2'" type="success">审核通过</el-tag>
+            <el-tag v-else-if="scope.row.productReviewStatus === '3'" type="danger">审核驳回</el-tag>
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" align="center" width="200" fixed="right">
+          <template #default="scope">
+            <div class="flex flex-col gap-1">
+              <!-- 根据审核状态显示不同按钮 -->
+              <template v-if="scope.row.productReviewStatus === '1'">
+                <!-- 待审核 -->
+                <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="handleReview(scope.row)">审核</el-link>
+                  <el-link type="danger" :underline="false" @click="handleReject(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="success" :underline="false" @click="handleReSubmit(scope.row)">重新提交</el-link>
+                </div>
+              </template>
+              <template v-else>
+                <!-- 其他状态 -->
+                <div class="flex gap-1 justify-center">
+                  <el-link type="primary" :underline="false" @click="handleEdit(scope.row)">编辑</el-link>
+                </div>
+              </template>
+            </div>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination 
+        v-show="total > 0" 
+        :total="total" 
+        v-model:page="queryParams.pageNum" 
+        v-model:limit="queryParams.pageSize" 
+        @pagination="getList" 
+      />
+    </el-card>
+
+    <!-- 审核对话框 -->
+    <el-dialog v-model="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 :label="2">审核通过</el-radio>
+            <el-radio :label="3">审核驳回</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="审核意见" prop="reviewRemark">
+          <el-input 
+            v-model="reviewForm.reviewRemark" 
+            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, updateBase, 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 categoryOptions = ref<categoryTreeVO[]>([]);
+
+const queryFormRef = ref<ElFormInstance>();
+const reviewFormRef = ref<ElFormInstance>();
+
+// 审核对话框
+const reviewDialog = reactive({
+  visible: false,
+  title: '商品审核'
+});
+
+// 审核表单
+const reviewForm = ref<any>({
+  id: undefined,
+  productNo: '',
+  itemName: '',
+  reviewStatus: 2,
+  reviewRemark: ''
+});
+
+// 审核表单验证规则
+const reviewRules = ref({
+  reviewStatus: [
+    { required: true, message: "请选择审核结果", trigger: "change" }
+  ],
+  reviewRemark: [
+    { required: true, message: "请输入审核意见", trigger: "blur" }
+  ]
+});
+
+const queryParams = ref<BaseQuery>({
+  pageNum: 1,
+  pageSize: 10,
+  searchText: undefined,
+  bottomCategoryId: undefined,
+  brandId: undefined,
+  productReviewStatus: '1' // 默认查询待审核状态
+});
+
+/** 查询商品列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listBase(queryParams.value);
+  baseList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+
+/** 初始化路由参数 */
+const initRouteParams = () => {
+  // 从路由参数中获取筛选条件
+  if (route.query.productReviewStatus) {
+    queryParams.value.productReviewStatus = route.query.productReviewStatus as string;
+  }
+  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;
+  getList();
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  // 重置后仍然保持待审核状态
+  queryParams.value.productReviewStatus = '1';
+  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 handleReview = (row: BaseVO) => {
+  reviewDialog.visible = true;
+  reviewDialog.title = '商品审核';
+  reviewForm.value = {
+    id: row.id,
+    productNo: row.productNo,
+    itemName: row.itemName,
+    reviewStatus: 2,
+    reviewRemark: ''
+  };
+}
+
+/** 驳回商品 */
+const handleReject = (row: BaseVO) => {
+  reviewDialog.visible = true;
+  reviewDialog.title = '商品驳回';
+  reviewForm.value = {
+    id: row.id,
+    productNo: row.productNo,
+    itemName: row.itemName,
+    reviewStatus: 3,
+    reviewRemark: ''
+  };
+}
+
+/** 重新提交 */
+const handleReSubmit = async (row: BaseVO) => {
+  await proxy?.$modal.confirm('确认重新提交该商品进行审核吗?');
+  const data: BaseForm = {
+    id: row.id,
+    productReviewStatus: '1' // 设置为待审核状态
+  };
+  await updateBase(data);
+  proxy?.$modal.msgSuccess("重新提交成功");
+  await getList();
+}
+
+/** 提交审核 */
+const submitReview = async () => {
+  await reviewFormRef.value?.validate();
+  const data: BaseForm = {
+    id: reviewForm.value.id,
+    productReviewStatus: String(reviewForm.value.reviewStatus),
+    remark: reviewForm.value.reviewRemark
+  };
+  await updateBase(data);
+  proxy?.$modal.msgSuccess(reviewForm.value.reviewStatus === 2 ? "审核通过" : "审核驳回");
+  reviewDialog.visible = false;
+  await getList();
+}
+
+/** 查询品牌列表 */
+const getBrandList = async () => {
+  const res = await brandList();
+  brandOptions.value = res.data || [];
+}
+
+/** 查询分类树 */
+const getCategoryTree = async () => {
+  const res = await categoryTree();
+  categoryOptions.value = res.data || [];
+}
+
+onMounted(() => {
+  getBrandList();
+  getCategoryTree();
+  initRouteParams();
+  getList();
+});
+</script>
+
+<style scoped lang="scss">
+// 可以添加自定义样式
+</style>

+ 249 - 0
src/views/product/brand/edit.vue

@@ -0,0 +1,249 @@
+<template>
+  <div class="p-4">
+    <div class="mb-4">
+      <el-button link type="primary" @click="goBack">返回</el-button>
+    </div>
+    <el-card shadow="never">
+      <template #header>
+        <div class="flex justify-between items-center">
+          <span class="text-lg font-bold">{{ pageTitle }}</span>
+        </div>
+      </template>
+
+      <el-form ref="brandFormRef" :model="form" :rules="rules" label-width="140px" class="max-w-5xl">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="品牌名称" prop="brandName">
+              <el-input v-model="form.brandName" placeholder="京东方" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="品牌首字母" prop="brandInitials">
+              <el-input v-model="form.brandInitials" placeholder="请输入品牌首字母" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="品牌名称(英文)" prop="brandEnglishName">
+              <el-input v-model="form.brandEnglishName" placeholder="请输入品牌英文名称" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="品牌推荐系数" prop="recommendValue">
+              <el-input-number 
+                v-model="form.recommendValue" 
+                :min="0" 
+                :max="9999" 
+                controls-position="right" 
+                style="width: 100%" 
+                placeholder="请输入推荐系数"
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-form-item label="品牌标题" prop="brandTitle">
+          <el-input v-model="form.brandTitle" placeholder="请输入内容" />
+        </el-form-item>
+
+        <el-form-item label="品牌LOGO" prop="brandLogo">
+          <image-upload v-model="form.brandLogo" />
+        </el-form-item>
+
+        <el-form-item label="品牌故事" prop="brandStory">
+          <el-input v-model="form.brandStory" type="textarea" :rows="3" placeholder="请输入内容" />
+        </el-form-item>
+
+        <el-form-item label="是否显示" prop="isShow">
+          <el-radio-group v-model="form.isShow">
+            <el-radio :value="1">是</el-radio>
+            <el-radio :value="0">否</el-radio>
+          </el-radio-group>
+          <div class="text-gray-500 text-sm mt-2">
+            当品牌下还没有商品的时候。分类的品牌区不会不显示品牌。
+          </div>
+        </el-form-item>
+
+        <el-form-item label="品牌介绍" prop="brandDescribe">
+          <editor v-model="form.brandDescribe" :min-height="192" />
+        </el-form-item>
+
+        <el-form-item label="商标注册人" prop="brandRegistrant">
+          <el-input v-model="form.brandRegistrant" placeholder="请输入商标注册人" />
+        </el-form-item>
+
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="上传营业执照" prop="license">
+              <image-upload v-model="form.license" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="上传商标注册证" prop="registrationCertificate">
+              <image-upload v-model="form.registrationCertificate" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-form-item label="到期时间" prop="expireTime">
+          <el-date-picker 
+            v-model="form.expireTime"
+            type="datetime"
+            value-format="YYYY-MM-DD HH:mm:ss"
+            placeholder="请选择"
+            style="width: 100%"
+          />
+        </el-form-item>
+
+        <el-divider content-position="left">授权信息</el-divider>
+
+        <el-table :data="authorizationList" border style="margin-bottom: 20px">
+          <el-table-column label="供应商名称" prop="supplierName" align="center" />
+          <el-table-column label="品牌名称" prop="brandName" align="center" />
+          <el-table-column label="一级类目" prop="firstCategory" align="center" />
+          <el-table-column label="二级类目" prop="secondCategory" align="center" />
+          <el-table-column label="三级类目" prop="thirdCategory" align="center" />
+          <el-table-column label="截止时间" prop="deadline" align="center" />
+          <el-table-column label="授权区域(省)" prop="provinceArea" align="center" />
+          <el-table-column label="授权区域(市)" prop="cityArea" align="center" />
+          <el-table-column label="状态" prop="status" align="center" />
+          <el-table-column label="操作" align="center">
+            <template #default="scope">
+              <el-button link type="primary" size="small" @click="handleEditAuth(scope.$index)">编辑</el-button>
+              <el-button link type="danger" size="small" @click="handleDeleteAuth(scope.$index)">删除</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+
+        <el-button type="primary" plain @click="handleAddAuth">添加授权</el-button>
+
+        <el-form-item>
+          <el-button type="primary" :loading="buttonLoading" @click="submitForm">保存</el-button>
+          <el-button @click="goBack">取消</el-button>
+        </el-form-item>
+      </el-form>
+    </el-card>
+  </div>
+</template>
+
+<script setup lang="ts" name="BrandEdit">
+import { getBrand, addBrand, updateBrand } from '@/api/product/brand';
+import { BrandForm } from '@/api/product/brand/types';
+
+const route = useRoute();
+const router = useRouter();
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const brandFormRef = ref<ElFormInstance>();
+const buttonLoading = ref(false);
+const pageTitle = computed(() => {
+  return route.query.type === 'add' ? '新增品牌' : '编辑品牌';
+});
+
+// 授权信息列表
+const authorizationList = ref<any[]>([]);
+
+const initFormData: BrandForm = {
+  id: undefined,
+  brandNo: undefined,
+  brandName: undefined,
+  brandInitials: undefined,
+  brandEnglishName: undefined,
+  recommendValue: undefined,
+  brandLogo: undefined,
+  brandTitle: undefined,
+  brandBigImage: undefined,
+  brandStory: undefined,
+  isShow: 1,
+  brandRegistrant: undefined,
+  license: undefined,
+  registrationCertificate: undefined,
+  expireTime: undefined,
+  brandDescribe: undefined,
+  position: undefined,
+  care: undefined,
+  dataSource: undefined,
+  remark: undefined,
+}
+
+const form = ref<BrandForm>({ ...initFormData });
+
+const rules = reactive({
+  brandName: [
+    { required: true, message: "品牌名称不能为空", trigger: "blur" }
+  ],
+  brandInitials: [
+    { required: false, message: "品牌首字母缩写不能为空", trigger: "blur" }
+  ],
+  brandEnglishName: [
+    { required: false, message: "品牌英文名称不能为空", trigger: "blur" }
+  ]
+});
+
+/** 获取品牌详情 */
+const getDetail = async () => {
+  const id = route.query.id as string;
+  if (id && route.query.type !== 'add') {
+    const res = await getBrand(id);
+    form.value = res.data;
+    // TODO: 加载授权信息列表
+  }
+};
+
+/** 添加授权 */
+const handleAddAuth = () => {
+  authorizationList.value.push({
+    supplierName: '',
+    brandName: form.value.brandName,
+    firstCategory: '',
+    secondCategory: '',
+    thirdCategory: '',
+    deadline: '',
+    provinceArea: '',
+    cityArea: '',
+    status: ''
+  });
+};
+
+/** 编辑授权 */
+const handleEditAuth = (index: number) => {
+  // TODO: 实现编辑授权逻辑
+  proxy?.$modal.msgInfo('编辑授权功能待实现');
+};
+
+/** 删除授权 */
+const handleDeleteAuth = (index: number) => {
+  authorizationList.value.splice(index, 1);
+};
+
+/** 提交表单 */
+const submitForm = () => {
+  brandFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      try {
+        if (form.value.id) {
+          await updateBrand(form.value);
+        } else {
+          await addBrand(form.value);
+        }
+        proxy?.$modal.msgSuccess("操作成功");
+        goBack();
+      } finally {
+        buttonLoading.value = false;
+      }
+    }
+  });
+};
+
+/** 返回 */
+const goBack = () => {
+  router.back();
+};
+
+onMounted(() => {
+  getDetail();
+});
+</script>

+ 37 - 101
src/views/product/brand/index.vue

@@ -4,65 +4,12 @@
       <div v-show="showSearch" class="mb-[10px]">
         <el-card shadow="hover">
           <el-form ref="queryFormRef" :model="queryParams" :inline="true">
+            <el-form-item label="品牌名称" prop="brandName">
+              <el-input v-model="queryParams.brandName" placeholder="请输入品牌名称" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
             <el-form-item label="品牌编号" prop="brandNo">
               <el-input v-model="queryParams.brandNo" placeholder="请输入品牌编号" clearable @keyup.enter="handleQuery" />
             </el-form-item>
-            <el-form-item label="品牌中文名称" prop="brandName">
-              <el-input v-model="queryParams.brandName" placeholder="请输入品牌中文名称" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="品牌首字母缩写" prop="brandInitials">
-              <el-input v-model="queryParams.brandInitials" placeholder="请输入品牌首字母缩写" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="品牌英文名称" prop="brandEnglishName">
-              <el-input v-model="queryParams.brandEnglishName" placeholder="请输入品牌英文名称" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="推荐值" prop="recommendValue">
-              <el-input v-model="queryParams.recommendValue" placeholder="请输入推荐值" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="品牌Logo图片路径或URL" prop="brandLogo">
-              <el-input v-model="queryParams.brandLogo" placeholder="请输入品牌Logo图片路径或URL" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="品牌标题" prop="brandTitle">
-              <el-input v-model="queryParams.brandTitle" placeholder="请输入品牌标题" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="品牌故事" prop="brandStory">
-              <el-input v-model="queryParams.brandStory" placeholder="请输入品牌故事" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="是否显示" prop="isShow">
-              <el-input v-model="queryParams.isShow" placeholder="请输入是否显示" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="品牌注册人" prop="brandRegistrant">
-              <el-input v-model="queryParams.brandRegistrant" placeholder="请输入品牌注册人" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="许可证编号" prop="license">
-              <el-input v-model="queryParams.license" placeholder="请输入许可证编号" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="注册证书编号" prop="registrationCertificate">
-              <el-input v-model="queryParams.registrationCertificate" placeholder="请输入注册证书编号" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="证书/许可过期时间" prop="expireTime">
-              <el-date-picker clearable
-                v-model="queryParams.expireTime"
-                type="date"
-                value-format="YYYY-MM-DD"
-                placeholder="请选择证书/许可过期时间"
-              />
-            </el-form-item>
-            <el-form-item label="品牌描述" prop="brandDescribe">
-              <el-input v-model="queryParams.brandDescribe" placeholder="请输入品牌描述" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="展示位置" prop="position">
-              <el-input v-model="queryParams.position" placeholder="请输入展示位置" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="关注度/收藏数" prop="care">
-              <el-input v-model="queryParams.care" placeholder="请输入关注度/收藏数" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="数据来源" prop="dataSource">
-              <el-input v-model="queryParams.dataSource" placeholder="请输入数据来源" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="平台标识" prop="platformCode">
-              <el-input v-model="queryParams.platformCode" placeholder="请输入平台标识" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
             <el-form-item>
               <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
               <el-button icon="Refresh" @click="resetQuery">重置</el-button>
@@ -76,16 +23,7 @@
       <template #header>
         <el-row :gutter="10" class="mb8">
           <el-col :span="1.5">
-            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['product:brand:add']">新增</el-button>
-          </el-col>
-          <el-col :span="1.5">
-            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['product:brand:edit']">修改</el-button>
-          </el-col>
-          <el-col :span="1.5">
-            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['product:brand:remove']">删除</el-button>
-          </el-col>
-          <el-col :span="1.5">
-            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['product:brand:export']">导出</el-button>
+            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['product:brand:add']">新增品牌</el-button>
           </el-col>
           <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
         </el-row>
@@ -93,41 +31,33 @@
 
       <el-table v-loading="loading" border :data="brandList" @selection-change="handleSelectionChange">
         <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="主键" align="center" prop="id" v-if="true" />
-        <el-table-column label="品牌编号" align="center" prop="brandNo" />
-        <el-table-column label="品牌中文名称" align="center" prop="brandName" />
-        <el-table-column label="品牌首字母缩写" align="center" prop="brandInitials" />
-        <el-table-column label="品牌英文名称" align="center" prop="brandEnglishName" />
-        <el-table-column label="推荐值" align="center" prop="recommendValue" />
-        <el-table-column label="品牌Logo图片路径或URL" align="center" prop="brandLogo" />
-        <el-table-column label="品牌标题" align="center" prop="brandTitle" />
-        <el-table-column label="品牌大图" align="center" prop="brandBigImageUrl" width="100">
+        <el-table-column label="品牌编号" align="center" prop="brandNo" width="120" />
+        <el-table-column label="品牌名称" align="center" prop="brandName" />
+        <el-table-column label="是否有授权" align="center" prop="license" width="120">
           <template #default="scope">
-            <image-preview :src="scope.row.brandBigImageUrl" :width="50" :height="50"/>
+            <span>{{ scope.row.license ? '是' : '无' }}</span>
           </template>
         </el-table-column>
-        <el-table-column label="品牌故事" align="center" prop="brandStory" />
-        <el-table-column label="是否显示" align="center" prop="isShow" />
-        <el-table-column label="品牌注册人" align="center" prop="brandRegistrant" />
-        <el-table-column label="许可证编号" align="center" prop="license" />
-        <el-table-column label="注册证书编号" align="center" prop="registrationCertificate" />
-        <el-table-column label="证书/许可过期时间" align="center" prop="expireTime" width="180">
+        <el-table-column label="授权区域" align="center" prop="position" show-overflow-tooltip>
           <template #default="scope">
-            <span>{{ parseTime(scope.row.expireTime, '{y}-{m}-{d}') }}</span>
+            <span>{{ scope.row.position || '无' }}</span>
           </template>
         </el-table-column>
-        <el-table-column label="品牌描述" align="center" prop="brandDescribe" />
-        <el-table-column label="展示位置" align="center" prop="position" />
-        <el-table-column label="关注度/收藏数" align="center" prop="care" />
-        <el-table-column label="数据来源" align="center" prop="dataSource" />
-        <el-table-column label="备注" align="center" prop="remark" />
-        <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <el-table-column label="到期时间" align="center" prop="expireTime" width="120">
           <template #default="scope">
-            <el-tooltip content="修改" placement="top">
-              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['product:brand:edit']"></el-button>
+            <span>{{ scope.row.expireTime ? parseTime(scope.row.expireTime, '{y}-{m}-{d}') : '无' }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="180">
+          <template #default="scope">
+            <el-tooltip content="查看" placement="top">
+              <el-button link type="primary" icon="View" @click="handleUpdate(scope.row)" v-hasPermi="['product:brand:query']">查看</el-button>
+            </el-tooltip>
+            <el-tooltip content="编辑" placement="top">
+              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['product:brand:edit']">编辑</el-button>
             </el-tooltip>
             <el-tooltip content="删除" placement="top">
-              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['product:brand:remove']"></el-button>
+              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['product:brand:remove']">删除</el-button>
             </el-tooltip>
           </template>
         </el-table-column>
@@ -216,6 +146,7 @@ import { listBrand, getBrand, delBrand, addBrand, updateBrand } from '@/api/prod
 import { BrandVO, BrandQuery, BrandForm } from '@/api/product/brand/types';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const router = useRouter();
 
 const brandList = ref<BrandVO[]>([]);
 const buttonLoading = ref(false);
@@ -379,19 +310,24 @@ const handleSelectionChange = (selection: BrandVO[]) => {
 
 /** 新增按钮操作 */
 const handleAdd = () => {
-  reset();
-  dialog.visible = true;
-  dialog.title = "添加产品品牌信息";
+  router.push({
+    path: '/product/brand/edit',
+    query: {
+      type: 'add'
+    }
+  });
 }
 
 /** 修改按钮操作 */
 const handleUpdate = async (row?: BrandVO) => {
-  reset();
-  const _id = row?.id || ids.value[0]
-  const res = await getBrand(_id);
-  Object.assign(form.value, res.data);
-  dialog.visible = true;
-  dialog.title = "修改产品品牌信息";
+  const _id = row?.id || ids.value[0];
+  router.push({
+    path: '/product/brand/edit',
+    query: {
+      id: _id,
+      type: 'edit'
+    }
+  });
 }
 
 /** 提交按钮 */

+ 193 - 325
src/views/product/category/index.vue

@@ -4,75 +4,9 @@
       <div v-show="showSearch" class="mb-[10px]">
         <el-card shadow="hover">
           <el-form ref="queryFormRef" :model="queryParams" :inline="true">
-            <el-form-item label="分类编号" prop="categoryNo">
-              <el-input v-model="queryParams.categoryNo" placeholder="请输入分类编号" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
             <el-form-item label="分类名称" prop="categoryName">
               <el-input v-model="queryParams.categoryName" placeholder="请输入分类名称" clearable @keyup.enter="handleQuery" />
             </el-form-item>
-            <el-form-item label="父级分类ID" prop="parentId">
-              <el-input v-model="queryParams.parentId" placeholder="请输入父级分类ID" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="祖籍列表" prop="ancestors">
-              <el-input v-model="queryParams.ancestors" placeholder="请输入祖籍列表" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="分类层级" prop="classLevel">
-              <el-input v-model="queryParams.classLevel" placeholder="请输入分类层级" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="是否显示" prop="isShow">
-              <el-input v-model="queryParams.isShow" placeholder="请输入是否显示" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="是否在GPS中显示" prop="isShowGps">
-              <el-input v-model="queryParams.isShowGps" placeholder="请输入是否在GPS中显示" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="折扣率" prop="discountRate">
-              <el-input v-model="queryParams.discountRate" placeholder="请输入折扣率" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="拼音码" prop="pyCode">
-              <el-input v-model="queryParams.pyCode" placeholder="请输入拼音码" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="分类描述" prop="classDescription">
-              <el-input v-model="queryParams.classDescription" placeholder="请输入分类描述" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="数据来源" prop="dataSource">
-              <el-input v-model="queryParams.dataSource" placeholder="请输入数据来源" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="自定义标签1" prop="oneLable1">
-              <el-input v-model="queryParams.oneLable1" placeholder="请输入自定义标签1" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="自定义标签2" prop="oneLable2">
-              <el-input v-model="queryParams.oneLable2" placeholder="请输入自定义标签2" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="自定义链接1" prop="oneLink1">
-              <el-input v-model="queryParams.oneLink1" placeholder="请输入自定义链接1" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="自定义链接2" prop="oneLink2">
-              <el-input v-model="queryParams.oneLink2" placeholder="请输入自定义链接2" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="排序值,默认为0" prop="sort">
-              <el-input v-model="queryParams.sort" placeholder="请输入排序值,默认为0" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="颜色" prop="color">
-              <el-input v-model="queryParams.color" placeholder="请输入颜色" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="采购编号" prop="purchaseNo">
-              <el-input v-model="queryParams.purchaseNo" placeholder="请输入采购编号" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="采购名称" prop="purchaseName">
-              <el-input v-model="queryParams.purchaseName" placeholder="请输入采购名称" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="采购负责人编号" prop="purchaseManagerNo">
-              <el-input v-model="queryParams.purchaseManagerNo" placeholder="请输入采购负责人编号" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="采购负责人姓名" prop="purchaseManagerName">
-              <el-input v-model="queryParams.purchaseManagerName" placeholder="请输入采购负责人姓名" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="所属平台" prop="platform">
-              <el-input v-model="queryParams.platform" placeholder="请输入所属平台" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="平台标识" prop="platformCode">
-              <el-input v-model="queryParams.platformCode" placeholder="请输入平台标识" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
             <el-form-item>
               <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
               <el-button icon="Refresh" @click="resetQuery">重置</el-button>
@@ -82,141 +16,142 @@
       </div>
     </transition>
 
-    <el-card shadow="never">
+    <el-card shadow="hover">
       <template #header>
-        <el-row :gutter="10" class="mb8">
-          <el-col :span="1.5">
-            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['product:category:add']">新增</el-button>
-          </el-col>
+        <el-row :gutter="10">
           <el-col :span="1.5">
-            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['product:category:edit']">修改</el-button>
+            <el-button v-hasPermi="['product:category:add']" type="primary" plain icon="Plus" @click="handleAdd()">新增分类</el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['product:category:remove']">删除</el-button>
+            <el-button type="info" plain icon="Sort" @click="handleToggleExpandAll">全部展开</el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['product:category:export']">导出</el-button>
+            <el-button type="info" plain icon="Sort" @click="handleToggleCollapseAll">全部收起</el-button>
           </el-col>
-          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+          <right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
         </el-row>
       </template>
 
-      <el-table v-loading="loading" border :data="categoryList" @selection-change="handleSelectionChange">
-        <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="主键" align="center" prop="id" v-if="true" />
-        <el-table-column label="分类编号" align="center" prop="categoryNo" />
-        <el-table-column label="分类名称" align="center" prop="categoryName" />
-        <el-table-column label="父级分类ID" align="center" prop="parentId" />
-        <el-table-column label="祖籍列表" align="center" prop="ancestors" />
-        <el-table-column label="分类层级" align="center" prop="classLevel" />
-        <el-table-column label="是否显示" align="center" prop="isShow" />
-        <el-table-column label="是否在GPS中显示" align="center" prop="isShowGps" />
-        <el-table-column label="折扣率" align="center" prop="discountRate" />
-        <el-table-column label="拼音码" align="center" prop="pyCode" />
-        <el-table-column label="分类描述" align="center" prop="classDescription" />
-        <el-table-column label="数据来源" align="center" prop="dataSource" />
-        <el-table-column label="自定义标签1" align="center" prop="oneLable1" />
-        <el-table-column label="自定义标签2" align="center" prop="oneLable2" />
-        <el-table-column label="自定义链接1" align="center" prop="oneLink1" />
-        <el-table-column label="自定义链接2" align="center" prop="oneLink2" />
-        <el-table-column label="排序值,默认为0" align="center" prop="sort" />
-        <el-table-column label="颜色" align="center" prop="color" />
-        <el-table-column label="采购编号" align="center" prop="purchaseNo" />
-        <el-table-column label="采购名称" align="center" prop="purchaseName" />
-        <el-table-column label="采购负责人编号" align="center" prop="purchaseManagerNo" />
-        <el-table-column label="采购负责人姓名" align="center" prop="purchaseManagerName" />
-        <el-table-column label="所属平台" align="center" prop="platform" />
-        <el-table-column label="备注" align="center" prop="remark" />
-        <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+      <el-table
+        ref="categoryTableRef"
+        v-loading="loading"
+        :data="categoryList"
+        row-key="id"
+        border
+        :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
+        :default-expand-all="isExpandAll"
+      >
+        <el-table-column prop="categoryName" label="分类名称" width="260"></el-table-column>
+        <el-table-column prop="classLevel" align="center" label="分类级别" width="150">
+          <template #default="scope">
+            <span>{{ scope.row.classLevel }}级</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="sort" align="center" label="排序" width="150"></el-table-column>
+        <el-table-column prop="platform" align="center" label="平台" width="150">
+          <template #default="scope">
+            <span>{{ scope.row.platform === 0 ? '工业品' : scope.row.platform === 1 ? 'PC端' : '未知' }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="isShow" align="center" label="是否提示" width="150">
+          <template #default="scope">
+            <span>{{ scope.row.isShow === 1 ? '是' : '否' }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column fixed="right" align="center" label="操作">
           <template #default="scope">
+            <el-tooltip content="新增分类" placement="top">
+              <el-button v-hasPermi="['product:category:add']" link type="primary" icon="Plus" @click="handleAdd(scope.row)" />
+            </el-tooltip>
             <el-tooltip content="修改" placement="top">
-              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['product:category:edit']"></el-button>
+              <el-button v-hasPermi="['product:category:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)" />
             </el-tooltip>
             <el-tooltip content="删除" placement="top">
-              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['product:category:remove']"></el-button>
+              <el-button v-hasPermi="['product:category:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)" />
             </el-tooltip>
           </template>
         </el-table-column>
       </el-table>
-
-      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
     </el-card>
     <!-- 添加或修改产品分类对话框 -->
-    <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+    <el-dialog v-model="dialog.visible" :title="dialog.title" destroy-on-close append-to-body width="700px">
       <el-form ref="categoryFormRef" :model="form" :rules="rules" label-width="80px">
-        <el-form-item label="分类编号" prop="categoryNo">
-          <el-input v-model="form.categoryNo" placeholder="请输入分类编号" />
-        </el-form-item>
-        <el-form-item label="分类名称" prop="categoryName">
-          <el-input v-model="form.categoryName" placeholder="请输入分类名称" />
-        </el-form-item>
-        <el-form-item label="父级分类ID" prop="parentId">
-          <el-input v-model="form.parentId" placeholder="请输入父级分类ID" />
-        </el-form-item>
-        <el-form-item label="祖籍列表" prop="ancestors">
-            <el-input v-model="form.ancestors" type="textarea" placeholder="请输入内容" />
-        </el-form-item>
-        <el-form-item label="分类层级" prop="classLevel">
-          <el-input v-model="form.classLevel" placeholder="请输入分类层级" />
-        </el-form-item>
-        <el-form-item label="是否显示" prop="isShow">
-          <el-input v-model="form.isShow" placeholder="请输入是否显示" />
-        </el-form-item>
-        <el-form-item label="是否在GPS中显示" prop="isShowGps">
-          <el-input v-model="form.isShowGps" placeholder="请输入是否在GPS中显示" />
-        </el-form-item>
-        <el-form-item label="折扣率" prop="discountRate">
-          <el-input v-model="form.discountRate" placeholder="请输入折扣率" />
-        </el-form-item>
-        <el-form-item label="拼音码" prop="pyCode">
-            <el-input v-model="form.pyCode" type="textarea" placeholder="请输入内容" />
-        </el-form-item>
-        <el-form-item label="分类描述" prop="classDescription">
-            <el-input v-model="form.classDescription" type="textarea" placeholder="请输入内容" />
-        </el-form-item>
-        <el-form-item label="数据来源" prop="dataSource">
-          <el-input v-model="form.dataSource" placeholder="请输入数据来源" />
-        </el-form-item>
-        <el-form-item label="自定义标签1" prop="oneLable1">
-          <el-input v-model="form.oneLable1" placeholder="请输入自定义标签1" />
-        </el-form-item>
-        <el-form-item label="自定义标签2" prop="oneLable2">
-          <el-input v-model="form.oneLable2" placeholder="请输入自定义标签2" />
-        </el-form-item>
-        <el-form-item label="自定义链接1" prop="oneLink1">
-          <el-input v-model="form.oneLink1" placeholder="请输入自定义链接1" />
-        </el-form-item>
-        <el-form-item label="自定义链接2" prop="oneLink2">
-          <el-input v-model="form.oneLink2" placeholder="请输入自定义链接2" />
-        </el-form-item>
-        <el-form-item label="排序值,默认为0" prop="sort">
-          <el-input v-model="form.sort" placeholder="请输入排序值,默认为0" />
-        </el-form-item>
-        <el-form-item label="颜色" prop="color">
-          <el-input v-model="form.color" placeholder="请输入颜色" />
-        </el-form-item>
-        <el-form-item label="采购编号" prop="purchaseNo">
-          <el-input v-model="form.purchaseNo" placeholder="请输入采购编号" />
-        </el-form-item>
-        <el-form-item label="采购名称" prop="purchaseName">
-          <el-input v-model="form.purchaseName" placeholder="请输入采购名称" />
-        </el-form-item>
-        <el-form-item label="采购负责人编号" prop="purchaseManagerNo">
-          <el-input v-model="form.purchaseManagerNo" placeholder="请输入采购负责人编号" />
-        </el-form-item>
-        <el-form-item label="采购负责人姓名" prop="purchaseManagerName">
-          <el-input v-model="form.purchaseManagerName" placeholder="请输入采购负责人姓名" />
-        </el-form-item>
-        <el-form-item label="所属平台" prop="platform">
-          <el-input v-model="form.platform" placeholder="请输入所属平台" />
-        </el-form-item>
-        <el-form-item label="备注" prop="remark">
-            <el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
-        </el-form-item>
+        <el-row :gutter="20">
+          <el-col v-if="form.parentId !== 0" :span="24">
+            <el-form-item label="上级分类" prop="parentId">
+              <el-tree-select
+                v-model="form.parentId"
+                :data="categoryOptions"
+                :props="{ value: 'id', label: 'categoryName', children: 'children' } as any"
+                value-key="id"
+                placeholder="选择上级分类"
+                check-strictly
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="分类名称" prop="categoryName">
+              <el-input v-model="form.categoryName" placeholder="请输入分类名称" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="分类编号" prop="categoryNo">
+              <el-input v-model="form.categoryNo" placeholder="请输入分类编号" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="标签1" prop="oneLable1">
+              <el-input v-model="form.oneLable1" placeholder="请输入标签1" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="链接1" prop="oneLink1">
+              <el-input v-model="form.oneLink1" placeholder="请输入链接1" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="标签2" prop="oneLable2">
+              <el-input v-model="form.oneLable2" placeholder="请输入标签2" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="链接2" prop="oneLink2">
+              <el-input v-model="form.oneLink2" placeholder="请输入链接2" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="排序" prop="sort">
+              <el-input-number v-model="form.sort" controls-position="right" :min="0" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="平台" prop="platform">
+              <el-select v-model="form.platform" placeholder="请选择所属平台" style="width: 100%">
+                <el-option label="工业品" :value="0" />
+                <el-option label="PC端" :value="1" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="是否提示" prop="isShow">
+              <el-switch v-model="form.isShow" :active-value="1" :inactive-value="0" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="是否导航" prop="isShowGps">
+              <el-switch v-model="form.isShowGps" :active-value="1" :inactive-value="0" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="描述" prop="classDescription">
+              <el-input v-model="form.classDescription" type="textarea" placeholder="请输入内容" :rows="3" />
+            </el-form-item>
+          </el-col>
+        </el-row>
       </el-form>
       <template #footer>
         <div class="dialog-footer">
-          <el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
+          <el-button type="primary" @click="submitForm">确 认</el-button>
           <el-button @click="cancel">取 消</el-button>
         </div>
       </template>
@@ -225,149 +160,59 @@
 </template>
 
 <script setup name="Category" lang="ts">
-import { listCategory, getCategory, delCategory, addCategory, updateCategory } from '@/api/product/category';
+import { listCategory, getCategory, delCategory, addCategory, updateCategory, listCategoryExcludeChild } from '@/api/product/category';
 import { CategoryVO, CategoryQuery, CategoryForm } from '@/api/product/category/types';
 
+interface CategoryOptionsType {
+  id: number | string;
+  categoryName: string;
+  children: CategoryOptionsType[];
+}
+
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
 const categoryList = ref<CategoryVO[]>([]);
-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 queryFormRef = ref<ElFormInstance>();
-const categoryFormRef = ref<ElFormInstance>();
+const categoryOptions = ref<CategoryOptionsType[]>([]);
+const isExpandAll = ref(false);
 
 const dialog = reactive<DialogOption>({
   visible: false,
   title: ''
 });
 
+const categoryTableRef = ref<ElTableInstance>();
+const queryFormRef = ref<ElFormInstance>();
+const categoryFormRef = ref<ElFormInstance>();
+
 const initFormData: CategoryForm = {
   id: undefined,
-  categoryNo: undefined,
   categoryName: undefined,
+  categoryNo: undefined,
   parentId: undefined,
-  ancestors: undefined,
-  classLevel: undefined,
-  isShow: undefined,
-  isShowGps: undefined,
-  discountRate: undefined,
-  pyCode: undefined,
-  classDescription: undefined,
-  dataSource: undefined,
+  sort: 0,
+  platform: undefined,
+  isShow: 1,
+  isShowGps: 0,
   oneLable1: undefined,
   oneLable2: undefined,
   oneLink1: undefined,
   oneLink2: undefined,
-  sort: undefined,
-  color: undefined,
-  purchaseNo: undefined,
-  purchaseName: undefined,
-  purchaseManagerNo: undefined,
-  purchaseManagerName: undefined,
-  platform: undefined,
-  remark: undefined,
+  classDescription: undefined
 }
 const data = reactive<PageData<CategoryForm, CategoryQuery>>({
   form: {...initFormData},
   queryParams: {
-    pageNum: 1,
-    pageSize: 10,
-    categoryNo: undefined,
-    categoryName: undefined,
-    parentId: undefined,
-    ancestors: undefined,
-    classLevel: undefined,
-    isShow: undefined,
-    isShowGps: undefined,
-    discountRate: undefined,
-    pyCode: undefined,
-    classDescription: undefined,
-    dataSource: undefined,
-    oneLable1: undefined,
-    oneLable2: undefined,
-    oneLink1: undefined,
-    oneLink2: undefined,
-    sort: undefined,
-    color: undefined,
-    purchaseNo: undefined,
-    purchaseName: undefined,
-    purchaseManagerNo: undefined,
-    purchaseManagerName: undefined,
-    platform: undefined,
-    platformCode: undefined,
-    params: {
-    }
+    categoryName: undefined
   },
   rules: {
-    categoryNo: [
-      { required: true, message: "分类编号不能为空", trigger: "blur" }
-    ],
     categoryName: [
       { required: true, message: "分类名称不能为空", trigger: "blur" }
     ],
-    parentId: [
-      { required: true, message: "父级分类ID不能为空", trigger: "blur" }
-    ],
-    ancestors: [
-      { required: true, message: "祖籍列表不能为空", trigger: "blur" }
-    ],
-    isShow: [
-      { required: true, message: "是否显示不能为空", trigger: "blur" }
-    ],
-    discountRate: [
-      { required: true, message: "折扣率不能为空", trigger: "blur" }
-    ],
-    pyCode: [
-      { required: true, message: "拼音码不能为空", trigger: "blur" }
-    ],
-    classDescription: [
-      { required: true, message: "分类描述不能为空", trigger: "blur" }
-    ],
-    dataSource: [
-      { required: true, message: "数据来源不能为空", trigger: "blur" }
-    ],
-    oneLable1: [
-      { required: true, message: "自定义标签1不能为空", trigger: "blur" }
-    ],
-    oneLable2: [
-      { required: true, message: "自定义标签2不能为空", trigger: "blur" }
-    ],
-    oneLink1: [
-      { required: true, message: "自定义链接1不能为空", trigger: "blur" }
-    ],
-    oneLink2: [
-      { required: true, message: "自定义链接2不能为空", trigger: "blur" }
-    ],
     sort: [
-      { required: true, message: "排序值,默认为0不能为空", trigger: "blur" }
-    ],
-    color: [
-      { required: true, message: "颜色不能为空", trigger: "blur" }
-    ],
-    purchaseNo: [
-      { required: true, message: "采购编号不能为空", trigger: "blur" }
-    ],
-    purchaseName: [
-      { required: true, message: "采购名称不能为空", trigger: "blur" }
-    ],
-    purchaseManagerNo: [
-      { required: true, message: "采购负责人编号不能为空", trigger: "blur" }
-    ],
-    purchaseManagerName: [
-      { required: true, message: "采购负责人姓名不能为空", trigger: "blur" }
-    ],
-    platform: [
-      { required: true, message: "所属平台不能为空", trigger: "blur" }
-    ],
-    remark: [
-      { required: true, message: "备注不能为空", trigger: "blur" }
-    ],
+      { required: true, message: "显示排序不能为空", trigger: "blur" }
+    ]
   }
 });
 
@@ -377,8 +222,10 @@ const { queryParams, form, rules } = toRefs(data);
 const getList = async () => {
   loading.value = true;
   const res = await listCategory(queryParams.value);
-  categoryList.value = res.rows;
-  total.value = res.total;
+  const data = proxy?.handleTree<CategoryVO>(res.rows, 'id');
+  if (data) {
+    categoryList.value = data;
+  }
   loading.value = false;
 }
 
@@ -396,7 +243,6 @@ const reset = () => {
 
 /** 搜索按钮操作 */
 const handleQuery = () => {
-  queryParams.value.pageNum = 1;
   getList();
 }
 
@@ -406,41 +252,71 @@ const resetQuery = () => {
   handleQuery();
 }
 
-/** 多选框选中数据 */
-const handleSelectionChange = (selection: CategoryVO[]) => {
-  ids.value = selection.map(item => item.id);
-  single.value = selection.length != 1;
-  multiple.value = !selection.length;
-}
+/** 展开/折叠操作 */
+const handleToggleExpandAll = () => {
+  isExpandAll.value = true;
+  toggleExpandAll(categoryList.value, true);
+};
+
+/** 全部收起操作 */
+const handleToggleCollapseAll = () => {
+  isExpandAll.value = false;
+  toggleExpandAll(categoryList.value, false);
+};
+
+/** 展开/折叠所有 */
+const toggleExpandAll = (data: CategoryVO[], status: boolean) => {
+  data.forEach((item) => {
+    categoryTableRef.value?.toggleRowExpansion(item, status);
+    if (item.children && item.children.length > 0) toggleExpandAll(item.children, status);
+  });
+};
 
 /** 新增按钮操作 */
-const handleAdd = () => {
+const handleAdd = async (row?: CategoryVO) => {
   reset();
-  dialog.visible = true;
-  dialog.title = "添加产品分类";
+  const res = await listCategory();
+  const data = proxy?.handleTree<CategoryOptionsType>(res.rows, 'id');
+  if (data) {
+    categoryOptions.value = data;
+    if (row && row.id) {
+      form.value.parentId = row?.id;
+    }
+    dialog.visible = true;
+    dialog.title = '添加产品分类';
+  }
 }
 
 /** 修改按钮操作 */
-const handleUpdate = async (row?: CategoryVO) => {
+const handleUpdate = async (row: CategoryVO) => {
   reset();
-  const _id = row?.id || ids.value[0]
-  const res = await getCategory(_id);
-  Object.assign(form.value, res.data);
+  const res = await getCategory(row.id);
+  form.value = res.data;
+  const response = await listCategoryExcludeChild(row.id);
+  // 根据后端返回结构,如果是 TableDataInfo 则用 rows,如果是直接数组则用 data
+  const responseData = (response as any).rows || response.data || [];
+  const data = proxy?.handleTree<CategoryOptionsType>(responseData, 'id');
+  if (data) {
+    categoryOptions.value = data;
+    if (data.length === 0) {
+      const noResultsOptions: CategoryOptionsType = {
+        id: res.data.parentId,
+        categoryName: res.data.parentName || '无',
+        children: []
+      };
+      categoryOptions.value.push(noResultsOptions);
+    }
+  }
   dialog.visible = true;
-  dialog.title = "修改产品分类";
+  dialog.title = '修改产品分类';
 }
 
 /** 提交按钮 */
 const submitForm = () => {
   categoryFormRef.value?.validate(async (valid: boolean) => {
     if (valid) {
-      buttonLoading.value = true;
-      if (form.value.id) {
-        await updateCategory(form.value).finally(() =>  buttonLoading.value = false);
-      } else {
-        await addCategory(form.value).finally(() =>  buttonLoading.value = false);
-      }
-      proxy?.$modal.msgSuccess("操作成功");
+      form.value.id ? await updateCategory(form.value) : await addCategory(form.value);
+      proxy?.$modal.msgSuccess('操作成功');
       dialog.visible = false;
       await getList();
     }
@@ -448,19 +324,11 @@ const submitForm = () => {
 }
 
 /** 删除按钮操作 */
-const handleDelete = async (row?: CategoryVO) => {
-  const _ids = row?.id || ids.value;
-  await proxy?.$modal.confirm('是否确认删除产品分类编号为"' + _ids + '"的数据项?').finally(() => loading.value = false);
-  await delCategory(_ids);
-  proxy?.$modal.msgSuccess("删除成功");
+const handleDelete = async (row: CategoryVO) => {
+  await proxy?.$modal.confirm('是否确认删除名称为"' + row.categoryName + '"的数据项?');
+  await delCategory(row.id);
   await getList();
-}
-
-/** 导出按钮操作 */
-const handleExport = () => {
-  proxy?.download('product/category/export', {
-    ...queryParams.value
-  }, `category_${new Date().getTime()}.xlsx`)
+  proxy?.$modal.msgSuccess('删除成功');
 }
 
 onMounted(() => {

+ 12 - 45
src/views/product/lable/index.vue

@@ -4,17 +4,8 @@
       <div v-show="showSearch" class="mb-[10px]">
         <el-card shadow="hover">
           <el-form ref="queryFormRef" :model="queryParams" :inline="true">
-            <el-form-item label="产品标签编号" prop="productLabelNo">
-              <el-input v-model="queryParams.productLabelNo" placeholder="请输入产品标签编号" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="标签显示名称" prop="productLabelName">
-              <el-input v-model="queryParams.productLabelName" placeholder="请输入标签显示名称" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="使用该标签的产品数量" prop="productQuantity">
-              <el-input v-model="queryParams.productQuantity" placeholder="请输入使用该标签的产品数量" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="平台标识" prop="platformCode">
-              <el-input v-model="queryParams.platformCode" placeholder="请输入平台标识" clearable @keyup.enter="handleQuery" />
+            <el-form-item label="标签名称" prop="productLabelName">
+              <el-input v-model="queryParams.productLabelName" placeholder="请输入标签名称" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item>
               <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
@@ -37,20 +28,17 @@
           <el-col :span="1.5">
             <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['product:lable:remove']">删除</el-button>
           </el-col>
-          <el-col :span="1.5">
+          <!-- <el-col :span="1.5">
             <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['product:lable:export']">导出</el-button>
-          </el-col>
+          </el-col> -->
           <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
         </el-row>
       </template>
 
       <el-table v-loading="loading" border :data="lableList" @selection-change="handleSelectionChange">
         <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="主键,自增ID" align="center" prop="id" v-if="true" />
-        <el-table-column label="产品标签编号" align="center" prop="productLabelNo" />
-        <el-table-column label="标签显示名称" align="center" prop="productLabelName" />
-        <el-table-column label="使用该标签的产品数量" align="center" prop="productQuantity" />
-        <el-table-column label="备注" align="center" prop="remark" />
+        <el-table-column label="标签名称" align="center" prop="productLabelName" />
+        <el-table-column label="产品数量" align="center" prop="productQuantity" />
         <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
           <template #default="scope">
             <el-tooltip content="修改" placement="top">
@@ -68,17 +56,8 @@
     <!-- 添加或修改产品标签信息(注意:名疑似拼写错误,应为 product_label)对话框 -->
     <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
       <el-form ref="lableFormRef" :model="form" :rules="rules" label-width="80px">
-        <el-form-item label="产品标签编号" prop="productLabelNo">
-          <el-input v-model="form.productLabelNo" placeholder="请输入产品标签编号" />
-        </el-form-item>
-        <el-form-item label="标签显示名称" prop="productLabelName">
-          <el-input v-model="form.productLabelName" placeholder="请输入标签显示名称" />
-        </el-form-item>
-        <el-form-item label="使用该标签的产品数量" prop="productQuantity">
-          <el-input v-model="form.productQuantity" placeholder="请输入使用该标签的产品数量" />
-        </el-form-item>
-        <el-form-item label="备注" prop="remark">
-            <el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
+        <el-form-item label="标签名称" prop="productLabelName">
+          <el-input v-model="form.productLabelName" placeholder="请输入标签名称" />
         </el-form-item>
       </el-form>
       <template #footer>
@@ -116,32 +95,20 @@ const dialog = reactive<DialogOption>({
 
 const initFormData: LableForm = {
   id: undefined,
-  productLabelNo: undefined,
   productLabelName: undefined,
-  productQuantity: undefined,
-  remark: undefined,
 }
 const data = reactive<PageData<LableForm, LableQuery>>({
   form: {...initFormData},
   queryParams: {
     pageNum: 1,
     pageSize: 10,
-    productLabelNo: undefined,
     productLabelName: undefined,
-    productQuantity: undefined,
-    platformCode: undefined,
     params: {
     }
   },
   rules: {
-    productLabelNo: [
-      { required: true, message: "产品标签编号不能为空", trigger: "blur" }
-    ],
     productLabelName: [
-      { required: true, message: "标签显示名称不能为空", trigger: "blur" }
-    ],
-    remark: [
-      { required: true, message: "备注不能为空", trigger: "blur" }
+      { required: true, message: "标签名称不能为空", trigger: "blur" }
     ],
   }
 });
@@ -192,7 +159,7 @@ const handleSelectionChange = (selection: LableVO[]) => {
 const handleAdd = () => {
   reset();
   dialog.visible = true;
-  dialog.title = "添加产品标签信息(注意:名疑似拼写错误,应为 product_label)";
+  dialog.title = "新增商品标签";
 }
 
 /** 修改按钮操作 */
@@ -202,7 +169,7 @@ const handleUpdate = async (row?: LableVO) => {
   const res = await getLable(_id);
   Object.assign(form.value, res.data);
   dialog.visible = true;
-  dialog.title = "修改产品标签信息(注意:名疑似拼写错误,应为 product_label)";
+  dialog.title = "编辑商品标签";
 }
 
 /** 提交按钮 */
@@ -225,7 +192,7 @@ const submitForm = () => {
 /** 删除按钮操作 */
 const handleDelete = async (row?: LableVO) => {
   const _ids = row?.id || ids.value;
-  await proxy?.$modal.confirm('是否确认删除产品标签信息(注意:名疑似拼写错误,应为 product_label)编号为"' + _ids + '"的数据项?').finally(() => loading.value = false);
+  await proxy?.$modal.confirm('是否确认删除选中的标签?').finally(() => loading.value = false);
   await delLable(_ids);
   proxy?.$modal.msgSuccess("删除成功");
   await getList();

+ 78 - 74
src/views/product/pool/index.vue

@@ -4,24 +4,13 @@
       <div v-show="showSearch" class="mb-[10px]">
         <el-card shadow="hover">
           <el-form ref="queryFormRef" :model="queryParams" :inline="true">
-            <el-form-item label="池编码" prop="poolNo">
-              <el-input v-model="queryParams.poolNo" placeholder="请输入池编码" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="池名称" prop="name">
-              <el-input v-model="queryParams.name" placeholder="请输入池名称" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="是否显示:1=是,0=否" prop="isShow">
-              <el-input v-model="queryParams.isShow" placeholder="请输入是否显示:1=是,0=否" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="审核原因" prop="reviewReason">
-              <el-input v-model="queryParams.reviewReason" placeholder="请输入审核原因" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="平台标识" prop="platformCode">
-              <el-input v-model="queryParams.platformCode" placeholder="请输入平台标识" clearable @keyup.enter="handleQuery" />
+            <el-form-item label="商品池名称" prop="name">
+              <el-input v-model="queryParams.name" placeholder="请输入分类商品池名称" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item>
               <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
               <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+              <el-button type="primary" icon="Plus" @click="handleAdd" v-hasPermi="['product:pool:add']">新增</el-button>
             </el-form-item>
           </el-form>
         </el-card>
@@ -29,41 +18,30 @@
     </transition>
 
     <el-card shadow="never">
-      <template #header>
-        <el-row :gutter="10" class="mb8">
-          <el-col :span="1.5">
-            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['product:pool:add']">新增</el-button>
-          </el-col>
-          <el-col :span="1.5">
-            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['product:pool:edit']">修改</el-button>
-          </el-col>
-          <el-col :span="1.5">
-            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['product:pool:remove']">删除</el-button>
-          </el-col>
-          <el-col :span="1.5">
-            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['product:pool:export']">导出</el-button>
-          </el-col>
-          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
-        </el-row>
-      </template>
 
-      <el-table v-loading="loading" border :data="poolList" @selection-change="handleSelectionChange">
-        <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="主键ID" align="center" prop="id" v-if="true" />
-        <el-table-column label="池编码" align="center" prop="poolNo" />
-        <el-table-column label="池名称" align="center" prop="name" />
-        <el-table-column label="是否显示:1=是,0=否" align="center" prop="isShow" />
-        <el-table-column label="产品审核状态 0=待提交,1=待审核,2=审核通过,3=审核驳回" align="center" prop="productReviewStatus" />
-        <el-table-column label="审核原因" align="center" prop="reviewReason" />
-        <el-table-column label="备注" align="center" prop="remark" />
-        <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+
+      <el-table v-loading="loading" border :data="poolList">
+        <el-table-column label="编号" align="center" prop="poolNo" />
+        <el-table-column label="分类商品池名称" align="center" prop="name" />
+        <el-table-column label="商品数量" align="center" prop="productCount" />
+        <el-table-column label="创建时间" align="center" prop="createTime" width="180" />
+        <el-table-column label="状态" align="center" prop="isShow">
           <template #default="scope">
-            <el-tooltip content="修改" placement="top">
-              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['product:pool:edit']"></el-button>
-            </el-tooltip>
-            <el-tooltip content="删除" placement="top">
-              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['product:pool:remove']"></el-button>
-            </el-tooltip>
+            <el-switch 
+              v-model="scope.row.isShow" 
+              active-value="1" 
+              inactive-value="0"
+              
+              
+              @change="handleStatusChange(scope.row)"
+            />
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" align="center" width="300">
+          <template #default="scope">
+            <el-link type="primary" @click="handleModifyPool(scope.row)" v-hasPermi="['product:pool:edit']">修改分类商品池售卖</el-link>
+            <el-divider direction="vertical" />
+            <el-link type="primary" @click="handlePoolManage(scope.row)" v-hasPermi="['product:pool:edit']">商品池管理</el-link>
           </template>
         </el-table-column>
       </el-table>
@@ -71,27 +49,33 @@
       <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
     </el-card>
     <!-- 添加或修改产品池对话框 -->
-    <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
-      <el-form ref="poolFormRef" :model="form" :rules="rules" label-width="80px">
-        <el-form-item label="池编码" prop="poolNo">
-          <el-input v-model="form.poolNo" placeholder="请输入池编码" />
-        </el-form-item>
-        <el-form-item label="池名称" prop="name">
-          <el-input v-model="form.name" placeholder="请输入池名称" />
+    <el-dialog title="项目/平台商品信息" v-model="dialog.visible" width="500px" append-to-body>
+      <el-form ref="poolFormRef" :model="form" :rules="rules" label-width="110px">
+        <el-form-item label="项目/平台名称:" prop="name">
+          <el-input v-model="form.name" placeholder="办公设备精选池" />
         </el-form-item>
-        <el-form-item label="是否显示:1=是,0=否" prop="isShow">
-          <el-input v-model="form.isShow" placeholder="请输入是否显示:1=是,0=否" />
+        <el-form-item label="状态:" prop="isShow">
+          <el-switch 
+            v-model="form.isShow" 
+            active-value="1" 
+            inactive-value="0"
+            active-text="启用"
+          />
         </el-form-item>
-        <el-form-item label="审核原因" prop="reviewReason">
-          <el-input v-model="form.reviewReason" placeholder="请输入审核原因" />
-        </el-form-item>
-        <el-form-item label="备注" prop="remark">
-            <el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
+        <el-form-item label="描述:" prop="remark">
+          <el-input 
+            v-model="form.remark" 
+            type="textarea" 
+            placeholder="请输入内容" 
+            maxlength="200"
+            show-word-limit
+            :rows="4"
+          />
         </el-form-item>
       </el-form>
       <template #footer>
         <div class="dialog-footer">
-          <el-button :loading="buttonLoading" type="primary" @click="submitForm">确 </el-button>
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm">确 </el-button>
           <el-button @click="cancel">取 消</el-button>
         </div>
       </template>
@@ -126,7 +110,7 @@ const initFormData: PoolForm = {
   id: undefined,
   poolNo: undefined,
   name: undefined,
-  isShow: undefined,
+  isShow: '1',
   productReviewStatus: undefined,
   reviewReason: undefined,
   remark: undefined,
@@ -146,11 +130,11 @@ const data = reactive<PageData<PoolForm, PoolQuery>>({
     }
   },
   rules: {
-    reviewReason: [
-      { required: true, message: "审核原因不能为空", trigger: "blur" }
+    name: [
+      { required: true, message: "项目/平台名称不能为空", trigger: "blur" }
     ],
-    remark: [
-      { required: true, message: "备注不能为空", trigger: "blur" }
+    isShow: [
+      { required: true, message: "状态不能为空", trigger: "change" }
     ],
   }
 });
@@ -190,18 +174,39 @@ const resetQuery = () => {
   handleQuery();
 }
 
-/** 多选框选中数据 */
-const handleSelectionChange = (selection: PoolVO[]) => {
-  ids.value = selection.map(item => item.id);
-  single.value = selection.length != 1;
-  multiple.value = !selection.length;
+/** 状态切换 */
+const handleStatusChange = async (row: PoolVO) => {
+  const text = row.isShow === '1' ? '启用' : '停用';
+  try {
+    await proxy?.$modal.confirm('确认要"' + text + '""' + row.name + '"吗?');
+    await updatePool(row);
+    proxy?.$modal.msgSuccess(text + '成功');
+    await getList();
+  } catch {
+    row.isShow = row.isShow === '1' ? '0' : '1';
+  }
+}
+
+/** 修改分类商品池售卖 */
+const handleModifyPool = async (row: PoolVO) => {
+  reset();
+  const res = await getPool(row.id);
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+}
+
+/** 商品池管理 */
+const handlePoolManage = (row: PoolVO) => {
+  proxy?.$router.push({
+    path: '/product/poolLink',
+    query: { poolId: row.id, poolName: row.name }
+  });
 }
 
 /** 新增按钮操作 */
 const handleAdd = () => {
   reset();
   dialog.visible = true;
-  dialog.title = "添加产品池";
 }
 
 /** 修改按钮操作 */
@@ -211,7 +216,6 @@ const handleUpdate = async (row?: PoolVO) => {
   const res = await getPool(_id);
   Object.assign(form.value, res.data);
   dialog.visible = true;
-  dialog.title = "修改产品池";
 }
 
 /** 提交按钮 */

+ 320 - 0
src/views/product/pool/review.vue

@@ -0,0 +1,320 @@
+<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" :inline="true">
+            <el-form-item label="产品池名称" prop="name">
+              <el-input v-model="queryParams.name" placeholder="请输入产品池名称" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="申请时间" prop="applyTime">
+              <el-date-picker
+                v-model="applyTimeRange"
+                type="daterange"
+                range-separator="至"
+                start-placeholder="开始日期"
+                end-placeholder="结束日期"
+                value-format="YYYY-MM-DD"
+                @change="handleApplyTimeChange"
+              />
+            </el-form-item>
+            <el-form-item label="审核时间" prop="reviewTime">
+              <el-date-picker
+                v-model="reviewTimeRange"
+                type="daterange"
+                range-separator="至"
+                start-placeholder="开始日期"
+                end-placeholder="结束日期"
+                value-format="YYYY-MM-DD"
+                @change="handleReviewTimeChange"
+              />
+            </el-form-item>
+            <el-form-item label="创建人" prop="createBy">
+              <el-input v-model="queryParams.createBy" placeholder="请输入创建人" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="审核人" prop="reviewBy">
+              <el-input v-model="queryParams.reviewBy" placeholder="请输入审核人" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
+              <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+<!--              <el-button type="primary" icon="Plus" @click="handleAdd" v-hasPermi="['product:pool:add']">新增入池单</el-button>-->
+            </el-form-item>
+          </el-form>
+        </el-card>
+      </div>
+    </transition>
+
+    <el-card shadow="never">
+      <template #header>
+        <div class="card-header">
+          <span>产品池信息列表</span>
+        </div>
+      </template>
+
+      <el-table v-loading="loading" border :data="poolList">
+        <el-table-column label="产品池名称" align="center" prop="name" min-width="200" />
+        <el-table-column label="创建人" align="center" prop="createBy" />
+        <el-table-column label="审核人" align="center" prop="reviewBy" />
+        <el-table-column label="状态" align="center" prop="productReviewStatus">
+          <template #default="scope">
+            <dict-tag :options="product_review_status" :value="scope.row.productReviewStatus" />
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" align="center" width="300" fixed="right">
+          <template #default="scope">
+            <!-- 待审核:查看、去审核 -->
+            <template v-if="scope.row.productReviewStatus === '1'">
+              <el-link type="primary" @click="handleView(scope.row)" v-hasPermi="['product:pool:query']">查看</el-link>
+              <el-divider direction="vertical" />
+              <el-link type="primary" @click="handleReview(scope.row)" v-hasPermi="['product:pool:review']">去审核</el-link>
+            </template>
+            <!-- 已驳回:查看 -->
+            <template v-else-if="scope.row.productReviewStatus === '3'">
+              <el-link type="primary" @click="handleView(scope.row)" v-hasPermi="['product:pool:query']">查看</el-link>
+            </template>
+            <!-- 已通过:查看 -->
+            <template v-else-if="scope.row.productReviewStatus === '2'">
+              <el-link type="primary" @click="handleView(scope.row)" v-hasPermi="['product:pool:query']">查看</el-link>
+            </template>
+            <!-- 待申请:查看、申请入池单、修改入池 -->
+            <template v-else-if="scope.row.productReviewStatus === '0'">
+              <el-link type="primary" @click="handleView(scope.row)" v-hasPermi="['product:pool:query']">查看</el-link>
+              <el-divider direction="vertical" />
+              <el-link type="primary" @click="handleApply(scope.row)" v-hasPermi="['product:pool:apply']">申请入池单</el-link>
+              <el-divider direction="vertical" />
+              <el-link type="primary" @click="handleModify(scope.row)" v-hasPermi="['product:pool:edit']">修改入池</el-link>
+            </template>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+    </el-card>
+
+    <!-- 查看对话框 -->
+    <el-dialog title="产品池详情" v-model="viewDialog.visible" width="600px" append-to-body>
+      <el-form ref="viewFormRef" :model="viewForm" label-width="120px">
+        <el-form-item label="产品池编号:">
+          <span>{{ viewForm.poolNo }}</span>
+        </el-form-item>
+        <el-form-item label="产品池名称:">
+          <span>{{ viewForm.name }}</span>
+        </el-form-item>
+        <el-form-item label="审核状态:">
+          <dict-tag :options="product_review_status" :value="viewForm.productReviewStatus" />
+        </el-form-item>
+        <el-form-item label="创建人:">
+          <span>{{ viewForm.createBy }}</span>
+        </el-form-item>
+        <el-form-item label="审核人:">
+          <span>{{ viewForm.reviewBy }}</span>
+        </el-form-item>
+        <el-form-item label="审核原因:" v-if="viewForm.reviewReason">
+          <span>{{ viewForm.reviewReason }}</span>
+        </el-form-item>
+        <el-form-item label="备注:" v-if="viewForm.remark">
+          <span>{{ viewForm.remark }}</span>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="viewDialog.visible = false">关 闭</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="PoolReview" lang="ts">
+import { listPool, getPool, updatePool } from '@/api/product/pool';
+import { PoolVO, PoolQuery, PoolReviewForm } from '@/api/product/pool/types';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const route = useRoute();
+
+const poolList = ref<PoolVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const total = ref(0);
+
+// 时间范围
+const applyTimeRange = ref<[string, string]>();
+const reviewTimeRange = ref<[string, string]>();
+
+const queryFormRef = ref<ElFormInstance>();
+const viewFormRef = ref<ElFormInstance>();
+
+// 查看对话框
+const viewDialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+// 字典数据
+const { product_review_status } = toRefs<any>(proxy?.useDict('product_review_status'));
+
+const initFormData: PoolQuery = {
+  pageNum: 1,
+  pageSize: 10,
+  name: undefined,
+  productReviewStatus: undefined,
+  createBy: undefined,
+  reviewBy: undefined,
+  params: {}
+}
+
+const data = reactive<PageData<PoolReviewForm, PoolQuery>>({
+  form: {},
+  queryParams: {...initFormData},
+  rules: {}
+});
+
+const { queryParams } = toRefs(data);
+
+// 查看表单
+const viewForm = ref<PoolVO>({
+  id: '',
+  poolNo: '',
+  name: '',
+  isShow: '',
+  productReviewStatus: '',
+  reviewReason: '',
+  remark: ''
+});
+
+/** 查询产品池列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listPool(queryParams.value);
+  poolList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  applyTimeRange.value = undefined;
+  reviewTimeRange.value = undefined;
+  queryFormRef.value?.resetFields();
+  handleQuery();
+}
+
+/** 申请时间范围变化 */
+const handleApplyTimeChange = (value: [string, string]) => {
+  if (value) {
+    queryParams.value.params = queryParams.value.params || {};
+    queryParams.value.params.beginApplyTime = value[0];
+    queryParams.value.params.endApplyTime = value[1];
+  } else {
+    if (queryParams.value.params) {
+      delete queryParams.value.params.beginApplyTime;
+      delete queryParams.value.params.endApplyTime;
+    }
+  }
+}
+
+/** 审核时间范围变化 */
+const handleReviewTimeChange = (value: [string, string]) => {
+  if (value) {
+    queryParams.value.params = queryParams.value.params || {};
+    queryParams.value.params.beginReviewTime = value[0];
+    queryParams.value.params.endReviewTime = value[1];
+  } else {
+    if (queryParams.value.params) {
+      delete queryParams.value.params.beginReviewTime;
+      delete queryParams.value.params.endReviewTime;
+    }
+  }
+}
+
+/** 新增入池单 */
+const handleAdd = () => {
+  proxy?.$router.push({
+    path: '/product/poolLink',
+    query: { type: 'add' }
+  });
+}
+
+/** 查看详情 */
+const handleView = async (row: PoolVO) => {
+  const res = await getPool(row.id);
+  viewForm.value = res.data;
+  viewDialog.visible = true;
+}
+
+/** 去审核 */
+const handleReview = (row: PoolVO) => {
+  proxy?.$router.push({
+    path: '/product/pool/reviewDetail',
+    query: { poolId: row.id }
+  });
+}
+
+/** 提交审核 */
+const submitReview = () => {
+  reviewFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      try {
+        await updatePool({
+          id: reviewForm.value.id,
+          productReviewStatus: reviewForm.value.productReviewStatus,
+          reviewReason: reviewForm.value.reviewReason
+        });
+        proxy?.$modal.msgSuccess("审核成功");
+        reviewDialog.visible = false;
+        await getList();
+      } finally {
+        buttonLoading.value = false;
+      }
+    }
+  });
+}
+
+/** 申请入池单 */
+const handleApply = async (row: PoolVO) => {
+  try {
+    await proxy?.$modal.confirm('确认要申请该产品池入池吗?');
+    await updatePool({
+      id: row.id,
+      productReviewStatus: '1' // 更新为待审核
+    });
+    proxy?.$modal.msgSuccess("申请成功");
+    await getList();
+  } catch {
+    // 取消操作
+  }
+}
+
+/** 修改入池 */
+const handleModify = (row: PoolVO) => {
+  proxy?.$router.push({
+    path: '/product/poolLink',
+    query: { poolId: row.id, poolName: row.name, type: 'edit' }
+  });
+}
+
+onMounted(() => {
+  // 从路由获取审核状态
+  if (route.query.productReviewStatus) {
+    queryParams.value.productReviewStatus = route.query.productReviewStatus as string;
+  }
+  getList();
+});
+</script>
+
+<style scoped lang="scss">
+.card-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+</style>

+ 408 - 0
src/views/product/pool/reviewDetail.vue

@@ -0,0 +1,408 @@
+<template>
+  <div class="p-2">
+    <!-- 返回按钮 -->
+    <div class="mb-4 flex items-center">
+      <el-button link icon="ArrowLeft" @click="goBack">返回</el-button>
+      <span class="ml-2 text-lg font-bold">入池清单信息</span>
+    </div>
+
+    <!-- 入池清单信息卡片 -->
+    <el-card shadow="hover" class="mb-4">
+      <el-descriptions :column="4" border>
+        <el-descriptions-item label="订单编号">{{ poolInfo.poolNo }}</el-descriptions-item>
+        <el-descriptions-item label="产品池名称">{{ poolInfo.name }}</el-descriptions-item>
+        <el-descriptions-item label="申请时间">{{ poolInfo.createTime }}</el-descriptions-item>
+        <el-descriptions-item label="审核时间">{{ poolInfo.reviewTime || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="状态">
+          <dict-tag :options="product_review_status" :value="poolInfo.productReviewStatus" />
+        </el-descriptions-item>
+        <el-descriptions-item label="创建人">{{ poolInfo.createBy }}</el-descriptions-item>
+        <el-descriptions-item label="11" label-class-name="custom-label">11</el-descriptions-item>
+        <el-descriptions-item label="审核人">{{ poolInfo.reviewBy || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="驳回意见" :span="4">
+          <span v-if="poolInfo.reviewReason">{{ poolInfo.reviewReason }}</span>
+          <span v-else class="text-gray-400">-</span>
+        </el-descriptions-item>
+      </el-descriptions>
+    </el-card>
+
+    <!-- 标签页 -->
+    <el-card shadow="never">
+      <el-tabs v-model="activeTab" @tab-click="handleTabClick">
+        <el-tab-pane label="待入池产品" name="pending">
+          <!-- 待入池产品按钮 -->
+          <div class="mb-4">
+            <el-button type="primary" icon="Plus">加入入池清单</el-button>
+          </div>
+
+          <!-- 待入池产品表格 -->
+          <el-table v-loading="pendingLoading" border :data="pendingProductList">
+            <el-table-column type="index" label="序号" width="60" align="center" />
+            <el-table-column label="商品编号" align="center" prop="productNo" width="120" />
+            <el-table-column label="商品图片" align="center" prop="productImageUrl" width="100">
+              <template #default="scope">
+                <image-preview :src="scope.row.productImageUrl" :width="60" :height="60"/>
+              </template>
+            </el-table-column>
+            <el-table-column label="商品信息" align="center" min-width="200">
+              <template #default="scope">
+                <div class="text-left" style="font-size: 12px;">
+                  <div>{{ scope.row.itemName }}</div>
+                  <div class="text-gray-500">品牌:{{ scope.row.brandName || '-' }}</div>
+                  <div class="text-gray-500">库存:{{ scope.row.stock || '-' }}</div>
+                </div>
+              </template>
+            </el-table-column>
+            <el-table-column label="单位" align="center" prop="unitName" width="80" />
+            <el-table-column label="SKU价格" 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.marketPrice || '0.00' }}</span>
+                  </div>
+                  <div>
+                    <span class="text-gray-500">平台售价:</span>
+                    <span class="text-red-500">¥{{ scope.row.platformPrice || '0.00' }}</span>
+                  </div>
+                  <div>
+                    <span class="text-gray-500">最低售价:</span>
+                    <span class="text-red-500">¥{{ scope.row.minPrice || '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.purchasePrice || '0.00' }}</span>
+                  </div>
+                  <div>
+                    <span class="text-gray-500">暂估毛利率:</span>
+                    <span>{{ scope.row.grossMargin || '0.00' }}%</span>
+                  </div>
+                </div>
+              </template>
+            </el-table-column>
+            <el-table-column label="项目/平台价" align="center" prop="productPrice" width="120">
+              <template #default="scope">
+                ¥{{ scope.row.productPrice || '0.00' }}
+              </template>
+            </el-table-column>
+            <el-table-column label="商品状态" align="center" width="100">
+              <template #default="scope">
+                <dict-tag :options="product_review_status" :value="scope.row.productReviewStatus" />
+              </template>
+            </el-table-column>
+            <el-table-column label="创建供应商" align="center" prop="supplier" width="120" />
+          </el-table>
+
+          <pagination v-show="pendingTotal > 0" :total="pendingTotal" v-model:page="pendingQuery.pageNum" v-model:limit="pendingQuery.pageSize" @pagination="getPendingList" />
+        </el-tab-pane>
+
+        <el-tab-pane label="入池清单" name="approved">
+          <!-- 入池清单表格 -->
+          <el-table v-loading="approvedLoading" border :data="approvedProductList">
+            <el-table-column type="index" label="序号" width="60" align="center" />
+            <el-table-column label="商品编号" align="center" prop="productNo" width="120" />
+            <el-table-column label="商品图片" align="center" prop="productImageUrl" width="100">
+              <template #default="scope">
+                <image-preview :src="scope.row.productImageUrl" :width="60" :height="60"/>
+              </template>
+            </el-table-column>
+            <el-table-column label="商品信息" align="center" min-width="200">
+              <template #default="scope">
+                <div class="text-left" style="font-size: 12px;">
+                  <div>{{ scope.row.itemName }}</div>
+                  <div class="text-gray-500">品牌:{{ scope.row.brandName || '-' }}</div>
+                  <div class="text-gray-500">库存:{{ scope.row.stock || '-' }}</div>
+                </div>
+              </template>
+            </el-table-column>
+            <el-table-column label="单位" align="center" prop="unitName" width="80" />
+            <el-table-column label="SKU价格" 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.marketPrice || '0.00' }}</span>
+                  </div>
+                  <div>
+                    <span class="text-gray-500">平台售价:</span>
+                    <span class="text-red-500">¥{{ scope.row.platformPrice || '0.00' }}</span>
+                  </div>
+                  <div>
+                    <span class="text-gray-500">最低售价:</span>
+                    <span class="text-red-500">¥{{ scope.row.minPrice || '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.purchasePrice || '0.00' }}</span>
+                  </div>
+                  <div>
+                    <span class="text-gray-500">暂估毛利率:</span>
+                    <span>{{ scope.row.grossMargin || '0.00' }}%</span>
+                  </div>
+                </div>
+              </template>
+            </el-table-column>
+            <el-table-column label="项目/平台价" align="center" prop="productPrice" width="120">
+              <template #default="scope">
+                ¥{{ scope.row.productPrice || '0.00' }}
+              </template>
+            </el-table-column>
+            <el-table-column label="商品状态" align="center" width="100">
+              <template #default="scope">
+                <dict-tag :options="product_review_status" :value="scope.row.productReviewStatus" />
+              </template>
+            </el-table-column>
+            <el-table-column label="创建供应商" align="center" prop="supplier" width="120" />
+          </el-table>
+
+          <pagination v-show="approvedTotal > 0" :total="approvedTotal" v-model:page="approvedQuery.pageNum" v-model:limit="approvedQuery.pageSize" @pagination="getApprovedList" />
+        </el-tab-pane>
+      </el-tabs>
+    </el-card>
+
+    <!-- 底部审核按钮 -->
+    <div class="fixed bottom-0 left-0 right-0 bg-white border-t p-4 flex justify-center gap-4" style="z-index: 100;">
+      <el-button @click="handleReject" size="large">驳回</el-button>
+      <el-button type="primary" @click="handleApprove" size="large">通过</el-button>
+    </div>
+
+    <!-- 驳回对话框 -->
+    <el-dialog title="驳回原因" v-model="rejectDialog.visible" width="500px" append-to-body>
+      <el-form ref="rejectFormRef" :model="rejectForm" :rules="rejectRules" label-width="100px">
+        <el-form-item label="驳回原因:" prop="reviewReason">
+          <el-input 
+            v-model="rejectForm.reviewReason" 
+            type="textarea" 
+            placeholder="请输入驳回原因" 
+            maxlength="200"
+            show-word-limit
+            :rows="4"
+          />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitReject">确 认</el-button>
+          <el-button @click="rejectDialog.visible = false">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="PoolReviewDetail" lang="ts">
+import { useRouter, useRoute } from 'vue-router';
+import { getPool, updatePool } from '@/api/product/pool';
+import { PoolVO } from '@/api/product/pool/types';
+import { listPoolLink } from '@/api/product/poolLink';
+import { PoolLinkVO, PoolLinkQuery } from '@/api/product/poolLink/types';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const router = useRouter();
+const route = useRoute();
+
+// 产品池信息
+const poolInfo = ref<PoolVO>({
+  id: '',
+  poolNo: '',
+  name: '',
+  isShow: '',
+  productReviewStatus: '',
+  reviewReason: '',
+  remark: '',
+  createTime: '',
+  createBy: '',
+  reviewBy: ''
+});
+
+// 当前标签页
+const activeTab = ref('pending');
+
+// 待入池产品列表
+const pendingProductList = ref<PoolLinkVO[]>([]);
+const pendingLoading = ref(false);
+const pendingTotal = ref(0);
+const pendingQuery = ref<PoolLinkQuery>({
+  pageNum: 1,
+  pageSize: 10,
+  poolId: undefined,
+  productReviewStatus: '1' // 待审核
+});
+
+// 入池清单列表
+const approvedProductList = ref<PoolLinkVO[]>([]);
+const approvedLoading = ref(false);
+const approvedTotal = ref(0);
+const approvedQuery = ref<PoolLinkQuery>({
+  pageNum: 1,
+  pageSize: 10,
+  poolId: undefined,
+  productReviewStatus: '2' // 审核通过
+});
+
+// 驳回对话框
+const rejectDialog = reactive({
+  visible: false
+});
+
+const rejectFormRef = ref<ElFormInstance>();
+const rejectForm = ref({
+  reviewReason: ''
+});
+
+const rejectRules = {
+  reviewReason: [
+    { required: true, message: "请输入驳回原因", trigger: "blur" }
+  ]
+};
+
+const buttonLoading = ref(false);
+
+// 字典数据
+const { product_review_status } = toRefs<any>(proxy?.$dict.getDict('product_review_status'));
+
+/** 获取产品池信息 */
+const getPoolInfo = async () => {
+  try {
+    const poolId = route.params.id || route.query.poolId;
+    if (!poolId) {
+      proxy?.$modal.msgError('缺少产品池ID');
+      goBack();
+      return;
+    }
+    
+    const res = await getPool(poolId);
+    poolInfo.value = res.data;
+    
+    // 设置查询参数的poolId
+    pendingQuery.value.poolId = poolId;
+    approvedQuery.value.poolId = poolId;
+  } catch (error) {
+    console.error('获取产品池信息失败:', error);
+    proxy?.$modal.msgError('获取产品池信息失败');
+  }
+};
+
+/** 获取待入池产品列表 */
+const getPendingList = async () => {
+  pendingLoading.value = true;
+  try {
+    const res = await listPoolLink(pendingQuery.value);
+    pendingProductList.value = res.rows || [];
+    pendingTotal.value = res.total || 0;
+  } catch (error) {
+    console.error('获取待入池产品列表失败:', error);
+    pendingProductList.value = [];
+    pendingTotal.value = 0;
+  } finally {
+    pendingLoading.value = false;
+  }
+};
+
+/** 获取入池清单列表 */
+const getApprovedList = async () => {
+  approvedLoading.value = true;
+  try {
+    const res = await listPoolLink(approvedQuery.value);
+    approvedProductList.value = res.rows || [];
+    approvedTotal.value = res.total || 0;
+  } catch (error) {
+    console.error('获取入池清单列表失败:', error);
+    approvedProductList.value = [];
+    approvedTotal.value = 0;
+  } finally {
+    approvedLoading.value = false;
+  }
+};
+
+/** 标签页切换 */
+const handleTabClick = () => {
+  if (activeTab.value === 'pending') {
+    getPendingList();
+  } else {
+    getApprovedList();
+  }
+};
+
+/** 返回 */
+const goBack = () => {
+  router.back();
+};
+
+/** 驳回 */
+const handleReject = () => {
+  rejectForm.value.reviewReason = '';
+  rejectDialog.visible = true;
+};
+
+/** 提交驳回 */
+const submitReject = () => {
+  rejectFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      try {
+        await updatePool({
+          id: poolInfo.value.id,
+          productReviewStatus: '3', // 驳回
+          reviewReason: rejectForm.value.reviewReason
+        });
+        proxy?.$modal.msgSuccess("驳回成功");
+        rejectDialog.visible = false;
+        goBack();
+      } finally {
+        buttonLoading.value = false;
+      }
+    }
+  });
+};
+
+/** 通过 */
+const handleApprove = async () => {
+  try {
+    await proxy?.$modal.confirm('确认通过该产品池审核吗?');
+    buttonLoading.value = true;
+    try {
+      await updatePool({
+        id: poolInfo.value.id,
+        productReviewStatus: '2', // 通过
+        reviewReason: ''
+      });
+      proxy?.$modal.msgSuccess("审核通过");
+      goBack();
+    } finally {
+      buttonLoading.value = false;
+    }
+  } catch {
+    // 取消操作
+  }
+};
+
+onMounted(async () => {
+  await getPoolInfo();
+  // 默认加载待入池产品
+  await getPendingList();
+});
+</script>
+
+<style scoped lang="scss">
+// 为底部固定按钮留出空间
+:deep(.el-card__body) {
+  padding-bottom: 80px;
+}
+
+.custom-label {
+  font-weight: normal;
+}
+</style>

+ 517 - 202
src/views/product/poolLink/index.vue

@@ -1,31 +1,82 @@
 <template>
   <div class="p-2">
+    <!-- 返回按钮 -->
+    <div class="mb-4 flex items-center">
+      <el-button link icon="ArrowLeft" @click="goBack">返回</el-button>
+      <span class="ml-2 text-lg font-bold">商品配置</span>
+    </div>
+
+    <!-- 搜索区域 -->
     <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" :inline="true">
-            <el-form-item label="所属池ID" prop="poolId">
-              <el-input v-model="queryParams.poolId" placeholder="请输入所属池ID" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="产品id" prop="productId">
-              <el-input v-model="queryParams.productId" placeholder="请输入产品id" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="产品价格" prop="productPrice">
-              <el-input v-model="queryParams.productPrice" placeholder="请输入产品价格" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="审核原因" prop="reviewReason">
-              <el-input v-model="queryParams.reviewReason" placeholder="请输入审核原因" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="是否显示:1=是,0=否" prop="isShow">
-              <el-input v-model="queryParams.isShow" placeholder="请输入是否显示:1=是,0=否" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="平台标识" prop="platformCode">
-              <el-input v-model="queryParams.platformCode" placeholder="请输入平台标识" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <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-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-input v-model="queryParams.brandId" placeholder="请选择" clearable />
+                </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-select>
+                </el-form-item>
+              </el-col>
+            </el-row>
+            <el-row :gutter="20">
+              <el-col :span="6">
+                <el-form-item label="商品类别" prop="categoryId">
+                  <el-tree-select
+                    v-model="queryParams.categoryId"
+                    :data="categoryOptions"
+                    :props="{ value: 'id', label: 'label', children: 'children' }"
+                    check-strictly
+                    :render-after-expand="false"
+                    clearable
+                    placeholder="请选择商品类别"
+                  >
+                    <template #default="{ data }">
+                      <span>{{ getCategoryFullPath(data.id) }}</span>
+                    </template>
+                  </el-tree-select>
+                </el-form-item>
+              </el-col>
+              <el-col :span="6">
+                <el-form-item label="创建供应商" prop="supplier">
+                  <el-input v-model="queryParams.supplier" placeholder="请选择创建供应商" clearable />
+                </el-form-item>
+              </el-col>
+              <el-col :span="6">
+                <el-form-item label="入池时间" prop="dateRange">
+                  <el-date-picker
+                    v-model="queryParams.dateRange"
+                    type="daterange"
+                    range-separator="至"
+                    start-placeholder="开始时间"
+                    end-placeholder="结束时间"
+                    style="width: 100%"
+                  />
+                </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-button type="danger" plain @click="handleClearPool">清空产品池</el-button>
+              </el-col>
+            </el-row>
           </el-form>
         </el-card>
       </div>
@@ -33,242 +84,506 @@
 
     <el-card shadow="never">
       <template #header>
-        <el-row :gutter="10" class="mb8">
-          <el-col :span="1.5">
-            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['product:poolLink:add']">新增</el-button>
-          </el-col>
-          <el-col :span="1.5">
-            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['product:poolLink:edit']">修改</el-button>
-          </el-col>
-          <el-col :span="1.5">
-            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['product:poolLink:remove']">删除</el-button>
-          </el-col>
-          <el-col :span="1.5">
-            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['product:poolLink:export']">导出</el-button>
-          </el-col>
-          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
-        </el-row>
+        <div class="flex justify-between items-center">
+          <span class="font-bold">商品列表信息列表</span>
+          <div class="flex gap-2">
+            <el-button type="primary" icon="Plus" @click="handleAddProduct">添加商品</el-button>
+            <el-button type="success" icon="Download" @click="handleExportProduct">导出商品</el-button>
+            <el-button type="warning" icon="Upload" @click="handleImportProduct">导入商品</el-button>
+          </div>
+        </div>
       </template>
 
-      <el-table v-loading="loading" border :data="poolLinkList" @selection-change="handleSelectionChange">
-        <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="主键ID" align="center" prop="id" v-if="true" />
-        <el-table-column label="所属池ID" align="center" prop="poolId" />
-        <el-table-column label="产品id" align="center" prop="productId" />
-        <el-table-column label="产品价格" align="center" prop="productPrice" />
-        <el-table-column label="是否在池中:1-是,0-否" align="center" prop="isPoolStatus" />
-        <el-table-column label="产品审核状态 0=待提交,1=待审核,2=审核通过,3=审核驳回" align="center" prop="productReviewStatus" />
-        <el-table-column label="审核原因" align="center" prop="reviewReason" />
-        <el-table-column label="是否显示:1=是,0=否" align="center" prop="isShow" />
-        <el-table-column label="备注" align="center" prop="remark" />
-        <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+      <el-table v-loading="loading" border :data="productList">
+        <el-table-column type="index" label="序号" width="60" align="center" />
+        <el-table-column label="商品编号" align="center" prop="productNo" width="120" />
+        <el-table-column label="商品图片" align="center" prop="productImageUrl" width="100">
+          <template #default="scope">
+            <image-preview :src="scope.row.productImageUrl" :width="60" :height="60"/>
+          </template>
+        </el-table-column>
+        <el-table-column label="商品信息" align="center" width="200">
+          <template #default="scope">
+            <div class="text-left" style="font-size: 12px;">
+              <div>{{ scope.row.itemName }}</div>
+              <div class="text-gray-500">品牌:{{ scope.row.brandName || '-' }}</div>
+              <div class="text-gray-500">库存:{{ scope.row.stock || '999' }}</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>{{ scope.row.categoryName || '办公设备+扫描设备+平板式扫描仪' }}</div>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="单位" align="center" prop="unitName" width="80" />
+        <el-table-column label="SKU价格" 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.marketPrice || '0.00' }}</span>
+              </div>
+              <div>
+                <span class="text-gray-500">平台售价:</span>
+                <span class="text-red-500">¥{{ scope.row.platformPrice || '0.00' }}</span>
+              </div>
+              <div>
+                <span class="text-gray-500">最低售价:</span>
+                <span class="text-red-500">¥{{ scope.row.minPrice || '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.purchasePrice || '0.00' }}</span>
+              </div>
+              <div>
+                <span class="text-gray-500">暂估毛利率:</span>
+                <span>{{ scope.row.grossMargin || '0.00' }}%</span>
+              </div>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="项目平台价" align="center" prop="platformPrice" width="100" />
+        <el-table-column label="商品状态" align="center" width="80">
           <template #default="scope">
-            <el-tooltip content="修改" placement="top">
-              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['product:poolLink:edit']"></el-button>
-            </el-tooltip>
-            <el-tooltip content="删除" placement="top">
-              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['product:poolLink:remove']"></el-button>
-            </el-tooltip>
+            <el-tag v-if="scope.row.productStatus === '1'" type="success">上架</el-tag>
+            <el-tag v-else type="warning">下架</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="入池时间" align="center" prop="createTime" width="120" />
+        <el-table-column label="创建供应商" align="center" prop="supplier" width="100" />
+        <el-table-column label="操作" align="center" width="120" fixed="right">
+          <template #default="scope">
+            <div class="flex flex-col gap-1">
+              <el-link type="primary" :underline="false" @click="handlePriceMaintain(scope.row)">价格维护</el-link>
+              <el-link type="primary" :underline="false" @click="handleInventoryMaintain(scope.row)">修改库存</el-link>
+              <el-link type="danger" :underline="false" @click="handleRemoveProduct(scope.row)">移除商品池</el-link>
+            </div>
           </template>
         </el-table-column>
       </el-table>
 
       <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
     </el-card>
-    <!-- 添加或修改产品池和产品关联对话框 -->
-    <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
-      <el-form ref="poolLinkFormRef" :model="form" :rules="rules" label-width="80px">
-        <el-form-item label="所属池ID" prop="poolId">
-          <el-input v-model="form.poolId" placeholder="请输入所属池ID" />
-        </el-form-item>
-        <el-form-item label="产品id" prop="productId">
-          <el-input v-model="form.productId" placeholder="请输入产品id" />
-        </el-form-item>
-        <el-form-item label="产品价格" prop="productPrice">
-          <el-input v-model="form.productPrice" placeholder="请输入产品价格" />
-        </el-form-item>
-        <el-form-item label="审核原因" prop="reviewReason">
-          <el-input v-model="form.reviewReason" placeholder="请输入审核原因" />
-        </el-form-item>
-        <el-form-item label="是否显示:1=是,0=否" prop="isShow">
-          <el-input v-model="form.isShow" placeholder="请输入是否显示:1=是,0=否" />
-        </el-form-item>
-        <el-form-item label="备注" prop="remark">
-            <el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <div class="dialog-footer">
-          <el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
-          <el-button @click="cancel">取 消</el-button>
-        </div>
-      </template>
+
+    <!-- 添加商品对话框 -->
+    <el-dialog title="添加商品" v-model="addProductDialog.visible" width="1400px" append-to-body top="5vh">
+      <div class="add-product-dialog">
+        <!-- 搜索区域 -->
+        <el-form :model="addProductQuery" :inline="true" class="mb-4">
+          <el-form-item>
+            <el-button type="primary" icon="Plus" @click="handleBatchAdd">加入清单</el-button>
+          </el-form-item>
+          <el-form-item label="商品名称:">
+            <el-input v-model="addProductQuery.itemName" placeholder="商品名称" clearable style="width: 200px" />
+          </el-form-item>
+          <el-form-item label="商品编号:">
+            <el-input v-model="addProductQuery.productNo" placeholder="商品编号" clearable style="width: 200px" />
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" icon="Search" @click="handleSearchProducts">搜索</el-button>
+          </el-form-item>
+        </el-form>
+
+        <!-- 商品列表 -->
+        <el-table
+          ref="addProductTableRef"
+          v-loading="addProductDialog.loading"
+          :data="addProductDialog.productList"
+          border
+          @selection-change="handleSelectionChange"
+          max-height="500"
+        >
+          <el-table-column type="selection" width="55" align="center" />
+          <el-table-column label="商品编号" align="center" prop="productNo" width="120" />
+          <el-table-column label="商品图片" align="center" prop="productImageUrl" width="100">
+            <template #default="scope">
+              <image-preview :src="scope.row.productImageUrl" :width="60" :height="60"/>
+            </template>
+          </el-table-column>
+          <el-table-column label="商品信息" align="center" min-width="200">
+            <template #default="scope">
+              <div class="text-left" style="font-size: 12px;">
+                <div>{{ scope.row.itemName }}</div>
+                <div class="text-gray-500">品牌:{{ scope.row.brandName || '雅唐' }}</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>{{ getCategoryName(scope.row) }}</div>
+              </div>
+            </template>
+          </el-table-column>
+          <el-table-column label="单位" align="center" width="100">
+            <template #default="scope">
+              <div class="text-left" style="font-size: 12px;">
+                <div>单位:{{ scope.row.unitName || '件' }}</div>
+                <div>起订量:{{ scope.row.minOrderQuantity || 1 }}</div>
+              </div>
+            </template>
+          </el-table-column>
+          <el-table-column label="SKU价格" 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.midRangePrice || '0.00' }}</span>
+                </div>
+                <div>
+                  <span class="text-gray-500">平台价:</span>
+                  <span class="text-red-500">¥{{ scope.row.standardPrice || '0.00' }}</span>
+                </div>
+                <div>
+                  <span class="text-gray-500">最低价:</span>
+                  <span>¥{{ scope.row.certificatePrice || '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 class="text-red-500">库存总数:{{ scope.row.stock || 0 }}</div>
+                <div>现有库存:{{ scope.row.availableStock || 0 }}</div>
+                <div>虚拟库存:{{ scope.row.virtualStock || 0 }}</div>
+                <div class="text-orange-500">[现有库存不足时]</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>供应商数量:{{ scope.row.supplierCount || 0 }}</div>
+              </div>
+            </template>
+          </el-table-column>
+          <el-table-column label="协议价" align="center" prop="agreementPrice" width="100" />
+          <el-table-column label="操作" align="center" width="100" fixed="right">
+            <template #default="scope">
+              <el-link type="primary" :underline="false" @click="handleAddSingleProduct(scope.row)">加入清单</el-link>
+            </template>
+          </el-table-column>
+        </el-table>
+
+        <pagination
+          v-show="addProductDialog.total > 0"
+          :total="addProductDialog.total"
+          v-model:page="addProductQuery.pageNum"
+          v-model:limit="addProductQuery.pageSize"
+          @pagination="getProductList"
+        />
+      </div>
     </el-dialog>
   </div>
 </template>
 
 <script setup name="PoolLink" lang="ts">
-import { listPoolLink, getPoolLink, delPoolLink, addPoolLink, updatePoolLink } from '@/api/product/poolLink';
-import { PoolLinkVO, PoolLinkQuery, PoolLinkForm } from '@/api/product/poolLink/types';
+import { useRouter, useRoute } from 'vue-router';
+import { categoryTree, listBase } from '@/api/product/base';
+import { BaseVO, BaseQuery } from '@/api/product/base/types';
+import { listPoolLink, batchAddProducts, BatchAddProductData } from '@/api/product/poolLink';
+import { PoolLinkQuery } from '@/api/product/poolLink/types';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const router = useRouter();
+const route = useRoute();
 
-const poolLinkList = ref<PoolLinkVO[]>([]);
-const buttonLoading = ref(false);
-const loading = ref(true);
+const productList = ref<any[]>([]);
+const loading = ref(false);
 const showSearch = ref(true);
-const ids = ref<Array<string | number>>([]);
-const single = ref(true);
-const multiple = ref(true);
 const total = ref(0);
 
 const queryFormRef = ref<ElFormInstance>();
-const poolLinkFormRef = ref<ElFormInstance>();
 
-const dialog = reactive<DialogOption>({
+const queryParams = ref({
+  pageNum: 1,
+  pageSize: 10,
+  poolId: route.params.id || route.query.poolId,
+  productNo: undefined,
+  itemName: undefined,
+  brandId: undefined,
+  productStatus: undefined,
+  categoryId: undefined,
+  supplier: undefined,
+  dateRange: undefined,
+});
+
+const categoryOptions = ref<any[]>([]);
+const categoryMap = ref<Map<string | number, any>>(new Map());
+
+// 添加商品对话框
+const addProductDialog = reactive({
   visible: false,
-  title: ''
+  loading: false,
+  productList: [] as BaseVO[],
+  total: 0,
 });
 
-const initFormData: PoolLinkForm = {
-  id: undefined,
-  poolId: undefined,
-  productId: undefined,
-  productPrice: undefined,
-  isPoolStatus: undefined,
-  productReviewStatus: undefined,
-  reviewReason: undefined,
-  isShow: undefined,
-  remark: undefined,
-}
-const data = reactive<PageData<PoolLinkForm, PoolLinkQuery>>({
-  form: {...initFormData},
-  queryParams: {
-    pageNum: 1,
-    pageSize: 10,
-    poolId: undefined,
-    productId: undefined,
-    productPrice: undefined,
-    isPoolStatus: undefined,
-    productReviewStatus: undefined,
-    reviewReason: undefined,
-    isShow: undefined,
-    platformCode: undefined,
-    params: {
-    }
-  },
-  rules: {
-    productPrice: [
-      { required: true, message: "产品价格不能为空", trigger: "blur" }
-    ],
-    productReviewStatus: [
-      { required: true, message: "产品审核状态 0=待提交,1=待审核,2=审核通过,3=审核驳回不能为空", trigger: "change" }
-    ],
-    reviewReason: [
-      { required: true, message: "审核原因不能为空", trigger: "blur" }
-    ],
-    isShow: [
-      { required: true, message: "是否显示:1=是,0=否不能为空", trigger: "blur" }
-    ],
-    remark: [
-      { required: true, message: "备注不能为空", trigger: "blur" }
-    ],
-  }
+// 添加商品查询参数
+const addProductQuery = ref<BaseQuery>({
+  pageNum: 1,
+  pageSize: 10,
+  productNo: undefined,
+  itemName: undefined,
 });
 
-const { queryParams, form, rules } = toRefs(data);
+// 选中的商品
+const selectedProducts = ref<BaseVO[]>([]);
+const addProductTableRef = ref<any>();
 
-/** 查询产品池和产品关联列表 */
+/** 获取分类树 */
+const getCategoryTree = async () => {
+  try {
+    const res = await categoryTree();
+    categoryOptions.value = res.data || [];
+    // 构建分类映射
+    buildCategoryMap(categoryOptions.value);
+  } catch (error) {
+    console.error('获取分类树失败:', error);
+  }
+};
+
+/** 构建分类映射 */
+const buildCategoryMap = (categories: any[], parentPath = '') => {
+  categories.forEach(category => {
+    const fullPath = parentPath ? `${parentPath} > ${category.label}` : category.label;
+    categoryMap.value.set(category.id, { ...category, fullPath });
+    if (category.children && category.children.length > 0) {
+      buildCategoryMap(category.children, fullPath);
+    }
+  });
+};
+
+/** 获取分类完整路径 */
+const getCategoryFullPath = (categoryId: string | number): string => {
+  const category = categoryMap.value.get(categoryId);
+  return category?.fullPath || '';
+};
+
+/** 查询商品列表 */
 const getList = async () => {
   loading.value = true;
-  const res = await listPoolLink(queryParams.value);
-  poolLinkList.value = res.rows;
-  total.value = res.total;
-  loading.value = false;
-}
-
-/** 取消按钮 */
-const cancel = () => {
-  reset();
-  dialog.visible = false;
+  try {
+    const query: PoolLinkQuery = {
+      pageNum: queryParams.value.pageNum,
+      pageSize: queryParams.value.pageSize,
+      poolId: queryParams.value.poolId,
+      productNo: queryParams.value.productNo,
+      itemName: queryParams.value.itemName,
+      brandId: queryParams.value.brandId,
+      categoryId: queryParams.value.categoryId,
+      productStatus: queryParams.value.productStatus,
+      supplier: queryParams.value.supplier
+    };
+    
+    // 处理日期范围
+    if (queryParams.value.dateRange && queryParams.value.dateRange.length === 2) {
+      query.params = {
+        beginCreateTime: queryParams.value.dateRange[0],
+        endCreateTime: queryParams.value.dateRange[1]
+      };
+    }
+    
+    const res = await listPoolLink(query);
+    productList.value = res.rows || [];
+    total.value = res.total || 0;
+  } catch (error) {
+    console.error('获取商品列表失败:', error);
+    productList.value = [];
+    total.value = 0;
+  } finally {
+    loading.value = false;
+  }
 }
 
-/** 表单重置 */
-const reset = () => {
-  form.value = {...initFormData};
-  poolLinkFormRef.value?.resetFields();
+/** 返回 */
+const goBack = () => {
+  router.back();
 }
 
-/** 搜索按钮操作 */
+/** 搜索 */
 const handleQuery = () => {
   queryParams.value.pageNum = 1;
   getList();
 }
 
-/** 重置按钮操作 */
+/** 重置 */
 const resetQuery = () => {
   queryFormRef.value?.resetFields();
   handleQuery();
 }
 
-/** 多选框选中数据 */
-const handleSelectionChange = (selection: PoolLinkVO[]) => {
-  ids.value = selection.map(item => item.id);
-  single.value = selection.length != 1;
-  multiple.value = !selection.length;
+/** 清空产品池 */
+const handleClearPool = async () => {
+  await proxy?.$modal.confirm('确认要清空该产品池的所有商品吗?');
+  // TODO: 调用清空API
+  proxy?.$modal.msgSuccess('清空成功');
+  await getList();
 }
 
-/** 新增按钮操作 */
-const handleAdd = () => {
-  reset();
-  dialog.visible = true;
-  dialog.title = "添加产品池和产品关联";
+/** 添加商品 */
+const handleAddProduct = () => {
+  addProductDialog.visible = true;
+  // 重置查询条件
+  addProductQuery.value = {
+    pageNum: 1,
+    pageSize: 10,
+    productNo: undefined,
+    itemName: undefined,
+  };
+  selectedProducts.value = [];
+  getProductList();
+};
+
+/** 获取商品列表 */
+const getProductList = async () => {
+  addProductDialog.loading = true;
+  try {
+    const res = await listBase(addProductQuery.value);
+    // 兼容两种返回结构
+    if (res.rows) {
+      addProductDialog.productList = res.rows;
+      addProductDialog.total = res.total || 0;
+    } else if (res.data) {
+      addProductDialog.productList = Array.isArray(res.data) ? res.data : [];
+      addProductDialog.total = addProductDialog.productList.length;
+    } else {
+      addProductDialog.productList = [];
+      addProductDialog.total = 0;
+    }
+  } catch (error) {
+    console.error('获取商品列表失败:', error);
+    addProductDialog.productList = [];
+    addProductDialog.total = 0;
+  } finally {
+    addProductDialog.loading = false;
+  }
+};
+
+/** 搜索商品 */
+const handleSearchProducts = () => {
+  addProductQuery.value.pageNum = 1;
+  getProductList();
+};
+
+/** 选择变化 */
+const handleSelectionChange = (selection: BaseVO[]) => {
+  selectedProducts.value = selection;
+};
+
+/** 批量加入清单 */
+const handleBatchAdd = async () => {
+  if (selectedProducts.value.length === 0) {
+    proxy?.$modal.msgWarning('请先选择要添加的商品');
+    return;
+  }
+  
+  try {
+    // 构造批量添加的数据
+    const batchData: BatchAddProductData = {
+      poolId: queryParams.value.poolId as number,
+      products: selectedProducts.value.map(product => ({
+        productId: product.id as number,
+        agreementPrice: product.standardPrice || product.midRangePrice // 使用平台价或市场价作为协议价
+      }))
+    };
+    
+    await batchAddProducts(batchData);
+    proxy?.$modal.msgSuccess(`成功添加 ${selectedProducts.value.length} 个商品到商品池`);
+    addProductDialog.visible = false;
+    // 清空选中项
+    selectedProducts.value = [];
+    if (addProductTableRef.value) {
+      addProductTableRef.value.clearSelection();
+    }
+    await getList();
+  } catch (error) {
+    console.error('添加商品失败:', error);
+  }
+};
+
+/** 添加单个商品 */
+const handleAddSingleProduct = async (row: BaseVO) => {
+  try {
+    // 构造单个商品添加的数据
+    const batchData: BatchAddProductData = {
+      poolId: queryParams.value.poolId as number,
+      products: [{
+        productId: row.id as number,
+        agreementPrice: row.standardPrice || row.midRangePrice // 使用平台价或市场价作为协议价
+      }]
+    };
+    
+    await batchAddProducts(batchData);
+    proxy?.$modal.msgSuccess('添加成功');
+    // 不关闭对话框,允许继续添加
+    await getList();
+  } catch (error) {
+    console.error('添加商品失败:', error);
+  }
+};
+
+/** 获取分类名称 */
+const getCategoryName = (row: BaseVO): string => {
+  // 优先使用完整路径
+  if (row.bottomCategoryId) {
+    return getCategoryFullPath(row.bottomCategoryId);
+  }
+  return '-';
+};
+
+/** 导出商品 */
+const handleExportProduct = () => {
+  // TODO: 导出功能
+  proxy?.$modal.msgInfo('导出商品功能');
 }
 
-/** 修改按钮操作 */
-const handleUpdate = async (row?: PoolLinkVO) => {
-  reset();
-  const _id = row?.id || ids.value[0]
-  const res = await getPoolLink(_id);
-  Object.assign(form.value, res.data);
-  dialog.visible = true;
-  dialog.title = "修改产品池和产品关联";
+/** 导入商品 */
+const handleImportProduct = () => {
+  // TODO: 导入功能
+  proxy?.$modal.msgInfo('导入商品功能');
 }
 
-/** 提交按钮 */
-const submitForm = () => {
-  poolLinkFormRef.value?.validate(async (valid: boolean) => {
-    if (valid) {
-      buttonLoading.value = true;
-      if (form.value.id) {
-        await updatePoolLink(form.value).finally(() =>  buttonLoading.value = false);
-      } else {
-        await addPoolLink(form.value).finally(() =>  buttonLoading.value = false);
-      }
-      proxy?.$modal.msgSuccess("操作成功");
-      dialog.visible = false;
-      await getList();
-    }
-  });
+/** 价格维护 */
+const handlePriceMaintain = (row: any) => {
+  // TODO: 打开价格维护对话框
+  proxy?.$modal.msgInfo('价格维护功能');
 }
 
-/** 删除按钮操作 */
-const handleDelete = async (row?: PoolLinkVO) => {
-  const _ids = row?.id || ids.value;
-  await proxy?.$modal.confirm('是否确认删除产品池和产品关联编号为"' + _ids + '"的数据项?').finally(() => loading.value = false);
-  await delPoolLink(_ids);
-  proxy?.$modal.msgSuccess("删除成功");
-  await getList();
+/** 修改库存 */
+const handleInventoryMaintain = (row: any) => {
+  // TODO: 打开修改库存对话框
+  proxy?.$modal.msgInfo('修改库存功能');
 }
 
-/** 导出按钮操作 */
-const handleExport = () => {
-  proxy?.download('product/poolLink/export', {
-    ...queryParams.value
-  }, `poolLink_${new Date().getTime()}.xlsx`)
+/** 移除商品池 */
+const handleRemoveProduct = async (row: any) => {
+  await proxy?.$modal.confirm('确认要移除该商品吗?');
+  // TODO: 调用移除API
+  proxy?.$modal.msgSuccess('移除成功');
+  await getList();
 }
 
 onMounted(() => {
+  getCategoryTree();
   getList();
 });
 </script>
+
+<style scoped lang="scss">
+.add-product-dialog {
+  :deep(.el-form--inline .el-form-item) {
+    margin-right: 10px;
+  }
+}
+</style>