Explorar el Código

feat(product): 新增商品添加页面实现多步骤向导功能

- 实现三级分类选择界面,支持搜索和层级导航
- 添加商品基础信息录入表单,包含商品编号、名称、规格等字段
- 集成商品价格管理,包括市场价、平台售价、采购价等价格体系
- 实现商品属性动态配置,根据分类加载对应属性选项
- 添加商品图片上传功能,支持主图和轮播图设置
- 集成富文本编辑器用于商品详情描述编写
- 实现定制说明功能,支持多种定制方式和工艺选择
- 添加供应商和人员选择功能,完善采购信息配置
- 实现多步骤导航,包含分类选择、信息填写和完成确认页面
- 配置表单验证规则确保数据完整性
- 集成文件选择器组件用于图片资源管理
- 更新生产环境API地址配置为线上域名
肖路 hace 1 mes
padre
commit
0f19b7f532

+ 1 - 1
.env.production

@@ -15,7 +15,7 @@ VITE_APP_MONITOR_ADMIN = '/admin/applications'
 VITE_APP_SNAILJOB_ADMIN = '/snail-job'
 
 # 生产环境
-VITE_APP_BASE_API = '/prod-api'
+VITE_APP_BASE_API = 'https://one.yoe365.com'
 
 # 是否在打包时开启压缩,支持 gzip 和 brotli
 VITE_BUILD_COMPRESS = gzip

+ 133 - 0
src/api/customer/customerInfo/index.ts

