Bladeren bron

feat(product): 更新商品添加页面功能

- 将主库简介改为主供应商,并集成动态下拉选项
- 在销售价格、采购价格和采购信息表单中添加验证规则
- 更新采购人员选择器为动态选项加载
- 添加供应商和员工信息API接口文件
- 添加协议产品管理和相关路由配置
- 实现供应商和采购人员动态数据加载功能
- 优化表单验证提示信息和初始值设置
肖路 3 maanden geleden
bovenliggende
commit
ad51e09e9e

+ 74 - 0
src/api/customer/businessInfo/index.ts

@@ -0,0 +1,74 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { BusinessInfoVO, BusinessInfoForm, BusinessInfoQuery } from '@/api/customer/businessInfo/types';
+
+/**
+ * 查询客户工商注册信息列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listBusinessInfo = (query?: BusinessInfoQuery): AxiosPromise<BusinessInfoVO[]> => {
+  return request({
+    url: '/customer/businessInfo/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询客户工商注册信息详细
+ * @param customerId
+ */
+export const getBusinessInfo = (customerId: string | number): AxiosPromise<BusinessInfoVO> => {
+  return request({
+    url: '/customer/businessInfo/' + customerId,
+    method: 'get'
+  });
+};
+
+/**
+ * 查询客户工商注册信息详细
+ * @param customerName
+ */
+export const getBusinessInfoBycustomerName = (customerName: string): AxiosPromise<BusinessInfoVO> => {
+  return request({
+    url: '/customer/businessInfo/selectBusinessByCustomerName/' + customerName,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增客户工商注册信息
+ * @param data
+ */
+export const addBusinessInfo = (data: BusinessInfoForm) => {
+  return request({
+    url: '/customer/businessInfo',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改客户工商注册信息
+ * @param data
+ */
+export const updateBusinessInfo = (data: BusinessInfoForm) => {
+  return request({
+    url: '/customer/businessInfo',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除客户工商注册信息
+ * @param customerId
+ */
+export const delBusinessInfo = (customerId: string | number | Array<string | number>) => {
+  return request({
+    url: '/customer/businessInfo/' + customerId,
+    method: 'delete'
+  });
+};

+ 230 - 0
src/api/customer/businessInfo/types.ts

@@ -0,0 +1,230 @@
+export interface BusinessInfoVO {
+  /**
+   * 关联客户ID
+   */
+  customerId: string | number;
+
+  /**
+   * 工商全称
+   */
+  businessCustomerName: string;
+
+  /**
+   * 统一社会信用代码
+   */
+  socialCreditCode: string;
+
+  /**
+   * 法人姓名
+   */
+  legalPersonName: string;
+
+  /**
+   * 注册资本
+   */
+  registeredCapital: string;
+
+  /**
+   * 实缴资本
+   */
+  paidInCapital: string | number;
+
+  /**
+   * 成立日期
+   */
+  establishmentDate: string;
+
+  /**
+   * 吊销日期
+   */
+  revocationDate: string;
+
+  /**
+   * 登记状态
+   */
+  registrationStatus: string;
+
+  /**
+   * 登记机关
+   */
+  registrationAuthority: string;
+
+  /**
+   * 经营范围
+   */
+  bussinessRange: string;
+
+  /**
+   * 营业执照路径
+   */
+  businessLicense: string;
+
+  /**
+   * 工商地址-详细地址
+   */
+  businessAddress: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status: string;
+
+  /**
+   * 备注
+   */
+  remark: string;
+}
+
+export interface BusinessInfoForm extends BaseEntity {
+  /**
+   * 关联客户ID
+   */
+  customerId?: string | number;
+
+  /**
+   * 工商全称
+   */
+  businessCustomerName?: string;
+
+  /**
+   * 统一社会信用代码
+   */
+  socialCreditCode?: string;
+
+  /**
+   * 法人姓名
+   */
+  legalPersonName?: string;
+
+  /**
+   * 注册资本
+   */
+  registeredCapital?: string;
+
+  /**
+   * 实缴资本
+   */
+  paidInCapital?: string | number;
+
+  /**
+   * 成立日期
+   */
+  establishmentDate?: string;
+
+  /**
+   * 吊销日期
+   */
+  revocationDate?: string;
+
+  /**
+   * 登记状态
+   */
+  registrationStatus?: string;
+
+  /**
+   * 登记机关
+   */
+  registrationAuthority?: string;
+
+  /**
+   * 经营范围
+   */
+  bussinessRange?: string;
+
+  /**
+   * 营业执照路径
+   */
+  businessLicense?: string;
+
+  /**
+   * 工商地址-详细地址
+   */
+  businessAddress?: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+}
+
+export interface BusinessInfoQuery extends PageQuery {
+  /**
+   * 工商全称
+   */
+  businessCustomerName?: string;
+
+  /**
+   * 统一社会信用代码
+   */
+  socialCreditCode?: string;
+
+  /**
+   * 法人姓名
+   */
+  legalPersonName?: string;
+
+  /**
+   * 注册资本
+   */
+  registeredCapital?: string;
+
+  /**
+   * 实缴资本
+   */
+  paidInCapital?: string | number;
+
+  /**
+   * 成立日期
+   */
+  establishmentDate?: string;
+
+  /**
+   * 吊销日期
+   */
+  revocationDate?: string;
+
+  /**
+   * 登记状态
+   */
+  registrationStatus?: string;
+
+  /**
+   * 登记机关
+   */
+  registrationAuthority?: string;
+
+  /**
+   * 经营范围
+   */
+  bussinessRange?: string;
+
+  /**
+   * 营业执照路径
+   */
+  businessLicense?: string;
+
+  /**
+   * 工商地址-详细地址
+   */
+  businessAddress?: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+  /**
+   * 日期范围参数
+   */
+  params?: any;
+}

+ 63 - 0
src/api/customer/customerContact/index.ts

@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { CustomerContactVO, CustomerContactForm, CustomerContactQuery } from '@/api/customer/customerFile/customerContact/types';
+
+/**
+ * 查询客户联系人信息列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listCustomerContact = (query?: CustomerContactQuery): AxiosPromise<CustomerContactVO[]> => {
+  return request({
+    url: '/customer/customerContact/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询客户联系人信息详细
+ * @param id
+ */
+export const getCustomerContact = (id: string | number): AxiosPromise<CustomerContactVO> => {
+  return request({
+    url: '/customer/customerContact/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增客户联系人信息
+ * @param data
+ */
+export const addCustomerContact = (data: CustomerContactForm) => {
+  return request({
+    url: '/customer/customerContact',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改客户联系人信息
+ * @param data
+ */
+export const updateCustomerContact = (data: CustomerContactForm) => {
+  return request({
+    url: '/customer/customerContact',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除客户联系人信息
+ * @param id
+ */
+export const delCustomerContact = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/customer/customerContact/' + id,
+    method: 'delete'
+  });
+};

+ 269 - 0
src/api/customer/customerContact/types.ts

@@ -0,0 +1,269 @@
+export interface CustomerContactVO {
+  /**
+   * 联系人ID
+   */
+  id: string | number;
+
+  /**
+   * 所属客户ID
+   */
+  customerId: string | number;
+
+  /**
+   * 联系人姓名
+   */
+  contactName: string;
+
+  customLoginName: string;
+
+  /**
+   * 手机号码
+   */
+  phone: string;
+
+  /**
+   * 办公电话
+   */
+  officePhone: string;
+
+  /**
+   * 办公电话2
+   */
+  officePhoneTwo: string;
+
+  /**
+   * 性别
+   */
+  gender: string;
+
+  /**
+   * 采购角色(如:采购经理、财务)
+   */
+  roleId: string | number;
+
+  roleName: string;
+
+  deptId: string | number;
+
+  deptName: string;
+
+  /**
+   * 是否主联系人:0=是,1=否
+   */
+  isPrimary: string;
+
+  birthday: Date;
+
+  /**
+   * 详细地址
+   */
+  addressDetail: string;
+
+  /**
+   * 所在省编码
+   */
+  addressProvince: string;
+
+  /**
+   * 所在市编码
+   */
+  addressCity: string;
+
+  /**
+   * 所在区县编码
+   */
+  addressCounty: string;
+
+  /**
+   * 省市区
+   */
+  provincialCityCounty: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status: string;
+
+  /**
+   * 备注
+   */
+  remark: string;
+}
+
+export interface CustomerContactForm extends BaseEntity {
+  /**
+   * 联系人ID
+   */
+  id?: string | number;
+
+  /**
+   * 所属客户ID
+   */
+  customerId?: string | number;
+
+  /**
+   * 联系人姓名
+   */
+  contactName?: string;
+
+  customLoginName?: string;
+
+  /**
+   * 手机号码
+   */
+  phone?: string;
+
+  /**
+   * 办公电话
+   */
+  officePhone?: string;
+
+  /**
+   * 办公电话2
+   */
+  officePhoneTwo?: string;
+
+  /**
+   * 性别
+   */
+  gender?: string;
+
+  /**
+   * 采购角色(如:采购经理、财务)
+   */
+  roleId?: string | number;
+
+  roleName?: string;
+
+  deptId?: string | number;
+
+  deptName?: string;
+
+  email: string;
+
+  /**
+   * 是否主联系人:0=是,1=否
+   */
+  isPrimary?: string;
+
+  birthday?: Date;
+
+  /**
+   * 详细地址
+   */
+  addressDetail?: string;
+
+  /**
+   * 所在省编码
+   */
+  addressProvince?: string;
+
+  /**
+   * 所在市编码
+   */
+  addressCity?: string;
+
+  /**
+   * 所在区县编码
+   */
+  addressCounty?: string;
+
+  /**
+   * 省市区
+   */
+  provincialCityCounty?: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+}
+
+export interface CustomerContactQuery extends PageQuery {
+  /**
+   * 所属客户ID
+   */
+  customerId?: string | number;
+
+  /**
+   * 联系人姓名
+   */
+  contactName?: string;
+
+  /**
+   * 手机号码
+   */
+  phone?: string;
+
+  email?: string;
+
+  /**
+   * 办公电话
+   */
+  officePhone?: string;
+
+  /**
+   * 办公电话2
+   */
+  officePhoneTwo?: string;
+
+  /**
+   * 性别
+   */
+  gender?: string;
+
+  /**
+   * 采购角色(如:采购经理、财务)
+   */
+  roleId?: string | number;
+
+  /**
+   * 是否主联系人:0=是,1=否
+   */
+  isPrimary?: string;
+
+  /**
+   * 详细地址
+   */
+  addressDetail?: string;
+
+  /**
+   * 所在省编码
+   */
+  addressProvince?: string;
+
+  /**
+   * 所在市编码
+   */
+  addressCity?: string;
+
+  /**
+   * 所在区县编码
+   */
+  addressCounty?: string;
+
+  /**
+   * 省市区
+   */
+  provincialCityCounty?: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+  /**
+   * 日期范围参数
+   */
+  params?: any;
+}

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

+ 63 - 0
src/api/customer/invoiceInfo/index.ts

@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { InvoiceInfoVO, InvoiceInfoForm, InvoiceInfoQuery } from '@/api/customer/customerFile/invoiceInfo/types';
+
+/**
+ * 查询客户开票信息列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listInvoiceInfo = (query?: InvoiceInfoQuery): AxiosPromise<InvoiceInfoVO[]> => {
+  return request({
+    url: '/customer/invoiceInfo/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询客户开票信息详细
+ * @param id
+ */
+export const getInvoiceInfo = (id: string | number): AxiosPromise<InvoiceInfoVO> => {
+  return request({
+    url: '/customer/invoiceInfo/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增客户开票信息
+ * @param data
+ */
+export const addInvoiceInfo = (data: InvoiceInfoForm) => {
+  return request({
+    url: '/customer/invoiceInfo',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改客户开票信息
+ * @param data
+ */
+export const updateInvoiceInfo = (data: InvoiceInfoForm) => {
+  return request({
+    url: '/customer/invoiceInfo',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除客户开票信息
+ * @param id
+ */
+export const delInvoiceInfo = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/customer/invoiceInfo/' + id,
+    method: 'delete'
+  });
+};

+ 185 - 0
src/api/customer/invoiceInfo/types.ts

@@ -0,0 +1,185 @@
+export interface InvoiceInfoVO {
+  /**
+   * 开票信息ID
+   */
+  id: string | number;
+
+  /**
+   * 所属客户ID
+   */
+  customerId: string | number;
+
+  /**
+   * 纳税人识别号
+   */
+  taxId: string | number;
+
+  /**
+   * 开户行行号
+   */
+  bankCode: string;
+
+  /**
+   * 开户行
+   */
+  bankId: string | number;
+
+  /**
+   * 开户行名称
+   */
+  bankName: string;
+
+  /**
+   * 银行账户
+   */
+  bankAccount: string;
+
+  /**
+   * 联系电话
+   */
+  phone: string;
+
+  /**
+   * 开户行地址
+   */
+  address: string;
+
+  /**
+   * 是否主账号:0=是,1=否
+   */
+  isPrimaryAccount: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status: string;
+
+  /**
+   * 备注
+   */
+  remark: string;
+}
+
+export interface InvoiceInfoForm extends BaseEntity {
+  /**
+   * 开票信息ID
+   */
+  id?: string | number;
+
+  /**
+   * 所属客户ID
+   */
+  customerId?: string | number;
+
+  /**
+   * 纳税人识别号
+   */
+  taxId?: string | number;
+
+  /**
+   * 开户行行号
+   */
+  bankCode?: string;
+
+  /**
+   * 开户行
+   */
+  bankId?: string | number;
+
+  /**
+   * 开户行名称
+   */
+  bankName?: string;
+
+  /**
+   * 银行账户
+   */
+  bankAccount?: string;
+
+  /**
+   * 联系电话
+   */
+  phone?: string;
+
+  /**
+   * 开户行地址
+   */
+  address?: string;
+
+  /**
+   * 是否主账号:0=是,1=否
+   */
+  isPrimaryAccount?: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+}
+
+export interface InvoiceInfoQuery extends PageQuery {
+  /**
+   * 所属客户ID
+   */
+  customerId?: string | number;
+
+  /**
+   * 纳税人识别号
+   */
+  taxId?: string | number;
+
+  /**
+   * 开户行行号
+   */
+  bankCode?: string;
+
+  /**
+   * 开户行
+   */
+  bankId?: string | number;
+
+  /**
+   * 开户行名称
+   */
+  bankName?: string;
+
+  /**
+   * 银行账户
+   */
+  bankAccount?: string;
+
+  /**
+   * 联系电话
+   */
+  phone?: string;
+
+  /**
+   * 开户行地址
+   */
+  address?: string;
+
+  /**
+   * 是否主账号:0=是,1=否
+   */
+  isPrimaryAccount?: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+  /**
+   * 日期范围参数
+   */
+  params?: any;
+}

+ 63 - 0
src/api/customer/salesInfo/index.ts

@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { SalesInfoVO, SalesInfoForm, SalesInfoQuery } from '@/api/customer/salesInfo/types';
+
+/**
+ * 查询客户销售信息列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listSalesInfo = (query?: SalesInfoQuery): AxiosPromise<SalesInfoVO[]> => {
+  return request({
+    url: '/customer/salesInfo/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询客户销售信息详细
+ * @param id
+ */
+export const getSalesInfo = (id: string | number): AxiosPromise<SalesInfoVO> => {
+  return request({
+    url: '/customer/salesInfo/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增客户销售信息
+ * @param data
+ */
+export const addSalesInfo = (data: SalesInfoForm) => {
+  return request({
+    url: '/customer/salesInfo',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改客户销售信息
+ * @param data
+ */
+export const updateSalesInfo = (data: SalesInfoForm) => {
+  return request({
+    url: '/customer/salesInfo',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除客户销售信息
+ * @param id
+ */
+export const delSalesInfo = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/customer/salesInfo/' + id,
+    method: 'delete'
+  });
+};

+ 290 - 0
src/api/customer/salesInfo/types.ts

@@ -0,0 +1,290 @@
+export interface SalesInfoVO {
+  /**
+   * 销售信息ID
+   */
+  id: string | number;
+
+  /**
+   * 关联客户ID
+   */
+  customerId: string | number;
+
+  /**
+   * 信用额度
+   */
+  creditAmount: number;
+
+  /**
+   * 剩余额度
+   */
+  remainingQuota: number;
+
+  /**
+   * 临时额度
+   */
+  temporaryQuota: number;
+
+  /**
+   * 账期(如:30天)
+   */
+  accountPeriod: string;
+
+  /**
+   * 账单日(每月几号)
+   */
+  billDate: number;
+
+  /**
+   * 计费日(如:每月1日)
+   */
+  billingDay: number;
+
+  /**
+   * 订单审核方式
+   */
+  orderAudit: string;
+
+  /**
+   * 信用管理方式
+   */
+  creditManagementId: number | string;
+
+  /**
+   * 信用支付密码
+   */
+  creditPaymentPassword: string;
+
+  /**
+   * 付款天数
+   */
+  payDays: number;
+
+  /**
+   * 销售人员
+   */
+  salesPersonId: string | number;
+
+  /**
+   * 销售人员名称
+   */
+  salesPerson: string;
+
+  /**
+   * 服务人员
+   */
+  serviceStaffId: string | number;
+
+  /**
+   * 服务人员名称
+   */
+  serviceStaff: string;
+
+  /**
+   * 所属部门
+   */
+  belongingDepartmentId: string | number;
+
+  /**
+   * 所属部门名称
+   */
+  belongingDepartment: string;
+
+  /**
+   * 应收账款
+   */
+  accountsReceivable: number;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status: string;
+
+  /**
+   * 备注
+   */
+  remark: string;
+}
+
+export interface SalesInfoForm extends BaseEntity {
+  /**
+   * 销售信息ID
+   */
+  id?: string | number;
+
+  /**
+   * 关联客户ID
+   */
+  customerId?: string | number;
+
+  /**
+   * 信用额度
+   */
+  creditAmount?: number;
+
+  /**
+   * 剩余额度
+   */
+  remainingQuota?: number;
+
+  /**
+   * 临时额度
+   */
+  temporaryQuota?: number;
+
+  /**
+   * 账期(如:30天)
+   */
+  accountPeriod?: string;
+
+  /**
+   * 账单日(每月几号)
+   */
+  billDate?: number;
+
+  /**
+   * 计费日(如:每月1日)
+   */
+  billingDay?: number;
+
+  /**
+   * 订单审核方式
+   */
+  orderAudit?: string;
+
+  /**
+   * 信用管理方式
+   */
+  creditManagementId?: number | string;
+
+  /**
+   * 信用支付密码
+   */
+  creditPaymentPassword?: string;
+
+  /**
+   * 付款天数
+   */
+  payDays?: number;
+
+  /**
+   * 销售人员
+   */
+  salesPersonId?: string | number;
+
+  /**
+   * 服务人员
+   */
+  serviceStaffId?: string | number;
+
+  /**
+   * 所属部门
+   */
+  belongingDepartmentId?: string | number;
+
+  /**
+   * 应收账款
+   */
+  accountsReceivable?: number;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+}
+
+export interface SalesInfoQuery extends PageQuery {
+  /**
+   * 关联客户ID
+   */
+  customerId?: string | number;
+
+  /**
+   * 信用额度
+   */
+  creditAmount?: number;
+
+  /**
+   * 剩余额度
+   */
+  remainingQuota?: number;
+
+  /**
+   * 临时额度
+   */
+  temporaryQuota?: number;
+
+  /**
+   * 账期(如:30天)
+   */
+  accountPeriod?: string;
+
+  /**
+   * 账单日(每月几号)
+   */
+  billDate?: number;
+
+  /**
+   * 计费日(如:每月1日)
+   */
+  billingDay?: number;
+
+  /**
+   * 订单审核方式
+   */
+  orderAudit?: string;
+
+  /**
+   * 信用管理方式
+   */
+  creditManagementId?: number | string;
+
+  /**
+   * 信用支付密码
+   */
+  creditPaymentPassword?: string;
+
+  /**
+   * 付款天数
+   */
+  payDays?: number;
+
+  /**
+   * 销售人员
+   */
+  salesPersonId?: string | number;
+
+  /**
+   * 服务人员
+   */
+  serviceStaffId?: string | number;
+
+  /**
+   * 所属部门
+   */
+  belongingDepartmentId?: string | number;
+
+  /**
+   * 应收账款
+   */
+  accountsReceivable?: number;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: 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;
+}
+
+
+

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

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

+ 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/info/list',
+    method: 'get',
+    params: query
+  });
+};
+
+
+
+/**
+ * 查询协议信息详细
+ * @param id
+ */
+export const getInfo = (id: string | number): AxiosPromise<InfoVO> => {
+  return request({
+    url: '/product/info/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增协议信息
+ * @param data
+ */
+export const addInfo = (data: InfoForm) => {
+  return request({
+    url: '/product/info',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改协议信息
+ * @param data
+ */
+export const updateInfo = (data: InfoForm) => {
+  return request({
+    url: '/product/info',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除协议信息
+ * @param id
+ */
+export const delInfo = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/product/info/' + 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;
+}

+ 6 - 0
src/router/index.ts

@@ -142,6 +142,12 @@ export const constantRoutes: RouteRecordRaw[] = [
         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 }
       }
     ]
   }

+ 81 - 25
src/views/product/base/add.vue

@@ -367,12 +367,15 @@
               />
             </el-form-item>
 
-            <!-- 主库简介 -->
+            <!-- 主供应商 -->
             <el-form-item label="主供应商:" prop="mainLibraryIntro" required>
-              <el-select v-model="productForm.mainLibraryIntro" placeholder="请选择" clearable class="w-full">
-                <el-option label="选项1" value="1" />
-                <el-option label="选项2" value="2" />
-                <el-option label="选项3" value="3" />
+              <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.enterpriseName"
+                  :value="String(option.id)"
+                />
               </el-select>
             </el-form-item>
 
@@ -415,7 +418,7 @@
             <span class="text-lg font-bold">销售价格</span>
           </template>
 
-          <el-form ref="priceFormRef" :model="productForm" label-width="120px" class="product-info-form">
+          <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>
@@ -471,7 +474,7 @@
             <span class="text-lg font-bold">采购价格</span>
           </template>
 
-          <el-form ref="purchasePriceFormRef" :model="productForm" label-width="120px" class="product-info-form">
+          <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>
@@ -501,7 +504,7 @@
             <span class="text-lg font-bold">采购信息</span>
           </template>
 
-          <el-form ref="purchaseInfoFormRef" :model="productForm" label-width="120px" class="product-info-form">
+          <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>
@@ -514,10 +517,13 @@
               </el-col>
               <el-col :span="12">
                 <el-form-item label="采购人员:" prop="purchasingPersonnel" required>
-                  <el-select v-model="productForm.purchasingPersonnel" placeholder="请选择" clearable class="w-full">
-                    <el-option label="采购员1" value="1" />
-                    <el-option label="采购员2" value="2" />
-                    <el-option label="采购员3" value="3" />
+                  <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.staffName"
+                      :value="String(option.staffId)"
+                    />
                   </el-select>
                 </el-form-item>
               </el-col>
@@ -779,6 +785,10 @@ 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, brandList, categoryTree, categoryAttributeList, getAfterSaleList, getServiceList, getUnitList } from '@/api/product/base';
+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();
@@ -954,7 +964,7 @@ const productForm = reactive<BaseForm>({
   weightUnit: 'kg',
   volume: undefined,
   volumeUnit: 'm3',
-  mainLibraryIntro: '1',
+  mainLibraryIntro: undefined,
   afterSalesService: undefined,
   serviceGuarantee: undefined, // 服务保障ID列表,逗号分隔
   freeInstallation: '0',
@@ -975,18 +985,18 @@ const productForm = reactive<BaseForm>({
 
 // 表单验证规则
 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: 'blur' }],
-  minOrderQuantity: [{ required: true, message: '请输入最低起订量', trigger: 'blur' }],
+  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: 'blur' }],
+  minOrderQuantity: [{ required: true, message: '最低起订量不能为空', trigger: 'blur' }],
 };
 
 // 分类和品牌选项
@@ -1011,6 +1021,12 @@ const serviceGuaranteeOptions = ref<any[]>([]);
 // 单位选项
 const unitOptions = ref<any[]>([]);
 
+// 主供应商选项
+const supplierOptions = ref<InfoVO[]>([]);
+
+// 采购人员选项
+const staffOptions = ref<ComStaffVO[]>([]);
+
 // 搜索关键词
 const searchLevel1 = ref('');
 const searchLevel2 = ref('');
@@ -1298,6 +1314,42 @@ const getUnitOptions = async () => {
   }
 };
 
+// 获取主供应商列表
+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 loadCategoryAttributes = async (categoryId: string | number) => {
   try {
@@ -1422,7 +1474,11 @@ onMounted(async () => {
   await getUnitOptions();
   await getAfterSalesOptions();
   await getServiceGuaranteeOptions();
+  // 先加载商品详情(如果是编辑模式)
   await loadProductDetail();
+  // 再加载下拉选项,这样如果详情中没有值,会自动设置第一个
+  await getSupplierOptions();
+  await getStaffOptions();
 });
 </script>
 

+ 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.productImageUrl" :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="tempGrossMargin" width="100" />
+        <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="120">
+          <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.productImageUrl" :width="60" :height="60"/>
+            </template>
+          </el-table-column>
+          <el-table-column label="商品信息" align="center" min-width="200">
+            <template #default="scope">
+              <div class="text-left" style="font-size: 12px;">
+                <div>{{ scope.row.itemName }}</div>
+                <div class="text-gray-500">品牌:{{ scope.row.brandName || '-' }}</div>
+              </div>
+            </template>
+          </el-table-column>
+          <el-table-column label="商品分类" align="center" width="150">
+            <template #default="scope">
+              <div class="text-left" style="font-size: 12px;">
+                <div>{{ 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 } 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 updateProducts({
+      id: row.id,
+      agreementPrice: row.agreementPrice
+    });
+    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>