@@ -0,0 +1,133 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { CustomerInfoVO, CustomerInfoForm, CustomerInfoQuery } from '@/api/customer/customerInfo/types';
+
+/**
+ * 查询客户信息列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listCustomerInfo = (query?: CustomerInfoQuery): AxiosPromise<CustomerInfoVO[]> => {
+  return request({
+    url: '/customer/customerInfo/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询客户信息详细
+ * @param id
+ */
+export const getCustomerInfo = (id: string | number): AxiosPromise<CustomerInfoVO> => {
+  return request({
+    url: '/customer/customerInfo/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增客户信息
+ * @param data
+ */
+export const addCustomerInfo = (data: CustomerInfoForm) => {
+  return request({
+    url: '/customer/customerInfo',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改客户信息
+ * @param data
+ */
+export const updateCustomerInfo = (data: CustomerInfoForm) => {
+  return request({
+    url: '/customer/customerInfo',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除客户信息
+ * @param id
+ */
+export const delCustomerInfo = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/customer/customerInfo/' + id,
+    method: 'delete'
+  });
+};
+
+/**
+ * 状态修改
+ * @param id 客户id
+ * @param status 状态
+ */
+export function changeStatus(id: string, status: string) {
+  const data = {
+    id,
+    status
+  };
+  return request({
+    url: '/customer/customerInfo/changeStatus',
+    method: 'put',
+    data: data
+  });
+}
+
+/**
+ * 修改临时额度
+ * @param customerIds 客户id
+ * @param creditAmount 额度
+ */
+export function updateCreditAmount(customerIds: number[], creditAmount: number) {
+  const data = {
+    customerIds,
+    creditAmount
+  };
+  return request({
+    url: '/customer/customerInfo/updateCreditAmount',
+    method: 'put',
+    data: data
+  });
+}
+
+/**
+ * 修改临时额度
+ * @param customerIds 客户id
+ * @param tagIds 标签id
+ */
+export function setCustomerInfoTag(customerIds: number[], tagIds: number[]) {
+  const data = {
+    customerIds,
+    tagIds
+  };
+  return request({
+    url: '/customer/customerInfo/setCustomerInfoTag',
+    method: 'put',
+    data: data
+  });
+}
+
+export const listContractList = (query?: CustomerInfoQuery): AxiosPromise<CustomerInfoVO[]> => {
+  return request({
+    url: '/customer/customerInfo/contractList',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 根据名称模糊查询客户信息
+ * @param customerName
+ */
+export const getListBycustomerName = (customerName: string): AxiosPromise<CustomerInfoVO[]> => {
+  return request({
+    url: '/customer/customerInfo/selectByCustomerName/' + customerName,
+    method: 'get'
+  });
+};

+ 452 - 0
src/api/customer/customerInfo/types.ts

@@ -0,0 +1,452 @@
+import type { BusinessInfoVO, BusinessInfoForm } from '@/api/customer/businessInfo/types';
+import type { CustomerContactVO, CustomerContactForm } from '@/api/customer/customerContact/types';
+import type { SalesInfoVO, SalesInfoForm } from '@/api/customer/salesInfo/types';
+import type { InvoiceInfoVO, InvoiceInfoForm } from '@/api/customer/invoiceInfo/types';
+
+export interface CustomerInfoVO {
+  /**
+   * 主键ID
+   */
+  id: string | number;
+
+  /**
+   * 客户编号
+   */
+  customerNo: string;
+
+  /**
+   * 所属公司
+   */
+  belongCompanyId: string | number;
+
+  /**
+   * 公司名称
+   */
+  companyName: string;
+
+  /**
+   * 客户名称
+   */
+  customerName: string;
+
+  /**
+   * 工商名称
+   */
+  businessCustomerName: string;
+
+  /**
+   * 企业简称
+   */
+  shortName: string;
+
+  /**
+   * 开票类型
+   */
+  invoiceTypeId: string | number;
+
+  /**
+   * 企业规模
+   */
+  enterpriseScaleId: string | number;
+
+  enterpriseScale: string;
+
+  /**
+   * 客户类别
+   */
+  customerTypeId: string | number;
+
+  /**
+   * 行业类别
+   */
+  industryCategoryId: string | number;
+
+  industryCategory: string;
+
+  /**
+   * 客户等级
+   */
+  customerLevelId: string | number;
+
+  /**
+   * 固定电话
+   */
+  landline: string;
+
+  /**
+   * 传真
+   */
+  fax: string;
+
+  /**
+   * 网址
+   */
+  url: string;
+
+  /**
+   * 邮政编码
+   */
+  postCode: string;
+
+  /**
+   * 开始时间
+   */
+  validityFromDate: string | number;
+
+  /**
+   * 结束时间
+   */
+  validityToDate: string | number;
+
+  /**
+   * 发票抬头
+   */
+  invoiceTop: string;
+
+  /**
+   * 详细地址(注册地址)
+   */
+  address: string;
+
+  /**
+   * 省份编码
+   */
+  regProvincialNo: string;
+
+  /**
+   * 城市编码
+   */
+  regCityNo: string;
+
+  /**
+   * 区县编码
+   */
+  regCountyNo: string;
+
+  /**
+   * 省市区
+   */
+  provincialCityCounty: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status: string;
+
+  /**
+   * 备注
+   */
+  remark: string;
+
+  /**
+   * 工商信息
+   */
+  customerBusinessInfoVo?: BusinessInfoVO;
+
+  /**
+   * 销售信息
+   */
+  customerSalesInfoVo?: SalesInfoVO;
+
+  /**
+   * 联系人列表
+   */
+  customerContactVoList?: CustomerContactVO[];
+
+  /**
+   * 开票信息列表
+   */
+  customerInvoiceInfoVoList?: InvoiceInfoVO[];
+}
+
+export interface CustomerInfoForm extends BaseEntity {
+  /**
+   * 主键ID
+   */
+  id?: string | number;
+
+  /**
+   * 客户编号
+   */
+  customerNo?: string;
+
+  /**
+   * 所属公司
+   */
+  belongCompanyId?: string | number;
+
+  /**
+   * 公司名称
+   */
+  companyName?: string;
+
+  /**
+   * 客户名称
+   */
+  customerName?: string;
+
+  /**
+   * 工商名称
+   */
+  businessCustomerName?: string;
+
+  /**
+   * 企业简称
+   */
+  shortName?: string;
+
+  /**
+   * 开票类型
+   */
+  invoiceTypeId?: string | number;
+
+  /**
+   * 企业规模
+   */
+  enterpriseScaleId?: string | number;
+
+  /**
+   * 客户类别
+   */
+  customerTypeId?: string | number;
+
+  /**
+   * 行业类别
+   */
+  industryCategoryId?: string | number;
+
+  /**
+   * 客户等级
+   */
+  customerLevelId?: string | number;
+
+  /**
+   * 固定电话
+   */
+  landline?: string;
+
+  /**
+   * 传真
+   */
+  fax?: string;
+
+  /**
+   * 网址
+   */
+  url?: string;
+
+  /**
+   * 邮政编码
+   */
+  postCode?: string;
+
+  /**
+   * 开始时间
+   */
+  validityFromDate?: string | number;
+
+  /**
+   * 结束时间
+   */
+  validityToDate?: string | number;
+
+  /**
+   * 发票抬头
+   */
+  invoiceTop?: string;
+
+  /**
+   * 详细地址(注册地址)
+   */
+  address?: string;
+
+  /**
+   * 省份编码
+   */
+  regProvincialNo?: string;
+
+  /**
+   * 城市编码
+   */
+  regCityNo?: string;
+
+  /**
+   * 区县编码
+   */
+  regCountyNo?: string;
+
+  /**
+   * 省市区
+   */
+  provincialCityCounty?: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+
+  /**
+   * 工商信息
+   */
+  customerBusinessBo?: BusinessInfoForm;
+
+  /**
+   * 销售信息
+   */
+  customerSalesInfoBo?: SalesInfoForm;
+
+  /**
+   * 联系人列表
+   */
+  customerContactBoList?: CustomerContactForm[];
+
+  /**
+   * 开票信息列表
+   */
+  customerInvoiceInfoBoList?: InvoiceInfoForm[];
+}
+
+export interface CustomerInfoQuery extends PageQuery {
+  /**
+   * 客户编号
+   */
+  customerNo?: string;
+
+  /**
+   * 所属公司
+   */
+  belongCompanyId?: string | number;
+
+  /**
+   * 公司名称
+   */
+  companyName?: string;
+
+  /**
+   * 客户名称
+   */
+  customerName?: string;
+
+  /**
+   * 工商名称
+   */
+  businessCustomerName?: string;
+
+  /**
+   * 企业简称
+   */
+  shortName?: string;
+
+  /**
+   * 开票类型
+   */
+  invoiceTypeId?: string | number;
+
+  /**
+   * 企业规模
+   */
+  enterpriseScaleId?: string | number;
+
+  /**
+   * 客户类别
+   */
+  customerTypeId?: string | number;
+
+  /**
+   * 行业类别
+   */
+  industryCategoryId?: string | number;
+
+  /**
+   * 客户等级
+   */
+  customerLevelId?: string | number;
+
+  salesPersonId?: string | number;
+
+  serviceStaffId?: string | number;
+
+  belongingDepartmentId?: string | number;
+
+  /**
+   * 固定电话
+   */
+  landline?: string;
+
+  /**
+   * 传真
+   */
+  fax?: string;
+
+  /**
+   * 网址
+   */
+  url?: string;
+
+  /**
+   * 邮政编码
+   */
+  postCode?: string;
+
+  /**
+   * 开始时间
+   */
+  validityFromDate?: string | number;
+
+  /**
+   * 结束时间
+   */
+  validityToDate?: string | number;
+
+  /**
+   * 发票抬头
+   */
+  invoiceTop?: string;
+
+  /**
+   * 详细地址(注册地址)
+   */
+  address?: string;
+
+  /**
+   * 省份编码
+   */
+  regProvincialNo?: string;
+
+  /**
+   * 城市编码
+   */
+  regCityNo?: string;
+
+  /**
+   * 区县编码
+   */
+  regCountyNo?: string;
+
+  /**
+   * 省市区
+   */
+  provincialCityCounty?: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+  customerTag?: string;
+
+  /**
+   * 日期范围参数
+   */
+  params?: any;
+}

+ 355 - 0
src/api/customer/supplierInfo/index.ts

@@ -0,0 +1,355 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { InfoVO, InfoForm, InfoQuery } from '@/api/customer/supplierInfo/types';
+
+/**
+ * 查询供应商信息列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listInfo = (query?: InfoQuery): AxiosPromise<InfoVO[]> => {
+  return request({
+    url: '/customer/info/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询供应商信息详细
+ * @param id
+ */
+export const getInfo = (id: string | number): AxiosPromise<InfoVO> => {
+  return request({
+    url: '/customer/info/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增供应商信息
+ * @param data
+ */
+export const addInfo = (data: InfoForm) => {
+  return request({
+    url: '/customer/info',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改供应商信息
+ * @param data
+ */
+export const updateInfo = (data: InfoForm) => {
+  return request({
+    url: '/customer/info',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除供应商信息
+ * @param id
+ */
+export const delInfo = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/customer/info/' + id,
+    method: 'delete'
+  });
+};
+
+
+/**
+ * 获取供应商类型列表
+ */
+export const getComSupTyList = () => {
+  return request({
+    url: '/system/type/getComSupTyList',
+    method: 'get'
+  });
+};
+/**
+ * 获取产品分类列表
+ */
+export const getProductCategoryList = () => {
+  return request({
+    url: '/product/category/getProductCategoryList',
+    method: 'get'
+  });
+};
+/**
+ * 获取人员信息列表
+ */
+export const getComStaffList = (params?: any) => {
+  return request({
+    url: '/system/comStaff/list',
+    method: 'get',
+    params: params
+  });
+};
+/**
+ * 获取字典数据
+ */
+export const getDictData = (dictType: string) => {
+  return request({
+    url: `/system/dict/data/type/${dictType}`,
+    method: 'get'
+  });
+};
+
+/**
+ * 查询供应商信息列表(新接口,包含产品经理和采购员)
+ * @param query
+ * @returns {*}
+ */
+export const getInfoList = (query?: InfoQuery): AxiosPromise<InfoVO[]> => {
+  return request({
+    url: '/customer/info/getList',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询待审核供应商信息列表
+ * @param query
+ * @returns {*}
+ */
+export const getApproveList = (query?: InfoQuery): AxiosPromise<InfoVO[]> => {
+  return request({
+    url: '/customer/info/getApproveList',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 根据供应商ID获取人员信息
+ * @param supplierId 供应商ID
+ */
+export const getStaffInfoById = (supplierId: string | number) => {
+  return request({
+    url: '/system/comStaff/informationById',
+    method: 'get',
+    params: { supplierId }
+  });
+};
+
+/**
+ * 获取所有人员信息(用于下拉框)
+ */
+export const getStaffListSplice = () => {
+  return request({
+    url: '/system/comStaff/listSplice',
+    method: 'get'
+  });
+};
+
+/**
+ * 根据供应商ID获取产品经理和采购员ID
+ * @param supplierId 供应商ID
+ */
+export const getSupplierStaffIds = (supplierId: string | number) => {
+  return request({
+    url: '/system/comStaff/informationById',
+    method: 'get',
+    params: { supplierId: supplierId }
+  });
+};
+
+/**
+ * 根据供应商ID获取联系人列表
+ * @param supplierId 供应商ID
+ * @param params 分页参数
+ */
+export const getContactListById = (supplierId: string | number, params?: any) => {
+  return request({
+    url: '/customer/contact/getSupplierContactlistById',
+    method: 'get',
+    params: {
+      supplierId: supplierId,
+      ...params
+    }
+  });
+};
+
+/**
+ * 根据供应商ID获取已选择的品目ID列表
+ * @param supplierId 供应商ID
+ */
+export const getSupplierCategories = (supplierId: string | number) => {
+  return request({
+    url: '/customer/info/getSupplierCategories',
+    method: 'get',
+    params: { supplierId }
+  });
+};
+
+/**
+ * 根据供应商ID获取合同列表
+ * @param supplierId 供应商ID
+ * @param params 查询参数
+ */
+export const getSupplierContractsById = (supplierId: string | number, params?: any) => {
+  return request({
+    url: '/customer/supplierContract/supplierContractsById',
+    method: 'get',
+    params: {
+      supplierId: supplierId,
+      ...params
+    }
+  });
+};
+
+/**
+ * 根据供应商ID获取合同列表(新接口)
+ * @param supplierId 供应商ID
+ * @param params 查询参数
+ */
+export const getContractListById = (supplierId: string | number, params?: any) => {
+  return request({
+    url: '/customer/supplierContract/getListbyId',
+    method: 'get',
+    params: {
+      supplierId: supplierId,
+      ...params
+    }
+  });
+};
+
+/**
+ * 根据供应商ID获取银行账户信息
+ * @param id 供应商ID
+ */
+export const getBankBySupplierId = (id: string | number) => {
+  return request({
+    url: '/customer/supplierbank/getBankBySupplierId',
+    method: 'get',
+    params: { id }
+  });
+};
+
+/**
+ * 根据供应商ID获取授权详情列表
+ * @param supplierId 供应商ID
+ */
+export const getAuthorizeDetailList = (supplierId: string | number) => {
+  return request({
+    url: '/customer/supplierauthorize/getAuthorizeDetailList',
+    method: 'get',
+    params: { supplierId }
+  });
+};
+
+/**
+ * 保存供应商采购信息(产品经理和采购员)
+ * @param data 采购信息数据
+ */
+export const savePurchaseInfo = (data: { supplierId: string | number; productManager: number | null; purchaser: number | null }) => {
+  return request({
+    url: '/system/comStaff/saveInformation',
+    method: 'post',
+    data: data
+  });
+};
+/**
+ * 根据供应商ID获取地址列表
+ * @param supplierId 供应商ID
+ */
+export const getSupplierAddressById = (supplierId: string | number) => {
+  return request({
+    url: '/customer/supplieraddress/supplierAddressById',
+    method: 'get',
+    params: { supplierId }
+  });
+};
+
+/**
+ * 获取公司列表
+ */
+export const getCompanyList = () => {
+  return request({
+    url: '/system/company/list',
+    method: 'get'
+  });
+};
+
+/**
+ * 获取供应商类型列表
+ */
+export const getSupplierTypeList = (params?: any) => {
+  return request({
+    url: '/system/type/list',
+    method: 'get',
+    params: {
+
+
+      dataSource: 'youyi',
+      ...params
+    }
+  });
+};
+
+/**
+ * 获取供应商等级列表
+ */
+export const getSupplierLevelList = () => {
+  return request({
+    url: '/system/level/list',
+    method: 'get'
+  });
+};
+
+/**
+ * 获取企业规模列表
+ */
+export const getEnterpriseScaleList = () => {
+  return request({
+    url: '/customer/enterpriseScale/getlist',
+    method: 'get'
+  });
+};
+
+/**
+ * 获取行业类别列表
+ */
+export const getIndustryCategoryList = () => {
+  return request({
+    url: '/customer/industryCategory/getlist',
+    method: 'get'
+  });
+};
+
+/**
+ * 获取税率列表
+ */
+export const getTaxRateList = () => {
+  return request({
+    url: '/system/taxrate/list',
+    method: 'get'
+  });
+};
+
+/**
+ * 获取结算方式列表
+ */
+export const getSettlementMethodList = () => {
+  return request({
+    url: '/system/settlementMethod/list',
+    method: 'get'
+  });
+};
+
+/**
+ * 获取发票类型列表
+ */
+export const getInvoiceTypeList = () => {
+  return request({
+    url: '/system/invoiceType/list',
+    method: 'get'
+  });
+};

+ 910 - 0
src/api/customer/supplierInfo/types.ts

@@ -0,0 +1,910 @@
+export interface InfoVO {
+  /**
+   * 主键ID
+   */
+  id: string | number;
+
+  /**
+   * 供应商编号
+   */
+  supplierNo: string;
+
+  /**
+   * 企业名称
+   */
+  enterpriseName: string;
+
+  /**
+   * 人员规模
+   */
+  membershipSize: string;
+
+  /**
+   * 供应商类型
+   */
+  supplierType: string | number;
+
+  /**
+   * 合作类型
+   */
+  cooperationType: string;
+
+  /**
+   * 固定电话
+   */
+  fixedPhone: string;
+
+  /**
+   * 传真
+   */
+  fax: string;
+
+  /**
+   * 网址
+   */
+  url: string;
+
+  /**
+   * 邮政编码
+   */
+  postCode: string;
+
+  /**
+   * 邮箱
+   */
+  mailbox: string;
+
+  /**
+   * 办公地址-省
+   */
+  officeProvince: string;
+
+  /**
+   * 办公地址-市
+   */
+  officeCity: string;
+
+  /**
+   * 办公地址-区/县
+   */
+  officeCounty?: string;
+
+  /**
+   * 办公详细地址
+   */
+  officeAddress: string;
+
+  /**
+   * 营业执照名称
+   */
+  businessName: string;
+
+  /**
+   * 统一社会信用代码
+   */
+  socialCreditCode: string;
+
+  /**
+   * 法人姓名
+   */
+  legalPersonName: string;
+
+  /**
+   * 法人身份证号
+   */
+  legalPersonId: string | number;
+
+  /**
+   * 注册资本
+   */
+  registeredCapital: string;
+
+  /**
+   * 注册地址-省
+   */
+  businessProvince: string;
+
+  /**
+   * 注册地址-市
+   */
+  businessCity: string;
+
+  /**
+   * 注册地址-区/县
+   */
+  businessCounty: string;
+
+  /**
+   * 注册详细地址
+   */
+  businessAddress: string;
+
+  /**
+   * 营业执照图片路径
+   */
+  businessLicense: string;
+
+  /**
+   * 发票类型
+   */
+  invoiceType: string;
+
+  /**
+   * 发票抬头
+   */
+  invoiceHeader: string;
+
+  /**
+   * 纳税人识别号
+   */
+  taxpayerIdentifier: string | number;
+
+  /**
+   * 开户银行
+   */
+  depositaryBank: string;
+
+  /**
+   * 行号(可能是银行行号)
+   */
+  rowNum: string;
+
+  /**
+   * 银行账号
+   */
+  bankAccounts: string;
+
+  /**
+   * 发票地址
+   */
+  invoiceAddress: string;
+
+  /**
+   * 发票电话
+   */
+  invoiceLandline: string;
+
+  /**
+   * 供货范围
+   */
+  scopeSupply: string;
+
+  /**
+   * 合作方式(0-公开招标, 1-邀请招标...)
+   */
+  cooperateWay: number;
+
+  /**
+   * 合作等级
+   */
+  cooperateLevel: string | number;
+
+  /**
+   * 合同到期时间
+   */
+  contractEndTime: string;
+
+  /**
+   * 供应状态(0-暂停, 1-正常...)
+   */
+  supplyStatus: number;
+
+  /**
+   * 供应评分
+   */
+  supplyScore: number;
+
+  /**
+   * 年销售额
+   */
+  yearSales: number;
+
+  /**
+   * 供应商联系人姓名
+   */
+  supplierName: string;
+
+  /**
+   * 供应商联系人电话
+   */
+  supplierPhone: string;
+
+  /**
+   * 供应商登录密码(已加密)
+   */
+  supplierPassword: string;
+
+  /**
+   * 经营品类
+   */
+  operatingCategory: string;
+
+  /**
+   * 经营品牌
+   */
+  operatingBrand: string;
+
+  /**
+   * 其他客户
+   */
+  otherCustomers: string;
+
+  /**
+   * 简称
+   */
+  shortName: string;
+
+  /**
+   * 所属行业
+   */
+  industrCategory: string;
+
+  /**
+   * 类型(可能与supplier_type重复或细分)
+   */
+  type: string;
+
+  /**
+   * 所属公司
+   */
+  ownedCompany: string;
+
+  /**
+   * 推送状态(0-未推送, 1-已推送...)
+   */
+  pushStatus: number;
+
+  /**
+   * 创建时间
+   */
+  created: string;
+
+  /**
+   * 修改时间
+   */
+  modify: string;
+
+  /**
+   * 有效期开始时间
+   */
+  validityFromDate: string | number;
+
+  /**
+   * 有效期结束时间
+   */
+  validityToDate: string | number;
+
+  /**
+   * 行号(可能是内部排序或备用字段)
+   */
+  rowNo: number;
+
+  /**
+   * 法人身份证图片路径
+   */
+  personImage: string;
+
+  /**
+   * 法人身份证图片路径Url
+   */
+  personImageUrl: string;
+  /**
+   * 对接次数
+   */
+  abutmentNo: number;
+
+  /**
+   * 是否合作(1-是, 0-否)
+   */
+  cooperative: number;
+
+  /**
+   * 供货区域(省)
+   */
+  province?: string;
+
+  /**
+   * 供货区域(市)
+   */
+  city?: string;
+
+}
+
+export interface InfoForm extends BaseEntity {
+  /**
+   * 主键ID
+   */
+  id?: string | number;
+
+  /**
+   * 供应商编号
+   */
+  supplierNo?: string;
+
+  /**
+   * 企业名称
+   */
+  enterpriseName?: string;
+
+  /**
+   * 人员规模
+   */
+  membershipSize?: string;
+
+  /**
+   * 供应商类型
+   */
+  supplierType?: string;
+
+  /**
+   * 合作类型
+   */
+  cooperationType?: string;
+
+  /**
+   * 固定电话
+   */
+  fixedPhone?: string;
+
+  /**
+   * 传真
+   */
+  fax?: string;
+
+  /**
+   * 网址
+   */
+  url?: string;
+
+  /**
+   * 邮政编码
+   */
+  postCode?: string;
+
+  /**
+   * 邮箱
+   */
+  mailbox?: string;
+
+  /**
+   * 办公地址-省
+   */
+  officeProvince?: string;
+
+  /**
+   * 办公地址-市
+   */
+  officeCity?: string;
+
+  /**
+   * 办公地址-区/县
+   */
+  officeCounty?: string;
+
+  /**
+   * 办公详细地址
+   */
+  officeAddress?: string;
+
+  /**
+   * 营业执照名称
+   */
+  businessName?: string;
+
+  /**
+   * 统一社会信用代码
+   */
+  socialCreditCode?: string;
+
+  /**
+   * 法人姓名
+   */
+  legalPersonName?: string;
+
+  /**
+   * 法人身份证号
+   */
+  legalPersonId?: string | number;
+
+  /**
+   * 注册资本
+   */
+  registeredCapital?: string;
+
+  /**
+   * 注册地址-省
+   */
+  businessProvince?: string;
+
+  /**
+   * 注册地址-市
+   */
+  businessCity?: string;
+
+  /**
+   * 注册地址-区/县
+   */
+  businessCounty?: string;
+
+  /**
+   * 注册详细地址
+   */
+  businessAddress?: string;
+
+  /**
+   * 营业执照图片路径
+   */
+  businessLicense?: string;
+
+  /**
+   * 发票类型
+   */
+  invoiceType?: string;
+
+  /**
+   * 发票抬头
+   */
+  invoiceHeader?: string;
+
+  /**
+   * 纳税人识别号
+   */
+  taxpayerIdentifier?: string | number;
+
+  /**
+   * 开户银行
+   */
+  depositaryBank?: string;
+
+  /**
+   * 行号(可能是银行行号)
+   */
+  rowNum?: string;
+
+  /**
+   * 银行账号
+   */
+  bankAccounts?: string;
+
+  /**
+   * 发票地址
+   */
+  invoiceAddress?: string;
+
+  /**
+   * 发票电话
+   */
+  invoiceLandline?: string;
+
+  /**
+   * 供货范围
+   */
+  scopeSupply?: string;
+
+  /**
+   * 合作方式(0-公开招标, 1-邀请招标...)
+   */
+  cooperateWay?: number;
+
+  /**
+   * 合作等级
+   */
+  cooperateLevel?: string;
+
+  /**
+   * 合同到期时间
+   */
+  contractEndTime?: string;
+
+  /**
+   * 供应状态(0-暂停, 1-正常...)
+   */
+  supplyStatus?: number;
+
+  /**
+   * 供应评分
+   */
+  supplyScore?: number;
+
+  /**
+   * 年销售额
+   */
+  yearSales?: number;
+
+  /**
+   * 供应商联系人姓名
+   */
+  supplierName?: string;
+
+  /**
+   * 供应商联系人电话
+   */
+  supplierPhone?: string;
+
+  /**
+   * 供应商登录密码(已加密)
+   */
+  supplierPassword?: string;
+
+  /**
+   * 经营品类
+   */
+  operatingCategory?: string;
+
+  /**
+   * 经营品牌
+   */
+  operatingBrand?: string;
+
+  /**
+   * 其他客户
+   */
+  otherCustomers?: string;
+
+  /**
+   * 简称
+   */
+  shortName?: string;
+
+  /**
+   * 所属行业
+   */
+  industrCategory?: string;
+
+  /**
+   * 类型(可能与supplier_type重复或细分)
+   */
+  type?: string;
+
+  /**
+   * 所属公司
+   */
+  ownedCompany?: string;
+
+  /**
+   * 推送状态(0-未推送, 1-已推送...)
+   */
+  pushStatus?: number;
+
+  /**
+   * 创建时间
+   */
+  created?: string;
+
+  /**
+   * 修改时间
+   */
+  modify?: string;
+
+  /**
+   * 有效期开始时间
+   */
+  validityFromDate?: string | number;
+
+  /**
+   * 有效期结束时间
+   */
+  validityToDate?: string | number;
+
+  /**
+   * 行号(可能是内部排序或备用字段)
+   */
+  rowNo?: number;
+
+  /**
+   * 法人身份证图片路径
+   */
+  personImage?: string;
+
+  /**
+   * 对接次数
+   */
+  abutmentNo?: number;
+
+  /**
+   * 是否合作(1-是, 0-否)
+   */
+  cooperative?: number;
+
+}
+
+export interface InfoQuery extends PageQuery {
+
+  /**
+   * 供应商编号
+   */
+  supplierNo?: string;
+
+  /**
+   * 企业名称
+   */
+  enterpriseName?: string;
+
+  /**
+   * 人员规模
+   */
+  membershipSize?: string;
+
+  /**
+   * 供应商类型
+   */
+  supplierType?: string;
+
+  /**
+   * 合作类型
+   */
+  cooperationType?: string;
+
+  /**
+   * 固定电话
+   */
+  fixedPhone?: string;
+
+  /**
+   * 传真
+   */
+  fax?: string;
+
+  /**
+   * 网址
+   */
+  url?: string;
+
+  /**
+   * 邮政编码
+   */
+  postCode?: string;
+
+  /**
+   * 邮箱
+   */
+  mailbox?: string;
+
+  /**
+   * 办公地址-省
+   */
+  officeProvince?: string;
+
+  /**
+   * 办公地址-市
+   */
+  officeCity?: string;
+
+  /**
+   * 办公地址-区/县
+   */
+  officeCounty?: string;
+
+  /**
+   * 办公详细地址
+   */
+  officeAddress?: string;
+
+  /**
+   * 营业执照名称
+   */
+  businessName?: string;
+
+  /**
+   * 统一社会信用代码
+   */
+  socialCreditCode?: string;
+
+  /**
+   * 法人姓名
+   */
+  legalPersonName?: string;
+
+  /**
+   * 法人身份证号
+   */
+  legalPersonId?: string | number;
+
+  /**
+   * 注册资本
+   */
+  registeredCapital?: string;
+
+  /**
+   * 注册地址-省
+   */
+  businessProvince?: string;
+
+  /**
+   * 注册地址-市
+   */
+  businessCity?: string;
+
+  /**
+   * 注册地址-区/县
+   */
+  businessCounty?: string;
+
+  /**
+   * 注册详细地址
+   */
+  businessAddress?: string;
+
+  /**
+   * 营业执照图片路径
+   */
+  businessLicense?: string;
+
+  /**
+   * 发票类型
+   */
+  invoiceType?: string;
+
+  /**
+   * 发票抬头
+   */
+  invoiceHeader?: string;
+
+  /**
+   * 纳税人识别号
+   */
+  taxpayerIdentifier?: string | number;
+
+  /**
+   * 开户银行
+   */
+  depositaryBank?: string;
+
+  /**
+   * 行号(可能是银行行号)
+   */
+  rowNum?: string;
+
+  /**
+   * 银行账号
+   */
+  bankAccounts?: string;
+
+  /**
+   * 发票地址
+   */
+  invoiceAddress?: string;
+
+  /**
+   * 发票电话
+   */
+  invoiceLandline?: string;
+
+  /**
+   * 供货范围
+   */
+  scopeSupply?: string;
+
+  /**
+   * 合作方式(0-公开招标, 1-邀请招标...)
+   */
+  cooperateWay?: number;
+
+  /**
+   * 合作等级
+   */
+  cooperateLevel?: string;
+
+  /**
+   * 合同到期时间
+   */
+  contractEndTime?: string;
+
+  /**
+   * 供应状态(0-暂停, 1-正常...)
+   */
+  supplyStatus?: number;
+
+  /**
+   * 供应评分
+   */
+  supplyScore?: number;
+
+  /**
+   * 年销售额
+   */
+  yearSales?: number;
+
+  /**
+   * 供应商联系人姓名
+   */
+  supplierName?: string;
+
+  /**
+   * 供应商联系人电话
+   */
+  supplierPhone?: string;
+
+  /**
+   * 供应商登录密码(已加密)
+   */
+  supplierPassword?: string;
+
+  /**
+   * 经营品类
+   */
+  operatingCategory?: string;
+
+  /**
+   * 经营品牌
+   */
+  operatingBrand?: string;
+
+  /**
+   * 其他客户
+   */
+  otherCustomers?: string;
+
+  /**
+   * 简称
+   */
+  shortName?: string;
+
+  /**
+   * 所属行业
+   */
+  industrCategory?: string;
+
+  /**
+   * 类型(可能与supplier_type重复或细分)
+   */
+  type?: string;
+
+  /**
+   * 所属公司
+   */
+  ownedCompany?: string;
+
+  /**
+   * 推送状态(0-未推送, 1-已推送...)
+   */
+  pushStatus?: number;
+
+  /**
+   * 创建时间
+   */
+  created?: string;
+
+  /**
+   * 修改时间
+   */
+  modify?: string;
+
+  /**
+   * 有效期开始时间
+   */
+  validityFromDate?: string | number;
+
+  /**
+   * 有效期结束时间
+   */
+  validityToDate?: string | number;
+
+  /**
+   * 行号(可能是内部排序或备用字段)
+   */
+  rowNo?: number;
+
+  /**
+   * 法人身份证图片路径
+   */
+  personImage?: string;
+
+  /**
+   * 对接次数
+   */
+  abutmentNo?: number;
+
+  /**
+   * 是否合作(1-是, 0-否)
+   */
+  cooperative?: number;
+
+    /**
+     * 日期范围参数
+     */
+    params?: any;
+}
+
+
+

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

@@ -199,3 +199,13 @@ export const changeProductType = (data: BaseForm) => {
   });
 };
 
+/**
+ * 获取税率列表
+ */
+export const getTaxRateList = () => {
+  return request({
+    url: '/system/taxrate/list',
+    method: 'get'
+  });
+};
+

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

@@ -243,6 +243,11 @@ export interface BaseVO {
    */
   attributesList?: string;
 
+  /**
+   * 商品轮播图URL(逗号分隔)
+   */
+  imageUrl?: string;
+
 }
 
 export interface BaseForm extends BaseEntity {
@@ -576,6 +581,11 @@ export interface BaseForm extends BaseEntity {
    */
   shelfComments?: string;
 
+  /**
+   * 商品轮播图URL(逗号分隔)
+   */
+  imageUrl?: string;
+
 }
 
 export interface BaseQuery extends PageQuery {

+ 8 - 58
src/api/product/brand/types.ts

@@ -98,17 +98,11 @@ export interface BrandVO {
    */
   dataSource: string;
 
-  remark: string;
-
   /**
-   * 品牌状态(0审核中 1已生效 2资质已过期 3驳回)
+   * 备注
    */
-  brandStatus?: number;
+  remark: string;
 
-  /**
-   * 供应商授权列表
-   */
-  supplierAuthorizeList?: SupplierAuthorizeVO[];
 }
 
 export interface BrandForm extends BaseEntity {
@@ -207,17 +201,11 @@ export interface BrandForm extends BaseEntity {
    */
   dataSource?: string;
 
-  remark?: string;
-
   /**
-   * 品牌状态(0审核中 1已生效 2资质已过期 3驳回)
+   * 备注
    */
-  brandStatus?: number;
+  remark?: string;
 
-  /**
-   * 供应商授权列表
-   */
-  supplierAuthorizeList?: SupplierAuthorizeVO[];
 }
 
 export interface BrandQuery extends PageQuery {
@@ -317,48 +305,10 @@ export interface BrandQuery extends PageQuery {
    */
   platformCode?: string;
 
-  /**
-   * 品牌状态(0审核中 1已生效 2资质已过期 3驳回)
-   */
-  brandStatus?: number;
-
-  /**
-   * 日期范围参数
-   */
-  params?: any;
-}
-
-export interface SupplierAuthorizeVO {
-  id?: string | number;
-  categoryId?: string | number;
-  brandId?: string | number;
-  supplierNo?: string;
-  supplierId?: string | number;
-  supplierName?: string;
-  supplyProCate?: string;
-  supplyBrand?: string;
-  authorizeNo?: string;
-  authBrand?: string;
-  brandNo?: string;
-  brandName?: string;
-  brandEnglishName?: string;
-  brandLogo?: string;
-  authorizeType?: string;
-  authorizationCategory?: string;
-  brandLicensor?: string;
-  authorizationRelationshipFile?: string;
-  authorizationStartTime?: string;
-  authorizationEndTime?: string;
-  authorizedArea?: string;
-  brandHoldType?: string;
-  authorizeLevel?: string;
-  authorizedStatus?: string;
-  authorizationRelationshipFileName?: string;
-  brandRegistrant?: string;
-  reviewFeedback?: string;
-  province?: string;
-  city?: string;
-  categorysMap?: Record<string, string>;
+    /**
+     * 日期范围参数
+     */
+    params?: any;
 }
 
 

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

@@ -68,17 +68,7 @@ export const delCategory = (id: string | number | Array<string | number>) => {
  */
 export const listCategoryExcludeChild = (id: string | number): AxiosPromise<CategoryVO[]> => {
   return request({
-    url: '/product/category/tree/exclude/' + id,
-    method: 'get'
-  });
-};
-
-/**
- * 获取产品分类列表
- */
-export const getProductCategoryList = () => {
-  return request({
-    url: '/product/category/getProductCategoryList',
+    url: '/product/category/list/exclude/' + id,
     method: 'get'
   });
 };

+ 90 - 0
src/api/product/products/index.ts

@@ -0,0 +1,90 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { ProductsVO, ProductsForm, ProductsQuery } from '@/api/product/protocolProducts/types';
+import { BaseQuery, BaseVO } from '@/api/product/base/types';
+
+/**
+ * 查询协议产品关联列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listProducts = (query?: ProductsQuery): AxiosPromise<BaseVO[]> => {
+  return request({
+    url: '/product/protocolProducts/list',
+    method: 'get',
+    params: query
+  });
+};
+
+
+/**
+* 获取协议的关联产品Id
+* */
+export const getProtocolProductIds = (protocolId: string | number): AxiosPromise<string[]> => {
+  return request({
+    url: '/product/protocolProducts/getProductIds/' + protocolId,
+    method: 'get'
+  });
+}
+
+
+
+
+/**
+ * 查询协议产品关联详细
+ * @param id
+ */
+export const getProducts = (id: string | number): AxiosPromise<ProductsVO> => {
+  return request({
+    url: '/product/protocolProducts/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增协议产品关联
+ * @param data
+ */
+export const addProducts = (data: ProductsForm) => {
+  return request({
+    url: '/product/protocolProducts',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改协议产品关联
+ * @param data
+ */
+export const updateProducts = (data: ProductsForm) => {
+  return request({
+    url: '/product/protocolProducts',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除协议产品关联
+ * @param id
+ */
+export const delProducts = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/product/protocolProducts/' + id,
+    method: 'delete'
+  });
+};
+
+/**
+ * 修改协议产品价格
+ * @param data
+ */
+export const updateProductsPrice = (data: ProductsForm) => {
+  return request({
+    url: '/product/protocolProducts/updateProtocolPrice',
+    method: 'put',
+    data: data
+  })
+}

+ 189 - 0
src/api/product/products/types.ts

@@ -0,0 +1,189 @@
+export interface ProductsVO {
+  /**
+   * 主键ID,自增
+   */
+  id: string | number;
+
+  /**
+   * 协议编号
+   */
+  protocolNo: string;
+
+  protocolId?: string | number;
+
+  /**
+   * 产品编号
+   */
+  productNo: string;
+
+  /**
+   * 协议价格(字符串形式,可能含货币符号或格式化)
+   */
+  agreementPrice: number;
+
+  /**
+   * 数据来源
+   */
+  dataSource: string;
+
+  /**
+   * 客户ID
+   */
+  customerId: string | number;
+
+  /**
+   * 备注信息
+   */
+  remark: string;
+
+  /**
+   * 税率ID或税务编码
+   */
+  taxId: string | number;
+
+  /**
+   * 类型(例如:1-标准产品,2-定制产品等)
+   */
+  type: number;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status: string;
+
+}
+
+export interface ProductsForm extends BaseEntity {
+  /**
+   * 主键ID,自增
+   */
+  id?: string | number;
+
+  /**
+   * 协议编号
+   */
+  protocolNo?: string;
+
+  protocolId?: string | number;
+
+  /**
+   * 产品编号
+   */
+  productNo?: string;
+
+  /**
+   * 商品id
+   */
+  productId?: string | number;
+
+  /**
+   * 协议价格(字符串形式,可能含货币符号或格式化)
+   */
+  agreementPrice?: number;
+
+  /**
+   * 数据来源
+   */
+  dataSource?: string;
+
+  /**
+   * 客户ID
+   */
+  customerId?: string | number;
+
+  /**
+   * 备注信息
+   */
+  remark?: string;
+
+  /**
+   * 税率ID或税务编码
+   */
+  taxId?: string | number;
+
+  /**
+   * 类型(例如:1-标准产品,2-定制产品等)
+   */
+  type?: number;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+}
+
+export interface ProductsQuery extends PageQuery {
+
+  /**
+   * 协议编号
+   */
+  protocolNo?: string;
+
+  protocolId?: string | number;
+
+  /**
+   * 产品编号
+   */
+  productNo?: string;
+
+  /**
+   * 协议价格(字符串形式,可能含货币符号或格式化)
+   */
+  agreementPrice?: number;
+
+  /**
+   * 数据来源
+   */
+  dataSource?: string;
+
+  /**
+   * 客户ID
+   */
+  customerId?: string | number;
+
+  /**
+   * 税率ID或税务编码
+   */
+  taxId?: string | number;
+
+  /**
+   * 类型(例如:1-标准产品,2-定制产品等)
+   */
+  type?: number;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+  /**
+   * 创建时间
+   */
+  productName?: string;
+  /**
+   * 商品品牌id
+   */
+  brandId?: string | number;
+  /**
+   * 商品状态 0-下架 1-上架
+   */
+  productStatus?: string;
+  /**
+   * 商品id
+   */
+  productId?: string | number;
+
+    /**
+   * 日期范围参数
+     */
+    params?: any;
+}
+
+
+

+ 65 - 0
src/api/product/protocolInfo/index.ts

@@ -0,0 +1,65 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { InfoVO, InfoForm, InfoQuery } from '@/api/product/protocolInfo/types';
+
+/**
+ * 查询协议信息列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listInfo = (query?: InfoQuery): AxiosPromise<InfoVO[]> => {
+  return request({
+    url: '/product/protocolInfo/list',
+    method: 'get',
+    params: query
+  });
+};
+
+
+
+/**
+ * 查询协议信息详细
+ * @param id
+ */
+export const getInfo = (id: string | number): AxiosPromise<InfoVO> => {
+  return request({
+    url: '/product/protocolInfo/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增协议信息
+ * @param data
+ */
+export const addInfo = (data: InfoForm) => {
+  return request({
+    url: '/product/protocolInfo',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改协议信息
+ * @param data
+ */
+export const updateInfo = (data: InfoForm) => {
+  return request({
+    url: '/product/protocolInfo',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除协议信息
+ * @param id
+ */
+export const delInfo = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/product/protocolInfo/' + id,
+    method: 'delete'
+  });
+};

+ 311 - 0
src/api/product/protocolInfo/types.ts

@@ -0,0 +1,311 @@
+export interface InfoVO {
+  /**
+   * 主键ID,自增
+   */
+  id: string | number;
+
+  /**
+   * 协议编号
+   */
+  protocolNo: string;
+
+  /**
+   * 客户编号
+   */
+  customerNo: string;
+
+  /**
+   * 客户id
+   */
+  customerId: string | number;
+
+  /**
+   * 客户名称
+   */
+  customerName: string;
+
+  /**
+   * 审核时间
+   */
+  reviewTime: string;
+
+  /**
+   * 审批状态(例如:0-待审批,1-已通过,2-已拒绝)
+   */
+  approvalStatus: number;
+
+  /**
+   * 协议开始时间
+   */
+  startTime: string;
+
+  /**
+   * 协议结束时间
+   */
+  endTime: string;
+
+  /**
+   * 产品数量
+   */
+  productNum: number;
+
+  /**
+   * 备注信息
+   */
+  remark: string;
+
+  /**
+   * 协议状态(例如:0-草稿,1-生效,2-终止)
+   */
+  protocolStatus: number;
+
+  /**
+   * 业务员id
+   */
+  salesmanId: string | number;
+
+  /**
+   * 公司id
+   */
+  companyId: string | number;
+
+  /**
+   * 客服id
+   */
+  serviceId: string | number;
+
+  /**
+   * 数据来源
+   */
+  dataSource: string;
+
+  /**
+   * 协议文件路径
+   */
+  protocolFile: string;
+
+  /**
+   * 协议文件原始名称
+   */
+  fileName: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status: string;
+
+}
+
+export interface InfoForm extends BaseEntity {
+  /**
+   * 主键ID,自增
+   */
+  id?: string | number;
+
+  /**
+   * 协议编号
+   */
+  protocolNo?: string;
+
+  /**
+   * 客户编号
+   */
+  customerNo?: string;
+
+  /**
+   * 客户id
+   */
+  customerId?: string | number;
+
+  /**
+   * 客户名称
+   */
+  customerName?: string;
+
+  /**
+   * 审核时间
+   */
+  reviewTime?: string;
+
+  /**
+   * 审批状态(例如:0-待审批,1-已通过,2-已拒绝)
+   */
+  approvalStatus?: number;
+
+  /**
+   * 协议开始时间
+   */
+  startTime?: string;
+
+  /**
+   * 协议结束时间
+   */
+  endTime?: string;
+
+  /**
+   * 产品数量
+   */
+  productNum?: number;
+
+  /**
+   * 备注信息
+   */
+  remark?: string;
+
+  /**
+   * 协议状态(例如:0-草稿,1-生效,2-终止)
+   */
+  protocolStatus?: number;
+
+  /**
+   * 业务员id
+   */
+  salesmanId?: string | number;
+
+  /**
+   * 业务员名称
+   */
+  salesmanName?: string;
+
+  /**
+   * 公司id
+   */
+  companyId?: string | number;
+
+  /**
+   * 公司名称
+   */
+  companyName?: string;
+
+  /**
+   * 客服id
+   */
+  serviceId?: string | number;
+
+  /**
+   * 客服名称
+   */
+  serviceName?: string;
+
+  /**
+   * 数据来源
+   */
+  dataSource?: string;
+
+  /**
+   * 协议文件路径
+   */
+  protocolFile?: string;
+
+  /**
+   * 协议文件原始名称
+   */
+  fileName?: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+}
+
+export interface InfoQuery extends PageQuery {
+
+  /**
+   * 协议编号
+   */
+  protocolNo?: string;
+
+  /**
+   * 客户编号
+   */
+  customerNo?: string;
+
+  /**
+   * 客户id
+   */
+  customerId?: string | number;
+
+  /**
+   * 客户名称
+   */
+  customerName?: string;
+
+  /**
+   * 审核时间
+   */
+  reviewTime?: string;
+
+  /**
+   * 审批状态(例如:0-待审批,1-已通过,2-已拒绝)
+   */
+  approvalStatus?: number;
+
+  /**
+   * 协议开始时间
+   */
+  startTime?: string;
+
+  /**
+   * 协议结束时间
+   */
+  endTime?: string;
+
+  /**
+   * 产品数量
+   */
+  productNum?: number;
+
+  /**
+   * 协议状态(例如:0-草稿,1-生效,2-终止)
+   */
+  protocolStatus?: number;
+
+  /**
+   * 业务员id
+   */
+  salesmanId?: string | number;
+
+  /**
+   * 公司id
+   */
+  companyId?: string | number;
+
+  /**
+   * 客服id
+   */
+  serviceId?: string | number;
+
+  /**
+   * 数据来源
+   */
+  dataSource?: string;
+
+  /**
+   * 协议文件路径
+   */
+  protocolFile?: string;
+
+  /**
+   * 协议文件原始名称
+   */
+  fileName?: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+    /**
+     * 日期范围参数
+     */
+    params?: any;
+}
+
+
+

+ 75 - 0
src/api/system/comStaff/index.ts

@@ -0,0 +1,75 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { ComStaffVO, ComStaffForm, ComStaffQuery } from '../comStaff/types';
+
+/**
+ * 查询人员信息列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listComStaff = (query?: ComStaffQuery): AxiosPromise<ComStaffVO[]> => {
+  return request({
+    url: '/system/comStaff/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询人员信息详细
+ * @param staffId
+ */
+export const getComStaff = (staffId: string | number): AxiosPromise<ComStaffVO> => {
+  return request({
+    url: '/system/comStaff/' + staffId,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增人员信息
+ * @param data
+ */
+export const addComStaff = (data: ComStaffForm) => {
+  return request({
+    url: '/system/comStaff',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改人员信息
+ * @param data
+ */
+export const updateComStaff = (data: ComStaffForm) => {
+  return request({
+    url: '/system/comStaff',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除人员信息
+ * @param staffId
+ */
+export const delComStaff = (staffId: string | number | Array<string | number>) => {
+  return request({
+    url: '/system/comStaff/' + staffId,
+    method: 'delete'
+  });
+};
+
+export function changeStatus(id: string | number, status: string) {
+  const data = {
+    id,
+    status
+  };
+  return request({
+    url: '/system/comStaff/changeStatus',
+    method: 'put',
+    data: data
+  });
+}

+ 215 - 0
src/api/system/comStaff/types.ts

@@ -0,0 +1,215 @@
+export interface ComStaffVO {
+  /**
+   * 人员ID
+   */
+  staffId: string | number;
+
+  /**
+   * 人员编码
+   */
+  staffCode: string;
+
+  /**
+   * 姓名
+   */
+  staffName: string;
+
+  /**
+   * 所属部门编码
+   */
+  deptId: string | number;
+
+  /**
+   * 联系电话
+   */
+  phone: string;
+
+  /**
+   * 岗位编码
+   */
+  postId: string | number;
+
+  /**
+   * 性别
+   */
+  sex: string;
+
+  /**
+   * 角色编码
+   */
+  roleId: string | number;
+
+  /**
+   * 数据来源
+   */
+  dataSource: string;
+
+  /**
+   * 密码
+   */
+  password: string;
+
+  /**
+   * 有效期起始
+   */
+  validFrom: string | number;
+
+  /**
+   * 有效期截止
+   */
+  validTo: string | number;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status: string;
+
+  /**
+   * 备注
+   */
+  remark: string;
+}
+
+export interface ComStaffForm extends BaseEntity {
+  /**
+   * 人员ID
+   */
+  staffId?: string | number;
+
+  /**
+   * 人员编码
+   */
+  staffCode?: string;
+
+  /**
+   * 姓名
+   */
+  staffName?: string;
+
+  /**
+   * 所属部门编码
+   */
+  deptId?: string | number;
+
+  /**
+   * 联系电话
+   */
+  phone?: string;
+
+  /**
+   * 岗位编码
+   */
+  postId?: string | number;
+
+  /**
+   * 性别
+   */
+  sex?: string;
+
+  /**
+   * 角色编码
+   */
+  roleId?: string | number;
+
+  /**
+   * 数据来源
+   */
+  dataSource?: string;
+
+  /**
+   * 密码
+   */
+  password?: string;
+
+  /**
+   * 有效期起始
+   */
+  validFrom?: string | number;
+
+  /**
+   * 有效期截止
+   */
+  validTo?: string | number;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+}
+
+export interface ComStaffQuery extends PageQuery {
+  /**
+   * 人员编码
+   */
+  staffCode?: string;
+
+  /**
+   * 姓名
+   */
+  staffName?: string;
+
+  /**
+   * 所属部门编码
+   */
+  deptId?: string | number;
+
+  /**
+   * 联系电话
+   */
+  phone?: string;
+
+  /**
+   * 岗位编码
+   */
+  postId?: string | number;
+
+  /**
+   * 性别
+   */
+  sex?: string;
+
+  /**
+   * 角色编码
+   */
+  roleId?: string | number;
+
+  /**
+   * 数据来源
+   */
+  dataSource?: string;
+
+  /**
+   * 密码
+   */
+  password?: string;
+
+  /**
+   * 有效期起始
+   */
+  validFrom?: string | number;
+
+  /**
+   * 有效期截止
+   */
+  validTo?: string | number;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+  /**
+   * 日期范围参数
+   */
+  params?: any;
+}

+ 62 - 0
src/router/index.ts

@@ -168,6 +168,68 @@ export const constantRoutes: RouteRecordRaw[] = [
         meta: { title: '编辑合约供货', activeMenu: '/supplier/contractsupply' }
       }
     ]
+  },
+  {
+    path: '/product',
+    component: Layout,
+    hidden: true,
+    redirect: 'noredirect',
+    children: [
+      {
+        path: 'base/add',
+        component: () => import('@/views/product/base/add.vue'),
+        name: 'ProductAdd',
+        meta: { title: '新增商品', activeMenu: '/product/base' }
+      },
+      {
+        path: 'base/edit/:id',
+        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', noCache: true }
+      },
+      {
+        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' }
+      },
+      {
+        path: 'pool/reviewDetail',
+        component: () => import('@/views/product/pool/reviewDetail.vue'),
+        name: 'PoolReviewDetail',
+        meta: { title: '入池清单审核', activeMenu: '/product/pool', noCache: true }
+      },
+      {
+        path: 'protocolInfo/productManage',
+        component: () => import('@/views/product/protocolInfo/productManage.vue'),
+        name: 'ProtocolProductManage',
+        meta: { title: '协议商品管理', activeMenu: '/product/protocolInfo', noCache: true }
+      }
+    ]
   }
 ];
 

+ 0 - 1
src/views/customer/info/detail.vue

@@ -666,7 +666,6 @@ import { listType } from '@/api/system/type';
 import { TypeVO } from '@/api/system/type/types';
 import { listBrand, getBrand } from '@/api/product/brand';
 import { BrandVO } from '@/api/product/brand/types';
-import { getProductCategoryList } from '@/api/product/category';
 import { listAddress, getAddress, addAddress, updateAddress, delAddress } from '@/api/supplier/address';
 import { AddressForm } from '@/api/supplier/address/types';
 import { addArea, listArea, getArea } from '@/api/supplier/area';

+ 256 - 0
src/views/product/associate/index.vue

@@ -0,0 +1,256 @@
+<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="主产品id" prop="productId">
+              <el-input v-model="queryParams.productId" placeholder="请输入主产品id" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="关联产品编号列表" prop="productIds">
+              <el-input v-model="queryParams.productIds" placeholder="请输入关联产品编号列表" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="是否单向关联:0=双向,1=单向" prop="isUnidirectional">
+              <el-input v-model="queryParams.isUnidirectional" placeholder="请输入是否单向关联:0=双向,1=单向" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="关联标题/展示名称" prop="relatedTitle">
+              <el-input v-model="queryParams.relatedTitle" 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>
+            </el-form-item>
+          </el-form>
+        </el-card>
+      </div>
+    </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:associate:add']">新增</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['product:associate:edit']">修改</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['product:associate:remove']">删除</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['product:associate: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="associateList" @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="productId" />
+        <el-table-column label="关联产品编号列表" align="center" prop="productIds" />
+        <el-table-column label="是否单向关联:0=双向,1=单向" align="center" prop="isUnidirectional" />
+        <el-table-column label="关联标题/展示名称" align="center" prop="relatedTitle" />
+        <el-table-column label="备注" align="center" prop="remark" />
+        <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="修改" placement="top">
+              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['product:associate:edit']"></el-button>
+            </el-tooltip>
+            <el-tooltip content="删除" placement="top">
+              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['product:associate:remove']"></el-button>
+            </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-form ref="associateFormRef" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="主产品id" prop="productId">
+          <el-input v-model="form.productId" placeholder="请输入主产品id" />
+        </el-form-item>
+        <el-form-item label="关联产品编号列表" prop="productIds">
+            <el-input v-model="form.productIds" type="textarea" placeholder="请输入内容" />
+        </el-form-item>
+        <el-form-item label="是否单向关联:0=双向,1=单向" prop="isUnidirectional">
+          <el-input v-model="form.isUnidirectional" placeholder="请输入是否单向关联:0=双向,1=单向" />
+        </el-form-item>
+        <el-form-item label="关联标题/展示名称" prop="relatedTitle">
+          <el-input v-model="form.relatedTitle" placeholder="请输入关联标题/展示名称" />
+        </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>
+  </div>
+</template>
+
+<script setup name="Associate" lang="ts">
+import { listAssociate, getAssociate, delAssociate, addAssociate, updateAssociate } from '@/api/product/associate';
+import { AssociateVO, AssociateQuery, AssociateForm } from '@/api/product/associate/types';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const associateList = ref<AssociateVO[]>([]);
+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 associateFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: AssociateForm = {
+  id: undefined,
+  productId: undefined,
+  productIds: undefined,
+  isUnidirectional: undefined,
+  relatedTitle: undefined,
+  remark: undefined,
+}
+const data = reactive<PageData<AssociateForm, AssociateQuery>>({
+  form: {...initFormData},
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    productId: undefined,
+    productIds: undefined,
+    isUnidirectional: undefined,
+    relatedTitle: undefined,
+    platformCode: undefined,
+    params: {
+    }
+  },
+  rules: {
+    productId: [
+      { required: true, message: "主产品id不能为空", trigger: "blur" }
+    ],
+    productIds: [
+      { required: true, message: "关联产品编号列表不能为空", trigger: "blur" }
+    ],
+    relatedTitle: [
+      { required: true, message: "关联标题/展示名称不能为空", trigger: "blur" }
+    ],
+    remark: [
+      { required: true, message: "备注不能为空", trigger: "blur" }
+    ],
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 查询产品关联列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listAssociate(queryParams.value);
+  associateList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+}
+
+/** 表单重置 */
+const reset = () => {
+  form.value = {...initFormData};
+  associateFormRef.value?.resetFields();
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+}
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection: AssociateVO[]) => {
+  ids.value = selection.map(item => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+}
+
+/** 新增按钮操作 */
+const handleAdd = () => {
+  reset();
+  dialog.visible = true;
+  dialog.title = "添加产品关联";
+}
+
+/** 修改按钮操作 */
+const handleUpdate = async (row?: AssociateVO) => {
+  reset();
+  const _id = row?.id || ids.value[0]
+  const res = await getAssociate(_id);
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+  dialog.title = "修改产品关联";
+}
+
+/** 提交按钮 */
+const submitForm = () => {
+  associateFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.id) {
+        await updateAssociate(form.value).finally(() =>  buttonLoading.value = false);
+      } else {
+        await addAssociate(form.value).finally(() =>  buttonLoading.value = false);
+      }
+      proxy?.$modal.msgSuccess("操作成功");
+      dialog.visible = false;
+      await getList();
+    }
+  });
+}
+
+/** 删除按钮操作 */
+const handleDelete = async (row?: AssociateVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('是否确认删除产品关联编号为"' + _ids + '"的数据项?').finally(() => loading.value = false);
+  await delAssociate(_ids);
+  proxy?.$modal.msgSuccess("删除成功");
+  await getList();
+}
+
+/** 导出按钮操作 */
+const handleExport = () => {
+  proxy?.download('product/associate/export', {
+    ...queryParams.value
+  }, `associate_${new Date().getTime()}.xlsx`)
+}
+
+onMounted(() => {
+  getList();
+});
+</script>

+ 8 - 8
src/views/product/attributes/edit.vue

@@ -21,7 +21,7 @@
             </el-form-item>
           </el-col>
         </el-row>
-
+        
         <el-row :gutter="20">
           <el-col :span="12">
             <el-form-item label="品牌名称(英文)" prop="categoryId">
@@ -44,12 +44,12 @@
           </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%"
+              <el-input-number 
+                v-model="extendFormData.recommendValue" 
+                :min="0" 
+                :max="9999" 
+                controls-position="right" 
+                style="width: 100%" 
                 placeholder="请输入推荐系数"
               />
             </el-form-item>
@@ -136,7 +136,7 @@ const form = ref<AttributesForm>({ ...initFormData });
 const rules = reactive({
   categoryId: [
     { required: true, message: "商品类别不能为空", trigger: "change" },
-    {
+    { 
       validator: (rule: any, value: any, callback: any) => {
         if (!value) {
           callback();

+ 6 - 6
src/views/product/attributes/index.vue

@@ -154,11 +154,11 @@
           </el-radio-group>
         </el-form-item>
         <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-input 
+            v-model="form.attributesList" 
+            type="textarea" 
+            :rows="4" 
+            placeholder="请输入属性值,各属性值请用逗号隔开并来示意。例如:A4,70g,100g" 
           />
         </el-form-item>
       </el-form>
@@ -231,7 +231,7 @@ const data = reactive<PageData<AttributesForm, AttributesQuery>>({
   rules: {
     categoryId: [
       { required: true, message: "商品类别不能为空", trigger: "change" },
-      {
+      { 
         validator: (rule: any, value: any, callback: any) => {
           if (!value) {
             callback();

+ 1815 - 0
src/views/product/base/add.vue

@@ -0,0 +1,1815 @@
+<template>
+  <div class="app-container">
+    <el-card shadow="never" class="mb-3">
+      <div class="flex items-center justify-between">
+        <div class="flex items-center">
+          <el-button icon="ArrowLeft" @click="handleBack">返回</el-button>
+          <span class="ml-4 text-xl font-bold">{{ pageTitle }}</span>
+        </div>
+      </div>
+    </el-card>
+
+    <div class="product-wizard-page">
+      <!-- 步骤条 -->
+      <el-card shadow="never" class="mb-3">
+        <el-steps :active="currentStep" finish-status="success" align-center>
+          <el-step title="选择分类" description="选择商品分类" />
+          <el-step title="填写商品信息" description="填写商品基本信息" />
+          <el-step title="完成" description="确认提交" />
+        </el-steps>
+      </el-card>
+
+      <!-- 步骤内容 -->
+      <div class="step-content" v-loading="loading">
+        <!-- 步骤1: 选择分类 -->
+        <el-card v-show="currentStep === 0" shadow="never" class="step-card">
+          <template #header>
+            <div class="flex items-center">
+              <!-- <el-icon class="mr-2"><Warning /></el-icon> -->
+              <span class="text-lg font-bold">选择分类</span>
+            </div>
+          </template>
+
+          <div class="category-selection">
+            <el-row :gutter="20">
+              <!-- 一级分类 -->
+              <el-col :span="8">
+                <div class="category-box">
+                  <div class="category-header">选择一级分类</div>
+                  <div class="category-search">
+                    <el-input
+                      v-model="searchLevel1"
+                      placeholder="搜索一级分类"
+                      clearable
+                      prefix-icon="Search"
+                      size="small"
+                    />
+                  </div>
+                  <div class="category-list">
+                    <div
+                      v-for="item in filteredLevel1Categories"
+                      :key="item.id"
+                      :class="['category-item', { 'active': categoryForm.topCategoryId === item.id }]"
+                      @click="selectLevel1(item)"
+                    >
+                      <span>{{ item.label }}</span>
+                      <el-icon v-if="categoryForm.topCategoryId === item.id"><ArrowRight /></el-icon>
+                    </div>
+                    <el-empty v-if="filteredLevel1Categories.length === 0" description="暂无数据" :image-size="60" />
+                  </div>
+                </div>
+              </el-col>
+
+              <!-- 二级分类 -->
+              <el-col :span="8">
+                <div class="category-box">
+                  <div class="category-header">选择二级分类</div>
+                  <div class="category-search">
+                    <el-input
+                      v-model="searchLevel2"
+                      placeholder="搜索二级分类"
+                      clearable
+                      prefix-icon="Search"
+                      size="small"
+                    />
+                  </div>
+                  <div class="category-list">
+                    <div
+                      v-for="item in filteredLevel2Categories"
+                      :key="item.id"
+                      :class="['category-item', { 'active': categoryForm.mediumCategoryId === item.id }]"
+                      @click="selectLevel2(item)"
+                    >
+                      <span>{{ item.label }}</span>
+                      <el-icon v-if="categoryForm.mediumCategoryId === item.id"><ArrowRight /></el-icon>
+                    </div>
+                    <el-empty v-if="filteredLevel2Categories.length === 0" description="请先选择一级分类" :image-size="60" />
+                  </div>
+                </div>
+              </el-col>
+
+              <!-- 三级分类 -->
+              <el-col :span="8">
+                <div class="category-box">
+                  <div class="category-header">选择三级分类</div>
+                  <div class="category-search">
+                    <el-input
+                      v-model="searchLevel3"
+                      placeholder="搜索三级分类"
+                      clearable
+                      prefix-icon="Search"
+                      size="small"
+                    />
+                  </div>
+                  <div class="category-list">
+                    <div
+                      v-for="item in filteredLevel3Categories"
+                      :key="item.id"
+                      :class="['category-item', { 'active': categoryForm.bottomCategoryId === item.id }]"
+                      @click="selectLevel3(item)"
+                    >
+                      <span>{{ item.label }}</span>
+                      <el-icon v-if="categoryForm.bottomCategoryId === item.id"><Check /></el-icon>
+                    </div>
+                    <el-empty v-if="filteredLevel3Categories.length === 0" description="请先选择二级分类" :image-size="60" />
+                  </div>
+                </div>
+              </el-col>
+            </el-row>
+          </div>
+
+          <!-- 已选分类提示 -->
+          <!-- <div class="mt-4">
+            <el-checkbox v-model="autoCreateCategory" label="如果选择的分类不存在,自动创建分类" />
+          </div>
+          <div class="mt-2">
+            <el-input
+              v-model="manualCategoryInput"
+              placeholder="请输入入口类名称"
+              clearable
+              style="width: 400px;"
+            />
+          </div> -->
+        </el-card>
+
+        <!-- 步骤2: 填写商品信息 -->
+        <el-card v-show="currentStep === 1" shadow="never" class="step-card">
+          <template #header>
+            <span class="text-lg font-bold">基本信息</span>
+          </template>
+
+          <el-form ref="productFormRef" :model="productForm" :rules="productRules" label-width="120px" class="product-info-form">
+            <!-- 商品分类显示 -->
+            <el-form-item label="商品分类:">
+              <div class="category-display">
+                <span class="category-text">{{ getCategoryPath() }}</span>
+                <el-link type="primary" :underline="false" @click="currentStep = 0" class="ml-2">修改</el-link>
+                <el-link type="danger" :underline="false" @click="clearCategory" class="ml-2">删除</el-link>
+              </div>
+            </el-form-item>
+
+            <!-- 商品编号 -->
+            <el-row :gutter="20">
+              <el-col :span="12">
+                <el-form-item label="商品编号:" prop="productNo" required>
+                  <el-input
+                    v-model="productForm.productNo"
+                    placeholder="002169745"
+                    maxlength="20"
+                    show-word-limit
+                  />
+                </el-form-item>
+              </el-col>
+              <el-col :span="12">
+                <el-form-item label="状态:">
+                  <span class="category-text">上架在售</span>
+                </el-form-item>
+              </el-col>
+            </el-row>
+
+            <!-- 商品名称 -->
+            <el-form-item label="商品名称:" prop="itemName" required>
+              <el-input
+                v-model="productForm.itemName"
+                type="textarea"
+                :rows="2"
+                placeholder="请输入商品名称"
+                maxlength="200"
+                show-word-limit
+              />
+            </el-form-item>
+
+            <!-- A10产品名称 -->
+            <el-form-item label="A10产品名称:">
+              <el-input
+                v-model="productForm.a10ProductName"
+                type="textarea"
+                :rows="2"
+                placeholder="请输入A10产品名称"
+                maxlength="200"
+                show-word-limit
+              />
+              <div class="form-item-tip">
+                A10产品名称应包含该产品的关键词,本身的名称、本身的型号、产品的主要特点、产品的用途、产品的颜色、产品的规格等。可以A3时间 A4 单击
+              </div>
+            </el-form-item>
+
+            <!-- 规格型号 和 UPC(69)条码 -->
+            <el-row :gutter="20">
+              <el-col :span="12">
+                <el-form-item label="规格型号:">
+                  <el-input
+                    v-model="productForm.specification"
+                    placeholder="请输入规格型号"
+                    maxlength="20"
+                    show-word-limit
+                  />
+                </el-form-item>
+              </el-col>
+              <el-col :span="12">
+                <el-form-item label="UPC(69)条码:">
+                  <el-input
+                    v-model="productForm.upcBarcode"
+                    placeholder="请输入UPC(69)条码"
+                    maxlength="20"
+                    show-word-limit
+                    @input="handleUpcInput"
+                  />
+                </el-form-item>
+              </el-col>
+            </el-row>
+
+            <!-- 发票名称 和 发票规格 -->
+            <el-row :gutter="20">
+              <el-col :span="12">
+                <el-form-item label="发票名称:">
+                  <el-input
+                    v-model="productForm.invoiceName"
+                    placeholder="请输入发票名称"
+                    maxlength="20"
+                    show-word-limit
+                  />
+                </el-form-item>
+              </el-col>
+              <el-col :span="12">
+                <el-form-item label="发票规格:">
+                  <el-input
+                    v-model="productForm.invoiceSpec"
+                    placeholder="请输入发票规格"
+                    maxlength="20"
+                    show-word-limit
+                  />
+                </el-form-item>
+              </el-col>
+            </el-row>
+            <el-row :gutter="20">
+              <!-- 商品品牌 -->
+               <el-col :span="12">
+                <el-form-item label="商品品牌:" prop="brandId" required>
+                  <el-select
+                    v-model="productForm.brandId"
+                    placeholder="请输入品牌名称搜索"
+                    filterable
+                    remote
+                    clearable
+                    :remote-method="handleBrandSearch"
+                    :loading="brandLoading"
+                    class="w-full"
+                  >
+                    <el-option
+                      v-for="item in brandOptions"
+                      :key="item.id"
+                      :label="item.brandName"
+                      :value="item.id"
+                    />
+                  </el-select>
+                </el-form-item>
+               </el-col>
+
+              <el-col :span="12">
+                <el-form-item label="单位:">
+                  <el-select v-model="productForm.unitId" placeholder="请选择" clearable class="w-full">
+                    <el-option
+                      v-for="option in unitOptions"
+                      :key="option.id"
+                      :label="option.unitName"
+                      :value="option.id"
+                    />
+                  </el-select>
+                </el-form-item>
+              </el-col>
+            </el-row>
+
+
+            <!-- 税率 和 币种 -->
+            <el-row :gutter="20">
+              <el-col :span="12">
+                <el-form-item label="税率:" required>
+                  <el-select v-model="productForm.taxRate" placeholder="请选择税率" clearable class="w-full">
+                    <el-option
+                      v-for="option in taxRateOptions"
+                      :key="option.id"
+                      :label="option.taxrateName"
+                      :value="option.taxrate"
+                    />
+                  </el-select>
+                </el-form-item>
+              </el-col>
+              <el-col :span="12">
+                <el-form-item label="币种:">
+                  <el-select v-model="productForm.currency" placeholder="请选择" class="w-full">
+                    <el-option label="人民币(RMB)" value="RMB" />
+                    <el-option label="美元(USD)" value="USD" />
+                    <el-option label="欧元(EUR)" value="EUR" />
+                  </el-select>
+                </el-form-item>
+              </el-col>
+            </el-row>
+
+            <!-- 销量人气 -->
+            <el-row :gutter="20">
+              <el-col :span="12">
+                <el-form-item label="销量人气:">
+                  <el-input
+                    v-model="productForm.salesVolume"
+                    type="number"
+                    placeholder="请输入销量人气"
+                    :min="0"
+                  />
+                </el-form-item>
+              </el-col>
+            </el-row>
+
+            <!-- 包装规格 -->
+            <el-form-item label="促销标题:">
+              <el-input
+                v-model="productForm.packagingSpec"
+                type="textarea"
+                :rows="3"
+                placeholder="请输入包装规格"
+                maxlength="300"
+                show-word-limit
+              />
+            </el-form-item>
+
+            <!-- 重量 和 体积 -->
+            <el-row :gutter="20">
+              <el-col :span="12">
+                <el-form-item label="商品重量:">
+                  <el-input
+                    v-model="productForm.weight"
+                    placeholder="0"
+                    maxlength="10"
+                    show-word-limit
+                  >
+                    <template #append>
+                      <el-select v-model="productForm.weightUnit" placeholder="请选择" style="width: 80px">
+                        <el-option label="kg" value="kg" />
+                        <el-option label="g" value="g" />
+                        <el-option label="t" value="t" />
+                      </el-select>
+                    </template>
+                  </el-input>
+                </el-form-item>
+              </el-col>
+              <el-col :span="12">
+                <el-form-item label="商品体积:">
+                  <el-input
+                    v-model="productForm.volume"
+                    placeholder="0"
+                    maxlength="10"
+                    show-word-limit
+                  >
+                    <template #append>
+                      <el-select v-model="productForm.volumeUnit" placeholder="请选择" style="width: 80px">
+                        <el-option label="m³" value="m3" />
+                        <el-option label="cm³" value="cm3" />
+                        <el-option label="L" value="L" />
+                      </el-select>
+                    </template>
+                  </el-input>
+                </el-form-item>
+              </el-col>
+            </el-row>
+
+            <!-- 参考链接 -->
+            <el-form-item label="参考链接">
+              <el-input
+                v-model="productForm.referenceLink"
+                type="textarea"
+                :rows="3"
+                placeholder="请输入参考链接"
+              />
+            </el-form-item>
+
+            <!-- 主供应商 -->
+            <el-form-item label="主供应商:" prop="mainLibraryIntro" required>
+              <el-select v-model="productForm.mainLibraryIntro" placeholder="请选择" clearable class="w-full" value-key="id">
+                <el-option
+                  v-for="option in supplierOptions"
+                  :key="option.id"
+                  :label="`${option.supplierNo},${option.enterpriseName}`"
+                  :value="String(option.id)"
+                />
+              </el-select>
+            </el-form-item>
+
+            <!-- 售后服务 -->
+            <el-form-item label="售后服务:">
+              <el-select v-model="productForm.afterSalesService" placeholder="请选择" clearable class="w-full">
+                <el-option
+                  v-for="option in afterSalesOptions"
+                  :key="option.id"
+                  :label="option.afterSalesItems"
+                  :value="option.id"
+                />
+              </el-select>
+            </el-form-item>
+
+            <!-- 服务保障 -->
+            <el-form-item label="服务保障:">
+              <el-checkbox-group v-model="serviceGuarantees">
+                <el-checkbox
+                  v-for="option in serviceGuaranteeOptions"
+                  :key="option.id"
+                  :label="option.ensureName"
+                  :value="option.id"
+                />
+              </el-checkbox-group>
+            </el-form-item>
+
+            <!-- 安装服务 -->
+            <el-form-item label="安装服务:">
+              <el-checkbox-group v-model="installationServices">
+                <el-checkbox label="免费安装" value="freeInstallation" />
+              </el-checkbox-group>
+            </el-form-item>
+          </el-form>
+        </el-card>
+
+        <!-- 销售价格 -->
+        <el-card v-show="currentStep === 1" shadow="never" class="step-card mt-3">
+          <template #header>
+            <span class="text-lg font-bold">销售价格</span>
+          </template>
+
+          <el-form ref="priceFormRef" :model="productForm" :rules="productRules" label-width="120px" class="product-info-form">
+            <el-row :gutter="20">
+              <el-col :span="8">
+                <el-form-item label="市场价:" prop="midRangePrice" required>
+                  <el-input
+                    v-model="productForm.midRangePrice"
+                    type="number"
+                    placeholder="请输入市场价"
+                    @blur="formatPrice('midRangePrice')"
+                  />
+                </el-form-item>
+              </el-col>
+              <el-col :span="8">
+                <el-form-item label="平台售价:" prop="standardPrice" required>
+                  <el-input
+                    v-model="productForm.standardPrice"
+                    type="number"
+                    placeholder="请输入平台售价"
+                    @blur="formatPrice('standardPrice')"
+                  />
+                </el-form-item>
+              </el-col>
+              <el-col :span="8">
+                <el-form-item label="最低售价:" prop="certificatePrice" required>
+                  <el-input
+                    v-model="productForm.certificatePrice"
+                    type="number"
+                    placeholder="请输入最低售价"
+                    @blur="formatPrice('certificatePrice')"
+                  />
+                </el-form-item>
+              </el-col>
+            </el-row>
+            <el-row :gutter="20">
+              <el-col :span="8">
+                <el-form-item label="最低起订量:" prop="minOrderQuantity" required>
+                  <el-input
+                    v-model="productForm.minOrderQuantity"
+                    type="number"
+                    placeholder="请输入最低起订量"
+                  />
+                </el-form-item>
+              </el-col>
+              <el-col :span="8">
+                <el-form-item label="备注:">
+                  <span class="currency-text">市场价>会员价>最低售价</span>
+                </el-form-item>
+              </el-col>
+            </el-row>
+
+          </el-form>
+        </el-card>
+
+        <!-- 采购价格 -->
+        <el-card v-show="currentStep === 1" shadow="never" class="step-card mt-3">
+          <template #header>
+            <span class="text-lg font-bold">采购价格</span>
+          </template>
+
+          <el-form ref="purchasePriceFormRef" :model="productForm" :rules="productRules" label-width="120px" class="product-info-form">
+            <el-row :gutter="20">
+              <el-col :span="12">
+                <el-form-item label="采购价:" prop="purchasePrice" required>
+                  <el-input
+                    v-model="productForm.purchasePrice"
+                    type="number"
+                    placeholder="请输入采购价"
+                    @blur="formatPrice('purchasePrice')"
+                  />
+                </el-form-item>
+              </el-col>
+              <el-col :span="12">
+                <el-form-item label="暂估采购价:">
+                  <el-input
+                    v-model="productForm.estimatedPurchasePrice"
+                    type="number"
+                    placeholder="请输入暂估采购价"
+                    @blur="formatPrice('estimatedPurchasePrice')"
+                  />
+                </el-form-item>
+              </el-col>
+            </el-row>
+          </el-form>
+        </el-card>
+
+        <!-- 采购信息 -->
+        <el-card v-show="currentStep === 1" shadow="never" class="step-card mt-3">
+          <template #header>
+            <span class="text-lg font-bold">采购信息</span>
+          </template>
+
+          <el-form ref="purchaseInfoFormRef" :model="productForm" :rules="productRules" label-width="120px" class="product-info-form">
+            <el-row :gutter="20">
+              <el-col :span="12">
+                <el-form-item label="产品经理:" prop="productNature" required>
+                  <el-select v-model="productForm.productNature" placeholder="请选择" clearable class="w-full" value-key="staffId">
+                    <el-option
+                      v-for="option in staffOptions"
+                      :key="option.staffId"
+                      :label="`${option.staffCode},${option.staffName}`"
+                      :value="String(option.staffId)"
+                    />
+                  </el-select>
+                </el-form-item>
+              </el-col>
+              <el-col :span="12">
+                <el-form-item label="采购人员:" prop="purchasingPersonnel" required>
+                  <el-select v-model="productForm.purchasingPersonnel" placeholder="请选择" clearable class="w-full" value-key="staffId">
+                    <el-option
+                      v-for="option in staffOptions"
+                      :key="option.staffId"
+                      :label="`${option.staffCode},${option.staffName}`"
+                      :value="String(option.staffId)"
+                    />
+                  </el-select>
+                </el-form-item>
+              </el-col>
+            </el-row>
+          </el-form>
+        </el-card>
+
+        <!-- 商品属性 -->
+        <el-card v-show="currentStep === 1" shadow="never" class="step-card mt-3">
+          <template #header>
+            <span class="text-lg font-bold">商品属性</span>
+          </template>
+
+          <el-form ref="attributeFormRef" :model="productForm" label-width="120px" class="product-info-form">
+            <div v-if="attributesList.length === 0" class="text-center text-gray-500 py-8">
+              该分类暂无属性配置
+            </div>
+            <template v-else>
+              <el-row :gutter="20" v-for="(row, rowIndex) in Math.ceil(attributesList.length / 2)" :key="rowIndex">
+                <el-col :span="12" v-for="colIndex in 2" :key="colIndex">
+                  <template v-if="attributesList[rowIndex * 2 + colIndex - 1]">
+                    <el-form-item
+                      :label="attributesList[rowIndex * 2 + colIndex - 1].productAttributesName + ':'"
+                      :required="attributesList[rowIndex * 2 + colIndex - 1].required === '1'"
+                    >
+                      <!-- 下拉选择 -->
+                      <el-select
+                        v-if="attributesList[rowIndex * 2 + colIndex - 1].entryMethod === '1'"
+                        v-model="productAttributesValues[attributesList[rowIndex * 2 + colIndex - 1].id]"
+                        placeholder="请选择"
+                        clearable
+                        class="w-full"
+                      >
+                        <el-option
+                          v-for="option in parseAttributesList(attributesList[rowIndex * 2 + colIndex - 1].attributesList)"
+                          :key="option"
+                          :label="option"
+                          :value="option"
+                        />
+                      </el-select>
+                      <!-- 多选 -->
+                      <el-select
+                        v-else-if="attributesList[rowIndex * 2 + colIndex - 1].entryMethod === '3'"
+                        v-model="productAttributesValues[attributesList[rowIndex * 2 + colIndex - 1].id]"
+                        placeholder="请选择"
+                        multiple
+                        clearable
+                        class="w-full"
+                      >
+                        <el-option
+                          v-for="option in parseAttributesList(attributesList[rowIndex * 2 + colIndex - 1].attributesList)"
+                          :key="option"
+                          :label="option"
+                          :value="option"
+                        />
+                      </el-select>
+                      <!-- 文本输入 -->
+                      <el-input
+                        v-else
+                        v-model="productAttributesValues[attributesList[rowIndex * 2 + colIndex - 1].id]"
+                        placeholder="请输入"
+                        clearable
+                      />
+                    </el-form-item>
+                  </template>
+                </el-col>
+              </el-row>
+            </template>
+          </el-form>
+        </el-card>
+
+        <!-- 商品详情 -->
+        <el-card v-show="currentStep === 1" shadow="never" class="step-card mt-3">
+          <template #header>
+            <span class="text-lg font-bold">商品详情</span>
+          </template>
+
+          <el-form ref="detailFormRef" :model="productForm" label-width="120px" class="product-info-form">
+            <!-- 商品主图 -->
+            <el-form-item label="商品主图:">
+              <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">
+                从图片库选择,建议尺寸300*300px
+              </div>
+            </el-form-item>
+
+            <!-- 商品轮播图 -->
+            <el-form-item label="商品轮播图:">
+              <div class="carousel-images-container">
+                <div class="carousel-image-list">
+                  <div v-for="(imgUrl, index) in carouselImages" :key="index" class="carousel-image-item">
+                    <img :src="imgUrl" class="carousel-preview-image" />
+                    <div class="carousel-image-actions">
+                      <el-button size="small" type="danger" @click="removeCarouselImage(index)">删除</el-button>
+                    </div>
+                  </div>
+                  <div class="image-upload-placeholder carousel-add-btn" @click="openCarouselImageSelector">
+                    <el-icon class="upload-icon"><Plus /></el-icon>
+                    <div class="upload-text">添加图片</div>
+                  </div>
+                </div>
+              </div>
+              <div class="form-item-tip">
+                从图片库选择,支持多选,建议尺寸300*300px
+              </div>
+            </el-form-item>
+
+            <!-- 商品详情 -->
+            <el-form-item label="商品详情:">
+              <el-tabs v-model="activeDetailTab" type="border-card">
+                <el-tab-pane label="电脑端详情" name="pc">
+                  <Editor v-model="productForm.pcDetail" :height="400" />
+                </el-tab-pane>
+                <el-tab-pane label="移动端详情" name="mobile">
+                  <Editor v-model="productForm.mobileDetail" :height="400" />
+                </el-tab-pane>
+              </el-tabs>
+            </el-form-item>
+          </el-form>
+        </el-card>
+
+        <!-- 定制说明 -->
+        <el-card v-show="currentStep === 1" shadow="never" class="step-card mt-3">
+          <template #header>
+            <span class="text-lg font-bold">定制说明</span>
+          </template>
+
+          <el-form ref="customFormRef" :model="customForm" label-width="120px" class="product-info-form">
+            <!-- 可定制开关 -->
+            <el-form-item label="可定制:">
+              <el-switch v-model="customForm.customizable" />
+            </el-form-item>
+
+            <!-- 定制内容 -->
+            <template v-if="customForm.customizable">
+              <!-- 定制方式 -->
+              <el-form-item label="定制方式:">
+                <div class="custom-options">
+                  <el-button
+                    v-for="option in customMethodOptions"
+                    :key="option.value"
+                    :type="customForm.selectedMethods.includes(option.value) ? 'primary' : 'default'"
+                    @click="toggleMethod(option.value)"
+                  >
+                    {{ option.label }}
+                  </el-button>
+                </div>
+              </el-form-item>
+
+              <!-- 定制工艺 -->
+              <el-form-item label="定制工艺:">
+                <div class="custom-options">
+                  <el-button
+                    v-for="craft in customCraftOptions"
+                    :key="craft.value"
+                    :type="customForm.selectedCrafts.includes(craft.value) ? 'primary' : 'default'"
+                    @click="toggleCraft(craft.value)"
+                  >
+                    {{ craft.label }}
+                  </el-button>
+                </div>
+              </el-form-item>
+
+              <!-- 定制方式表格 -->
+              <el-form-item label="" label-width="120">
+                <el-table :data="customForm.customDetails" border class="custom-table">
+                  <el-table-column label="装饰方法" width="120">
+                    <template #default="{ row }">
+                      <span>{{ row.decorationMethod }}</span>
+                    </template>
+                  </el-table-column>
+                  <el-table-column label="定制工艺" width="120">
+                    <template #default="{ row }">
+                      <span>{{ row.craft }}</span>
+                    </template>
+                  </el-table-column>
+                  <el-table-column label="起订数量" width="150">
+                    <template #default="{ row }">
+                      <el-input v-model="row.minOrderQty" placeholder="请输入" />
+                    </template>
+                  </el-table-column>
+                  <el-table-column label="起订价格" width="150">
+                    <template #default="{ row }">
+                      <el-input v-model="row.minOrderPrice" placeholder="请输入" />
+                    </template>
+                  </el-table-column>
+                  <el-table-column label="打样工期[天]" width="150">
+                    <template #default="{ row }">
+                      <el-input v-model="row.samplePeriod" placeholder="请输入" />
+                    </template>
+                  </el-table-column>
+                  <el-table-column label="生产周期[天]" width="150">
+                    <template #default="{ row }">
+                      <el-input v-model="row.productionPeriod" placeholder="请输入" />
+                    </template>
+                  </el-table-column>
+                  <el-table-column label="操作" width="100" fixed="right">
+                    <template #default="{ $index }">
+                      <el-link type="danger" :underline="false" @click="removeCustomDetail($index)">
+                        删除
+                      </el-link>
+                    </template>
+                  </el-table-column>
+                </el-table>
+              </el-form-item>
+
+              <!-- 定制说明 -->
+              <el-form-item label="定制说明:">
+                <el-input
+                  v-model="customForm.customDescription"
+                  type="textarea"
+                  :rows="5"
+                  placeholder="请输入定制说明"
+                />
+              </el-form-item>
+            </template>
+          </el-form>
+        </el-card>
+
+        <!-- 步骤3: 完成 -->
+        <el-card v-show="currentStep === 2" shadow="never" class="step-card completion-card">
+          <div class="completion-content">
+            <div class="success-icon">
+              <el-icon :size="80" color="#67c23a">
+                <CircleCheck />
+              </el-icon>
+            </div>
+            <div class="completion-text">
+              商品编辑完成,请点击返回,继续其他操作
+            </div>
+            <div class="completion-action">
+              <el-button type="primary" @click="handleBackToList">返回</el-button>
+            </div>
+          </div>
+        </el-card>
+      </div>
+
+      <!-- 底部操作按钮 -->
+      <el-card v-if="currentStep < 2" shadow="never" class="mt-3">
+        <div class="flex justify-center gap-4">
+          <el-button v-if="currentStep > 0" @click="prevStep">上一步</el-button>
+          <el-button v-if="currentStep < 2" type="primary" @click="nextStep">下一步</el-button>
+          <el-button @click="handleBack">取消</el-button>
+        </div>
+      </el-card>
+    </div>
+
+    <!-- 文件选择器组件 -->
+    <FileSelector
+      v-model="mainImageSelectorVisible"
+      :allowed-types="[1]"
+      :multiple="false"
+      title="选择商品主图"
+      @confirm="handleMainImageSelected"
+    />
+    <!-- 轮播图文件选择器 -->
+    <FileSelector
+      v-model="carouselImageSelectorVisible"
+      :allowed-types="[1]"
+      :multiple="true"
+      title="选择商品轮播图"
+      @confirm="handleCarouselImagesSelected"
+    />
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, reactive, computed, onMounted, watch } from 'vue';
+import { useRoute, useRouter } from 'vue-router';
+import { ElMessage } from 'element-plus';
+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';
+import { AttributesVO } from '@/api/product/attributes/types';
+import { addBase, updateBase, getBase, categoryTree, categoryAttributeList, getAfterSaleList, getServiceList, getUnitList, getTaxRateList } from '@/api/product/base';
+import { listBrand } from '@/api/product/brand';
+import { listInfo } from '@/api/customer/supplierInfo';
+import { InfoVO } from '@/api/customer/supplierInfo/types';
+import { listComStaff } from '@/api/system/comStaff';
+import { ComStaffVO } from '@/api/system/comStaff/types';
+
+const route = useRoute();
+const router = useRouter();
+
+const currentStep = ref(0);
+const loading = ref(false);
+const submitLoading = ref(false);
+const productFormRef = ref();
+
+// 服务保障和安装服务的多选框
+const serviceGuarantees = ref<(string | number)[]>([]);
+const installationServices = ref<string[]>([]);
+
+// 商品详情选项卡
+const activeDetailTab = ref('pc');
+
+// 文件选择器相关
+const mainImageSelectorVisible = ref(false);
+const carouselImageSelectorVisible = ref(false);
+
+// 轮播图URL数组(UI管理用)
+const carouselImages = ref<string[]>([]);
+
+// 税率选项
+const taxRateOptions = ref<any[]>([]);
+
+// 定制说明表单
+const customForm = reactive({
+  customizable: false,
+  selectedMethods: [] as string[],
+  selectedCrafts: [] as string[],
+  customDetails: [] as Array<{
+    decorationMethod: string;
+    craft: string;
+    minOrderQty: string;
+    minOrderPrice: string;
+    samplePeriod: string;
+    productionPeriod: string;
+  }>,
+  customDescription: ''
+});
+
+// 定制方式选项
+const customMethodOptions = [
+  { label: '包装定制', value: 'package' },
+  { label: '商品定制', value: 'product' },
+  { label: '开模定制', value: 'mold' }
+];
+
+// 定制工艺选项
+const customCraftOptions = [
+  { label: '丝印', value: 'silkScreen' },
+  { label: '热转印', value: 'thermalTransfer' },
+  { label: '激光', value: 'laser' },
+  { label: '烤花', value: 'baking' },
+  { label: '压印', value: 'embossing' }
+];
+
+// 定制方式映射
+const customMethodMap: Record<string, string> = {
+  'package': '包装定制',
+  'product': '商品定制',
+  'mold': '开模定制'
+};
+
+// 定制工艺映射
+const customCraftMap: Record<string, string> = {
+  'silkScreen': '丝印',
+  'thermalTransfer': '热转印',
+  'laser': '激光',
+  'baking': '烤花',
+  'embossing': '压印'
+};
+
+// 服务保障选择不需要watch,在提交时直接转换为逗号分隔字符串
+
+// 监听安装服务复选框变化,同步到表单
+watch(installationServices, (newVal) => {
+  productForm.freeInstallation = newVal.includes('freeInstallation') ? '1' : '0';
+}, { deep: true });
+
+// 监听定制方式和工艺选择变化,更新表格数据
+watch([() => customForm.selectedMethods, () => customForm.selectedCrafts], ([newMethods, newCrafts]) => {
+  const newDetails: typeof customForm.customDetails = [];
+
+  // 遍历所有选中的定制方式和工艺组合
+  newMethods.forEach(method => {
+    const decorationMethod = customMethodMap[method];
+
+    newCrafts.forEach(craft => {
+      const craftName = customCraftMap[craft];
+
+      // 查找是否已存在该组合的数据
+      const existing = customForm.customDetails.find(
+        item => item.decorationMethod === decorationMethod && item.craft === craftName
+      );
+
+      newDetails.push(existing || {
+        decorationMethod,
+        craft: craftName,
+        minOrderQty: '',
+        minOrderPrice: '',
+        samplePeriod: '',
+        productionPeriod: ''
+      });
+    });
+  });
+
+  customForm.customDetails = newDetails;
+}, { deep: true });
+
+// 切换定制方式选择
+const toggleMethod = (method: string) => {
+  const index = customForm.selectedMethods.indexOf(method);
+  if (index > -1) {
+    customForm.selectedMethods.splice(index, 1);
+  } else {
+    customForm.selectedMethods.push(method);
+  }
+};
+
+// 切换定制工艺选择
+const toggleCraft = (craft: string) => {
+  const index = customForm.selectedCrafts.indexOf(craft);
+  if (index > -1) {
+    customForm.selectedCrafts.splice(index, 1);
+  } else {
+    customForm.selectedCrafts.push(craft);
+  }
+};
+
+// 删除定制详情行
+const removeCustomDetail = (index: number) => {
+  customForm.customDetails.splice(index, 1);
+};
+
+const pageTitle = computed(() => {
+  return route.params.id ? '编辑商品' : '新增商品';
+});
+
+// 分类选择表单
+const categoryForm = reactive({
+  topCategoryId: undefined as string | number | undefined,
+  mediumCategoryId: undefined as string | number | undefined,
+  bottomCategoryId: undefined as string | number | undefined,
+});
+
+const autoCreateCategory = ref(false);
+const manualCategoryInput = ref('');
+
+// 商品信息表单
+const productForm = reactive<BaseForm>({
+  id: undefined,
+  productNo: undefined,
+  itemName: undefined,
+  brandId: undefined,
+  topCategoryId: undefined,
+  mediumCategoryId: undefined,
+  bottomCategoryId: undefined,
+  unitId: undefined,
+  productImage: undefined,
+  imageUrl: undefined,
+  isSelf: 0,
+  productReviewStatus: 0,
+  homeRecommended: 0,
+  categoryRecommendation: 0,
+  cartRecommendation: 0,
+  recommendedProductOrder: 0,
+  isPopular: 0,
+  isNew: 0,
+  productStatus: '0',
+  remark: undefined,
+  a10ProductName: undefined,
+  specification: undefined,
+  upcBarcode: undefined,
+  invoiceName: undefined,
+  invoiceSpec: undefined,
+  packagingSpec: undefined,
+  referenceLink: undefined,
+  weight: undefined,
+  weightUnit: 'kg',
+  volume: undefined,
+  volumeUnit: 'm3',
+  mainLibraryIntro: undefined,
+  afterSalesService: undefined,
+  serviceGuarantee: undefined, // 服务保障ID列表,逗号分隔
+  freeInstallation: '0',
+  midRangePrice: undefined,
+  standardPrice: undefined,
+  certificatePrice: undefined,
+  purchasePrice: undefined,
+  estimatedPurchasePrice: undefined,
+  productNature: '1',
+  purchasingPersonnel: '1',
+  pcDetail: undefined,
+  mobileDetail: undefined,
+  taxRate: undefined,
+  currency: 'RMB',
+  minOrderQuantity: undefined,
+  salesVolume: undefined,
+});
+
+// 表单验证规则
+const productRules = {
+  productNo: [{ required: true, message: '商品编号不能为空', trigger: 'blur' }],
+  itemName: [{ required: true, message: '商品名称不能为空', trigger: 'blur' }],
+  brandId: [{ required: true, message: '商品品牌不能为空', trigger: 'change' }],
+  mainLibraryIntro: [{ required: true, message: '主供应商不能为空', trigger: 'change' }],
+  midRangePrice: [{ required: true, message: '市场价不能为空', trigger: 'blur' }],
+  standardPrice: [{ required: true, message: '平台售价不能为空', trigger: 'blur' }],
+  certificatePrice: [{ required: true, message: '最低售价不能为空', trigger: 'blur' }],
+  purchasePrice: [{ required: true, message: '采购价不能为空', trigger: 'blur' }],
+  productNature: [{ required: true, message: '产品经理不能为空', trigger: 'change' }],
+  purchasingPersonnel: [{ required: true, message: '采购人员不能为空', trigger: 'change' }],
+  taxRate: [{ required: true, message: '税率不能为空', trigger: 'change' }],
+  minOrderQuantity: [{ required: true, message: '最低起订量不能为空', trigger: 'blur' }],
+};
+
+// 分类和品牌选项
+const categoryOptions = ref<categoryTreeVO[]>([]);
+const brandOptions = ref<BrandVO[]>([]);
+const brandLoading = ref(false);
+let brandSearchTimer: ReturnType<typeof setTimeout> | null = null;
+
+// 商品属性列表
+const attributesList = ref<AttributesVO[]>([]);
+const productAttributesValues = ref<Record<string | number, any>>({});
+
+// 售后服务和服务保障选项
+const afterSalesOptions = ref<any[]>([]);
+const serviceGuaranteeOptions = ref<any[]>([]);
+
+// 单位选项
+const unitOptions = ref<any[]>([]);
+
+// 主供应商选项
+const supplierOptions = ref<InfoVO[]>([]);
+
+// 采购人员选项
+const staffOptions = ref<ComStaffVO[]>([]);
+
+// 搜索关键词
+const searchLevel1 = ref('');
+const searchLevel2 = ref('');
+const searchLevel3 = ref('');
+
+// 一级分类列表
+const level1Categories = computed(() => {
+  return categoryOptions.value || [];
+});
+
+// 二级分类列表
+const level2Categories = ref<categoryTreeVO[]>([]);
+
+// 三级分类列表
+const level3Categories = ref<categoryTreeVO[]>([]);
+
+// 过滤后的一级分类列表
+const filteredLevel1Categories = computed(() => {
+  if (!searchLevel1.value) {
+    return level1Categories.value;
+  }
+  return level1Categories.value.filter(item =>
+    item.label.toLowerCase().includes(searchLevel1.value.toLowerCase())
+  );
+});
+
+// 过滤后的二级分类列表
+const filteredLevel2Categories = computed(() => {
+  if (!searchLevel2.value) {
+    return level2Categories.value;
+  }
+  return level2Categories.value.filter(item =>
+    item.label.toLowerCase().includes(searchLevel2.value.toLowerCase())
+  );
+});
+
+// 过滤后的三级分类列表
+const filteredLevel3Categories = computed(() => {
+  if (!searchLevel3.value) {
+    return level3Categories.value;
+  }
+  return level3Categories.value.filter(item =>
+    item.label.toLowerCase().includes(searchLevel3.value.toLowerCase())
+  );
+});
+
+// 选中的分类名称
+const selectedLevel1Name = ref('');
+const selectedLevel2Name = ref('');
+const selectedLevel3Name = ref('');
+
+// 选择一级分类
+const selectLevel1 = (item: categoryTreeVO) => {
+  categoryForm.topCategoryId = item.id;
+  categoryForm.mediumCategoryId = undefined;
+  categoryForm.bottomCategoryId = undefined;
+  selectedLevel1Name.value = item.label;
+  selectedLevel2Name.value = '';
+  selectedLevel3Name.value = '';
+
+  level2Categories.value = item.children || [];
+  level3Categories.value = [];
+};
+
+// 选择二级分类
+const selectLevel2 = (item: categoryTreeVO) => {
+  categoryForm.mediumCategoryId = item.id;
+  categoryForm.bottomCategoryId = undefined;
+  selectedLevel2Name.value = item.label;
+  selectedLevel3Name.value = '';
+
+  level3Categories.value = item.children || [];
+};
+
+// 选择三级分类
+const selectLevel3 = async (item: categoryTreeVO) => {
+  categoryForm.bottomCategoryId = item.id;
+  selectedLevel3Name.value = item.label;
+
+  // 加载该分类下的属性列表
+  await loadCategoryAttributes(item.id);
+};
+
+// 获取分类路径
+const getCategoryPath = () => {
+  const parts = [];
+  if (selectedLevel1Name.value) parts.push(selectedLevel1Name.value);
+  if (selectedLevel2Name.value) parts.push(selectedLevel2Name.value);
+  if (selectedLevel3Name.value) parts.push(selectedLevel3Name.value);
+  return parts.join(' > ') || '请选择分类';
+};
+
+// 清除分类
+const clearCategory = () => {
+  categoryForm.topCategoryId = undefined;
+  categoryForm.mediumCategoryId = undefined;
+  categoryForm.bottomCategoryId = undefined;
+  selectedLevel1Name.value = '';
+  selectedLevel2Name.value = '';
+  selectedLevel3Name.value = '';
+  level2Categories.value = [];
+  level3Categories.value = [];
+  attributesList.value = [];
+  productAttributesValues.value = {};
+};
+
+// 下一步
+const nextStep = async () => {
+  if (currentStep.value === 0) {
+    // 验证分类选择
+    if (!categoryForm.topCategoryId) {
+      ElMessage.warning('请选择一级分类');
+      return;
+    }
+    if (!categoryForm.mediumCategoryId) {
+      ElMessage.warning('请选择二级分类');
+      return;
+    }
+    if (!categoryForm.bottomCategoryId) {
+      ElMessage.warning('请选择三级分类');
+      return;
+    }
+
+    // 将分类信息同步到商品表单
+    productForm.topCategoryId = categoryForm.topCategoryId;
+    productForm.mediumCategoryId = categoryForm.mediumCategoryId;
+    productForm.bottomCategoryId = categoryForm.bottomCategoryId;
+    
+    currentStep.value++;
+  } else if (currentStep.value === 1) {
+    // 验证商品信息表单并提交
+    try {
+      await productFormRef.value?.validate();
+      // 调用提交函数
+      await handleSubmit();
+    } catch (error) {
+      ElMessage.warning('请完善商品信息');
+      return;
+    }
+  }
+};
+
+// 上一步
+const prevStep = () => {
+  if (currentStep.value > 0) {
+    currentStep.value--;
+  }
+};
+
+// 提交
+const handleSubmit = async () => {
+  try {
+    submitLoading.value = true;
+
+    // 准备提交数据,包含定制信息
+    const submitData = {
+      ...productForm,
+      // 将服务保障ID数组转换为逗号分隔字符串
+      serviceGuarantee: serviceGuarantees.value.map(id => String(id)).join(','),
+      // 轮播图URL逗号分隔
+      imageUrl: carouselImages.value.join(','),
+      // 将商品属性值转换为JSON字符串
+      attributesList: JSON.stringify(productAttributesValues.value),
+      customizable: customForm.customizable,
+      customizedStyle: customForm.selectedMethods.join(','),
+      customizedCraft: customForm.selectedCrafts.join(','),
+      customDescription: customForm.customDescription,
+      customDetailsJson: JSON.stringify(customForm.customDetails)
+    };
+
+    if (productForm.id) {
+      await updateBase(submitData);
+      ElMessage.success('修改成功');
+    } else {
+      await addBase(submitData);
+      ElMessage.success('新增成功');
+    }
+    // 跳转到完成页面(步骤3)
+    currentStep.value = 2;
+  } catch (error) {
+    console.error('提交失败:', error);
+  } finally {
+    submitLoading.value = false;
+  }
+};
+
+// 返回
+const handleBack = () => {
+  router.back();
+};
+
+// 返回列表
+const handleBackToList = () => {
+  router.push('/product/base');
+};
+
+// 打开主图选择器
+const openMainImageSelector = () => {
+  mainImageSelectorVisible.value = true;
+};
+
+// 处理主图选择
+const handleMainImageSelected = (files: any[]) => {
+  if (files && files.length > 0) {
+    productForm.productImage = files[0].url;
+  }
+};
+
+// 清除主图
+const clearMainImage = () => {
+  productForm.productImage = undefined;
+};
+
+// 打开轮播图选择器
+const openCarouselImageSelector = () => {
+  carouselImageSelectorVisible.value = true;
+};
+
+// 处理轮播图选择(多选)
+const handleCarouselImagesSelected = (files: any[]) => {
+  if (files && files.length > 0) {
+    files.forEach(file => {
+      if (!carouselImages.value.includes(file.url)) {
+        carouselImages.value.push(file.url);
+      }
+    });
+  }
+};
+
+// 删除轮播图
+const removeCarouselImage = (index: number) => {
+  carouselImages.value.splice(index, 1);
+};
+
+// UPC(69)条码只允许输入数字
+const handleUpcInput = () => {
+  if (productForm.upcBarcode) {
+    productForm.upcBarcode = productForm.upcBarcode.replace(/\D/g, '');
+  }
+};
+
+// 格式化价格为两位小数
+const formatPrice = (field: string) => {
+  const val = (productForm as any)[field];
+  if (val !== undefined && val !== null && val !== '') {
+    const num = parseFloat(String(val));
+    if (!isNaN(num)) {
+      (productForm as any)[field] = parseFloat(num.toFixed(2));
+    }
+  }
+};
+
+// 获取分类树
+const getCategoryTree = async () => {
+  try {
+    const res = await categoryTree();
+    categoryOptions.value = res.data || [];
+  } catch (error) {
+    console.error('获取分类树失败:', error);
+  }
+};
+
+// 加载品牌选项(默认100条)
+const loadBrandOptions = async (keyword?: string) => {
+  brandLoading.value = true;
+  try {
+    const res = await listBrand({ pageNum: 1, pageSize: 100, brandName: keyword });
+    brandOptions.value = res.rows || [];
+  } catch (error) {
+    console.error('加载品牌列表失败:', error);
+  } finally {
+    brandLoading.value = false;
+  }
+};
+
+// 品牌远程搜索(防抖)
+const handleBrandSearch = (query: string) => {
+  if (brandSearchTimer) clearTimeout(brandSearchTimer);
+  brandSearchTimer = setTimeout(() => {
+    loadBrandOptions(query || undefined);
+  }, 300);
+};
+
+// 处理品牌下拉框显示/隐藏
+const handleBrandVisibleChange = (visible: boolean) => {
+  if (visible && brandOptions.value.length === 0) {
+    loadBrandOptions();
+  }
+};
+
+// 获取售后服务列表
+const getAfterSalesOptions = async () => {
+  try {
+    const res = await getAfterSaleList();
+    afterSalesOptions.value = res.data || [];
+    // 如果是新增模式且有选项,设置第一个为默认值
+    if (!route.params.id && afterSalesOptions.value.length > 0 && !productForm.afterSalesService) {
+      productForm.afterSalesService = afterSalesOptions.value[0].id;
+    }
+  } catch (error) {
+    console.error('获取售后服务列表失败:', error);
+  }
+};
+
+// 获取服务保障列表
+const getServiceGuaranteeOptions = async () => {
+  try {
+    const res = await getServiceList();
+    serviceGuaranteeOptions.value = res.data || [];
+    // 如果是新增模式且有选项,设置第一个为默认选中
+    if (!route.params.id && serviceGuaranteeOptions.value.length > 0 && serviceGuarantees.value.length === 0) {
+      serviceGuarantees.value = [serviceGuaranteeOptions.value[0].id];
+    }
+  } catch (error) {
+    console.error('获取服务保障列表失败:', error);
+  }
+};
+
+// 获取单位列表
+const getUnitOptions = async () => {
+  try {
+    const res = await getUnitList();
+    unitOptions.value = res.data || [];
+    // 如果是新增模式且有选项,设置第一个为默认值
+    if (!route.params.id && unitOptions.value.length > 0 && !productForm.unitId) {
+      productForm.unitId = unitOptions.value[0].id;
+    }
+  } catch (error) {
+    console.error('获取单位列表失败:', error);
+  }
+};
+
+// 获取主供应商列表
+const getSupplierOptions = async () => {
+  try {
+    const res = await listInfo();
+    console.log('供应商接口返回:', res);
+    // 处理可能的数据结构: res.data 或 res.rows
+    const dataList = res.data || res.rows || [];
+    supplierOptions.value = dataList;
+    console.log('供应商列表:', supplierOptions.value);
+    // 如果有选项且当前没有选中值,设置第一个为默认值
+    if (supplierOptions.value.length > 0 && !productForm.mainLibraryIntro) {
+      productForm.mainLibraryIntro = String(supplierOptions.value[0].id);
+    }
+  } catch (error) {
+    console.error('获取主供应商列表失败:', error);
+  }
+};
+
+// 获取采购人员列表
+const getStaffOptions = async () => {
+  try {
+    const res = await listComStaff();
+    console.log('采购人员接口返回:', res);
+    // 处理可能的数据结构: res.data 或 res.rows
+    const dataList = res.data || res.rows || [];
+    staffOptions.value = dataList;
+    console.log('采购人员列表:', staffOptions.value);
+    // 如果有选项且当前没有选中值,设置第一个为默认值
+    if (staffOptions.value.length > 0 && !productForm.purchasingPersonnel) {
+      productForm.purchasingPersonnel = String(staffOptions.value[0].staffId);
+    }
+  } catch (error) {
+    console.error('获取采购人员列表失败:', error);
+  }
+};
+
+// 获取税率列表
+const getTaxRateOptions = async () => {
+  try {
+    const res = await getTaxRateList();
+    taxRateOptions.value = res.rows || [];
+  } catch (error) {
+    console.error('获取税率列表失败:', error);
+  }
+};
+
+// 加载分类属性列表
+const loadCategoryAttributes = async (categoryId: string | number) => {
+  try {
+    const res = await categoryAttributeList(categoryId);
+    attributesList.value = res.data || [];
+    // 清空之前的属性值
+    productAttributesValues.value = {};
+
+    // 如果是新增模式,为有选项的属性设置默认值
+    if (!route.params.id) {
+      attributesList.value.forEach(attr => {
+        if (attr.entryMethod === '1' && attr.attributesList) { // 下拉选择
+          const options = parseAttributesList(attr.attributesList);
+          if (options.length > 0) {
+            productAttributesValues.value[attr.id] = options[0];
+          }
+        } else if (attr.entryMethod === '3' && attr.attributesList) { // 多选
+          const options = parseAttributesList(attr.attributesList);
+          if (options.length > 0) {
+            productAttributesValues.value[attr.id] = [options[0]];
+          }
+        }
+      });
+    }
+  } catch (error) {
+    console.error('加载分类属性失败:', error);
+    attributesList.value = [];
+  }
+};
+
+// 解析属性值列表(JSON数组或逗号分隔字符串)
+const parseAttributesList = (attributesListStr: string): string[] => {
+  if (!attributesListStr) return [];
+
+  try {
+    // 尝试解析为JSON数组
+    const parsed = JSON.parse(attributesListStr);
+    if (Array.isArray(parsed)) {
+      return parsed;
+    }
+  } catch (e) {
+    // 如果不是JSON,按逗号分隔
+    return attributesListStr.split(',').map(item => item.trim()).filter(item => item);
+  }
+
+  return [];
+};
+
+// 加载商品详情(编辑模式)
+const loadProductDetail = async () => {
+  const id = route.params.id;
+  if (id) {
+    try {
+      loading.value = true;
+      const res = await getBase(id as string);
+      Object.assign(productForm, res.data);
+
+      // 回显轮播图
+      if (res.data.imageUrl) {
+        carouselImages.value = res.data.imageUrl.split(',').filter((url: string) => url.trim());
+      } else {
+        carouselImages.value = [];
+      }
+
+      // 回显分类选择
+      categoryForm.topCategoryId = res.data.topCategoryId;
+      categoryForm.mediumCategoryId = res.data.mediumCategoryId;
+      categoryForm.bottomCategoryId = res.data.bottomCategoryId;
+
+      // 回显服务保障复选框 - 将逗号分隔的ID字符串转换为数组
+      if (res.data.serviceGuarantee) {
+        serviceGuarantees.value = res.data.serviceGuarantee.split(',').map((id: string) => {
+          // 尝试转换为数字,如果失败则保持字符串
+          const numId = Number(id.trim());
+          return isNaN(numId) ? id.trim() : numId;
+        });
+      } else {
+        serviceGuarantees.value = [];
+      }
+
+      // 回显安装服务复选框
+      const services: string[] = [];
+      if (res.data.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);
+        if (level1) {
+          selectLevel1(level1);
+
+          if (categoryForm.mediumCategoryId) {
+            const level2 = level2Categories.value.find(item => item.id === categoryForm.mediumCategoryId);
+            if (level2) {
+              selectLevel2(level2);
+
+              if (categoryForm.bottomCategoryId) {
+                const level3 = level3Categories.value.find(item => item.id === categoryForm.bottomCategoryId);
+                if (level3) {
+                  await selectLevel3(level3);
+                }
+              }
+            }
+          }
+        }
+      }
+    } catch (error) {
+      console.error('加载商品详情失败:', error);
+      ElMessage.error('加载商品详情失败');
+    } finally {
+      loading.value = false;
+    }
+  }
+};
+
+onMounted(async () => {
+  await getCategoryTree();
+  await getUnitOptions();
+  await getAfterSalesOptions();
+  await getServiceGuaranteeOptions();
+  await getTaxRateOptions();
+  // 先加载商品详情(如果是编辑模式)
+  await loadProductDetail();
+  // 再加载下拉选项,这样如果详情中没有值,会自动设置第一个
+  await getSupplierOptions();
+  await getStaffOptions();
+  loadBrandOptions();
+});
+</script>
+
+<style scoped lang="scss">
+.product-wizard-page {
+  .category-selection {
+    margin-top: 12px;
+  }
+
+  .category-box {
+    border: 1px solid #e4e7ed;
+    border-radius: 4px;
+    overflow: hidden;
+
+    .category-header {
+      background-color: #f5f7fa;
+      padding: 10px 12px;
+      font-weight: 600;
+      border-bottom: 1px solid #e4e7ed;
+      text-align: center;
+      font-size: 14px;
+    }
+
+    .category-search {
+      padding: 10px;
+      border-bottom: 1px solid #e4e7ed;
+      background-color: #fff;
+    }
+
+    .category-list {
+      height: 280px;
+      overflow-y: auto;
+
+      .category-item {
+        padding: 10px 12px;
+        cursor: pointer;
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        border-bottom: 1px solid #f0f0f0;
+        transition: all 0.3s;
+
+        &:hover {
+          background-color: #f5f7fa;
+        }
+
+        &.active {
+          background-color: #ecf5ff;
+          color: #409eff;
+          font-weight: 600;
+        }
+
+        &:last-child {
+          border-bottom: none;
+        }
+      }
+    }
+  }
+
+  .confirm-info {
+    margin-top: 12px;
+    text-align: left;
+  }
+
+  .product-info-form {
+    .category-display {
+      display: flex;
+      align-items: center;
+
+      .category-text {
+        color: #606266;
+      }
+    }
+
+    .form-item-tip {
+      font-size: 12px;
+      color: #909399;
+      line-height: 1.5;
+      margin-top: 4px;
+    }
+
+    .currency-text {
+      color: #303133;
+      font-size: 14px;
+    }
+  }
+
+  .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;
+    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;
+    }
+
+    .upload-icon {
+      font-size: 28px;
+      color: #8c939d;
+      margin-bottom: 8px;
+    }
+
+    .upload-text {
+      color: #8c939d;
+      font-size: 14px;
+    }
+  }
+
+  .custom-options {
+    display: flex;
+    gap: 10px;
+    flex-wrap: wrap;
+  }
+
+  .carousel-images-container {
+    width: 100%;
+
+    .carousel-image-list {
+      display: flex;
+      flex-wrap: wrap;
+      gap: 10px;
+
+      .carousel-image-item {
+        position: relative;
+
+        .carousel-preview-image {
+          width: 120px;
+          height: 120px;
+          display: block;
+          object-fit: cover;
+          border-radius: 6px;
+          border: 1px solid #dcdfe6;
+        }
+
+        .carousel-image-actions {
+          margin-top: 6px;
+          display: flex;
+          justify-content: center;
+        }
+      }
+
+      .carousel-add-btn {
+        width: 120px;
+        height: 120px;
+      }
+    }
+  }
+
+  .custom-table {
+    width: 100%;
+    margin-top: 10px;
+  }
+
+  .completion-card {
+    min-height: 400px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+
+    .completion-content {
+      text-align: center;
+      padding: 40px 0;
+
+      .success-icon {
+        margin-bottom: 24px;
+      }
+
+      .completion-text {
+        font-size: 16px;
+        color: #606266;
+        margin-bottom: 32px;
+        line-height: 1.6;
+      }
+
+      .completion-action {
+        display: flex;
+        justify-content: center;
+      }
+    }
+  }
+}
+</style>

+ 639 - 0
src/views/product/base/index.vue

@@ -0,0 +1,639 @@
+<template>
+  <div class="p-2">
+    <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+      <div v-show="showSearch" class="mb-[10px]">
+        <el-card shadow="hover">
+          <el-form ref="queryFormRef" :model="queryParams" label-width="90px">
+            <el-row :gutter="20">
+              <el-col :span="6">
+                <el-form-item label="商品编号" prop="productNo">
+                  <el-input v-model="queryParams.productNo" placeholder="请输入商品编号" clearable @keyup.enter="handleQuery" />
+                </el-form-item>
+              </el-col>
+              <el-col :span="6">
+                <el-form-item label="商品名称" prop="itemName">
+                  <el-input v-model="queryParams.itemName" placeholder="请输入商品名称" clearable @keyup.enter="handleQuery" />
+                </el-form-item>
+              </el-col>
+              <el-col :span="6">
+                <el-form-item label="商品品牌" prop="brandName">
+                  <el-select
+                    v-model="queryParams.brandName"
+                    placeholder="请输入品牌名称搜索"
+                    filterable
+                    remote
+                    clearable
+                    :remote-method="handleBrandSearch"
+                    :loading="brandLoading"
+                    value-key="brandName"
+                    style="width: 100%"
+                    @keyup.enter="handleQuery"
+                  >
+                    <el-option
+                      v-for="item in brandOptions"
+                      :key="item.id"
+                      :label="item.brandName"
+                      :value="item.brandName"
+                    />
+                  </el-select>
+                </el-form-item>
+              </el-col>
+              <el-col :span="6">
+                <el-form-item label="商品来源" prop="purchaseNature">
+                  <el-input v-model="queryParams.itemName" placeholder="请输入商品来源" clearable @keyup.enter="handleQuery" />
+                </el-form-item>
+              </el-col>
+            </el-row>
+            <el-row :gutter="20">
+              <el-col :span="6">
+                <el-form-item label="商品分类" prop="bottomCategoryId">
+                  <el-tree-select
+                    v-model="queryParams.bottomCategoryId"
+                    :data="categoryOptions"
+                    :props="{ value: 'id', label: 'label', children: 'children' } as any"
+                    value-key="id"
+                    placeholder="请选择商品分类"
+                    clearable
+                    check-strictly
+                  />
+                </el-form-item>
+              </el-col>
+              <el-col :span="6">
+                <el-form-item label="是否自营" prop="isSelf">
+                  <el-select v-model="queryParams.isSelf" placeholder="请选择" clearable>
+                    <el-option label="是" value="1" />
+                    <el-option label="否" value="0" />
+                  </el-select>
+                </el-form-item>
+              </el-col>
+              <el-col :span="6">
+                <el-form-item label="审核状态" prop="productReviewStatus">
+                  <el-select v-model="queryParams.productReviewStatus" placeholder="请选择" clearable>
+                    <el-option label="待采购审核" value="0" />
+                    <el-option label="审核通过" value="1" />
+                    <el-option label="驳回" value="2" />
+                    <el-option label="待营销审核" value="3" />
+                  </el-select>
+                </el-form-item>
+              </el-col>
+              <el-col :span="6">
+                <el-form-item label="上下架状态" prop="productStatus">
+                  <el-select v-model="queryParams.productStatus" placeholder="请选择" clearable>
+                    <el-option label="已上架" :value="1" />
+                    <el-option label="下架" :value="0" />
+                    <el-option label="上架中" :value="2" />
+                  </el-select>
+                </el-form-item>
+              </el-col>
+            </el-row>
+            <el-row>
+              <el-col :span="24" class="text-left">
+                <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
+                <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+              </el-col>
+            </el-row>
+          </el-form>
+        </el-card>
+      </div>
+    </transition>
+
+    <!-- 统计信息区域 -->
+    <el-card shadow="never" class="mb-[10px]">
+      <div class="flex items-center text-sm text-gray-600">
+        <span>商品总数: </span>
+        <span class="text-blue-600 mx-1"
+          >总=<span class="text-red-600">{{ statistics.total || 0 }}</span
+          >条</span
+        >
+        <span class="mx-2">【上架/总数({{ statistics.onSale || 0 }}/{{ statistics.total || 0 }})】</span>
+        <span class="mx-2">审核状态: 待审核<span class="text-red-600">{{ statistics.waitAudit || 0 }}</span>条,通过<span class="text-green-600">{{ statistics.auditPass || 0 }}</span>条,驳回<span class="text-orange-600">{{ statistics.auditReject || 0 }}</span>条</span>
+        <span class="mx-2">上下架状态: 已上架<span class="text-green-600">{{ statistics.onSale || 0 }}</span>条,下架<span class="text-gray-600">{{ statistics.offSale || 0 }}</span>条</span>
+        <div class="ml-auto flex gap-2">
+          <el-button 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>
+        </div>
+      </div>
+    </el-card>
+
+    <el-card shadow="never">
+      <el-table v-loading="loading" border :data="baseList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="商品编号" align="center" prop="productNo" width="120" fixed="left">
+          <template #default="scope">
+            <el-link type="primary" @click="handleView(scope.row)">{{ scope.row.productNo }}</el-link>
+          </template>
+        </el-table-column>
+        <el-table-column label="商品图片" align="center" prop="productImage" width="100">
+          <template #default="scope">
+            <image-preview :src="scope.row.productImage" :width="60" :height="60" />
+          </template>
+        </el-table-column>
+        <el-table-column label="商品信息" align="center" min-width="250">
+          <template #default="scope">
+            <div class="text-left">
+              <div style="white-space: normal; word-break: break-all; line-height: 1.4">{{ scope.row.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" prop="categoryName" width="120" />
+        <el-table-column label="单位" align="center" prop="unitName" width="60" />
+        <el-table-column label="SKU价格" align="center" width="120">
+          <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" prop="dataSource" width="80">
+          <template #default="scope">
+            <span>{{ scope.row.dataSource || '-' }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="是否自营" align="center" width="80">
+          <template #default="scope">
+            <el-tag v-if="scope.row.isSelf === 1" type="success">是</el-tag>
+            <el-tag v-else-if="scope.row.isSelf === 0" type="info">否</el-tag>
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="审核状态" align="center" prop="productReviewStatus" width="90">
+          <template #default="scope">
+            <span v-if="scope.row.productReviewStatus === 0">待采购审核</span>
+            <span v-else-if="scope.row.productReviewStatus === 1">审核通过</span>
+            <span v-else-if="scope.row.productReviewStatus === 2">驳回</span>
+            <span v-else-if="scope.row.productReviewStatus === 3">待营销审核</span>
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="上下架状态" align="center" prop="productStatus" width="100">
+          <template #default="scope">
+            <el-tag v-if="scope.row.productStatus === 1" type="success">已上架</el-tag>
+            <el-tag v-else-if="scope.row.productStatus === 0" type="warning">下架</el-tag>
+            <el-tag v-else-if="scope.row.productStatus === 2" type="info">上架中</el-tag>
+            <el-tag v-else type="info">未知</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" align="center" width="150" fixed="right">
+          <template #default="scope">
+            <!-- 待审核状态:只显示编辑 -->
+            <div v-if="scope.row.productReviewStatus !== 1" class="flex gap-1 justify-center">
+              <el-link type="primary" :underline="false" @click="handleUpdate(scope.row)">编辑</el-link>
+            </div>
+
+            <!-- 审核通过 -->
+            <div v-else-if="scope.row.productReviewStatus === 1" 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="warning" :underline="false" @click="handleShelf(scope.row)">下架</el-link>
+                <el-link type="danger" :underline="false" @click="handleDiscontinue(scope.row)">停售</el-link>
+              </div>
+              <div v-else-if="scope.row.productStatus === 1" class="flex gap-1 justify-center">
+                <el-link type="primary" :underline="false" @click="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>
+
+      <!-- 游标分页控制 -->
+      <pagination
+        v-show="baseList.length > 0"
+        v-model:page="queryParams.pageNum"
+        v-model:limit="queryParams.pageSize"
+        v-model:way="queryParams.way"
+        :cursor-mode="true"
+        :has-more="hasMore"
+        @pagination="getList"
+      />
+    </el-card>
+  </div>
+</template>
+
+<script setup name="Base" lang="ts">
+import { listBase, getBase, delBase, brandList, categoryTree, shelfReview, changeProductType, getProductStatusCount } from '@/api/product/base';
+import { BaseVO, BaseQuery, BaseForm, StatusCountVo } from '@/api/product/base/types';
+import { BrandVO } from '@/api/product/brand/types';
+import { listBrand } from '@/api/product/brand';
+import { categoryTreeVO } from '@/api/product/category/types';
+import { useRoute, useRouter } from 'vue-router';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const router = useRouter();
+const route = useRoute();
+
+const baseList = ref<BaseVO[]>([]);
+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 categoryOptions = ref<categoryTreeVO[]>([]);
+const brandOptions = ref<BrandVO[]>([]);
+const brandLoading = ref(false);
+let brandSearchTimer: ReturnType<typeof setTimeout> | null = null;
+const hasMore = ref(true); // 是否还有更多数据
+// 页面历史记录,存储每页的第一个id和最后一个id,用于支持双向翻页
+const pageHistory = ref([]);
+
+// 统计信息
+const statistics = ref<StatusCountVo>({
+  total: 0,
+  onSale: 0,
+  offSale: 0,
+  waitAudit: 0,
+  auditPass: 0,
+  auditReject: 0
+});
+
+const queryFormRef = ref<ElFormInstance>();
+const baseFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: BaseForm = {
+  id: undefined,
+  productNo: undefined,
+  itemName: undefined,
+  brandId: undefined,
+  topCategoryId: undefined,
+  mediumCategoryId: undefined,
+  bottomCategoryId: undefined,
+  unitId: undefined,
+  productImage: undefined,
+  isSelf: undefined,
+  productReviewStatus: undefined,
+  homeRecommended: undefined,
+  categoryRecommendation: undefined,
+  cartRecommendation: undefined,
+  recommendedProductOrder: undefined,
+  isPopular: undefined,
+  isNew: undefined,
+  productStatus: undefined,
+  remark: undefined
+};
+const data = reactive<PageData<BaseForm, BaseQuery>>({
+  form: { ...initFormData },
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    productNo: undefined,
+    itemName: undefined,
+    brandName: undefined,
+    productTag: undefined,
+    purchaseNature: undefined,
+    supplierType: undefined,
+    supplierNature: undefined,
+    projectOrg: undefined,
+    topCategoryId: undefined,
+    mediumCategoryId: undefined,
+    bottomCategoryId: undefined,
+    isSelf: undefined,
+    productReviewStatus: undefined,
+    productStatus: undefined,
+    lastSeenId: undefined, // 游标分页的lastSeenId
+    way: undefined,
+    params: {}
+  },
+  rules: {
+    productNo: [{ required: true, message: '产品编号不能为空', trigger: 'blur' }],
+    itemName: [{ required: true, message: '项目名称不能为空', trigger: 'blur' }],
+    brandId: [{ required: true, message: '品牌id不能为空', trigger: 'blur' }],
+    topCategoryId: [{ required: true, message: '顶级分类id不能为空', trigger: 'blur' }],
+    mediumCategoryId: [{ required: true, message: '中级分类id不能为空', trigger: 'blur' }],
+    bottomCategoryId: [{ required: true, message: '底层分类id不能为空', trigger: 'blur' }],
+    unitId: [{ required: true, message: '单位id不能为空', trigger: 'blur' }],
+    productImage: [{ required: true, message: '产品图片URL不能为空', trigger: 'blur' }],
+    productReviewStatus: [{ required: true, message: '产品审核状态 0=待采购审核,1=审核通过,2=驳回,3=待营销审核不能为空', trigger: 'change' }],
+    homeRecommended: [{ required: true, message: '首页推荐:1=推荐,0=不推荐不能为空', trigger: 'blur' }],
+    categoryRecommendation: [{ required: true, message: '分类推荐:1=推荐,0=不推荐不能为空', trigger: 'blur' }],
+    cartRecommendation: [{ required: true, message: '购物车推荐:1=推荐,0=不推荐不能为空', trigger: 'blur' }],
+    recommendedProductOrder: [{ required: true, message: '推荐产品顺序不能为空', trigger: 'blur' }],
+    isPopular: [{ required: true, message: '是否热门:1=是,0=否不能为空', trigger: 'blur' }],
+    isNew: [{ required: true, message: '是否新品:1=是,0=否不能为空', trigger: 'blur' }],
+    remark: [{ required: true, message: '备注不能为空', trigger: 'blur' }]
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+
+/** 查询产品基础信息列表 */
+const getList = async () => {
+  loading.value = true;
+  try {
+    const params = { ...queryParams.value };
+    const currentPageNum = queryParams.value.pageNum;
+
+    // 第一页不需要游标参数
+    if (currentPageNum === 1) {
+      delete params.lastSeenId;
+      delete params.way;
+    } else {
+      // way参数:0=上一页,1=下一页
+      if (queryParams.value.way === 0) {
+        // 上一页:使用目标页(即当前显示页)的firstId
+        const nextPageHistory = pageHistory.value[currentPageNum];
+        if (nextPageHistory) {
+          params.firstSeenId = nextPageHistory.firstId;
+          params.way = 0;
+        }
+      } else {
+        // 下一页:使用前一页的lastId作为lastSeenId
+        const prevPageHistory = pageHistory.value[currentPageNum - 1];
+        if (prevPageHistory) {
+          params.lastSeenId = prevPageHistory.lastId;
+          params.way = 1;
+        }
+      }
+    }
+
+    const res = await listBase(params);
+    baseList.value = res.rows || [];
+
+    // 判断是否还有更多数据
+    hasMore.value = baseList.value.length === queryParams.value.pageSize;
+
+    // 记录当前页的第一个id和最后一个id
+    if (baseList.value.length > 0) {
+      const firstItem = baseList.value[0];
+      const lastItem = baseList.value[baseList.value.length - 1];
+      //如果长度小于currentPageNum则创建
+
+      if (pageHistory.value.length <= currentPageNum) {
+        pageHistory.value[currentPageNum] = {
+          firstId: firstItem.id,
+          lastId: lastItem.id
+        };
+      }
+    }
+
+    total.value = res.total || 0;
+  } catch (error) {
+    console.error('获取列表失败:', error);
+  } finally {
+    loading.value = false;
+  }
+};
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+};
+
+/** 初始化路由参数 */
+const initRouteParams = () => {
+  // 从路由参数中获取筛选条件
+  if (route.query.productReviewStatus) {
+    queryParams.value.productReviewStatus = Number(route.query.productReviewStatus);
+  }
+  if (route.query.brandName) {
+    queryParams.value.brandName = route.query.brandName as string;
+  }
+  if (route.query.bottomCategoryId) {
+    queryParams.value.bottomCategoryId = route.query.bottomCategoryId as string;
+  }
+  if (route.query.isSelf) {
+    queryParams.value.isSelf = Number(route.query.isSelf);
+  }
+  if (route.query.productCategory) {
+    queryParams.value.productCategory = Number(route.query.productCategory);
+  }
+};
+
+/** 表单重置 */
+const reset = () => {
+  form.value = { ...initFormData };
+  baseFormRef.value?.resetFields();
+};
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  // 同步查询参数到游标分页参数
+  queryParams.value = {
+    ...queryParams.value,
+    pageNum: 1,
+    productNo: queryParams.value.productNo,
+    itemName: queryParams.value.itemName,
+    brandName: queryParams.value.brandName,
+    bottomCategoryId: queryParams.value.bottomCategoryId,
+    isSelf: queryParams.value.isSelf,
+    productReviewStatus: queryParams.value.productReviewStatus,
+    productStatus: queryParams.value.productStatus,
+    lastSeenId: undefined
+  };
+  pageHistory.value = []; // 重置页面历史
+  getList();
+};
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  queryParams.value.lastSeenId = undefined;
+  pageHistory.value = []; // 重置页面历史
+  handleQuery();
+};
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection: BaseVO[]) => {
+  ids.value = selection.map((item) => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+};
+
+/** 新增按钮操作 */
+const handleAdd = () => {
+  router.push('/product/base/add');
+};
+
+/** 修改按钮操作 */
+const handleUpdate = async (row?: BaseVO) => {
+  const _id = row?.id || ids.value[0];
+  router.push(`/product/base/edit/${_id}`);
+};
+
+/** 删除按钮操作 */
+const handleDelete = async (row?: BaseVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('是否确认删除产品基础信息编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
+  await delBase(_ids);
+  proxy?.$modal.msgSuccess('删除成功');
+  await getList();
+};
+
+/** 导出按钮操作 */
+const handleExport = () => {
+  proxy?.download(
+    'product/base/export',
+    {
+      ...queryParams.value
+    },
+    `base_${new Date().getTime()}.xlsx`
+  );
+};
+
+/** 查看商品详情 */
+const handleView = (row: BaseVO) => {
+  console.log('查看商品', row);
+  // TODO: 实现查看详情逻辑
+};
+
+/** 上下架操作 */
+const handleShelf = async (row: BaseVO) => {
+  // productStatus字段定义为string类型:1=已上架,0=下架,2=上架中
+  const isOnShelf = row.productStatus === '1';
+  const action = isOnShelf ? '下架' : '上架';
+  await proxy?.$modal.confirm(`确认${action}该商品吗?`);
+
+  try {
+    // 上架:状态改为2(上架中),下架:状态改为0(下架)
+    const productStatus = isOnShelf ? '0' : '2';
+    await shelfReview({
+      id: row.id,
+      productStatus: productStatus,
+      shelfComments: `${action}操作`
+    });
+    proxy?.$modal.msgSuccess(`${action}成功`);
+    await getList();
+  } catch (error) {
+    console.error(`${action}失败:`, error);
+    proxy?.$modal.msgError(`${action}失败`);
+  }
+};
+
+/** 价格设置 */
+const handlePrice = (row: BaseVO) => {
+  console.log('设置价格', row);
+  // TODO: 打开价格设置对话框
+};
+
+/** 供货存管理 */
+const handleSupply = (row: BaseVO) => {
+  console.log('供货存管理', row);
+  // TODO: 打开供货存管理对话框
+};
+
+/** 停售操作 */
+const handleDiscontinue = async (row: BaseVO) => {
+  await proxy?.$modal.confirm('确认停售该商品吗?停售后商品将无法正常售卖。');
+  
+  try {
+    // 调用停售API,将商品类型改为3(停售商品)
+    await changeProductType({
+      id: row.id,
+      productCategory: 3
+    });
+    proxy?.$modal.msgSuccess('停售成功');
+    await getList();
+  } catch (error) {
+    console.error('停售失败:', error);
+    proxy?.$modal.msgError('停售失败');
+  }
+};
+
+/** 跳转到商品审核页面 */
+const handleGoReview = () => {
+  router.push({
+    path: '/product/base/review',
+    query: {
+      productReviewStatus: 1 // 默认显示待审核的商品
+    }
+  });
+};
+
+/** 查询分类树 */
+const getCategoryTree = async () => {
+  const res = await categoryTree();
+  categoryOptions.value = res.data || [];
+};
+
+/** 加载品牌选项(默认100条) */
+const loadBrandOptions = async (keyword?: string) => {
+  brandLoading.value = true;
+  try {
+    const res = await listBrand({ pageNum: 1, pageSize: 100, brandName: keyword });
+    brandOptions.value =  res.rows || [];
+  } catch (error) {
+    console.error('加载品牌列表失败:', error);
+  } finally {
+    brandLoading.value = false;
+  }
+};
+
+/** 品牌远程搜索(防抖) */
+const handleBrandSearch = (query: string) => {
+  if (brandSearchTimer) clearTimeout(brandSearchTimer);
+  brandSearchTimer = setTimeout(() => {
+    loadBrandOptions(query || undefined);
+  }, 300);
+};
+
+/** 获取统计信息 */
+const getStatistics = async () => {
+  try {
+    const res = await getProductStatusCount();
+    if (res.data) {
+      statistics.value = res.data;
+    }
+  } catch (error) {
+    console.error('获取统计信息失败:', error);
+  }
+};
+
+onMounted(() => {
+  getList();
+  getCategoryTree();
+  getStatistics();
+  loadBrandOptions();
+});
+</script>

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

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

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

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

+ 1 - 1
src/views/product/category/index.vue

@@ -365,7 +365,7 @@ const handleSetReviewer = (row: CategoryVO) => {
 /** 审核员选择回调 */
 const handleReviewerSelected = async (users: UserVO[]) => {
   if (!currentReviewerCategory.value || users.length === 0) return;
-
+  
   const user = users[0];
   try {
     await setCategoryReviewer(currentReviewerCategory.value.id, user.userId);

+ 2 - 2
src/views/product/classification/index.vue

@@ -92,8 +92,8 @@
 </template>
 
 <script setup name="Classification" lang="ts">
-import { listClassification, getClassification, delClassification, addClassification, updateClassification } from '@/api/classification/index';
-import { ClassificationVO, ClassificationQuery, ClassificationForm } from '@/api/classification/types';
+import { listClassification, getClassification, delClassification, addClassification, updateClassification } from '@/api/product/classification';
+import { ClassificationVO, ClassificationQuery, ClassificationForm } from '@/api/product/classification/types';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 

+ 307 - 0
src/views/product/customization/index.vue

@@ -0,0 +1,307 @@
+<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="customizationNo">
+              <el-input v-model="queryParams.customizationNo" placeholder="请输入定制编号" 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="定制方式  支持多选,分隔  (0=包装定制,1=商品定制,2=开模定制)" prop="customizedStyle">
+              <el-input v-model="queryParams.customizedStyle" placeholder="请输入定制方式  支持多选,分隔  (0=包装定制,1=商品定制,2=开模定制)" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="定制工艺  支持多选,分隔  (0=丝印,1=热转印,2=激光,烤花,压印)" prop="customizedCraft">
+              <el-input v-model="queryParams.customizedCraft" placeholder="请输入定制工艺  支持多选,分隔  (0=丝印,1=热转印,2=激光,烤花,压印)" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="打样周期" prop="proofingPeriod">
+              <el-input v-model="queryParams.proofingPeriod" placeholder="请输入打样周期" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="生产周期" prop="productionCycle">
+              <el-input v-model="queryParams.productionCycle" placeholder="请输入生产周期" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="最小起订量" prop="moq">
+              <el-input v-model="queryParams.moq" placeholder="请输入最小起订量" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="MOQ对应价格" prop="moqPrice">
+              <el-input v-model="queryParams.moqPrice" placeholder="请输入MOQ对应价格" 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>
+        </el-card>
+      </div>
+    </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:customization:add']">新增</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['product:customization:edit']">修改</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['product:customization:remove']">删除</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['product:customization: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="customizationList" @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="customizationNo" />
+        <el-table-column label="产品id" align="center" prop="productId" />
+        <el-table-column label="定制方式  支持多选,分隔  (0=包装定制,1=商品定制,2=开模定制)" align="center" prop="customizedStyle" />
+        <el-table-column label="定制工艺  支持多选,分隔  (0=丝印,1=热转印,2=激光,烤花,压印)" align="center" prop="customizedCraft" />
+        <el-table-column label="打样周期" align="center" prop="proofingPeriod" />
+        <el-table-column label="生产周期" align="center" prop="productionCycle" />
+        <el-table-column label="最小起订量" align="center" prop="moq" />
+        <el-table-column label="MOQ对应价格" align="center" prop="moqPrice" />
+        <el-table-column label="备注" align="center" prop="remark" />
+        <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="修改" placement="top">
+              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['product:customization:edit']"></el-button>
+            </el-tooltip>
+            <el-tooltip content="删除" placement="top">
+              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['product:customization:remove']"></el-button>
+            </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-form ref="customizationFormRef" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="定制编号" prop="customizationNo">
+          <el-input v-model="form.customizationNo" placeholder="请输入定制编号" />
+        </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="定制方式  支持多选,分隔  (0=包装定制,1=商品定制,2=开模定制)" prop="customizedStyle">
+          <el-input v-model="form.customizedStyle" placeholder="请输入定制方式  支持多选,分隔  (0=包装定制,1=商品定制,2=开模定制)" />
+        </el-form-item>
+        <el-form-item label="定制工艺  支持多选,分隔  (0=丝印,1=热转印,2=激光,烤花,压印)" prop="customizedCraft">
+          <el-input v-model="form.customizedCraft" placeholder="请输入定制工艺  支持多选,分隔  (0=丝印,1=热转印,2=激光,烤花,压印)" />
+        </el-form-item>
+        <el-form-item label="打样周期" prop="proofingPeriod">
+          <el-input v-model="form.proofingPeriod" placeholder="请输入打样周期" />
+        </el-form-item>
+        <el-form-item label="生产周期" prop="productionCycle">
+          <el-input v-model="form.productionCycle" placeholder="请输入生产周期" />
+        </el-form-item>
+        <el-form-item label="最小起订量" prop="moq">
+          <el-input v-model="form.moq" placeholder="请输入最小起订量" />
+        </el-form-item>
+        <el-form-item label="MOQ对应价格" prop="moqPrice">
+          <el-input v-model="form.moqPrice" placeholder="请输入MOQ对应价格" />
+        </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>
+  </div>
+</template>
+
+<script setup name="Customization" lang="ts">
+import { listCustomization, getCustomization, delCustomization, addCustomization, updateCustomization } from '@/api/product/customization';
+import { CustomizationVO, CustomizationQuery, CustomizationForm } from '@/api/product/customization/types';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const customizationList = ref<CustomizationVO[]>([]);
+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 customizationFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: CustomizationForm = {
+  id: undefined,
+  customizationNo: undefined,
+  productId: undefined,
+  customizedStyle: undefined,
+  customizedCraft: undefined,
+  proofingPeriod: undefined,
+  productionCycle: undefined,
+  moq: undefined,
+  moqPrice: undefined,
+  remark: undefined,
+}
+const data = reactive<PageData<CustomizationForm, CustomizationQuery>>({
+  form: {...initFormData},
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    customizationNo: undefined,
+    productId: undefined,
+    customizedStyle: undefined,
+    customizedCraft: undefined,
+    proofingPeriod: undefined,
+    productionCycle: undefined,
+    moq: undefined,
+    moqPrice: undefined,
+    platformCode: undefined,
+    params: {
+    }
+  },
+  rules: {
+    customizationNo: [
+      { required: true, message: "定制编号不能为空", trigger: "blur" }
+    ],
+    productId: [
+      { required: true, message: "产品id不能为空", trigger: "blur" }
+    ],
+    customizedStyle: [
+      { required: true, message: "定制方式  支持多选,分隔  (0=包装定制,1=商品定制,2=开模定制)不能为空", trigger: "blur" }
+    ],
+    customizedCraft: [
+      { required: true, message: "定制工艺  支持多选,分隔  (0=丝印,1=热转印,2=激光,烤花,压印)不能为空", trigger: "blur" }
+    ],
+    proofingPeriod: [
+      { required: true, message: "打样周期不能为空", trigger: "blur" }
+    ],
+    productionCycle: [
+      { required: true, message: "生产周期不能为空", trigger: "blur" }
+    ],
+    moq: [
+      { required: true, message: "最小起订量不能为空", trigger: "blur" }
+    ],
+    moqPrice: [
+      { required: true, message: "MOQ对应价格不能为空", trigger: "blur" }
+    ],
+    remark: [
+      { required: true, message: "备注不能为空", trigger: "blur" }
+    ],
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 查询产品定制信息列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listCustomization(queryParams.value);
+  customizationList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+}
+
+/** 表单重置 */
+const reset = () => {
+  form.value = {...initFormData};
+  customizationFormRef.value?.resetFields();
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+}
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection: CustomizationVO[]) => {
+  ids.value = selection.map(item => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+}
+
+/** 新增按钮操作 */
+const handleAdd = () => {
+  reset();
+  dialog.visible = true;
+  dialog.title = "添加产品定制信息";
+}
+
+/** 修改按钮操作 */
+const handleUpdate = async (row?: CustomizationVO) => {
+  reset();
+  const _id = row?.id || ids.value[0]
+  const res = await getCustomization(_id);
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+  dialog.title = "修改产品定制信息";
+}
+
+/** 提交按钮 */
+const submitForm = () => {
+  customizationFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.id) {
+        await updateCustomization(form.value).finally(() =>  buttonLoading.value = false);
+      } else {
+        await addCustomization(form.value).finally(() =>  buttonLoading.value = false);
+      }
+      proxy?.$modal.msgSuccess("操作成功");
+      dialog.visible = false;
+      await getList();
+    }
+  });
+}
+
+/** 删除按钮操作 */
+const handleDelete = async (row?: CustomizationVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('是否确认删除产品定制信息编号为"' + _ids + '"的数据项?').finally(() => loading.value = false);
+  await delCustomization(_ids);
+  proxy?.$modal.msgSuccess("删除成功");
+  await getList();
+}
+
+/** 导出按钮操作 */
+const handleExport = () => {
+  proxy?.download('product/customization/export', {
+    ...queryParams.value
+  }, `customization_${new Date().getTime()}.xlsx`)
+}
+
+onMounted(() => {
+  getList();
+});
+</script>

+ 8 - 8
src/views/product/pool/index.vue

@@ -21,7 +21,7 @@
 
 
       <el-table v-loading="loading" border :data="poolList">
-
+        
         <el-table-column label="分类商品池名称" align="center" prop="name" />
         <el-table-column label="产品池类型" align="center" prop="type">
           <template #default="scope">
@@ -62,18 +62,18 @@
           </el-select>
         </el-form-item>
         <el-form-item label="状态:" prop="isShow">
-          <el-switch
-            v-model="form.isShow"
-            active-value="1"
+          <el-switch 
+            v-model="form.isShow" 
+            active-value="1" 
             inactive-value="0"
             active-text="启用"
           />
         </el-form-item>
         <el-form-item label="描述:" prop="remark">
-          <el-input
-            v-model="form.remark"
-            type="textarea"
-            placeholder="请输入内容"
+          <el-input 
+            v-model="form.remark" 
+            type="textarea" 
+            placeholder="请输入内容" 
             maxlength="200"
             show-word-limit
             :rows="4"

+ 295 - 0
src/views/product/products/index.vue

@@ -0,0 +1,295 @@
+<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="protocolNo">
+              <el-input v-model="queryParams.protocolNo" placeholder="请输入协议编号" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="产品编号" prop="productNo">
+              <el-input v-model="queryParams.productNo" placeholder="请输入产品编号" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="协议价格" prop="agreementPrice">
+              <el-input v-model="queryParams.agreementPrice" 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="客户ID" prop="customerId">
+              <el-input v-model="queryParams.customerId" placeholder="请输入客户ID" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="税率ID或税务编码" prop="taxId">
+              <el-input v-model="queryParams.taxId" placeholder="请输入税率ID或税务编码" 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>
+        </el-card>
+      </div>
+    </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:products:add']">新增</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['product:products:edit']">修改</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['product:products:remove']">删除</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['product:products: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="productsList" @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="protocolNo" />
+        <el-table-column label="产品编号" align="center" prop="productNo" />
+        <el-table-column label="协议价格" align="center" prop="agreementPrice" />
+        <el-table-column label="数据来源" align="center" prop="dataSource" />
+        <el-table-column label="客户ID" align="center" prop="customerId" />
+        <el-table-column label="备注信息" align="center" prop="remark" />
+        <el-table-column label="税率ID或税务编码" align="center" prop="taxId" />
+        <el-table-column label="类型" align="center" prop="type" />
+        <el-table-column label="状态" align="center" prop="status" />
+        <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="修改" placement="top">
+              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['product:products:edit']"></el-button>
+            </el-tooltip>
+            <el-tooltip content="删除" placement="top">
+              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['product:products:remove']"></el-button>
+            </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-form ref="productsFormRef" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="协议编号" prop="protocolNo">
+          <el-input v-model="form.protocolNo" placeholder="请输入协议编号" />
+        </el-form-item>
+        <el-form-item label="产品编号" prop="productNo">
+          <el-input v-model="form.productNo" placeholder="请输入产品编号" />
+        </el-form-item>
+        <el-form-item label="协议价格" prop="agreementPrice">
+          <el-input v-model="form.agreementPrice" placeholder="请输入协议价格" />
+        </el-form-item>
+        <el-form-item label="数据来源" prop="dataSource">
+          <el-input v-model="form.dataSource" placeholder="请输入数据来源" />
+        </el-form-item>
+        <el-form-item label="客户ID" prop="customerId">
+          <el-input v-model="form.customerId" placeholder="请输入客户ID" />
+        </el-form-item>
+        <el-form-item label="备注信息" prop="remark">
+          <el-input v-model="form.remark" placeholder="请输入备注信息" />
+        </el-form-item>
+        <el-form-item label="税率ID或税务编码" prop="taxId">
+          <el-input v-model="form.taxId" placeholder="请输入税率ID或税务编码" />
+        </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>
+  </div>
+</template>
+
+<script setup name="Products" lang="ts">
+import { listProducts, getProducts, delProducts, addProducts, updateProducts } from '@/api/product/products';
+import { ProductsVO, ProductsQuery, ProductsForm } from '@/api/product/products/types';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const productsList = ref<ProductsVO[]>([]);
+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 productsFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: ProductsForm = {
+  id: undefined,
+  protocolNo: undefined,
+  productNo: undefined,
+  agreementPrice: undefined,
+  dataSource: undefined,
+  customerId: undefined,
+  remark: undefined,
+  taxId: undefined,
+  type: undefined,
+  status: undefined,
+}
+const data = reactive<PageData<ProductsForm, ProductsQuery>>({
+  form: {...initFormData},
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    protocolNo: undefined,
+    productNo: undefined,
+    agreementPrice: undefined,
+    dataSource: undefined,
+    customerId: undefined,
+    taxId: undefined,
+    type: undefined,
+    status: undefined,
+    platformCode: undefined,
+    params: {
+    }
+  },
+  rules: {
+    protocolNo: [
+      { required: true, message: "协议编号不能为空", trigger: "blur" }
+    ],
+    productNo: [
+      { required: true, message: "产品编号不能为空", trigger: "blur" }
+    ],
+    agreementPrice: [
+      { required: true, message: "协议价格不能为空", trigger: "blur" }
+    ],
+    dataSource: [
+      { required: true, message: "数据来源不能为空", trigger: "blur" }
+    ],
+    customerId: [
+      { required: true, message: "客户ID不能为空", trigger: "blur" }
+    ],
+    remark: [
+      { required: true, message: "备注信息不能为空", trigger: "blur" }
+    ],
+    taxId: [
+      { required: true, message: "税率ID或税务编码不能为空", trigger: "blur" }
+    ],
+    type: [
+      { required: true, message: "类型不能为空", trigger: "change" }
+    ],
+    status: [
+      { required: true, message: "状态不能为空", trigger: "change" }
+    ],
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 查询协议产品关联列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listProducts(queryParams.value);
+  productsList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+}
+
+/** 表单重置 */
+const reset = () => {
+  form.value = {...initFormData};
+  productsFormRef.value?.resetFields();
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+}
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection: ProductsVO[]) => {
+  ids.value = selection.map(item => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+}
+
+/** 新增按钮操作 */
+const handleAdd = () => {
+  reset();
+  dialog.visible = true;
+  dialog.title = "添加协议产品关联";
+}
+
+/** 修改按钮操作 */
+const handleUpdate = async (row?: ProductsVO) => {
+  reset();
+  const _id = row?.id || ids.value[0]
+  const res = await getProducts(_id);
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+  dialog.title = "修改协议产品关联";
+}
+
+/** 提交按钮 */
+const submitForm = () => {
+  productsFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.id) {
+        await updateProducts(form.value).finally(() =>  buttonLoading.value = false);
+      } else {
+        await addProducts(form.value).finally(() =>  buttonLoading.value = false);
+      }
+      proxy?.$modal.msgSuccess("操作成功");
+      dialog.visible = false;
+      await getList();
+    }
+  });
+}
+
+/** 删除按钮操作 */
+const handleDelete = async (row?: ProductsVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('是否确认删除协议产品关联编号为"' + _ids + '"的数据项?').finally(() => loading.value = false);
+  await delProducts(_ids);
+  proxy?.$modal.msgSuccess("删除成功");
+  await getList();
+}
+
+/** 导出按钮操作 */
+const handleExport = () => {
+  proxy?.download('product/products/export', {
+    ...queryParams.value
+  }, `products_${new Date().getTime()}.xlsx`)
+}
+
+onMounted(() => {
+  getList();
+});
+</script>

+ 491 - 0
src/views/product/protocolInfo/index.vue

@@ -0,0 +1,491 @@
+<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="customerNo">
+              <el-input v-model="queryParams.customerNo" placeholder="请输入客户编号" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="客户名称" prop="customerName">
+              <el-input v-model="queryParams.customerName" placeholder="请输入客户名称" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="状态" prop="protocolStatus">
+              <el-select v-model="queryParams.protocolStatus" placeholder="请选择" clearable>
+                <el-option label="生效" value="1" />
+                <el-option label="待审核" value="0" />
+                <el-option label="失效" value="2" />
+              </el-select>
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
+              <el-button type="primary" icon="Refresh" @click="resetQuery">重置</el-button>
+            </el-form-item>
+          </el-form>
+        </el-card>
+      </div>
+    </transition>
+
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8" justify="space-between">
+          <el-col :span="1.5">
+            <span class="table-title">协议商品信息列表</span>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="primary" icon="Download" @click="handleExport">导出</el-button>
+            <el-button type="primary" icon="Plus" @click="handleAdd">协议新增</el-button>
+            <!-- <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> -->
+          </el-col>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" border :data="infoList" @selection-change="handleSelectionChange">
+        <el-table-column label="创建时间" align="center" prop="createTime" width="120">
+          <template #default="scope">
+            <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="客户编号" align="center" prop="customerNo" width="100" />
+        <el-table-column label="客户名称" align="center" prop="customerName" min-width="180" />
+        <el-table-column label="业务员" align="center" prop="salesmanName" width="100" />
+        <el-table-column label="客服" align="center" prop="serviceName" width="100" />
+        <el-table-column label="截止时间" align="center" prop="endTime" width="120">
+          <template #default="scope">
+            <span>{{ parseTime(scope.row.endTime, '{y}-{m}-{d}') }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="商品数量" align="center" prop="productNum" width="100" />
+        <el-table-column label="状态" align="center" prop="approvalStatus" width="80">
+          <template #default="scope">
+            <span>{{ getStatusLabel(scope.row.protocolStatus) }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" align="center" fixed="right" width="300" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-button link type="primary" @click="handleDetail(scope.row)" >基本信息</el-button>
+            <el-button link type="primary" @click="handleProductManage(scope.row)" >商品管理</el-button>
+            <el-button link type="primary" @click="handleInvalid(scope.row)" v-if="scope.row.protocolStatus === 1" >失效</el-button>
+            <el-button link type="primary" @click="handleAudit(scope.row)" v-if="scope.row.protocolStatus === 0" >审核</el-button>
+          </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="800px" append-to-body>
+      <el-form ref="infoFormRef" :model="form" :rules="rules" label-width="100px">
+        <el-form-item label="客户名称" prop="customerId">
+          <el-select 
+            v-model="form.customerId" 
+            placeholder="请选择" 
+            clearable 
+            filterable
+            :disabled="dialog.isView"
+            @change="handleCustomerChange"
+            style="width: 100%"
+          >
+            <el-option 
+              v-for="item in customerList" 
+              :key="item.id" 
+              :label="item.customerName" 
+              :value="item.id"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="所属公司" prop="companyId">
+          <el-input v-model="form.customerName" disabled />
+        </el-form-item>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="业务员" prop="salesmanId">
+              <el-input v-model="form.salesmanName" disabled />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="客服" prop="serviceId">
+              <el-input v-model="form.serviceName" disabled />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="起始时间" prop="startTime">
+              <el-date-picker 
+                v-model="form.startTime"
+                type="date"
+                placeholder="请选择"
+                value-format="YYYY-MM-DD"
+                :disabled="dialog.isView"
+                style="width: 100%"
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="截止时间" prop="endTime">
+              <el-date-picker 
+                v-model="form.endTime"
+                type="date"
+                placeholder="请选择"
+                value-format="YYYY-MM-DD"
+                :disabled="dialog.isView"
+                style="width: 100%"
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-form-item label="附件" prop="protocolFile">
+          <file-upload v-model="form.protocolFile" :disabled="dialog.isView"/>
+        </el-form-item>
+        <el-form-item label="备注" prop="remark">
+          <el-input v-model="form.remark" type="textarea" :rows="4" placeholder="请输入内容" :disabled="dialog.isView" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button v-if="!dialog.isView" :loading="buttonLoading" type="primary" @click="submitForm">确认</el-button>
+          <el-button @click="cancel">{{ dialog.isView ? '关闭' : '取消' }}</el-button>
+        </div>
+      </template>
+    </el-dialog>
+
+    <!-- 审核对话框 -->
+    <el-dialog title="审核" v-model="auditDialog.visible" width="500px" append-to-body>
+      <el-form ref="auditFormRef" :model="auditForm" :rules="auditRules" label-width="80px">
+        <el-form-item label="审核:" prop="protocolStatus">
+          <el-select v-model="auditForm.protocolStatus" placeholder="请选择" style="width: 100%">
+            <el-option label="生效" :value="1" />
+            <el-option label="失效" :value="2" />
+          </el-select>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="auditButtonLoading" type="primary" @click="submitAudit">确认</el-button>
+          <el-button @click="cancelAudit">取消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="Info" lang="ts">
+import { listInfo, getInfo, delInfo, addInfo, updateInfo } from '@/api/product/protocolInfo';
+import { InfoVO, InfoQuery, InfoForm } from '@/api/product/protocolInfo/types';
+import { listCustomerInfo } from '@/api/customer/customerInfo';
+import { CustomerInfoVO } from '@/api/customer/customerInfo/types';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const router = useRouter();
+
+const infoList = ref<InfoVO[]>([]);
+const customerList = ref<CustomerInfoVO[]>([]);
+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 infoFormRef = ref<ElFormInstance>();
+const auditFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption & { isView?: boolean }>({
+  visible: false,
+  title: '',
+  isView: false
+});
+
+// 审核对话框
+const auditDialog = reactive({
+  visible: false
+});
+
+// 审核按钮loading
+const auditButtonLoading = ref(false);
+
+// 审核表单
+const auditForm = ref<any>({
+  id: undefined,
+  protocolStatus: undefined
+});
+
+// 审核表单验证规则
+const auditRules = ref({
+  protocolStatus: [{ required: true, message: '请选择审核结果', trigger: 'change' }]
+});
+
+const initFormData: InfoForm = {
+  id: undefined,
+  protocolNo: undefined,
+  customerNo: undefined,
+  customerId: undefined,
+  customerName: undefined,
+  reviewTime: undefined,
+  approvalStatus: undefined,
+  startTime: undefined,
+  endTime: undefined,
+  productNum: undefined,
+  remark: undefined,
+  protocolStatus: undefined,
+  salesmanId: undefined,
+  salesmanName: undefined,
+  companyId: undefined,
+  companyName: undefined,
+  serviceId: undefined,
+  serviceName: undefined,
+  dataSource: undefined,
+  protocolFile: undefined,
+  fileName: undefined,
+  status: undefined,
+}
+const data = reactive<PageData<InfoForm, InfoQuery>>({
+  form: {...initFormData},
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    protocolNo: undefined,
+    customerNo: undefined,
+    customerId: undefined,
+    customerName: undefined,
+    reviewTime: undefined,
+    approvalStatus: undefined,
+    startTime: undefined,
+    endTime: undefined,
+    productNum: undefined,
+    protocolStatus: undefined,
+    salesmanId: undefined,
+    companyId: undefined,
+    serviceId: undefined,
+    dataSource: undefined,
+    protocolFile: undefined,
+    fileName: undefined,
+    status: undefined,
+    platformCode: undefined,
+    params: {
+    }
+  },
+  rules: {
+    customerId: [
+      { required: true, message: "客户名称不能为空", trigger: "change" }
+    ],
+    startTime: [
+      { required: true, message: "起始时间不能为空", trigger: "change" }
+    ],
+    endTime: [
+      { required: true, message: "截止时间不能为空", trigger: "change" }
+    ]
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 查询协议信息列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listInfo(queryParams.value);
+  infoList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+
+/** 加载客户列表 */
+const loadCustomerList = async () => {
+  const res = await listCustomerInfo({ pageNum: 1, pageSize: 1000 });
+  customerList.value = res.rows;
+}
+
+/** 客户选择改变 */
+const handleCustomerChange = (customerId: string | number) => {
+  const customer = customerList.value.find(item => item.id === customerId);
+  if (customer) {
+    form.value.customerNo = customer.customerNo;
+    form.value.customerName = customer.customerName;
+    // 公司名称显示客户名称
+    form.value.companyId = customer.belongCompanyId;
+    form.value.companyName = customer.customerName;
+    
+    // 从客户信息中获取业务员和客服信息
+    if (customer.customerSalesInfoVo) {
+      form.value.salesmanId = customer.customerSalesInfoVo.salesPersonId;
+      form.value.salesmanName = customer.customerSalesInfoVo.salesPerson || '';
+      form.value.serviceId = customer.customerSalesInfoVo.serviceStaffId;
+      form.value.serviceName = customer.customerSalesInfoVo.serviceStaff || '';
+    } else {
+      form.value.salesmanId = undefined;
+      form.value.salesmanName = '';
+      form.value.serviceId = undefined;
+      form.value.serviceName = '';
+    }
+  }
+}
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+}
+
+/** 表单重置 */
+const reset = () => {
+  form.value = {...initFormData};
+  infoFormRef.value?.resetFields();
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+}
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection: InfoVO[]) => {
+  ids.value = selection.map(item => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+}
+
+/** 新增按钮操作 */
+const handleAdd = () => {
+  reset();
+  dialog.visible = true;
+  dialog.title = "添加协议信息";
+  dialog.isView = false;
+}
+
+/** 修改按钮操作 */
+const handleUpdate = async (row?: InfoVO) => {
+  reset();
+  const _id = row?.id || ids.value[0]
+  const res = await getInfo(_id);
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+  dialog.title = "修改协议信息";
+  dialog.isView = false;
+}
+
+/** 提交按钮 */
+const submitForm = () => {
+  infoFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.id) {
+        await updateInfo(form.value).finally(() =>  buttonLoading.value = false);
+      } else {
+        await addInfo(form.value).finally(() =>  buttonLoading.value = false);
+      }
+      proxy?.$modal.msgSuccess("操作成功");
+      dialog.visible = false;
+      await getList();
+    }
+  });
+}
+
+/** 删除按钮操作 */
+const handleDelete = async (row?: InfoVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('是否确认删除协议信息编号为"' + _ids + '"的数据项?').finally(() => loading.value = false);
+  await delInfo(_ids);
+  proxy?.$modal.msgSuccess("删除成功");
+  await getList();
+}
+
+/** 导出按钮操作 */
+const handleExport = () => {
+  proxy?.download('product/info/export', {
+    ...queryParams.value
+  }, `info_${new Date().getTime()}.xlsx`)
+}
+
+/** 获取状态标签 */
+const getStatusLabel = (status: number) => {
+  const statusMap: Record<number, string> = {
+    0: '待审核',
+    1: '生效',
+    2: '失效'
+  };
+  console.log('status', statusMap[status] || status);
+  return statusMap[status] || status;
+}
+
+/** 查看基本信息 */
+const handleDetail = async (row: InfoVO) => {
+  reset();
+  const res = await getInfo(row.id);
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+  dialog.title = "查看基本信息";
+  dialog.isView = true;
+}
+
+/** 商品管理 */
+const handleProductManage = (row: InfoVO) => {
+  router.push({
+    path: '/product/protocolInfo/productManage',
+    query: { protocolId: row.id }
+  });
+}
+
+/** 失效操作 */
+const handleInvalid = async (row: InfoVO) => {
+  await proxy?.$modal.confirm('是否确认将该协议设为失效?');
+  // TODO: 调用失效接口
+  console.log('失效操作', row);
+  await getList();
+}
+
+/** 审核操作 */
+const handleAudit = (row: InfoVO) => {
+  auditForm.value = {
+    id: row.id,
+    protocolStatus: undefined
+  };
+  auditDialog.visible = true;
+}
+
+/** 取消审核 */
+const cancelAudit = () => {
+  auditForm.value = {
+    id: undefined,
+    protocolStatus: undefined
+  };
+  auditFormRef.value?.resetFields();
+  auditDialog.visible = false;
+}
+
+/** 提交审核 */
+const submitAudit = () => {
+  auditFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      auditButtonLoading.value = true;
+      try {
+        // 构造更新数据
+        const updateData: InfoForm = {
+          id: auditForm.value.id,
+          protocolStatus: auditForm.value.protocolStatus
+        };
+        await updateInfo(updateData).finally(() => auditButtonLoading.value = false);
+        proxy?.$modal.msgSuccess("审核成功");
+        auditDialog.visible = false;
+        await getList();
+      } catch (error) {
+        auditButtonLoading.value = false;
+      }
+    }
+  });
+}
+
+onMounted(() => {
+  getList();
+  loadCustomerList();
+});
+</script>

+ 664 - 0
src/views/product/protocolInfo/productManage.vue

@@ -0,0 +1,664 @@
+<template>
+  <div class="p-2">
+    <!-- 头部信息 -->
+    <el-card shadow="hover" class="mb-[10px]">
+      <div class="flex items-center">
+        <el-button link type="primary" @click="handleBack">
+          <el-icon class="mr-1"><ArrowLeft /></el-icon>
+          返回
+        </el-button>
+        <el-divider direction="vertical" />
+        <span class="mr-4">客户编号: {{ protocolInfo.customerNo }}</span>
+        <span class="mr-4">客户名称: {{ protocolInfo.customerName }}</span>
+        <span>截止日期: {{ protocolInfo.endTime }}</span>
+      </div>
+    </el-card>
+
+    <!-- 搜索区域 -->
+    <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="productNo">
+              <el-input v-model="queryParams.productNo" placeholder="请输入产品编号" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="产品名称" prop="productName">
+              <el-input v-model="queryParams.productName" placeholder="请输入产品名称" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="商品品牌" prop="brandId">
+              <el-select v-model="queryParams.brandId" placeholder="请选择" clearable style="width: 200px">
+                <el-option v-for="item in brandList" :key="item.id" :label="item.brandName" :value="item.id" />
+              </el-select>
+            </el-form-item>
+            <el-form-item label="商品状态" prop="productStatus">
+              <el-select v-model="queryParams.productStatus" placeholder="请选择" clearable style="width: 200px">
+                <el-option label="上架" value="1" />
+                <el-option label="下架" value="0" />
+              </el-select>
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
+              <el-button type="primary" icon="Refresh" @click="resetQuery">重置</el-button>
+              <el-button type="primary" icon="Plus" @click="handleAdd">添加商品</el-button>
+              <el-button type="primary" icon="Upload" @click="handleImport">导入商品</el-button>
+              <el-button type="primary" icon="Download" @click="handleExport">导出商品</el-button>
+            </el-form-item>
+          </el-form>
+        </el-card>
+      </div>
+    </transition>
+
+    <!-- 列表区域 -->
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8" justify="space-between">
+          <el-col :span="1.5">
+            <span class="table-title">协议商品详细信息列表</span>
+          </el-col>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" border :data="productsList">
+        <el-table-column label="产品编号" align="center" prop="productNo" width="120" />
+        <el-table-column label="商品图片" align="center" width="100">
+          <template #default="scope">
+            <image-preview :src="scope.row.productImage" :width="60" :height="60"/>
+          </template>
+        </el-table-column>
+        <el-table-column label="商品名称" align="center" min-width="180">
+          <template #default="scope">
+            <div>
+              <div class="text-primary">{{ scope.row.itemName }}</div>
+              <div class="text-gray-400 text-sm">{{ scope.row.brandName }}</div>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="产品类别" align="center" prop="categoryName" width="100" />
+        <el-table-column label="单位" align="center" prop="unitName" width="80" />
+        <el-table-column label="Sku价格" align="center" width="140">
+          <template #default="scope">
+            <div class="text-left">
+              <div>市场价: ¥{{ scope.row.marketPrice || 0 }}</div>
+              <div class="text-red-500">平台价: ¥{{ scope.row.memberPrice || 0 }}</div>
+              <div>标准成本: ¥{{ scope.row.purchasingPrice || 0 }}</div>
+              <div>最低售价: ¥{{ scope.row.minSellingPrice || 0 }}</div>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="产品来源" align="center" prop="dataSource" width="100" />
+        <el-table-column label="状态" align="center" width="80">
+          <template #default="scope">
+            <span :class="scope.row.productStatus === '1' ? 'text-green-500' : 'text-red-500'">
+              {{ scope.row.productStatus === '1' ? '上架' : '下架' }}
+            </span>
+          </template>
+        </el-table-column>
+        <el-table-column label="协议供货价" align="center" width="140">
+          <template #default="scope">
+            <el-input-number
+              v-model="scope.row.agreementPrice"
+              :min="0"
+              :precision="2"
+              size="small"
+              controls-position="right"
+              @change="handlePriceChange(scope.row)"
+            />
+          </template>
+        </el-table-column>
+        <el-table-column label="供货价毛利率" align="center" width="120">
+          <template #default="scope">
+            {{ calculateMargin(scope.row) }}
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" align="center" fixed="right" width="80">
+          <template #default="scope">
+            <el-button link type="danger" @click="handleDelete(scope.row)">删除</el-button>
+          </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="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" :selectable="checkSelectable" />
+          <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.productImage " :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>{{ scope.row.categoryName || '-' }}</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 || scope.row.marketPrice || '0.00' }}</span>
+                </div>
+                <div>
+                  <span class="text-gray-500">平台价:</span>
+                  <span class="text-red-500">¥{{ scope.row.standardPrice || scope.row.memberPrice || '0.00' }}</span>
+                </div>
+                <div>
+                  <span class="text-gray-500">最低价:</span>
+                  <span>¥{{ scope.row.certificatePrice || scope.row.minSellingPrice || '0.00' }}</span>
+                </div>
+              </div>
+            </template>
+          </el-table-column>
+          <el-table-column label="状态" align="center" width="100">
+            <template #default="scope">
+              <el-tag v-if="isProductAdded(scope.row.id)" type="info">已添加</el-tag>
+              <el-tag v-else-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" width="100" fixed="right">
+            <template #default="scope">
+              <el-link
+                v-if="!isProductAdded(scope.row.id)"
+                type="primary"
+                :underline="false"
+                @click="handleAddSingleProduct(scope.row)"
+              >加入清单</el-link>
+              <span v-else class="text-gray-400">已添加</span>
+            </template>
+          </el-table-column>
+        </el-table>
+
+        <!-- 分页 -->
+        <pagination
+          v-show="addProductDialog.productList.length > 0"
+          v-model:page="addProductQuery.pageNum"
+          v-model:limit="addProductQuery.pageSize"
+          v-model:way="addProductQuery.way"
+          :cursor-mode="true"
+          :has-more="addProductHasMore"
+          @pagination="getProductListForAdd"
+        />
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="ProductManage" lang="ts">
+import { listProducts, delProducts, updateProducts, addProducts, getProtocolProductIds, updateProductsPrice } from '@/api/product/products';
+import { ProductsQuery, ProductsForm } from '@/api/product/products/types';
+import { getInfo } from '@/api/product/protocolInfo';
+import { listBrand } from '@/api/product/brand';
+import { BrandVO } from '@/api/product/brand/types';
+import { BaseVO, BaseQuery } from '@/api/product/base/types';
+import { listBase } from '@/api/product/base';
+import { ArrowLeft } from '@element-plus/icons-vue';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const router = useRouter();
+const route = useRoute();
+
+// 协议ID
+const protocolId = ref<string | number>('');
+// 协议编号
+const protocolNo = ref<string>('');
+// 协议信息
+const protocolInfo = ref<any>({
+  customerNo: '',
+  customerName: '',
+  endTime: ''
+});
+
+const productsList = ref<BaseVO[]>([]);
+const brandList = ref<BrandVO[]>([]);
+const loading = ref(true);
+const showSearch = ref(true);
+const total = ref(0);
+
+const queryFormRef = ref<ElFormInstance>();
+
+const queryParams = ref<ProductsQuery>({
+  pageNum: 1,
+  pageSize: 10,
+  protocolId: route.query.protocolId as string | number,
+  protocolNo: undefined,
+  productNo: undefined,
+  productName: undefined,
+  brandId: undefined,
+  productStatus: undefined
+});
+
+// 已关联的产品ID列表
+const addedProductIds = ref<Set<string | number>>(new Set());
+
+// 添加商品对话框
+const addProductDialog = reactive({
+  visible: false,
+  loading: false,
+  productList: [] as BaseVO[],
+  total: 0
+});
+
+// 游标分页相关变量
+const addProductHasMore = ref(true);
+const addProductPageHistory = ref<Array<{ firstId: string | number; lastId: string | number }>>([]);
+
+// 添加商品查询参数
+const addProductQuery = ref<BaseQuery>({
+  pageNum: 1,
+  pageSize: 10,
+  way: undefined,
+  productNo: undefined,
+  itemName: undefined,
+  lastSeenId: undefined
+});
+
+// 选中的商品
+const selectedProducts = ref<BaseVO[]>([]);
+const addProductTableRef = ref<any>();
+
+/** 查询协议商品列表 */
+const getList = async () => {
+  loading.value = true;
+  try {
+    const res = await listProducts(queryParams.value);
+    productsList.value = res.rows || [];
+    total.value = res.total || 0;
+  } finally {
+    loading.value = false;
+  }
+};
+
+/** 加载协议信息 */
+const loadProtocolInfo = async () => {
+  if (!protocolId.value) return;
+  const res = await getInfo(protocolId.value);
+  if (res.data) {
+    protocolInfo.value = {
+      customerNo: res.data.customerNo || '',
+      customerName: res.data.customerName || '',
+      endTime: res.data.endTime ? parseTime(res.data.endTime, '{y}/{m}/{d} {h}:{i}:{s}') : ''
+    };
+    // 设置协议编号用于查询
+    protocolNo.value = res.data.protocolNo || '';
+    queryParams.value.protocolNo = res.data.protocolNo;
+  }
+};
+
+/** 加载品牌列表 */
+const loadBrandList = async () => {
+  const res = await listBrand({ pageNum: 1, pageSize: 1000 });
+  brandList.value = res.rows || [];
+};
+
+/** 加载已关联的产品ID列表 */
+const loadAddedProductIds = async () => {
+  const protocolId = route.query.protocolId as string | number;
+  try {
+    const res = await getProtocolProductIds(protocolId);
+    addedProductIds.value = new Set(res.data || []);
+  } catch (error) {
+    console.error('获取已关联产品ID失败:', error);
+    addedProductIds.value = new Set();
+  }
+};
+
+/** 判断商品是否已添加 */
+const isProductAdded = (productId: string | number): boolean => {
+  return addedProductIds.value.has(String(productId)) || addedProductIds.value.has(Number(productId));
+};
+
+/** 检查行是否可选择 */
+const checkSelectable = (row: BaseVO): boolean => {
+  return !isProductAdded(row.id);
+};
+
+/** 格式化时间 */
+const parseTime = (time: any, pattern?: string) => {
+  if (!time) return '';
+  const date = new Date(time);
+  const fmt = pattern || '{y}-{m}-{d} {h}:{i}:{s}';
+  const o: Record<string, number> = {
+    y: date.getFullYear(),
+    m: date.getMonth() + 1,
+    d: date.getDate(),
+    h: date.getHours(),
+    i: date.getMinutes(),
+    s: date.getSeconds()
+  };
+  return fmt.replace(/{([ymdhis])+}/g, (match, key) => {
+    const val = o[key];
+    return val.toString().padStart(2, '0');
+  });
+};
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+};
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  // 保留协议编号
+  const pNo = queryParams.value.protocolNo;
+  queryParams.value = {
+    pageNum: 1,
+    pageSize: 10,
+    protocolNo: pNo,
+    productNo: undefined,
+    productName: undefined,
+    brandId: undefined,
+    productStatus: undefined
+  };
+  handleQuery();
+};
+
+/** 返回按钮 */
+const handleBack = () => {
+  router.back();
+};
+
+/** 添加商品按钮 */
+const handleAdd = async () => {
+  addProductDialog.visible = true;
+  // 重置查询条件
+  addProductQuery.value = {
+    pageNum: 1,
+    pageSize: 10,
+    way: undefined,
+    productNo: undefined,
+    itemName: undefined,
+    lastSeenId: undefined
+  };
+  // 重置游标分页状态
+  addProductPageHistory.value = [];
+  addProductHasMore.value = true;
+  selectedProducts.value = [];
+  // 先加载已关联的产品ID
+  await loadAddedProductIds();
+  // 再加载商品列表
+  getProductListForAdd();
+};
+
+/** 获取商品列表用于添加 */
+const getProductListForAdd = async () => {
+  addProductDialog.loading = true;
+  try {
+    const params = { ...addProductQuery.value };
+    const currentPageNum = addProductQuery.value.pageNum;
+
+    // 第一页不需要游标参数
+    if (currentPageNum === 1) {
+      delete params.lastSeenId;
+      delete params.firstSeenId;
+      delete params.way;
+    } else {
+      // way参数:0=上一页,1=下一页
+      if (addProductQuery.value.way === 0) {
+        // 上一页:使用目标页的firstId
+        const nextPageHistory = addProductPageHistory.value[currentPageNum];
+        if (nextPageHistory) {
+          params.firstSeenId = nextPageHistory.firstId;
+          params.way = 0;
+        }
+      } else {
+        // 下一页:使用前一页的lastId作为lastSeenId
+        const prevPageHistory = addProductPageHistory.value[currentPageNum - 1];
+        if (prevPageHistory) {
+          params.lastSeenId = prevPageHistory.lastId;
+          params.way = 1;
+        }
+      }
+    }
+
+    const res = await listBase(params);
+    // 兼容两种返回结构
+    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;
+    }
+
+    // 判断是否还有更多数据
+    addProductHasMore.value = addProductDialog.productList.length === addProductQuery.value.pageSize;
+
+    // 记录当前页的第一个id和最后一个id
+    if (addProductDialog.productList.length > 0) {
+      const firstItem = addProductDialog.productList[0];
+      const lastItem = addProductDialog.productList[addProductDialog.productList.length - 1];
+      // 如果长度小于currentPageNum则创建
+      if (addProductPageHistory.value.length <= currentPageNum) {
+        addProductPageHistory.value[currentPageNum] = {
+          firstId: firstItem.id,
+          lastId: lastItem.id
+        };
+      }
+    }
+  } catch (error) {
+    console.error('获取商品列表失败:', error);
+    addProductDialog.productList = [];
+    addProductDialog.total = 0;
+  } finally {
+    addProductDialog.loading = false;
+  }
+};
+
+/** 搜索商品 */
+const handleSearchProducts = () => {
+  addProductQuery.value.pageNum = 1;
+  addProductQuery.value.lastSeenId = undefined;
+  addProductPageHistory.value = []; // 重置页面历史
+  addProductHasMore.value = true;
+  getProductListForAdd();
+};
+
+/** 选择变化 */
+const handleSelectionChange = (selection: BaseVO[]) => {
+  selectedProducts.value = selection;
+};
+
+/** 批量加入清单 */
+const handleBatchAdd = async () => {
+  if (selectedProducts.value.length === 0) {
+    proxy?.$modal.msgWarning('请先选择要添加的商品');
+    return;
+  }
+
+  try {
+    // 逐个添加商品
+    for (const product of selectedProducts.value) {
+      const data: ProductsForm = {
+        protocolNo: protocolNo.value,
+        protocolId: route.query.protocolId as string | number,
+        productNo: product.productNo,
+        productId: product.id,
+        agreementPrice: product.standardPrice || product.midRangePrice || product.memberPrice || 0,
+        customerId: protocolInfo.value.customerId,
+        dataSource: product.dataSource
+      };
+      await addProducts(data);
+    }
+
+    proxy?.$modal.msgSuccess(`成功添加 ${selectedProducts.value.length} 个商品`);
+    addProductDialog.visible = false;
+    // 清空选中项
+    selectedProducts.value = [];
+    if (addProductTableRef.value) {
+      addProductTableRef.value.clearSelection();
+    }
+    // 刷新列表
+    await loadAddedProductIds();
+    await getList();
+  } catch (error) {
+    console.error('添加商品失败:', error);
+    proxy?.$modal.msgError('添加商品失败');
+  }
+};
+
+/** 添加单个商品 */
+const handleAddSingleProduct = async (row: BaseVO) => {
+  try {
+    const data: ProductsForm = {
+      protocolNo: protocolNo.value,
+      protocolId: route.query.protocolId as string | number,
+      productNo: row.productNo,
+      productId: row.id,
+      agreementPrice: row.standardPrice || row.midRangePrice || row.memberPrice || 0,
+      customerId: protocolInfo.value.customerId,
+      dataSource: row.dataSource
+    };
+    await addProducts(data);
+
+    proxy?.$modal.msgSuccess('添加成功');
+    // 更新已添加的产品ID列表
+    addedProductIds.value.add(row.id);
+    // 刷新主列表
+    await getList();
+  } catch (error) {
+    console.error('添加商品失败:', error);
+    proxy?.$modal.msgError('添加商品失败');
+  }
+};
+
+/** 导入商品按钮 */
+const handleImport = () => {
+  proxy?.$modal.msgWarning('导入商品功能待实现');
+};
+
+/** 导出商品按钮 */
+const handleExport = () => {
+  proxy?.download('product/products/export', {
+    ...queryParams.value
+  }, `products_${new Date().getTime()}.xlsx`);
+};
+
+/** 删除按钮操作 */
+const handleDelete = async (row: any) => {
+  await proxy?.$modal.confirm('是否确认删除该商品?');
+  await delProducts(row.id);
+  proxy?.$modal.msgSuccess('删除成功');
+  await getList();
+};
+
+/** 协议供货价变更 */
+const handlePriceChange = async (row: any) => {
+  try {
+    await updateProductsPrice({
+      agreementPrice: row.agreementPrice,
+      productId: row.id,
+      protocolId: route.query.protocolId as string | number
+    });
+    proxy?.$modal.msgSuccess('价格更新成功');
+  } catch (error) {
+    proxy?.$modal.msgError('价格更新失败');
+  }
+};
+
+/** 计算供货价毛利率 */
+const calculateMargin = (row: any) => {
+  if (!row.agreementPrice || !row.purchasingPrice || row.purchasingPrice === 0) {
+    return '-';
+  }
+  const margin = ((row.agreementPrice - row.purchasingPrice) / row.agreementPrice * 100).toFixed(2)+'%';
+  return margin;
+};
+
+onMounted(() => {
+  // 从路由参数获取协议ID
+  protocolId.value = route.query.protocolId as string || route.params.id as string || '';
+  if (protocolId.value) {
+    loadProtocolInfo();
+    loadBrandList();
+    getList();
+  } else {
+    proxy?.$modal.msgError('缺少协议ID参数');
+    router.back();
+  }
+});
+</script>
+
+<style scoped>
+.text-primary {
+  color: #409eff;
+}
+.text-gray-400 {
+  color: #909399;
+}
+.text-gray-500 {
+  color: #909399;
+}
+.text-red-500 {
+  color: #f56c6c;
+}
+.text-green-500 {
+  color: #67c23a;
+}
+.text-sm {
+  font-size: 12px;
+}
+.add-product-dialog {
+  :deep(.el-form--inline .el-form-item) {
+    margin-right: 10px;
+  }
+}
+</style>