Эх сурвалжийг харах

新增对账结算 快递100查询物流信息等

hurx 4 сар өмнө
parent
commit
360405e3d2
37 өөрчлөгдсөн 6062 нэмэгдсэн , 28 устгасан
  1. 63 0
      src/api/bill/invoiceInfo/index.ts
  2. 161 0
      src/api/bill/invoiceInfo/types.ts
  3. 63 0
      src/api/bill/statementDetail/index.ts
  4. 204 0
      src/api/bill/statementDetail/types.ts
  5. 75 0
      src/api/bill/statementInvoice/index.ts
  6. 179 0
      src/api/bill/statementInvoice/types.ts
  7. 63 0
      src/api/bill/statementInvoiceDetail/index.ts
  8. 266 0
      src/api/bill/statementInvoiceDetail/types.ts
  9. 63 0
      src/api/bill/statementInvoiceProduct/index.ts
  10. 251 0
      src/api/bill/statementInvoiceProduct/types.ts
  11. 110 0
      src/api/bill/statementOrder/index.ts
  12. 248 0
      src/api/bill/statementOrder/types.ts
  13. 63 0
      src/api/bill/statementProduct/index.ts
  14. 251 0
      src/api/bill/statementProduct/types.ts
  15. 11 0
      src/api/customer/customerFile/businessInfo/index.ts
  16. 11 0
      src/api/customer/customerFile/customerInfo/index.ts
  17. 14 0
      src/api/order/orderDeliver/index.ts
  18. 12 2
      src/api/order/orderDeliver/types.ts
  19. 21 0
      src/api/order/orderMain/index.ts
  20. 573 0
      src/views/bill/statementInvoice/addDrawer.vue
  21. 247 0
      src/views/bill/statementInvoice/addInvoiceDialog.vue
  22. 332 0
      src/views/bill/statementInvoice/detailDrawer.vue
  23. 322 0
      src/views/bill/statementInvoice/index.vue
  24. 277 0
      src/views/bill/statementInvoice/statementOrderDrawer.vue
  25. 791 0
      src/views/bill/statementOrder/addDrawer.vue
  26. 561 0
      src/views/bill/statementOrder/detailDrawer.vue
  27. 300 0
      src/views/bill/statementOrder/index.vue
  28. 235 0
      src/views/bill/statementOrder/orderMainDrawer.vue
  29. 22 4
      src/views/customer/customerFile/customerInfo/add.vue
  30. 25 7
      src/views/customer/customerFile/customerInfo/overview/baseInfo.vue
  31. 4 0
      src/views/customer/customerFile/customerInfo/overview/shippingAddress.vue
  32. 1 1
      src/views/order/expense/index.vue
  33. 1 1
      src/views/order/orderMain/index.vue
  34. 1 1
      src/views/order/revenueHeader/add.vue
  35. 19 8
      src/views/order/saleOrder/deliverDialog.vue
  36. 26 4
      src/views/order/saleOrder/index.vue
  37. 196 0
      src/views/order/saleOrder/logisticsDetail.vue

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

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

+ 161 - 0
src/api/bill/invoiceInfo/types.ts

@@ -0,0 +1,161 @@
+export interface InvoiceInfoVO {
+  /**
+   * 主键ID
+   */
+  id: string | number;
+
+  /**
+   * 对账单发票id
+   */
+  statementInvoiceId: string | number;
+
+  /**
+   * 对账单发票编号
+   */
+  statementInvoiceNo: string;
+
+  /**
+   * 发票类型
+   */
+  invoiceType: string;
+
+  /**
+   * 发票代码
+   */
+  invoiceCode: string;
+
+  /**
+   * 发票金额
+   */
+  invoiceAmount: number;
+
+  /**
+   * 发票附件路径或URL
+   */
+  invoiceAnnex: string;
+
+  /**
+   * 开票公司名称
+   */
+  companyName: string;
+
+  /**
+   * 发票开具日期
+   */
+  invoiceDate: string;
+
+  /**
+   * 备注
+   */
+  remark: string;
+
+}
+
+export interface InvoiceInfoForm extends BaseEntity {
+  /**
+   * 主键ID
+   */
+  id?: string | number;
+
+  /**
+   * 对账单发票id
+   */
+  statementInvoiceId?: string | number;
+
+  /**
+   * 对账单发票编号
+   */
+  statementInvoiceNo?: string;
+
+  /**
+   * 发票类型
+   */
+  invoiceType?: string;
+
+  /**
+   * 发票代码
+   */
+  invoiceCode?: string;
+
+  /**
+   * 发票金额
+   */
+  invoiceAmount?: number;
+
+  /**
+   * 发票附件路径或URL
+   */
+  invoiceAnnex?: string;
+
+  /**
+   * 开票公司名称
+   */
+  companyName?: string;
+
+  /**
+   * 发票开具日期
+   */
+  invoiceDate?: string;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+
+}
+
+export interface InvoiceInfoQuery extends PageQuery {
+
+  /**
+   * 对账单发票id
+   */
+  statementInvoiceId?: string | number;
+
+  /**
+   * 对账单发票编号
+   */
+  statementInvoiceNo?: string;
+
+  /**
+   * 发票类型
+   */
+  invoiceType?: string;
+
+  /**
+   * 发票代码
+   */
+  invoiceCode?: string;
+
+  /**
+   * 发票金额
+   */
+  invoiceAmount?: number;
+
+  /**
+   * 发票附件路径或URL
+   */
+  invoiceAnnex?: string;
+
+  /**
+   * 开票公司名称
+   */
+  companyName?: string;
+
+  /**
+   * 发票开具日期
+   */
+  invoiceDate?: string;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+    /**
+     * 日期范围参数
+     */
+    params?: any;
+}
+
+
+

+ 63 - 0
src/api/bill/statementDetail/index.ts

@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { StatementDetailVO, StatementDetailForm, StatementDetailQuery } from '@/api/bill/statementDetail/types';
+
+/**
+ * 查询对账单交易明细列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listStatementDetail = (query?: StatementDetailQuery): AxiosPromise<StatementDetailVO[]> => {
+  return request({
+    url: '/bill/statementDetail/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询对账单交易明细详细
+ * @param id
+ */
+export const getStatementDetail = (id: string | number): AxiosPromise<StatementDetailVO> => {
+  return request({
+    url: '/bill/statementDetail/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增对账单交易明细
+ * @param data
+ */
+export const addStatementDetail = (data: StatementDetailForm) => {
+  return request({
+    url: '/bill/statementDetail',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改对账单交易明细
+ * @param data
+ */
+export const updateStatementDetail = (data: StatementDetailForm) => {
+  return request({
+    url: '/bill/statementDetail',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除对账单交易明细
+ * @param id
+ */
+export const delStatementDetail = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/bill/statementDetail/' + id,
+    method: 'delete'
+  });
+};

+ 204 - 0
src/api/bill/statementDetail/types.ts

@@ -0,0 +1,204 @@
+export interface StatementDetailVO {
+  /**
+   * 主键ID
+   */
+  id: string | number;
+
+  /**
+   * 对账单id
+   */
+  statementOrderId: string | number;
+
+  /**
+   * 对账单编号
+   */
+  statementOrderNo: string;
+
+  /**
+   * 明细类型(如:1-销售订单, 2-退款等)
+   */
+  type: string;
+
+  /**
+   * 关联的订单id
+   */
+  orderId: string | number;
+
+  /**
+   * 关联的订单编号
+   */
+  orderNo: string;
+
+  /**
+   * 该明细金额
+   */
+  amount: number;
+
+  /**
+   * 订单时间
+   */
+  orderTime: string;
+
+  /**
+   * 签收日期
+   */
+  signingDate: string;
+
+  /**
+   * 操作用户id
+   */
+  userId: string | number;
+
+  /**
+   * 操作用户编号
+   */
+  userName: string;
+
+  userDeptId: string | number;
+
+  /**
+   * 用户所属部门
+   */
+  userDept: string;
+
+  /**
+   * 备注
+   */
+  remark: string;
+}
+
+export interface StatementDetailForm extends BaseEntity {
+  /**
+   * 主键ID
+   */
+  id?: string | number;
+
+  /**
+   * 对账单id
+   */
+  statementOrderId?: string | number;
+
+  /**
+   * 对账单编号
+   */
+  statementOrderNo?: string;
+
+  /**
+   * 明细类型(如:1-销售订单, 2-退款等)
+   */
+  type?: string;
+
+  /**
+   * 关联的订单id
+   */
+  orderId?: string | number;
+
+  /**
+   * 关联的订单编号
+   */
+  orderNo?: string;
+
+  /**
+   * 该明细金额
+   */
+  amount?: number;
+
+  /**
+   * 订单时间
+   */
+  orderTime?: string;
+
+  /**
+   * 签收日期
+   */
+  signingDate?: string;
+
+  /**
+   * 操作用户id
+   */
+  userId?: string | number;
+
+  /**
+   * 操作用户编号
+   */
+  userName?: string;
+
+  userDeptId?: string | number;
+
+  /**
+   * 用户所属部门
+   */
+  userDept?: string;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+}
+
+export interface StatementDetailQuery extends PageQuery {
+  /**
+   * 对账单id
+   */
+  statementOrderId?: string | number;
+
+  /**
+   * 对账单编号
+   */
+  statementOrderNo?: string;
+
+  /**
+   * 明细类型(如:1-销售订单, 2-退款等)
+   */
+  type?: string;
+
+  /**
+   * 关联的订单id
+   */
+  orderId?: string | number;
+
+  /**
+   * 关联的订单编号
+   */
+  orderNo?: string;
+
+  /**
+   * 该明细金额
+   */
+  amount?: number;
+
+  /**
+   * 订单时间
+   */
+  orderTime?: string;
+
+  /**
+   * 签收日期
+   */
+  signingDate?: string;
+
+  /**
+   * 操作用户id
+   */
+  userId?: string | number;
+
+  /**
+   * 操作用户编号
+   */
+  userName?: string;
+
+  /**
+   * 用户所属部门
+   */
+  userDept?: string;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+  /**
+   * 日期范围参数
+   */
+  params?: any;
+}

+ 75 - 0
src/api/bill/statementInvoice/index.ts

@@ -0,0 +1,75 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { StatementInvoiceVO, StatementInvoiceForm, StatementInvoiceQuery } from '@/api/bill/statementInvoice/types';
+
+/**
+ * 查询销售发票主列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listStatementInvoice = (query?: StatementInvoiceQuery): AxiosPromise<StatementInvoiceVO[]> => {
+  return request({
+    url: '/bill/statementInvoice/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询销售发票主详细
+ * @param id
+ */
+export const getStatementInvoice = (id: string | number): AxiosPromise<StatementInvoiceVO> => {
+  return request({
+    url: '/bill/statementInvoice/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增销售发票主
+ * @param data
+ */
+export const addStatementInvoice = (data: StatementInvoiceForm) => {
+  return request({
+    url: '/bill/statementInvoice',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改销售发票主
+ * @param data
+ */
+export const updateStatementInvoice = (data: StatementInvoiceForm) => {
+  return request({
+    url: '/bill/statementInvoice',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除销售发票主
+ * @param id
+ */
+export const delStatementInvoice = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/bill/statementInvoice/' + id,
+    method: 'delete'
+  });
+};
+
+export function changeStatus(id: string | number, invoiceStatus: string) {
+  const data = {
+    id,
+    invoiceStatus
+  };
+  return request({
+    url: '/bill/statementInvoice/changeStatus',
+    method: 'put',
+    data: data
+  });
+}

+ 179 - 0
src/api/bill/statementInvoice/types.ts

@@ -0,0 +1,179 @@
+export interface StatementInvoiceVO {
+  /**
+   *
+   */
+  id: string | number;
+
+  /**
+   * 发票编号
+   */
+  statementInvoiceNo: string;
+
+  /**
+   * 客户编号
+   */
+  customerId: string | number;
+
+  /**
+   * 客户编号
+   */
+  customerNo: string;
+
+  /**
+   * 客户名称
+   */
+  customerName: string;
+
+  /**
+   * 发票金额
+   */
+  invoiceAmount: number;
+
+  /**
+   * 发票状态
+   */
+  invoiceStatus: string;
+
+  /**
+   * 开票时间
+   */
+  invoiceTime: string;
+
+  /**
+   * 驳回备注
+   */
+  rejectRemark: string;
+
+  /**
+   * 备注
+   */
+  remark: string;
+}
+
+export interface StatementInvoiceForm extends BaseEntity {
+  /**
+   *
+   */
+  id?: string | number;
+
+  /**
+   * 发票编号
+   */
+  statementInvoiceNo?: string;
+
+  /**
+   * 客户编号
+   */
+  customerId?: string | number;
+
+  /**
+   * 客户编号
+   */
+  customerNo?: string;
+
+  /**
+   * 客户名称
+   */
+  customerName?: string;
+
+  /**
+   * 发票金额
+   */
+  invoiceAmount?: number;
+
+  /**
+   * 发票状态
+   */
+  invoiceStatus?: string;
+
+  /**
+   * 开票时间
+   */
+  invoiceTime?: string;
+
+  /**
+   * 驳回备注
+   */
+  rejectRemark?: string;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+
+  /**
+   * 附件地址
+   */
+  annexAddress?: string;
+
+  /**
+   * 发票明细列表
+   */
+  detailList?: any[];
+
+  /**
+   * 商品清单列表
+   */
+  productList?: any[];
+
+  /**
+   * 发票列表
+   */
+  invoiceList?: any[];
+}
+
+export interface StatementInvoiceQuery extends PageQuery {
+  /**
+   * 发票编号
+   */
+  statementInvoiceNo?: string;
+
+  /**
+   * 客户编号
+   */
+  customerId?: string | number;
+
+  /**
+   * 客户编号
+   */
+  customerNo?: string;
+
+  /**
+   * 客户名称
+   */
+  customerName?: string;
+
+  /**
+   * 发票金额
+   */
+  invoiceAmount?: number;
+
+  /**
+   * 发票状态
+   */
+  invoiceStatus?: string;
+
+  statementOrderNo?: string;
+
+  orderNo?: string;
+
+  /**
+   * 开票时间
+   */
+  invoiceTime?: string;
+
+  /**
+   * 驳回备注
+   */
+  rejectRemark?: string;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+  /**
+   * 日期范围参数
+   */
+  params?: any;
+}

+ 63 - 0
src/api/bill/statementInvoiceDetail/index.ts

@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { StatementInvoiceDetailVO, StatementInvoiceDetailForm, StatementInvoiceDetailQuery } from '@/api/bill/statementInvoiceDetail/types';
+
+/**
+ * 查询销售发票明细列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listStatementInvoiceDetail = (query?: StatementInvoiceDetailQuery): AxiosPromise<StatementInvoiceDetailVO[]> => {
+  return request({
+    url: '/bill/statementInvoiceDetail/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询销售发票明细详细
+ * @param id
+ */
+export const getStatementInvoiceDetail = (id: string | number): AxiosPromise<StatementInvoiceDetailVO> => {
+  return request({
+    url: '/bill/statementInvoiceDetail/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增销售发票明细
+ * @param data
+ */
+export const addStatementInvoiceDetail = (data: StatementInvoiceDetailForm) => {
+  return request({
+    url: '/bill/statementInvoiceDetail',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改销售发票明细
+ * @param data
+ */
+export const updateStatementInvoiceDetail = (data: StatementInvoiceDetailForm) => {
+  return request({
+    url: '/bill/statementInvoiceDetail',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除销售发票明细
+ * @param id
+ */
+export const delStatementInvoiceDetail = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/bill/statementInvoiceDetail/' + id,
+    method: 'delete'
+  });
+};

+ 266 - 0
src/api/bill/statementInvoiceDetail/types.ts

@@ -0,0 +1,266 @@
+export interface StatementInvoiceDetailVO {
+  /**
+   * 
+   */
+  id: string | number;
+
+  /**
+   * 发票主表id(关联主表)
+   */
+  statementInvoiceId: string | number;
+
+  /**
+   * 发票编号(关联主表)
+   */
+  statementInvoiceNo: string;
+
+  /**
+   * 对账金额
+   */
+  statementAmount: number;
+
+  /**
+   * 订单类型
+   */
+  orderType: string;
+
+  /**
+   * 关联的订单id
+   */
+  orderId: string | number;
+
+  /**
+   * 订单编号
+   */
+  orderNo: string;
+
+  /**
+   * 订单金额
+   */
+  orderAmount: number;
+
+  /**
+   * 订单时间
+   */
+  orderTime: string;
+
+  /**
+   * 签约/对账日期
+   */
+  signingDate: string;
+
+  /**
+   * 操作用户id
+   */
+  userId: string | number;
+
+  /**
+   * 操作人
+   */
+  userName: string;
+
+  /**
+   * 操作人部门
+   */
+  userDeptId: string | number;
+
+  /**
+   * 操作人部门
+   */
+  userDept: string;
+
+  /**
+   * 对账单id
+   */
+  statementOrderId: string | number;
+
+  /**
+   * 对账单号(可选)
+   */
+  statementOrderNo: string;
+
+  /**
+   * 备注
+   */
+  remark: string;
+
+}
+
+export interface StatementInvoiceDetailForm extends BaseEntity {
+  /**
+   * 
+   */
+  id?: string | number;
+
+  /**
+   * 发票主表id(关联主表)
+   */
+  statementInvoiceId?: string | number;
+
+  /**
+   * 发票编号(关联主表)
+   */
+  statementInvoiceNo?: string;
+
+  /**
+   * 对账金额
+   */
+  statementAmount?: number;
+
+  /**
+   * 订单类型
+   */
+  orderType?: string;
+
+  /**
+   * 关联的订单id
+   */
+  orderId?: string | number;
+
+  /**
+   * 订单编号
+   */
+  orderNo?: string;
+
+  /**
+   * 订单金额
+   */
+  orderAmount?: number;
+
+  /**
+   * 订单时间
+   */
+  orderTime?: string;
+
+  /**
+   * 签约/对账日期
+   */
+  signingDate?: string;
+
+  /**
+   * 操作用户id
+   */
+  userId?: string | number;
+
+  /**
+   * 操作人
+   */
+  userName?: string;
+
+  /**
+   * 操作人部门
+   */
+  userDeptId?: string | number;
+
+  /**
+   * 操作人部门
+   */
+  userDept?: string;
+
+  /**
+   * 对账单id
+   */
+  statementOrderId?: string | number;
+
+  /**
+   * 对账单号(可选)
+   */
+  statementOrderNo?: string;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+
+}
+
+export interface StatementInvoiceDetailQuery extends PageQuery {
+
+  /**
+   * 发票主表id(关联主表)
+   */
+  statementInvoiceId?: string | number;
+
+  /**
+   * 发票编号(关联主表)
+   */
+  statementInvoiceNo?: string;
+
+  /**
+   * 对账金额
+   */
+  statementAmount?: number;
+
+  /**
+   * 订单类型
+   */
+  orderType?: string;
+
+  /**
+   * 关联的订单id
+   */
+  orderId?: string | number;
+
+  /**
+   * 订单编号
+   */
+  orderNo?: string;
+
+  /**
+   * 订单金额
+   */
+  orderAmount?: number;
+
+  /**
+   * 订单时间
+   */
+  orderTime?: string;
+
+  /**
+   * 签约/对账日期
+   */
+  signingDate?: string;
+
+  /**
+   * 操作用户id
+   */
+  userId?: string | number;
+
+  /**
+   * 操作人
+   */
+  userName?: string;
+
+  /**
+   * 操作人部门
+   */
+  userDeptId?: string | number;
+
+  /**
+   * 操作人部门
+   */
+  userDept?: string;
+
+  /**
+   * 对账单id
+   */
+  statementOrderId?: string | number;
+
+  /**
+   * 对账单号(可选)
+   */
+  statementOrderNo?: string;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+    /**
+     * 日期范围参数
+     */
+    params?: any;
+}
+
+
+

+ 63 - 0
src/api/bill/statementInvoiceProduct/index.ts

@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { StatementInvoiceProductVO, StatementInvoiceProductForm, StatementInvoiceProductQuery } from '@/api/bill/statementInvoiceProduct/types';
+
+/**
+ * 查询销售发票商品列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listStatementInvoiceProduct = (query?: StatementInvoiceProductQuery): AxiosPromise<StatementInvoiceProductVO[]> => {
+  return request({
+    url: '/bill/statementInvoiceProduct/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询销售发票商品详细
+ * @param id
+ */
+export const getStatementInvoiceProduct = (id: string | number): AxiosPromise<StatementInvoiceProductVO> => {
+  return request({
+    url: '/bill/statementInvoiceProduct/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增销售发票商品
+ * @param data
+ */
+export const addStatementInvoiceProduct = (data: StatementInvoiceProductForm) => {
+  return request({
+    url: '/bill/statementInvoiceProduct',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改销售发票商品
+ * @param data
+ */
+export const updateStatementInvoiceProduct = (data: StatementInvoiceProductForm) => {
+  return request({
+    url: '/bill/statementInvoiceProduct',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除销售发票商品
+ * @param id
+ */
+export const delStatementInvoiceProduct = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/bill/statementInvoiceProduct/' + id,
+    method: 'delete'
+  });
+};

+ 251 - 0
src/api/bill/statementInvoiceProduct/types.ts

@@ -0,0 +1,251 @@
+export interface StatementInvoiceProductVO {
+  /**
+   * 
+   */
+  id: string | number;
+
+  /**
+   * 发票主表id(关联主表)
+   */
+  statementInvoiceId: string | number;
+
+  /**
+   * 发票编号(关联主表)
+   */
+  statementInvoiceNo: string;
+
+  /**
+   * 商品类型
+   */
+  type: string;
+
+  /**
+   * 关联的订单id
+   */
+  orderId: string | number;
+
+  /**
+   * 所属订单编号
+   */
+  orderNo: string;
+
+  /**
+   * 品类id
+   */
+  categoryId: string | number;
+
+  /**
+   * 品类编号
+   */
+  categoryNo: string;
+
+  /**
+   * 商品id
+   */
+  productId: string | number;
+
+  /**
+   * 商品编号
+   */
+  productNo: string;
+
+  /**
+   * 商品名称
+   */
+  itemName: string;
+
+  /**
+   * 单位
+   */
+  unitId: string | number;
+
+  /**
+   * 单位名称
+   */
+  unitName: string;
+
+  /**
+   * 数量
+   */
+  quantity: number;
+
+  /**
+   * 单价
+   */
+  unitPrice: number;
+
+  /**
+   * 备注
+   */
+  remark: string;
+
+}
+
+export interface StatementInvoiceProductForm extends BaseEntity {
+  /**
+   * 
+   */
+  id?: string | number;
+
+  /**
+   * 发票主表id(关联主表)
+   */
+  statementInvoiceId?: string | number;
+
+  /**
+   * 发票编号(关联主表)
+   */
+  statementInvoiceNo?: string;
+
+  /**
+   * 商品类型
+   */
+  type?: string;
+
+  /**
+   * 关联的订单id
+   */
+  orderId?: string | number;
+
+  /**
+   * 所属订单编号
+   */
+  orderNo?: string;
+
+  /**
+   * 品类id
+   */
+  categoryId?: string | number;
+
+  /**
+   * 品类编号
+   */
+  categoryNo?: string;
+
+  /**
+   * 商品id
+   */
+  productId?: string | number;
+
+  /**
+   * 商品编号
+   */
+  productNo?: string;
+
+  /**
+   * 商品名称
+   */
+  itemName?: string;
+
+  /**
+   * 单位
+   */
+  unitId?: string | number;
+
+  /**
+   * 单位名称
+   */
+  unitName?: string;
+
+  /**
+   * 数量
+   */
+  quantity?: number;
+
+  /**
+   * 单价
+   */
+  unitPrice?: number;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+
+}
+
+export interface StatementInvoiceProductQuery extends PageQuery {
+
+  /**
+   * 发票主表id(关联主表)
+   */
+  statementInvoiceId?: string | number;
+
+  /**
+   * 发票编号(关联主表)
+   */
+  statementInvoiceNo?: string;
+
+  /**
+   * 商品类型
+   */
+  type?: string;
+
+  /**
+   * 关联的订单id
+   */
+  orderId?: string | number;
+
+  /**
+   * 所属订单编号
+   */
+  orderNo?: string;
+
+  /**
+   * 品类id
+   */
+  categoryId?: string | number;
+
+  /**
+   * 品类编号
+   */
+  categoryNo?: string;
+
+  /**
+   * 商品id
+   */
+  productId?: string | number;
+
+  /**
+   * 商品编号
+   */
+  productNo?: string;
+
+  /**
+   * 商品名称
+   */
+  itemName?: string;
+
+  /**
+   * 单位
+   */
+  unitId?: string | number;
+
+  /**
+   * 单位名称
+   */
+  unitName?: string;
+
+  /**
+   * 数量
+   */
+  quantity?: number;
+
+  /**
+   * 单价
+   */
+  unitPrice?: number;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+    /**
+     * 日期范围参数
+     */
+    params?: any;
+}
+
+
+

+ 110 - 0
src/api/bill/statementOrder/index.ts

@@ -0,0 +1,110 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { StatementOrderVO, StatementOrderForm, StatementOrderQuery } from '@/api/bill/statementOrder/types';
+
+/**
+ * 查询对账单主列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listStatementOrder = (query?: StatementOrderQuery): AxiosPromise<StatementOrderVO[]> => {
+  return request({
+    url: '/bill/statementOrder/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询对账单主详细
+ * @param id
+ */
+export const getStatementOrder = (id: string | number): AxiosPromise<StatementOrderVO> => {
+  return request({
+    url: '/bill/statementOrder/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增对账单主
+ * @param data
+ */
+export const addStatementOrder = (data: StatementOrderForm) => {
+  return request({
+    url: '/bill/statementOrder',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改对账单主
+ * @param data
+ */
+export const updateStatementOrder = (data: StatementOrderForm) => {
+  return request({
+    url: '/bill/statementOrder',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除对账单主
+ * @param id
+ */
+export const delStatementOrder = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/bill/statementOrder/' + id,
+    method: 'delete'
+  });
+};
+
+export function changeStatus(id: string | number, statementStatus: string) {
+  const data = {
+    id,
+    statementStatus
+  };
+  return request({
+    url: '/bill/statementOrder/changeStatus',
+    method: 'put',
+    data: data
+  });
+}
+
+/**
+ * 根据客户ID查询对账单明细(分页)
+ * @param customerId 客户ID
+ * @param pageQuery 分页参数
+ */
+export const getCustomerOrderDetails = (customerId: string | number, pageQuery?: { pageNum?: number; pageSize?: number }) => {
+  return request({
+    url: '/bill/statementOrder/getCustomerOrderDetails/' + customerId,
+    method: 'get',
+    params: pageQuery
+  });
+};
+
+/**
+ * 根据多个 (对账单ID + 订单ID) 查询对应的订单商品列表
+ * @param items - 包含 statementOrderId 和 orderId 的数组
+ */
+export function getStatementProductList(items: { statementOrderId: number | string; orderId: number | string }[]) {
+  if (!Array.isArray(items) || items.length === 0) {
+    return Promise.reject(new Error('至少选择一个订单'));
+  }
+
+  // 可选:校验每项是否完整
+  const invalidItem = items.find((item) => item.statementOrderId == null || item.orderId == null);
+  if (invalidItem) {
+    return Promise.reject(new Error('每项必须包含 statementOrderId 和 orderId'));
+  }
+
+  return request({
+    url: '/bill/statementOrder/getStatementProductList',
+    method: 'post',
+    data: items // 发送 JSON 数组
+  });
+}

+ 248 - 0
src/api/bill/statementOrder/types.ts

@@ -0,0 +1,248 @@
+export interface StatementOrderVO {
+  /**
+   * 主键ID
+   */
+  id: string | number;
+
+  /**
+   * 对账单编号
+   */
+  statementOrderNo: string;
+
+  /**
+   * 客户编号
+   */
+  customerId: string | number;
+
+  /**
+   * 客户编号
+   */
+  customerNo: string;
+
+  /**
+   * 客户名称
+   */
+  customerName: string;
+
+  /**
+   * 对账总金额
+   */
+  amount: number;
+
+  statementSelfId: string | number;
+
+  /**
+   * 对账人姓名
+   */
+  statementSelf: string;
+
+  /**
+   * 对账人电话
+   */
+  statementSelfPhone: string;
+
+  /**
+   * 对账状态(如:0-待确认, 1-已确认, 2-已驳回)
+   */
+  statementStatus: string;
+
+  /**
+   * 是否已付款
+   */
+  isPaymentStatus: string;
+
+  /**
+   * 是否已开票
+   */
+  isInvoiceStatus: string;
+
+  /**
+   * 对账日期
+   */
+  statementDate: string;
+
+  /**
+   * 附件存储路径
+   */
+  annexAddress: string;
+
+  /**
+   * 驳回原因
+   */
+  rejectRemark: string;
+
+  /**
+   * 备注
+   */
+  remark: string;
+
+  /**
+   * 对账明细列表
+   */
+  detailList?: any[];
+
+  /**
+   * 商品清单列表
+   */
+  productList?: any[];
+}
+
+export interface StatementOrderForm extends BaseEntity {
+  /**
+   * 主键ID
+   */
+  id?: string | number;
+
+  /**
+   * 对账单编号
+   */
+  statementOrderNo?: string;
+
+  /**
+   * 客户编号
+   */
+  customerId?: string | number;
+
+  /**
+   * 客户编号
+   */
+  customerNo?: string;
+
+  /**
+   * 客户名称
+   */
+  customerName?: string;
+
+  /**
+   * 对账总金额
+   */
+  amount?: number;
+
+  statementSelfId?: string | number;
+
+  /**
+   * 对账人姓名
+   */
+  statementSelf?: string;
+
+  /**
+   * 对账人电话
+   */
+  statementSelfPhone?: string;
+
+  /**
+   * 对账状态(如:0-待确认, 1-已确认, 2-已驳回)
+   */
+  statementStatus?: string;
+
+  /**
+   * 是否已付款
+   */
+  isPaymentStatus?: string;
+
+  /**
+   * 是否已开票
+   */
+  isInvoiceStatus?: string;
+
+  /**
+   * 对账日期
+   */
+  statementDate?: string;
+
+  /**
+   * 附件存储路径
+   */
+  annexAddress?: string;
+
+  /**
+   * 驳回原因
+   */
+  rejectRemark?: string;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+
+  detailList?: any[];
+
+  productList?: any[];
+}
+
+export interface StatementOrderQuery extends PageQuery {
+  /**
+   * 对账单编号
+   */
+  statementOrderNo?: string;
+
+  /**
+   * 客户编号
+   */
+  customerId?: string | number;
+
+  /**
+   * 客户编号
+   */
+  customerNo?: string;
+
+  /**
+   * 客户名称
+   */
+  customerName?: string;
+
+  /**
+   * 对账总金额
+   */
+  amount?: number;
+
+  /**
+   * 对账人姓名
+   */
+  statementSelf?: string;
+
+  /**
+   * 对账人电话
+   */
+  statementSelfPhone?: string;
+
+  /**
+   * 对账状态(如:0-待确认, 1-已确认, 2-已驳回)
+   */
+  statementStatus?: string;
+
+  /**
+   * 是否已付款
+   */
+  isPaymentStatus?: string;
+
+  /**
+   * 是否已开票
+   */
+  isInvoiceStatus?: string;
+
+  /**
+   * 对账日期
+   */
+  statementDate?: string;
+
+  /**
+   * 附件存储路径
+   */
+  annexAddress?: string;
+
+  /**
+   * 驳回原因
+   */
+  rejectRemark?: string;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+  /**
+   * 日期范围参数
+   */
+  params?: any;
+}

+ 63 - 0
src/api/bill/statementProduct/index.ts

@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { StatementProductVO, StatementProductForm, StatementProductQuery } from '@/api/bill/statementProduct/types';
+
+/**
+ * 查询对账单商品明细列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listStatementProduct = (query?: StatementProductQuery): AxiosPromise<StatementProductVO[]> => {
+  return request({
+    url: '/bill/statementProduct/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询对账单商品明细详细
+ * @param id
+ */
+export const getStatementProduct = (id: string | number): AxiosPromise<StatementProductVO> => {
+  return request({
+    url: '/bill/statementProduct/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增对账单商品明细
+ * @param data
+ */
+export const addStatementProduct = (data: StatementProductForm) => {
+  return request({
+    url: '/bill/statementProduct',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改对账单商品明细
+ * @param data
+ */
+export const updateStatementProduct = (data: StatementProductForm) => {
+  return request({
+    url: '/bill/statementProduct',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除对账单商品明细
+ * @param id
+ */
+export const delStatementProduct = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/bill/statementProduct/' + id,
+    method: 'delete'
+  });
+};

+ 251 - 0
src/api/bill/statementProduct/types.ts

@@ -0,0 +1,251 @@
+export interface StatementProductVO {
+  /**
+   * 主键ID
+   */
+  id: string | number;
+
+  /**
+   * 对账单id
+   */
+  statementOrderId: string | number;
+
+  /**
+   * 对账单编号
+   */
+  statementOrderNo: string;
+
+  /**
+   * 商品类型(如:1-实物, 2-服务等)
+   */
+  type: string;
+
+  /**
+   * 关联的订单id
+   */
+  orderId: string | number;
+
+  /**
+   * 关联的订单编号
+   */
+  orderNo: string;
+
+  /**
+   * 品类id
+   */
+  categoryId: string | number;
+
+  /**
+   * 品类编号
+   */
+  categoryNo: string;
+
+  /**
+   * 商品id
+   */
+  productId: string | number;
+
+  /**
+   * 商品编号
+   */
+  productNo: string;
+
+  /**
+   * 商品名称
+   */
+  itemName: string;
+
+  /**
+   * 单位
+   */
+  unitId: string | number;
+
+  /**
+   * 单位
+   */
+  unitName: string;
+
+  /**
+   * 数量
+   */
+  quantity: number;
+
+  /**
+   * 单价
+   */
+  unitPrice: number;
+
+  /**
+   * 备注
+   */
+  remark: string;
+
+}
+
+export interface StatementProductForm extends BaseEntity {
+  /**
+   * 主键ID
+   */
+  id?: string | number;
+
+  /**
+   * 对账单id
+   */
+  statementOrderId?: string | number;
+
+  /**
+   * 对账单编号
+   */
+  statementOrderNo?: string;
+
+  /**
+   * 商品类型(如:1-实物, 2-服务等)
+   */
+  type?: string;
+
+  /**
+   * 关联的订单id
+   */
+  orderId?: string | number;
+
+  /**
+   * 关联的订单编号
+   */
+  orderNo?: string;
+
+  /**
+   * 品类id
+   */
+  categoryId?: string | number;
+
+  /**
+   * 品类编号
+   */
+  categoryNo?: string;
+
+  /**
+   * 商品id
+   */
+  productId?: string | number;
+
+  /**
+   * 商品编号
+   */
+  productNo?: string;
+
+  /**
+   * 商品名称
+   */
+  itemName?: string;
+
+  /**
+   * 单位
+   */
+  unitId?: string | number;
+
+  /**
+   * 单位
+   */
+  unitName?: string;
+
+  /**
+   * 数量
+   */
+  quantity?: number;
+
+  /**
+   * 单价
+   */
+  unitPrice?: number;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+
+}
+
+export interface StatementProductQuery extends PageQuery {
+
+  /**
+   * 对账单id
+   */
+  statementOrderId?: string | number;
+
+  /**
+   * 对账单编号
+   */
+  statementOrderNo?: string;
+
+  /**
+   * 商品类型(如:1-实物, 2-服务等)
+   */
+  type?: string;
+
+  /**
+   * 关联的订单id
+   */
+  orderId?: string | number;
+
+  /**
+   * 关联的订单编号
+   */
+  orderNo?: string;
+
+  /**
+   * 品类id
+   */
+  categoryId?: string | number;
+
+  /**
+   * 品类编号
+   */
+  categoryNo?: string;
+
+  /**
+   * 商品id
+   */
+  productId?: string | number;
+
+  /**
+   * 商品编号
+   */
+  productNo?: string;
+
+  /**
+   * 商品名称
+   */
+  itemName?: string;
+
+  /**
+   * 单位
+   */
+  unitId?: string | number;
+
+  /**
+   * 单位
+   */
+  unitName?: string;
+
+  /**
+   * 数量
+   */
+  quantity?: number;
+
+  /**
+   * 单价
+   */
+  unitPrice?: number;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+    /**
+     * 日期范围参数
+     */
+    params?: any;
+}
+
+
+

+ 11 - 0
src/api/customer/customerFile/businessInfo/index.ts

@@ -27,6 +27,17 @@ export const getBusinessInfo = (customerId: string | number): AxiosPromise<Busin
   });
 };
 
+/**
+ * 查询客户工商注册信息详细
+ * @param customerName
+ */
+export const getBusinessInfoBycustomerName = (customerName: string): AxiosPromise<BusinessInfoVO> => {
+  return request({
+    url: '/customer/businessInfo/selectBusinessByCustomerName/' + customerName,
+    method: 'get'
+  });
+};
+
 /**
  * 新增客户工商注册信息
  * @param data

+ 11 - 0
src/api/customer/customerFile/customerInfo/index.ts

@@ -120,3 +120,14 @@ export const listContractList = (query?: CustomerInfoQuery): AxiosPromise<Custom
     params: query
   });
 };
+
+/**
+ * 根据名称模糊查询客户信息
+ * @param customerName
+ */
+export const getListBycustomerName = (customerName: string): AxiosPromise<CustomerInfoVO[]> => {
+  return request({
+    url: '/customer/customerInfo/selectByCustomerName/' + customerName,
+    method: 'get'
+  });
+};

+ 14 - 0
src/api/order/orderDeliver/index.ts

@@ -61,3 +61,17 @@ export const delOrderDeliver = (id: string | number | Array<string | number>) =>
     method: 'delete'
   });
 };
+
+/**
+ * 查询订单发货物流信息
+ * @param query
+ * @returns {*}
+ */
+
+export const queryTrack = (query?: OrderDeliverQuery): AxiosPromise<OrderDeliverVO[]> => {
+  return request({
+    url: '/order/orderDeliver/queryTrack',
+    method: 'get',
+    params: query
+  });
+};

+ 12 - 2
src/api/order/orderDeliver/types.ts

@@ -52,7 +52,12 @@ export interface OrderDeliverVO {
   /**
    * 承运物流公司
    */
-  logisticsCompany: number;
+  logisticsCompanyId: number;
+
+  /**
+   * 承运物流公司编码
+   */
+  logisticsCompanyCode: string;
 
   /**
    * 物流单号
@@ -133,7 +138,12 @@ export interface OrderDeliverForm extends BaseEntity {
   /**
    * 承运物流公司
    */
-  logisticsCompany?: number;
+  logisticsCompanyId?: number;
+
+  /**
+   * 承运物流公司编码
+   */
+  logisticsCompanyCode?: string;
 
   /**
    * 物流单号

+ 21 - 0
src/api/order/orderMain/index.ts

@@ -122,3 +122,24 @@ export function changeStatus(id: string | number, orderStatus: string) {
     data: data
   });
 }
+
+/**
+ * 根据多个订单ID查询对应的订单商品列表
+ * @param orderIds
+ */
+export function getCustomerOrderProductList(orderIds: (string | number)[]) {
+  if (!Array.isArray(orderIds) || orderIds.length === 0) {
+    return Promise.reject(new Error('订单ID不能为空'));
+  }
+
+  // 手动构造查询参数,确保格式为 orderId=1&orderId=2&orderId=3
+  const params = new URLSearchParams();
+  orderIds.forEach((id) => {
+    params.append('orderId', String(id));
+  });
+
+  return request({
+    url: '/order/orderMain/getCustomerOrderProductList?' + params.toString(),
+    method: 'get'
+  });
+}

+ 573 - 0
src/views/bill/statementInvoice/addDrawer.vue

@@ -0,0 +1,573 @@
+<template>
+  <!-- 新增/编辑发票抽屉 -->
+  <el-drawer v-model="drawer.visible" size="75%" direction="rtl" :before-close="handleDrawerClose" :close-on-click-modal="true">
+    <template #header>
+      <div class="drawer-header">
+        <span class="order-title">{{ drawer.title }}</span>
+      </div>
+    </template>
+
+    <div class="drawer-content">
+      <el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
+        <!-- 基本信息 -->
+        <el-divider content-position="left">
+          <span style="color: #409eff; font-weight: 600">基本信息</span>
+        </el-divider>
+
+        <el-row :gutter="20">
+          <el-col :span="8">
+            <el-form-item label="客户名称" prop="customerId">
+              <el-select
+                v-model="form.customerId"
+                filterable
+                remote
+                reserve-keyword
+                placeholder="请输入客户名称"
+                :remote-method="remoteSearchCustomer"
+                :loading="customerLoading"
+                clearable
+                style="width: 100%"
+                @change="handleCustomerChange"
+              >
+                <el-option v-for="item in customerOptions" :key="item.id" :label="item.customerName" :value="item.id" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="开票金额" prop="invoiceAmount">
+              <el-input v-model="form.invoiceAmount" disabled />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="开票日期" prop="invoiceTime">
+              <el-date-picker v-model="form.invoiceTime" type="date" placeholder="请选择开票日期" style="width: 100%" value-format="YYYY-MM-DD" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <!-- 账单列表 -->
+        <el-divider content-position="left">
+          <span style="color: #409eff; font-weight: 600">账单列表</span>
+        </el-divider>
+        <div class="table-actions" style="margin-bottom: 15px">
+          <el-button type="primary" icon="Plus" @click="handleSelectStatement">选择对账单</el-button>
+        </div>
+
+        <el-table :data="form.detailList" border style="width: 100%; margin-bottom: 20px">
+          <el-table-column type="index" label="序号" width="60" align="center" />
+          <el-table-column prop="statementOrderNo" label="对账单编号" min-width="150" align="center" />
+          <el-table-column prop="statementAmount" label="对账金额" min-width="120" align="center" />
+          <el-table-column prop="orderNo" label="订单编号" min-width="150" align="center" />
+          <el-table-column prop="orderAmount" label="金额" min-width="120" align="center" />
+          <el-table-column prop="orderTime" label="下单日期" min-width="120" align="center" />
+          <el-table-column prop="signingDate" label="签收日期" min-width="120" align="center" />
+          <el-table-column prop="userName" label="下单人" min-width="100" align="center" />
+          <el-table-column prop="userDept" label="部门" min-width="120" align="center" />
+        </el-table>
+
+        <!-- 商品清单 -->
+        <el-divider content-position="left">
+          <span style="color: #409eff; font-weight: 600">商品清单</span>
+        </el-divider>
+
+        <el-table :data="pagedProductList" border style="width: 100%; margin-bottom: 20px">
+          <el-table-column
+            type="index"
+            label="序号"
+            width="60"
+            align="center"
+            :index="(index) => (productPage.pageNum - 1) * productPage.pageSize + index + 1"
+          />
+          <el-table-column prop="orderNo" label="订单编号" min-width="120" align="center" />
+          <el-table-column prop="productNo" label="商品编号" min-width="120" align="center" />
+          <el-table-column prop="itemName" label="商品名称" min-width="180" align="center" />
+          <el-table-column prop="specifications" label="规格型号" min-width="120" align="center" />
+          <el-table-column prop="unitName" label="单位" align="center" />
+          <el-table-column prop="quantity" label="数量" align="center" />
+          <el-table-column prop="unitPrice" label="单价" align="center">
+            <template #default="scope">
+              {{ scope.row.unitPrice ? Number(scope.row.unitPrice).toFixed(2) : '0.00' }}
+            </template>
+          </el-table-column>
+          <el-table-column label="金额" align="center">
+            <template #default="scope">
+              {{ (Number(scope.row.quantity || 0) * Number(scope.row.unitPrice || 0)).toFixed(2) }}
+            </template>
+          </el-table-column>
+          <el-table-column label="小计" align="center">
+            <template #default="scope">
+              {{ (Number(scope.row.quantity || 0) * Number(scope.row.unitPrice || 0)).toFixed(2) }}
+            </template>
+          </el-table-column>
+        </el-table>
+
+        <!-- 分页 -->
+        <el-pagination
+          v-if="form.productList && form.productList.length > 0"
+          v-model:current-page="productPage.pageNum"
+          v-model:page-size="productPage.pageSize"
+          :page-sizes="[10, 20, 50, 100]"
+          layout="total, sizes, prev, pager, next, jumpers"
+          :total="productPage.total"
+          @size-change="handleProductSizeChange"
+          @current-change="handleProductCurrentChange"
+          style="margin-top: 15px"
+        />
+
+        <!-- 发票列表 -->
+        <el-divider content-position="left">
+          <span style="color: #409eff; font-weight: 600">发票列表</span>
+        </el-divider>
+
+        <div class="table-actions" style="margin-bottom: 15px">
+          <el-button type="primary" icon="Plus" @click="handleAddInvoice">新增发票</el-button>
+        </div>
+
+        <!-- 发票表格 -->
+        <el-table :data="invoiceList" border style="width: 100%; margin-bottom: 20px">
+          <el-table-column prop="invoiceType" label="发票类型" min-width="120" align="center">
+            <template #default="scope">
+              <dict-tag :options="invoice_type" :value="scope.row.invoiceType" />
+            </template>
+          </el-table-column>
+          <el-table-column prop="invoiceDate" label="开票日期" min-width="120" align="center" />
+          <el-table-column prop="invoiceCode" label="发票代码" min-width="150" align="center" />
+          <el-table-column prop="invoiceAmount" label="发票金额" min-width="120" align="center">
+            <template #default="scope">
+              {{ Number(scope.row.invoiceAmount || 0).toFixed(2) }}
+            </template>
+          </el-table-column>
+
+          <el-table-column label="操作" width="150" align="center">
+            <template #default="scope">
+              <el-button link type="primary" size="small" @click="handleEditInvoice(scope.row, scope.$index)">编辑</el-button>
+              <el-button link type="danger" size="small" @click="handleDeleteInvoice(scope.$index)">删除</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+      </el-form>
+    </div>
+
+    <template #footer>
+      <div class="drawer-footer">
+        <el-button @click="handleDrawerClose(() => (drawer.visible = false))">取消</el-button>
+        <el-button type="primary" :loading="buttonLoading" @click="handleSubmit">确定</el-button>
+      </div>
+    </template>
+  </el-drawer>
+
+  <!-- 对账单选择抽屉 -->
+  <StatementOrderDrawer ref="statementOrderDrawerRef" @success="handleStatementSelected" />
+
+  <!-- 文件选择器 -->
+  <FileSelector v-model="fileSelectorVisible" :multiple="true" :allowed-types="[1, 2, 3, 4, 5]" title="选择发票附件" @confirm="handleFileSelected" />
+
+  <!-- 新增发票对话框 -->
+  <AddInvoiceDialog ref="addInvoiceDialogRef" @success="handleInvoiceAdded" />
+</template>
+
+<script setup name="InvoiceAddDrawer" lang="ts">
+import { addStatementInvoice, updateStatementInvoice } from '@/api/bill/statementInvoice';
+import { StatementInvoiceForm } from '@/api/bill/statementInvoice/types';
+import { getListBycustomerName } from '@/api/customer/customerFile/customerInfo';
+import { CustomerInfoVO } from '@/api/customer/customerFile/customerInfo/types';
+import { StatementOrderVO } from '@/api/bill/statementOrder/types';
+import StatementOrderDrawer from './statementOrderDrawer.vue';
+import FileSelector from '@/components/FileSelector/index.vue';
+import AddInvoiceDialog from './addInvoiceDialog.vue';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { invoice_type } = toRefs<any>(proxy?.useDict('invoice_type'));
+const initFormData: StatementInvoiceForm = {
+  id: undefined,
+  statementInvoiceNo: undefined,
+  customerId: undefined,
+  customerNo: undefined,
+  customerName: undefined,
+  invoiceAmount: undefined,
+  invoiceStatus: undefined,
+  invoiceTime: undefined,
+  rejectRemark: undefined,
+  remark: undefined,
+  detailList: [],
+  productList: [],
+  invoiceList: []
+};
+
+const formRef = ref<ElFormInstance>();
+const buttonLoading = ref(false);
+const form = ref<StatementInvoiceForm>({ ...initFormData });
+
+const rules = reactive({
+  customerId: [{ required: true, message: '请选择客户', trigger: 'change' }],
+  invoiceTime: [{ required: true, message: '请选择开票日期', trigger: 'change' }]
+} as any);
+
+const fileList = ref<any[]>([]);
+
+const productPage = reactive({
+  pageNum: 1,
+  pageSize: 10,
+  total: 0
+});
+
+const drawer = reactive<DialogOption>({
+  visible: false,
+  title: '新增发票'
+});
+
+const isEdit = ref(false);
+
+const customerLoading = ref(false);
+const customerOptions = ref<CustomerInfoVO[]>([]);
+const statementOrderDrawerRef = ref<any>();
+const fileSelectorVisible = ref(false);
+const addInvoiceDialogRef = ref<any>();
+const currentSelectedStatements = ref<StatementOrderVO[]>([]);
+const invoiceList = ref<any[]>([]); // 发票列表
+
+/** 计算当前页的商品列表 */
+const pagedProductList = computed(() => {
+  const start = (productPage.pageNum - 1) * productPage.pageSize;
+  const end = start + productPage.pageSize;
+  return form.value.productList?.slice(start, end) || [];
+});
+
+/** 打开新增/编辑发票抽屉 */
+const open = (id?: string | number, data?: StatementInvoiceForm) => {
+  reset();
+
+  if (id && data) {
+    // 编辑模式
+    isEdit.value = true;
+    drawer.title = '编辑发票';
+    Object.assign(form.value, data);
+
+    // 将客户信息添加到下拉选项中,以便回显
+    if (data.customerId && data.customerName) {
+      customerOptions.value = [
+        {
+          id: data.customerId,
+          customerName: data.customerName,
+          customerNo: data.customerNo || ''
+        } as CustomerInfoVO
+      ];
+    }
+
+    // 解析附件地址并回显附件列表
+    if (data.annexAddress) {
+      const urls = data.annexAddress.split(',').filter((url) => url.trim());
+      fileList.value = urls.map((url, index) => {
+        const fileName = url.split('/').pop() || `附件${index + 1}`;
+        return {
+          name: fileName,
+          url: url.trim(),
+          id: undefined
+        };
+      });
+    }
+
+    // 设置商品列表分页总数
+    if (data.productList && data.productList.length > 0) {
+      productPage.total = data.productList.length;
+    }
+
+    // 回显发票列表
+    if (data.invoiceList && data.invoiceList.length > 0) {
+      invoiceList.value = data.invoiceList;
+    }
+  } else {
+    // 新增模式
+    isEdit.value = false;
+    drawer.title = '新增发票';
+  }
+
+  drawer.visible = true;
+};
+
+/** 表单重置 */
+const reset = () => {
+  form.value = { ...initFormData };
+  fileList.value = [];
+  invoiceList.value = [];
+  productPage.pageNum = 1;
+  productPage.pageSize = 10;
+  productPage.total = 0;
+  customerOptions.value = [];
+  currentSelectedStatements.value = [];
+  formRef.value?.clearValidate();
+};
+
+/** 远程搜索客户 */
+const remoteSearchCustomer = async (query: string) => {
+  if (query) {
+    customerLoading.value = true;
+    try {
+      const res = await getListBycustomerName(query);
+      customerOptions.value = res.data || [];
+    } catch (error) {
+      console.error('搜索客户失败:', error);
+    } finally {
+      customerLoading.value = false;
+    }
+  } else {
+    customerOptions.value = [];
+  }
+};
+
+/** 客户变更 */
+const handleCustomerChange = (value: string | number) => {
+  const selectedCustomer = customerOptions.value.find((customer) => customer.id === value);
+  if (selectedCustomer) {
+    form.value.customerId = selectedCustomer.id;
+    form.value.customerNo = selectedCustomer.customerNo;
+    form.value.customerName = selectedCustomer.customerName;
+
+    // 清空之前选择的对账单和商品
+    form.value.detailList = [];
+    form.value.productList = [];
+    form.value.invoiceAmount = undefined;
+    productPage.total = 0;
+    currentSelectedStatements.value = [];
+  }
+};
+
+/** 选择对账单 */
+const handleSelectStatement = () => {
+  if (!form.value.customerId) {
+    proxy?.$modal.msgWarning('请先选择客户');
+    return;
+  }
+
+  // 使用nextTick确保组件已挂载
+  nextTick(() => {
+    if (statementOrderDrawerRef.value && typeof statementOrderDrawerRef.value.open === 'function') {
+      statementOrderDrawerRef.value.open(form.value.customerId, currentSelectedStatements.value);
+    } else {
+      console.error('对账单抽屉组件未正确加载');
+    }
+  });
+};
+
+/** 对账单选择成功回调 */
+const handleStatementSelected = (data: any) => {
+  const { statements, details, products } = data;
+
+  if (statements && statements.length > 0) {
+    let totalAmount = 0;
+
+    // 保存当前选中的对账单
+    currentSelectedStatements.value = statements;
+
+    // 清空现有数据
+    form.value.detailList = [];
+    form.value.productList = [];
+
+    // 填充发票明细
+    if (details && details.length > 0) {
+      form.value.detailList = details.map((detail: any) => ({
+        statementOrderNo: detail.statementOrderNo || '',
+        statementAmount: detail.statementAmount || 0,
+        orderNo: detail.orderNo || '',
+        orderAmount: detail.amount || 0,
+        signingDate: detail.signingDate || '',
+        orderTime: detail.orderTime || '',
+        userDept: detail.userDept || '',
+        userName: detail.userName || ''
+      }));
+
+      // 计算总金额
+      totalAmount = details.reduce((sum: number, detail: any) => sum + Number(detail.amount || 0), 0);
+    }
+
+    // 设置开票金额
+    form.value.invoiceAmount = totalAmount;
+
+    // 直接使用传递过来的产品列表
+    if (products && products.length > 0) {
+      form.value.productList = products;
+      productPage.total = products.length;
+    }
+  }
+};
+
+/** 打开文件选择器 */
+const handleOpenFileSelector = () => {
+  fileSelectorVisible.value = true;
+};
+
+/** 文件选择完成 */
+const handleFileSelected = (files: any[]) => {
+  if (files && files.length > 0) {
+    files.forEach((file) => {
+      fileList.value.push({
+        name: file.fileName || file.name,
+        url: file.fileUrl || file.url,
+        id: file.id
+      });
+    });
+    form.value.annexAddress = fileList.value.map((f) => f.url || f.name).join(',');
+  }
+  fileSelectorVisible.value = false;
+};
+
+/** 删除文件 */
+const handleRemoveFile = (index: number) => {
+  fileList.value.splice(index, 1);
+  form.value.annexAddress = fileList.value.map((f) => f.url || f.name).join(',');
+};
+
+/** 下载文件 */
+const handleDownloadFile = async (file: any) => {
+  if (!file.url) {
+    proxy?.$modal.msgWarning('文件地址不存在');
+    return;
+  }
+
+  try {
+    const response = await fetch(file.url);
+    const blob = await response.blob();
+    const blobUrl = window.URL.createObjectURL(blob);
+    const link = document.createElement('a');
+    link.href = blobUrl;
+    link.download = file.name || '附件';
+    document.body.appendChild(link);
+    link.click();
+    document.body.removeChild(link);
+    window.URL.revokeObjectURL(blobUrl);
+  } catch (error) {
+    console.error('下载失败:', error);
+    const link = document.createElement('a');
+    link.href = file.url;
+    link.download = file.name || '附件';
+    link.target = '_blank';
+    document.body.appendChild(link);
+    link.click();
+    document.body.removeChild(link);
+  }
+};
+
+/** 预览文件 */
+const handlePreviewFile = (file: any) => {
+  if (!file.url) {
+    proxy?.$modal.msgWarning('文件地址不存在');
+    return;
+  }
+  window.open(file.url, '_blank');
+};
+
+/** 产品分页大小改变 */
+const handleProductSizeChange = (val: number) => {
+  productPage.pageSize = val;
+};
+
+/** 产品分页页码改变 */
+const handleProductCurrentChange = (val: number) => {
+  productPage.pageNum = val;
+};
+
+/** 新增发票 */
+const handleAddInvoice = () => {
+  addInvoiceDialogRef.value?.open();
+};
+
+/** 发票添加成功 */
+const handleInvoiceAdded = (invoiceData: any) => {
+  // 将发票数据添加到发票列表
+  invoiceList.value.push({
+    ...invoiceData,
+    id: Date.now() // 临时ID
+  });
+};
+
+/** 编辑发票 */
+const handleEditInvoice = (invoice: any, index: number) => {
+  addInvoiceDialogRef.value?.open(invoice);
+};
+
+/** 删除发票 */
+const handleDeleteInvoice = (index: number) => {
+  proxy?.$modal
+    .confirm('确认删除该发票吗?')
+    .then(() => {
+      invoiceList.value.splice(index, 1);
+      proxy?.$modal.msgSuccess('删除成功');
+    })
+    .catch(() => {});
+};
+
+/** 提交表单 */
+const handleSubmit = async () => {
+  if (!formRef.value) return;
+
+  await formRef.value.validate(async (valid) => {
+    if (valid) {
+      buttonLoading.value = true;
+      try {
+        // 组装发票列表数据
+        form.value.invoiceList = invoiceList.value;
+
+        if (isEdit.value) {
+          await updateStatementInvoice(form.value);
+          proxy?.$modal.msgSuccess('修改成功');
+        } else {
+          await addStatementInvoice(form.value);
+          proxy?.$modal.msgSuccess('新增成功');
+        }
+        drawer.visible = false;
+        emit('success');
+      } catch (error) {
+        console.error(error);
+      } finally {
+        buttonLoading.value = false;
+      }
+    }
+  });
+};
+
+/** 关闭抽屉前的回调 */
+const handleDrawerClose = (done: () => void) => {
+  if (buttonLoading.value) {
+    return;
+  }
+  done();
+  reset();
+};
+
+const emit = defineEmits(['success']);
+
+defineExpose({
+  open
+});
+</script>
+
+<style scoped lang="scss">
+.drawer-header {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+
+  .order-title {
+    font-size: 16px;
+    font-weight: 600;
+    color: #303133;
+  }
+}
+
+.drawer-content {
+  padding: 0 20px 20px;
+  height: calc(100% - 60px);
+  overflow-y: auto;
+}
+
+.empty-data {
+  text-align: center;
+  padding: 20px 0;
+}
+
+.drawer-footer {
+  display: flex;
+  justify-content: flex-end;
+  gap: 10px;
+}
+</style>

+ 247 - 0
src/views/bill/statementInvoice/addInvoiceDialog.vue

@@ -0,0 +1,247 @@
+<template>
+  <!-- 新增发票对话框 -->
+  <el-dialog v-model="dialog.visible" :title="dialog.title" width="800px" :before-close="handleClose">
+    <el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
+      <!-- 发票信息 -->
+      <el-divider content-position="left">
+        <span style="color: #409eff; font-weight: 600">发票信息</span>
+      </el-divider>
+
+      <el-row :gutter="20">
+        <el-col :span="12">
+          <el-form-item label="发票类型" prop="invoiceType">
+            <el-select v-model="form.invoiceType" placeholder="请选择发票类型" clearable style="width: 100%">
+              <el-option v-for="dict in invoice_type" :key="dict.value" :label="dict.label" :value="dict.value" />
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="发票代码" prop="invoiceCode">
+            <el-input v-model="form.invoiceCode" placeholder="请填写发票代码" clearable />
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <el-row :gutter="20">
+        <el-col :span="12">
+          <el-form-item label="开票公司名称" prop="invoiceCompanyName">
+            <el-input v-model="form.invoiceCompanyName" placeholder="请填写开票公司名称" clearable />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="发票金额" prop="invoiceAmount">
+            <el-input-number v-model="form.invoiceAmount" :controls="false" clearable style="width: 100%"> </el-input-number>
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <el-row :gutter="20">
+        <el-col :span="12">
+          <el-form-item label="开票日期" prop="invoiceDate">
+            <el-date-picker v-model="form.invoiceDate" type="date" placeholder="请选择开票日期" style="width: 100%" value-format="YYYY-MM-DD" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <el-row :gutter="20">
+        <el-col :span="24">
+          <el-form-item label="发票附件" prop="invoiceAttachment">
+            <el-button type="primary" size="small" @click="handleOpenFileSelector">
+              <el-icon><Upload /></el-icon>
+              点击上传
+            </el-button>
+            <div style="color: #999; font-size: 12px; margin-top: 5px">支持jpg/png/xlsx等文件</div>
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <!-- 附件列表 -->
+      <el-row :gutter="20" v-if="fileList.length > 0">
+        <el-col :span="24">
+          <el-table :data="fileList" border style="width: 100%; margin-top: 10px">
+            <el-table-column type="index" label="序号" width="60" align="center" />
+            <el-table-column prop="name" label="附件名称" min-width="200" align="center" />
+            <el-table-column label="操作" width="150" align="center">
+              <template #default="scope">
+                <el-button link type="primary" size="small" @click="handlePreviewFile(scope.row)">预览</el-button>
+                <el-button link type="danger" size="small" @click="handleRemoveFile(scope.$index)">删除</el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+        </el-col>
+      </el-row>
+    </el-form>
+
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button @click="handleClose">取消</el-button>
+        <el-button type="primary" :loading="buttonLoading" @click="handleSubmit">提交</el-button>
+      </div>
+    </template>
+  </el-dialog>
+
+  <!-- 文件选择器 -->
+  <FileSelector v-model="fileSelectorVisible" :multiple="true" :allowed-types="[1, 2, 3, 4, 5]" title="选择发票附件" @confirm="handleFileSelected" />
+</template>
+
+<script setup name="AddInvoiceDialog" lang="ts">
+import { Upload } from '@element-plus/icons-vue';
+import FileSelector from '@/components/FileSelector/index.vue';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { invoice_type } = toRefs<any>(proxy?.useDict('invoice_type'));
+interface InvoiceForm {
+  invoiceType?: string;
+  invoiceCode?: string;
+  invoiceCompanyName?: string;
+  invoiceAmount?: number;
+  invoiceDate?: string;
+  invoiceAttachment?: string;
+}
+
+const initFormData: InvoiceForm = {
+  invoiceType: undefined,
+  invoiceCode: undefined,
+  invoiceCompanyName: undefined,
+  invoiceAmount: undefined,
+  invoiceDate: undefined,
+  invoiceAttachment: undefined
+};
+
+const formRef = ref<ElFormInstance>();
+const buttonLoading = ref(false);
+const form = ref<InvoiceForm>({ ...initFormData });
+const fileList = ref<any[]>([]);
+const fileSelectorVisible = ref(false);
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: '发票信息'
+});
+
+const rules = reactive({
+  invoiceType: [{ required: true, message: '请选择发票类型', trigger: 'change' }],
+  invoiceCode: [{ required: true, message: '请填写发票代码', trigger: 'blur' }],
+  invoiceCompanyName: [{ required: true, message: '请填写开票公司名称', trigger: 'blur' }],
+  invoiceAmount: [
+    { required: true, message: '请填写发票金额', trigger: 'blur' },
+    {
+      pattern: /^(0|[1-9]\d*)(\.\d{1,2})?$/ as RegExp,
+      message: '请输入正确的金额格式,最多保留两位小数',
+      trigger: 'blur'
+    }
+  ]
+  // invoiceDate: [{ required: true, message: '请选择开票日期', trigger: 'change' }]
+} as any);
+
+/** 打开对话框 */
+const open = (data?: InvoiceForm) => {
+  reset();
+
+  if (data) {
+    // 编辑模式
+    Object.assign(form.value, data);
+
+    // 解析附件地址并回显附件列表
+    if (data.invoiceAttachment) {
+      const urls = data.invoiceAttachment.split(',').filter((url) => url.trim());
+      fileList.value = urls.map((url, index) => {
+        const fileName = url.split('/').pop() || `附件${index + 1}`;
+        return {
+          name: fileName,
+          url: url.trim(),
+          id: undefined
+        };
+      });
+    }
+  }
+
+  dialog.visible = true;
+};
+
+/** 重置表单 */
+const reset = () => {
+  form.value = { ...initFormData };
+  fileList.value = [];
+  formRef.value?.clearValidate();
+};
+
+/** 打开文件选择器 */
+const handleOpenFileSelector = () => {
+  fileSelectorVisible.value = true;
+};
+
+/** 文件选择完成 */
+const handleFileSelected = (files: any[]) => {
+  if (files && files.length > 0) {
+    files.forEach((file) => {
+      fileList.value.push({
+        name: file.fileName || file.name,
+        url: file.fileUrl || file.url,
+        id: file.id
+      });
+    });
+    form.value.invoiceAttachment = fileList.value.map((f) => f.url || f.name).join(',');
+  }
+  fileSelectorVisible.value = false;
+};
+
+/** 删除文件 */
+const handleRemoveFile = (index: number) => {
+  fileList.value.splice(index, 1);
+  form.value.invoiceAttachment = fileList.value.map((f) => f.url || f.name).join(',');
+};
+
+/** 预览文件 */
+const handlePreviewFile = (file: any) => {
+  if (!file.url) {
+    proxy?.$modal.msgWarning('文件地址不存在');
+    return;
+  }
+  window.open(file.url, '_blank');
+};
+
+/** 提交表单 */
+const handleSubmit = async () => {
+  if (!formRef.value) return;
+
+  await formRef.value.validate(async (valid) => {
+    if (valid) {
+      buttonLoading.value = true;
+      try {
+        // 返回发票数据给父组件
+        emit('success', form.value);
+        dialog.visible = false;
+        proxy?.$modal.msgSuccess('添加成功');
+      } catch (error) {
+        console.error(error);
+      } finally {
+        buttonLoading.value = false;
+      }
+    }
+  });
+};
+
+/** 关闭对话框 */
+const handleClose = () => {
+  if (buttonLoading.value) {
+    return;
+  }
+  dialog.visible = false;
+  reset();
+};
+
+const emit = defineEmits(['success']);
+
+defineExpose({
+  open
+});
+</script>
+
+<style scoped lang="scss">
+.dialog-footer {
+  display: flex;
+  justify-content: flex-end;
+  gap: 10px;
+}
+</style>

+ 332 - 0
src/views/bill/statementInvoice/detailDrawer.vue

@@ -0,0 +1,332 @@
+<template>
+  <!-- 发票详情 -->
+  <el-drawer v-model="drawer.visible" size="75%" direction="rtl" :before-close="handleDrawerClose" :close-on-click-modal="true">
+    <template #header>
+      <div class="drawer-header">
+        <span class="order-title">发票详情</span>
+      </div>
+    </template>
+
+    <div class="drawer-content">
+      <el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
+        <!-- 基本信息 -->
+        <el-divider content-position="left">
+          <span style="color: #409eff; font-weight: 600">基本信息</span>
+        </el-divider>
+
+        <el-row :gutter="20">
+          <el-col :span="8">
+            <el-form-item label="开票单号:" prop="statementInvoiceNo">
+              <el-input v-model="form.statementInvoiceNo" disabled />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="开票状态:" prop="invoiceStatus">
+              <dict-tag :options="invoice_status" :value="form.invoiceStatus" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="客户名称:" prop="customerName">
+              <el-input v-model="form.customerName" disabled />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="8">
+            <el-form-item label="开票金额:" prop="invoiceAmount">
+              <el-input v-model="form.invoiceAmount" disabled />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="开票日期:" prop="invoiceTime">
+              <el-date-picker
+                v-model="form.invoiceTime"
+                type="date"
+                placeholder="请选择开票日期"
+                style="width: 100%"
+                value-format="YYYY-MM-DD"
+                disabled
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <!-- 账单列表 -->
+        <el-divider content-position="left">
+          <span style="color: #409eff; font-weight: 600">账单列表</span>
+        </el-divider>
+
+        <el-table :data="form.detailList" border style="width: 100%; margin-bottom: 20px">
+          <el-table-column type="index" label="序号" width="60" align="center" />
+          <el-table-column prop="statementOrderNo" label="对账单编号" min-width="150" align="center" />
+          <el-table-column prop="statementAmount" label="对账金额" min-width="120" align="center" />
+          <el-table-column prop="orderNo" label="订单编号" min-width="150" align="center" />
+          <el-table-column prop="orderAmount" label="金额" min-width="120" align="center" />
+          <el-table-column prop="orderTime" label="下单日期" min-width="120" align="center" />
+          <el-table-column prop="signingDate" label="签收日期" min-width="120" align="center" />
+          <el-table-column prop="userName" label="下单人" min-width="100" align="center" />
+          <el-table-column prop="userDept" label="部门" min-width="120" align="center" />
+        </el-table>
+
+        <!-- 商品清单 -->
+        <el-divider content-position="left">
+          <span style="color: #409eff; font-weight: 600">商品清单</span>
+        </el-divider>
+
+        <el-table :data="pagedProductList" border style="width: 100%; margin-bottom: 20px">
+          <el-table-column
+            type="index"
+            label="序号"
+            width="60"
+            align="center"
+            :index="(index) => (productPage.pageNum - 1) * productPage.pageSize + index + 1"
+          />
+          <el-table-column prop="orderNo" label="订单编号" min-width="120" align="center" />
+          <el-table-column prop="productNo" label="商品编号" min-width="120" align="center" />
+          <el-table-column prop="itemName" label="商品名称" min-width="180" align="center" />
+          <el-table-column prop="specifications" label="规格型号" min-width="120" align="center" />
+          <el-table-column prop="unitName" label="单位" align="center" />
+          <el-table-column prop="quantity" label="数量" align="center" />
+          <el-table-column prop="unitPrice" label="单价" align="center">
+            <template #default="scope">
+              {{ scope.row.unitPrice ? Number(scope.row.unitPrice).toFixed(2) : '0.00' }}
+            </template>
+          </el-table-column>
+          <el-table-column label="金额" align="center">
+            <template #default="scope">
+              {{ (Number(scope.row.quantity || 0) * Number(scope.row.unitPrice || 0)).toFixed(2) }}
+            </template>
+          </el-table-column>
+          <el-table-column label="小计" align="center">
+            <template #default="scope">
+              {{ (Number(scope.row.quantity || 0) * Number(scope.row.unitPrice || 0)).toFixed(2) }}
+            </template>
+          </el-table-column>
+        </el-table>
+
+        <!-- 分页 -->
+        <el-pagination
+          v-if="form.productList && form.productList.length > 0"
+          v-model:current-page="productPage.pageNum"
+          v-model:page-size="productPage.pageSize"
+          :page-sizes="[10, 20, 50, 100]"
+          layout="total, sizes, prev, pager, next, jumpers"
+          :total="productPage.total"
+          @size-change="handleProductSizeChange"
+          @current-change="handleProductCurrentChange"
+          style="margin-top: 15px"
+        />
+
+        <!-- 发票列表 -->
+        <el-divider content-position="left">
+          <span style="color: #409eff; font-weight: 600">发票列表</span>
+        </el-divider>
+
+        <el-table :data="form.invoiceList" border style="width: 100%; margin-bottom: 20px">
+          <el-table-column type="index" label="序号" width="80" align="center" />
+          <el-table-column prop="invoiceType" label="发票类型" min-width="120" align="center">
+            <template #default="scope">
+              <dict-tag :options="invoice_type" :value="scope.row.invoiceType" />
+            </template>
+          </el-table-column>
+          <el-table-column prop="invoiceDate" label="开票日期" min-width="120" align="center" />
+          <el-table-column prop="invoiceCode" label="发票代码" min-width="150" align="center" />
+          <el-table-column prop="invoiceAmount" label="发票金额" min-width="120" align="center">
+            <template #default="scope">
+              {{ Number(scope.row.invoiceAmount || 0).toFixed(2) }}
+            </template>
+          </el-table-column>
+          <el-table-column label="操作" width="160" align="center">
+            <template #default="scope">
+              <el-button type="primary" link @click="handleDownloadFile(scope.row)">下载</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+      </el-form>
+    </div>
+
+    <template #footer>
+      <div class="drawer-footer">
+        <el-button @click="handleDrawerClose(() => (drawer.visible = false))">取消</el-button>
+        <el-button type="primary" :loading="buttonLoading" @click="handleDrawerClose(() => (drawer.visible = false))">提交</el-button>
+      </div>
+    </template>
+  </el-drawer>
+</template>
+
+<script setup name="InvoiceDetailDrawer" lang="ts">
+import { StatementInvoiceForm } from '@/api/bill/statementInvoice/types';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { invoice_type, invoice_status } = toRefs<any>(proxy?.useDict('invoice_type', 'invoice_status'));
+const initFormData: StatementInvoiceForm = {
+  id: undefined,
+  statementInvoiceNo: undefined,
+  customerId: undefined,
+  customerNo: undefined,
+  customerName: undefined,
+  invoiceAmount: undefined,
+  invoiceStatus: undefined,
+  invoiceTime: undefined,
+  rejectRemark: undefined,
+  remark: undefined,
+  detailList: [],
+  productList: []
+};
+
+const formRef = ref<ElFormInstance>();
+const buttonLoading = ref(false);
+const form = ref<StatementInvoiceForm>({ ...initFormData });
+
+const rules = reactive({
+  customerId: [{ required: true, message: '请选择客户', trigger: 'change' }],
+  invoiceTime: [{ required: true, message: '请选择开票日期', trigger: 'change' }]
+} as any);
+
+const fileList = ref<any[]>([]);
+
+const productPage = reactive({
+  pageNum: 1,
+  pageSize: 10,
+  total: 0
+});
+
+const drawer = reactive<DialogOption>({
+  visible: false,
+  title: '发票详情'
+});
+
+/** 计算当前页的商品列表 */
+const pagedProductList = computed(() => {
+  const start = (productPage.pageNum - 1) * productPage.pageSize;
+  const end = start + productPage.pageSize;
+  return form.value.productList?.slice(start, end) || [];
+});
+
+/** 打开发票详情抽屉 */
+const open = (id?: string | number, data?: StatementInvoiceForm) => {
+  reset();
+
+  if (id && data) {
+    // 查看模式
+    drawer.title = '发票详情';
+    Object.assign(form.value, data);
+
+    // 解析附件地址并回显附件列表
+    if (data.annexAddress) {
+      const urls = data.annexAddress.split(',').filter((url) => url.trim());
+      fileList.value = urls.map((url, index) => {
+        const fileName = url.split('/').pop() || `附件${index + 1}`;
+        return {
+          name: fileName,
+          url: url.trim(),
+          id: undefined
+        };
+      });
+    }
+
+    // 设置商品列表分页总数
+    if (data.productList && data.productList.length > 0) {
+      productPage.total = data.productList.length;
+    }
+  }
+
+  drawer.visible = true;
+};
+
+/** 表单重置 */
+const reset = () => {
+  form.value = { ...initFormData };
+  fileList.value = [];
+  productPage.pageNum = 1;
+  productPage.pageSize = 10;
+  productPage.total = 0;
+  formRef.value?.clearValidate();
+};
+
+/** 下载文件 */
+const handleDownloadFile = async (file: any) => {
+  if (!file.invoiceAnnex) {
+    proxy?.$modal.msgWarning('文件地址不存在');
+    return;
+  }
+
+  try {
+    const response = await fetch(file.invoiceAnnex);
+    const blob = await response.blob();
+    const blobUrl = window.URL.createObjectURL(blob);
+    const link = document.createElement('a');
+    link.href = blobUrl;
+    link.download = file.name || '附件';
+    document.body.appendChild(link);
+    link.click();
+    document.body.removeChild(link);
+    window.URL.revokeObjectURL(blobUrl);
+  } catch (error) {
+    console.error('下载失败:', error);
+    const link = document.createElement('a');
+    link.href = file.invoiceAnnex;
+    link.download = file.name || '附件';
+    link.target = '_blank';
+    document.body.appendChild(link);
+    link.click();
+    document.body.removeChild(link);
+  }
+};
+
+/** 产品分页大小改变 */
+const handleProductSizeChange = (val: number) => {
+  productPage.pageSize = val;
+};
+
+/** 产品分页页码改变 */
+const handleProductCurrentChange = (val: number) => {
+  productPage.pageNum = val;
+};
+
+/** 关闭抽屉前的回调 */
+const handleDrawerClose = (done: () => void) => {
+  if (buttonLoading.value) {
+    return;
+  }
+  done();
+  reset();
+};
+
+const emit = defineEmits(['success']);
+
+defineExpose({
+  open
+});
+</script>
+
+<style scoped lang="scss">
+.drawer-header {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+
+  .order-title {
+    font-size: 16px;
+    font-weight: 600;
+    color: #303133;
+  }
+}
+
+.drawer-content {
+  padding: 0 20px 20px;
+  height: calc(100% - 60px);
+  overflow-y: auto;
+}
+
+.empty-data {
+  text-align: center;
+  padding: 20px 0;
+}
+
+.drawer-footer {
+  display: flex;
+  justify-content: flex-end;
+  gap: 10px;
+}
+</style>

+ 322 - 0
src/views/bill/statementInvoice/index.vue

@@ -0,0 +1,322 @@
+<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="statementInvoiceNo">
+              <el-input v-model="queryParams.statementInvoiceNo" placeholder="请输入发票编号" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="开票日期" prop="statementDate">
+              <el-date-picker v-model="dateRange" type="daterange" range-separator="至" start-placeholder="开始时间" end-placeholder="结束时间" />
+            </el-form-item>
+            <el-form-item label="客户名称" prop="customerId">
+              <el-select
+                v-model="queryParams.customerId"
+                filterable
+                remote
+                reserve-keyword
+                placeholder="请输入客户名称"
+                :remote-method="remoteSearchCustomer"
+                :loading="customerLoading"
+                clearable
+                style="width: 100%"
+                @change="handleCustomerChange"
+              >
+                <el-option v-for="item in customerOptions" :key="item.id" :label="item.customerName" :value="item.id" />
+              </el-select>
+            </el-form-item>
+            <el-form-item label="订单编号" prop="orderNo">
+              <el-input v-model="queryParams.orderNo" placeholder="请输入订单编号" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="开票状态" prop="invoiceStatus">
+              <el-select v-model="queryParams.invoiceStatus" placeholder="请选择开票状态" clearable>
+                <el-option v-for="dict in invoice_status" :key="dict.value" :label="dict.label" :value="dict.value" />
+              </el-select>
+            </el-form-item>
+            <el-form-item label="对账单号" prop="statementOrderNo">
+              <el-input v-model="queryParams.statementOrderNo" 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="22"> 销售发票信息列表 </el-col>
+          <el-col :span="1.5">
+            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['bill:statementInvoice:add']">新增</el-button>
+          </el-col>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" border :data="statementInvoiceList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="开票编号" align="center" prop="statementInvoiceNo" />
+        <el-table-column label="开票时间" align="center" prop="invoiceTime">
+          <template #default="scope">
+            <span>{{ parseTime(scope.row.invoiceTime, '{y}-{m}-{d}') }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="客户名称" align="center" prop="customerName" />
+        <el-table-column label="开票金额" align="center" prop="invoiceAmount">
+          <template #default="scope">
+            <span>{{ Number(scope.row.invoiceAmount).toFixed(2) }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="开票状态" align="center" prop="invoiceStatus">
+          <template #default="scope">
+            <dict-tag :options="invoice_status" :value="scope.row.invoiceStatus" />
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right">
+          <template #default="scope">
+            <el-button link type="primary" @click="handleDetail(scope.row)" v-hasPermi="['bill:statementInvoice:edit']">查看</el-button>
+            <el-button
+              link
+              type="primary"
+              @click="handleUpdate(scope.row)"
+              v-if="scope.row.statementStatus != 4"
+              v-hasPermi="['bill:statementInvoice:edit']"
+              >编辑</el-button
+            >
+            <el-button
+              link
+              type="primary"
+              @click="handleSend(scope.row)"
+              v-if="scope.row.statementStatus != 4"
+              v-hasPermi="['bill:statementInvoice:edit']"
+              >发送对账单</el-button
+            >
+            <el-button
+              link
+              type="primary"
+              @click="handleWithdraw(scope.row)"
+              v-if="scope.row.statementStatus != 4"
+              v-hasPermi="['bill:statementInvoice:edit']"
+              >撤回</el-button
+            >
+            <el-button
+              link
+              type="primary"
+              @click="handleObsolete(scope.row)"
+              v-if="scope.row.statementStatus != 4"
+              v-hasPermi="['bill:statementInvoice:remove']"
+              >作废</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>
+    <!-- 新增开票的抽屉 -->
+    <add-drawer ref="addDrawerRef" @success="getList" />
+    <!-- 查看开票信息的抽屉 -->
+    <detail-drawer ref="detailDrawerRef" @success="getList" />
+  </div>
+</template>
+
+<script setup name="StatementInvoice" lang="ts">
+import { listStatementInvoice, getStatementInvoice, changeStatus } from '@/api/bill/statementInvoice';
+import { StatementInvoiceVO, StatementInvoiceQuery, StatementInvoiceForm } from '@/api/bill/statementInvoice/types';
+import { getListBycustomerName } from '@/api/customer/customerFile/customerInfo';
+import { CustomerInfoVO } from '@/api/customer/customerFile/customerInfo/types';
+import AddDrawer from './addDrawer.vue';
+import DetailDrawer from './detailDrawer.vue';
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { invoice_status } = toRefs<any>(proxy?.useDict('invoice_status'));
+const addDrawerRef = ref<InstanceType<typeof AddDrawer>>();
+const detailDrawerRef = ref<InstanceType<typeof DetailDrawer>>();
+const statementInvoiceList = ref<StatementInvoiceVO[]>([]);
+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 dateRange = ref<[DateModelType, DateModelType]>(['', '']);
+const queryFormRef = ref<ElFormInstance>();
+const statementInvoiceFormRef = ref<ElFormInstance>();
+const customerLoading = ref(false);
+const customerOptions = ref<CustomerInfoVO[]>([]);
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: StatementInvoiceForm = {
+  id: undefined,
+  statementInvoiceNo: undefined,
+  customerId: undefined,
+  customerNo: undefined,
+  customerName: undefined,
+  invoiceAmount: undefined,
+  invoiceStatus: undefined,
+  invoiceTime: undefined,
+  rejectRemark: undefined,
+  remark: undefined
+};
+const data = reactive<PageData<StatementInvoiceForm, StatementInvoiceQuery>>({
+  form: { ...initFormData },
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    statementInvoiceNo: undefined,
+    customerId: undefined,
+    customerNo: undefined,
+    customerName: undefined,
+    invoiceAmount: undefined,
+    invoiceStatus: undefined,
+    invoiceTime: undefined,
+    rejectRemark: undefined,
+    platformCode: undefined,
+    params: {}
+  },
+  rules: {}
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 查询销售发票主列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listStatementInvoice(proxy?.addDateRange(queryParams.value, dateRange.value));
+  statementInvoiceList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+};
+
+/** 远程搜索客户 */
+const remoteSearchCustomer = async (query: string) => {
+  if (query) {
+    customerLoading.value = true;
+    try {
+      const res = await getListBycustomerName(query);
+      customerOptions.value = res.data;
+    } catch (error) {
+      console.error(error);
+    } finally {
+      customerLoading.value = false;
+    }
+  } else {
+    customerOptions.value = [];
+  }
+};
+
+/** 客户变更 */
+const handleCustomerChange = async (customerId: string | number) => {
+  if (customerId) {
+    const customer = customerOptions.value.find((item) => item.id === customerId);
+    if (customer) {
+      form.value.customerName = customer.customerName;
+      form.value.customerNo = customer.customerNo;
+    }
+  } else {
+    form.value.customerName = undefined;
+    form.value.customerNo = undefined;
+  }
+};
+
+/** 表单重置 */
+const reset = () => {
+  form.value = { ...initFormData };
+  statementInvoiceFormRef.value?.resetFields();
+};
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+};
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  dateRange.value = ['', ''];
+  queryFormRef.value?.resetFields();
+  handleQuery();
+};
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection: StatementInvoiceVO[]) => {
+  ids.value = selection.map((item) => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+};
+
+/** 新增按钮操作 */
+const handleAdd = () => {
+  reset();
+  // 打开抽屉
+  addDrawerRef.value?.open();
+};
+
+/** 修改按钮操作 */
+const handleUpdate = async (row?: StatementInvoiceVO) => {
+  const _id = row?.id || ids.value[0];
+  const res = await getStatementInvoice(_id);
+  // 打开抽屉,传递id和数据
+  addDrawerRef.value?.open(_id, res.data);
+};
+
+/** 查看按钮操作 */
+const handleDetail = async (row?: StatementInvoiceVO) => {
+  const _id = row?.id || ids.value[0];
+  const res = await getStatementInvoice(_id);
+  // 打开抽屉,传递id和数据
+  detailDrawerRef.value?.open(_id, res.data);
+};
+
+/** 发送按钮操作 */
+const handleSend = async (row?: StatementInvoiceVO) => {
+  const _no = row?.statementInvoiceNo;
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('是否发送当前发票编号【' + _no + '】吗?').finally(() => (loading.value = false));
+  //todo 发送
+  proxy?.$modal.msgSuccess('发送成功');
+  await getList();
+};
+
+/** 撤回按钮操作 */
+const handleWithdraw = async (row?: StatementInvoiceVO) => {
+  const _no = row?.statementInvoiceNo;
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('是否撤回当前发票编号【' + _no + '】吗?').finally(() => (loading.value = false));
+  //todo 撤回
+  proxy?.$modal.msgSuccess('撤回成功');
+  await getList();
+};
+
+/** 作废按钮操作 */
+const handleObsolete = async (row?: StatementInvoiceVO) => {
+  const _no = row?.statementInvoiceNo;
+  const oldValue = row.invoiceStatus; // 保存旧值
+  await proxy?.$modal.confirm('是否作废当前发票编号【' + _no + '】吗?').finally(() => (loading.value = false));
+  await changeStatus(row.id, '4'); // 传新值 4 - 作废
+  proxy?.$modal.msgSuccess('作废成功');
+  await getList();
+};
+
+/** 导出按钮操作 */
+const handleExport = () => {
+  proxy?.download(
+    'bill/statementInvoice/export',
+    {
+      ...queryParams.value
+    },
+    `statementInvoice_${new Date().getTime()}.xlsx`
+  );
+};
+
+onMounted(() => {
+  getList();
+});
+</script>

+ 277 - 0
src/views/bill/statementInvoice/statementOrderDrawer.vue

@@ -0,0 +1,277 @@
+<template>
+  <!-- 对账单选择抽屉 -->
+  <el-drawer v-model="drawerVisible" size="70%" direction="rtl" :close-on-click-modal="true" :close-on-press-escape="true" destroy-on-close>
+    <template #header>
+      <div class="drawer-header">
+        <span class="order-title">对账单信息</span>
+      </div>
+    </template>
+
+    <div class="drawer-content">
+      <!-- 对账单明细列表 -->
+      <el-table ref="tableRef" :data="tableData" border @selection-change="handleSelectionChange" style="width: 100%" v-loading="loading">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column prop="statementOrderNo" label="对账单号" min-width="140" align="center" />
+        <el-table-column prop="statementAmount" label="对账金额" align="center">
+          <template #default="scope">
+            {{ Number(scope.row.statementAmount || 0).toFixed(2) }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="type" label="订单类型" min-width="100" align="center">
+          <template #default="scope">
+            {{ scope.row.type === '1' ? '销售订单' : scope.row.type || '-' }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="orderNo" label="订单编号" min-width="140" align="center" />
+        <el-table-column prop="amount" label="金额" min-width="100" align="center">
+          <template #default="scope">
+            {{ Number(scope.row.amount || 0).toFixed(2) }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="orderTime" label="下单日期" min-width="110" align="center" />
+        <el-table-column prop="signingDate" label="签收日期" min-width="110" align="center">
+          <template #default="scope">
+            {{ scope.row.signingDate || '-' }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="userName" label="下单人" min-width="100" align="center" />
+        <el-table-column prop="userDept" label="部门" min-width="120" align="center" />
+      </el-table>
+
+      <!-- 分页 -->
+      <el-pagination
+        v-model:current-page="queryParams.pageNum"
+        v-model:page-size="queryParams.pageSize"
+        :page-sizes="[10, 20, 50, 100]"
+        layout="total, sizes, prev, pager, next, jumper"
+        :total="total"
+        @size-change="handleSizeChange"
+        @current-change="handleCurrentChange"
+        style="margin-top: 20px"
+      />
+    </div>
+
+    <template #footer>
+      <div class="drawer-footer">
+        <el-button @click="handleCancel">取消</el-button>
+        <el-button type="primary" @click="handleConfirm">确定</el-button>
+      </div>
+    </template>
+  </el-drawer>
+</template>
+
+<script setup name="StatementOrderDrawer" lang="ts">
+import { getCustomerOrderDetails, getStatementProductList } from '@/api/bill/statementOrder';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+// 抽屉显示状态
+const drawerVisible = ref(false);
+const loading = ref(false);
+const tableRef = ref<any>();
+const tableData = ref<any[]>([]);
+const selectedStatements = ref<any[]>([]);
+const total = ref(0);
+const customerId = ref<string | number>();
+
+const queryParams = ref({
+  pageNum: 1,
+  pageSize: 10
+});
+
+const mergedProductList = ref<any[]>([]);
+
+/** 打开抽屉 */
+const open = (customerIdParam: string | number, preSelectedStatements?: any[]) => {
+  customerId.value = customerIdParam;
+  selectedStatements.value = preSelectedStatements || [];
+  drawerVisible.value = true;
+
+  // 查询数据
+  loadData();
+};
+
+/** 加载数据 */
+const loadData = async () => {
+  if (!customerId.value) {
+    console.error('customerId为空');
+    return;
+  }
+
+  loading.value = true;
+  try {
+    const res = await getCustomerOrderDetails(customerId.value, {
+      pageNum: queryParams.value.pageNum,
+      pageSize: queryParams.value.pageSize
+    });
+
+    // 解析数据 - 支持多种返回格式
+    let rows: any[] = [];
+    let totalCount = 0;
+
+    if (res.rows) {
+      rows = res.rows;
+      totalCount = res.total || 0;
+    } else if (res.data && res.data.rows) {
+      rows = res.data.rows;
+      totalCount = res.data.total || 0;
+    } else if (res.data && Array.isArray(res.data)) {
+      rows = res.data;
+      totalCount = res.data.length;
+    } else if (Array.isArray(res)) {
+      rows = res;
+      totalCount = res.length;
+    }
+
+    tableData.value = rows;
+    total.value = totalCount;
+  } catch (error) {
+    console.error('加载数据失败:', error);
+    tableData.value = [];
+    total.value = 0;
+  } finally {
+    loading.value = false;
+  }
+};
+
+/** 表格选择变化 */
+const handleSelectionChange = async (selection: any[]) => {
+  selectedStatements.value = selection;
+  // 合并选中订单的产品列表
+  await mergeProductList();
+};
+
+/** 合并选中订单的产品列表 */
+const mergeProductList = async () => {
+  if (selectedStatements.value.length === 0) {
+    mergedProductList.value = [];
+    return;
+  }
+
+  try {
+    const items = selectedStatements.value
+      .map((order) => ({
+        statementOrderId: order.statementOrderId,
+        orderId: order.orderId
+      }))
+      .filter((item) => item.statementOrderId != null && item.orderId != null);
+
+    if (items.length === 0) {
+      mergedProductList.value = [];
+      return;
+    }
+
+    const res = await getStatementProductList(items);
+    const productList = res.rows || [];
+
+    // 构建产品列表
+    const newProductList: any[] = [];
+    productList.forEach((item: any) => {
+      const product: any = {
+        id: undefined,
+        statementOrderId: undefined,
+        orderNo: item.orderNo || '',
+        productNo: item.productNo || item.skuNo || item.productCode || '',
+        itemName: item.productName || item.skuName || item.itemName || item.productFullName || '',
+        specifications: item.specifications || item.spec || item.model || item.specModel || '',
+        quantity: item.quantity || item.num || item.orderQuantity,
+        unitPrice: item.price || item.unitPrice || item.salePrice || item.orderPrice,
+        productUnit: item.productUnit || item.unitName || '',
+        unitName: item.productUnit || item.unitName || '',
+        type: item.productType || item.type || '',
+        orderId: item.orderId,
+        categoryId: item.categoryId,
+        categoryNo: item.categoryNo || '',
+        productId: item.productId || item.id,
+        unitId: item.productUnitId || item.unitId
+      };
+      newProductList.push(product);
+    });
+
+    // 更新产品列表
+    mergedProductList.value = newProductList;
+  } catch (error) {
+    console.error('获取订单产品列表失败:', error);
+    mergedProductList.value = [];
+  }
+};
+
+/** 确定选择 */
+const handleConfirm = async () => {
+  if (selectedStatements.value.length === 0) {
+    proxy?.$modal.msgWarning('请至少选择一条订单明细');
+    return;
+  }
+
+  // 传递订单列表、明细列表和产品列表
+  emit('success', {
+    statements: selectedStatements.value,
+    details: selectedStatements.value,
+    products: mergedProductList.value
+  });
+  drawerVisible.value = false;
+};
+
+/** 分页大小改变 */
+const handleSizeChange = (val: number) => {
+  queryParams.value.pageSize = val;
+  queryParams.value.pageNum = 1;
+  loadData();
+};
+
+/** 分页页码改变 */
+const handleCurrentChange = (val: number) => {
+  queryParams.value.pageNum = val;
+  loadData();
+};
+
+/** 取消 */
+const handleCancel = () => {
+  drawerVisible.value = false;
+};
+
+/** 重置数据 */
+const resetData = () => {
+  tableData.value = [];
+  selectedStatements.value = [];
+  mergedProductList.value = [];
+  total.value = 0;
+  customerId.value = undefined;
+  queryParams.value = {
+    pageNum: 1,
+    pageSize: 10
+  };
+};
+
+const emit = defineEmits(['success']);
+
+defineExpose({
+  open
+});
+</script>
+
+<style scoped lang="scss">
+.drawer-header {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+
+  .order-title {
+    font-size: 16px;
+    font-weight: 600;
+    color: #303133;
+  }
+}
+
+.drawer-content {
+  padding: 0 20px 20px;
+  height: calc(100% - 60px);
+  overflow-y: auto;
+}
+
+.drawer-footer {
+  display: flex;
+  justify-content: flex-end;
+  gap: 10px;
+}
+</style>

+ 791 - 0
src/views/bill/statementOrder/addDrawer.vue

@@ -0,0 +1,791 @@
+<template>
+  <!-- 新增对账详情抽屉 -->
+  <el-drawer v-model="drawer.visible" size="75%" direction="rtl" :before-close="handleDrawerClose" :close-on-click-modal="true">
+    <template #header>
+      <div class="drawer-header">
+        <span class="order-title">新增对账</span>
+      </div>
+    </template>
+
+    <div class="drawer-content">
+      <el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
+        <!-- 基本信息 -->
+        <el-divider content-position="left">
+          <span style="color: #409eff; font-weight: 600">基本信息</span>
+        </el-divider>
+
+        <el-row :gutter="20">
+          <el-col :span="8" v-if="isEdit">
+            <el-form-item label="对账单号" prop="statementOrderNo">
+              <el-input v-model="form.statementOrderNo" disabled />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8" v-if="isEdit">
+            <el-form-item label="对账状态" prop="statementStatus">
+              <el-input v-model="form.statementStatus" disabled />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="客户名称" prop="customerId">
+              <el-select
+                v-model="form.customerId"
+                filterable
+                remote
+                reserve-keyword
+                placeholder="请输入客户名称"
+                :remote-method="remoteSearchCustomer"
+                :loading="customerLoading"
+                clearable
+                style="width: 100%"
+                @change="handleCustomerChange"
+              >
+                <el-option v-for="item in customerOptions" :key="item.id" :label="item.customerName" :value="item.id" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <el-col :span="8">
+            <el-form-item label="对账单金额" prop="amount">
+              <el-input v-model="form.amount" disabled />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="对账人" prop="statementSelfId">
+              <el-select
+                v-model="form.statementSelfId"
+                placeholder="请选择对账人"
+                clearable
+                filterable
+                style="width: 100%"
+                @change="handleStaffChange"
+              >
+                <el-option v-for="staff in staffOptions" :key="staff.staffId" :label="staff.staffName" :value="staff.staffId" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="对账人联系方式" prop="statementSelfPhone">
+              <el-input v-model="form.statementSelfPhone" placeholder="请填写对账人联系方式" clearable />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <el-col :span="8">
+            <el-form-item label="对账日期" prop="statementDate">
+              <el-date-picker v-model="form.statementDate" type="date" placeholder="请选择对账日期" style="width: 100%" value-format="YYYY-MM-DD" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="支付状态" prop="isPaymentStatus">
+              <el-select v-model="form.isPaymentStatus" placeholder="请选择支付状态" clearable style="width: 100%">
+                <el-option v-for="dict in payment_status" :key="dict.value" :label="dict.label" :value="dict.value" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="发票状态" prop="isInvoiceStatus">
+              <el-select v-model="form.isInvoiceStatus" placeholder="请选择发票状态" clearable style="width: 100%">
+                <el-option v-for="dict in invoice_issuance_status" :key="dict.value" :label="dict.label" :value="dict.value" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <el-col :span="24">
+            <el-form-item label="备注" prop="remark">
+              <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注" maxlength="500" show-word-limit />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <!-- 对账明细 -->
+        <el-divider content-position="left">
+          <span style="color: #409eff; font-weight: 600">对账明细</span>
+        </el-divider>
+
+        <div class="table-actions" style="margin-bottom: 15px">
+          <el-button type="primary" icon="Plus" @click="handleAddDetail">选择订单</el-button>
+        </div>
+
+        <el-table :data="form.detailList" border style="width: 100%; margin-bottom: 20px">
+          <el-table-column type="index" label="序号" width="60" align="center" />
+          <el-table-column prop="type" label="订单类型" min-width="100" align="center">
+            <template #default="scope">
+              <dict-tag :options="statement_order_type" :value="scope.row.type" />
+            </template>
+          </el-table-column>
+          <el-table-column prop="orderNo" label="订单编号" min-width="150" align="center" />
+          <el-table-column prop="amount" label="金额" min-width="120" align="center" />
+          <el-table-column prop="orderTime" label="下单日期" min-width="120" align="center" />
+          <el-table-column prop="signingDate" label="签收日期" min-width="120" align="center" />
+          <el-table-column prop="userName" label="下单人" min-width="100" align="center" />
+          <el-table-column prop="userDept" label="部门" min-width="120" align="center" />
+        </el-table>
+        <!-- 
+        <div v-if="form.detailList.length === 0" class="empty-data">
+          <el-empty description="暂无数据" :image-size="100" />
+        </div> -->
+
+        <!-- 商品清单 -->
+        <el-divider content-position="left">
+          <span style="color: #409eff; font-weight: 600">商品清单</span>
+        </el-divider>
+
+        <el-table :data="pagedProductList" border style="width: 100%; margin-bottom: 20px">
+          <el-table-column
+            type="index"
+            label="序号"
+            width="60"
+            align="center"
+            :index="(index) => (productPage.pageNum - 1) * productPage.pageSize + index + 1"
+          />
+          <el-table-column prop="orderNo" label="订单编号" min-width="120" align="center" />
+          <el-table-column prop="productNo" label="商品编号" min-width="120" align="center" />
+          <el-table-column prop="itemName" label="商品名称" min-width="180" align="center" />
+          <el-table-column prop="specifications" label="规格型号" min-width="120" align="center" />
+          <el-table-column prop="unitName" label="单位" width="80" align="center" />
+          <el-table-column prop="quantity" label="数量" width="100" align="center" />
+          <el-table-column prop="unitPrice" label="单价" width="100" align="center">
+            <template #default="scope">
+              {{ scope.row.unitPrice ? Number(scope.row.unitPrice).toFixed(2) : '0.00' }}
+            </template>
+          </el-table-column>
+          <el-table-column label="金额" width="120" align="center">
+            <template #default="scope">
+              {{ Number(scope.row.subtotal || 0).toFixed(2) }}
+            </template>
+          </el-table-column>
+          <el-table-column label="小计" width="120" align="center">
+            <template #default="scope">
+              {{ (Number(scope.row.quantity || 0) * Number(scope.row.unitPrice || 0)).toFixed(2) }}
+            </template>
+          </el-table-column>
+        </el-table>
+
+        <!-- 分页 -->
+        <el-pagination
+          v-if="form.productList.length > 0"
+          v-model:current-page="productPage.pageNum"
+          v-model:page-size="productPage.pageSize"
+          :page-sizes="[10, 20, 50, 100]"
+          layout="total, sizes, prev, pager, next, jumpers"
+          :total="productPage.total"
+          @size-change="handleProductSizeChange"
+          @current-change="handleProductCurrentChange"
+          style="margin-top: 15px"
+        />
+
+        <!-- 对账附件 -->
+        <el-divider content-position="left">
+          <span style="color: #409eff; font-weight: 600">对账附件</span>
+        </el-divider>
+
+        <div style="margin-bottom: 10px">
+          <el-button type="primary" icon="Upload" @click="handleOpenFileSelector">点击上传</el-button>
+        </div>
+
+        <el-table :data="fileList" border style="width: 100%; margin-bottom: 20px">
+          <el-table-column type="index" label="序号" width="80" align="center" />
+          <el-table-column prop="name" label="文件名称" min-width="200" align="center" />
+          <el-table-column prop="url" label="文件地址" min-width="250" align="center">
+            <template #default="scope">
+              {{ scope.row.url || '暂无地址' }}
+            </template>
+          </el-table-column>
+          <el-table-column label="操作" width="200" align="center">
+            <template #default="scope">
+              <el-button type="primary" link @click="handleDownloadFile(scope.row)">下载</el-button>
+              <el-button type="primary" link @click="handlePreviewFile(scope.row)">预览</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+        <!-- 
+        <div v-if="fileList.length === 0" class="empty-data">
+          <el-empty description="暂无附件" :image-size="100" />
+        </div> -->
+      </el-form>
+    </div>
+
+    <template #footer>
+      <div class="drawer-footer">
+        <el-button @click="handleDrawerClose(() => (drawer.visible = false))">取消</el-button>
+        <el-button type="primary" :loading="buttonLoading" @click="handleSubmit">确定</el-button>
+      </div>
+    </template>
+  </el-drawer>
+
+  <!-- 客户订单抽屉 -->
+  <OrderMainDrawer ref="orderMainDrawerRef" @success="handleOrderSelected" />
+
+  <!-- 文件选择器 -->
+  <FileSelector v-model="fileSelectorVisible" :multiple="true" :allowed-types="[1, 2, 3, 4, 5]" title="选择对账附件" @confirm="handleFileSelected" />
+</template>
+
+<script setup name="AddDrawer" lang="ts">
+import { addStatementOrder, updateStatementOrder } from '@/api/bill/statementOrder';
+import { StatementOrderForm } from '@/api/bill/statementOrder/types';
+import { StatementDetailForm } from '@/api/bill/statementDetail/types';
+import { getListBycustomerName } from '@/api/customer/customerFile/customerInfo';
+import { CustomerInfoVO } from '@/api/customer/customerFile/customerInfo/types';
+import { listOrderMain, getOrderMain } from '@/api/order/orderMain';
+import { OrderMainVO, OrderMainQuery, OrderMainForm } from '@/api/order/orderMain/types';
+import { listComStaff } from '@/api/company/comStaff';
+import { ComStaffVO, ComStaffQuery } from '@/api/company/comStaff/types';
+import OrderMainDrawer from './orderMainDrawer.vue';
+import FileSelector from '@/components/FileSelector/index.vue';
+import { any } from 'vue-types';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { statement_status, statement_order_type, invoice_issuance_status, payment_status } = toRefs<any>(
+  proxy?.useDict('statement_status', 'statement_order_type', 'invoice_issuance_status', 'payment_status')
+);
+
+const initFormData: StatementOrderForm = {
+  id: undefined,
+  statementOrderNo: undefined,
+  customerId: undefined,
+  customerNo: undefined,
+  customerName: undefined,
+  amount: undefined,
+  statementSelfId: undefined,
+  statementSelf: undefined,
+  statementSelfPhone: undefined,
+  statementStatus: undefined,
+  isPaymentStatus: undefined,
+  isInvoiceStatus: undefined,
+  statementDate: undefined,
+  annexAddress: undefined,
+  rejectRemark: undefined,
+  remark: undefined,
+  detailList: [],
+  productList: []
+};
+
+const emit = defineEmits(['success']);
+
+const formRef = ref<any>();
+const buttonLoading = ref(false);
+
+const form = ref<
+  StatementOrderForm & {
+    actualAmount?: string;
+    accountingVerification?: string;
+    skuProductName?: string;
+    skuProductModel?: string;
+    attachmentRemark?: string;
+  }
+>({ ...initFormData });
+
+const rules = reactive({
+  customerName: [{ required: true, message: '请输入客户名称', trigger: 'blur' }],
+  statementSelfId: [{ required: true, message: '请选择对账人', trigger: 'blur' }],
+  statementSelfPhone: [
+    { required: true, message: '请输入对账人手机号', trigger: 'blur' },
+    { pattern: /^1[3-9]\d{9}$/ as RegExp, message: '请输入正确的手机号格式', trigger: 'blur' }
+  ]
+} as any);
+
+const fileList = ref<any[]>([]);
+
+const productPage = reactive({
+  pageNum: 1,
+  pageSize: 10,
+  total: 0
+});
+
+const drawer = reactive<DialogOption>({
+  visible: false,
+  title: '新增对账'
+});
+
+const isEdit = ref(false); // 是否为编辑模式
+
+const customerLoading = ref(false);
+const customerOptions = ref<CustomerInfoVO[]>([]);
+const staffOptions = ref<ComStaffVO[]>([]);
+const orderMainDrawerRef = ref<any>();
+const fileSelectorRef = ref<any>();
+const fileSelectorVisible = ref(false);
+const currentSelectedOrders = ref<OrderMainVO[]>([]);
+const preloadedOrders = ref<OrderMainVO[]>([]); // 预加载的订单列表
+
+/** 计算当前页的商品列表 */
+const pagedProductList = computed(() => {
+  const start = (productPage.pageNum - 1) * productPage.pageSize;
+  const end = start + productPage.pageSize;
+  return form.value.productList?.slice(start, end) || [];
+});
+const preloadedTotal = ref(0); // 预加载的订单总数
+
+/** 打开新增/编辑对账详情抽屉 */
+const open = (id?: string | number, data?: StatementOrderForm) => {
+  reset();
+
+  if (id && data) {
+    // 编辑模式
+    isEdit.value = true;
+    drawer.title = '编辑对账';
+    Object.assign(form.value, data);
+
+    // 将客户信息添加到下拉选项中,以便回显
+    if (data.customerId && data.customerName) {
+      customerOptions.value = [
+        {
+          id: data.customerId,
+          customerName: data.customerName,
+          customerNo: data.customerNo || ''
+        } as CustomerInfoVO
+      ];
+    }
+
+    // 解析附件地址并回显附件列表
+    if (data.annexAddress) {
+      const urls = data.annexAddress.split(',').filter((url) => url.trim());
+      fileList.value = urls.map((url, index) => {
+        const fileName = url.split('/').pop() || `附件${index + 1}`;
+        return {
+          name: fileName,
+          url: url.trim(),
+          id: undefined
+        };
+      });
+    }
+
+    // 设置商品列表分页总数
+    if (data.productList && data.productList.length > 0) {
+      productPage.total = data.productList.length;
+    }
+  } else {
+    // 新增模式
+    isEdit.value = false;
+    drawer.title = '新增对账';
+  }
+
+  // 先打开抽屉
+  drawer.visible = true;
+
+  // 异步加载员工列表
+  getStaffList();
+
+  // 编辑模式下,如果有客户ID,异步预加载订单列表
+  if (id && data?.customerId) {
+    preloadOrders(data.customerId);
+  }
+};
+
+/** 表单重置 */
+const reset = () => {
+  form.value = { ...initFormData };
+  fileList.value = [];
+  productPage.pageNum = 1;
+  productPage.pageSize = 10;
+  productPage.total = 0;
+  customerOptions.value = [];
+  formRef.value?.clearValidate();
+};
+
+/** 远程搜索客户 */
+const remoteSearchCustomer = async (query: string) => {
+  if (query) {
+    customerLoading.value = true;
+    try {
+      const res = await getListBycustomerName(query);
+      customerOptions.value = res.data;
+    } catch (error) {
+      console.error(error);
+    } finally {
+      customerLoading.value = false;
+    }
+  } else {
+    customerOptions.value = [];
+  }
+};
+
+/** 客户变更 */
+const handleCustomerChange = async (customerId: string | number) => {
+  if (customerId) {
+    const customer = customerOptions.value.find((item) => item.id === customerId);
+    if (customer) {
+      form.value.customerName = customer.customerName;
+      form.value.customerNo = customer.customerNo;
+    }
+    // 预加载订单列表
+    await preloadOrders(customerId);
+  } else {
+    form.value.customerName = undefined;
+    form.value.customerNo = undefined;
+    preloadedOrders.value = [];
+    preloadedTotal.value = 0;
+  }
+};
+
+/** 预加载订单列表 */
+const preloadOrders = async (customerId: string | number) => {
+  try {
+    const params: OrderMainQuery = {
+      pageNum: 1,
+      pageSize: 10,
+      customerId: customerId
+    };
+    const res = await listOrderMain(params);
+    preloadedOrders.value = res.rows || [];
+    preloadedTotal.value = res.total || 0;
+  } catch (error) {
+    console.error('预加载订单列表失败:', error);
+    preloadedOrders.value = [];
+    preloadedTotal.value = 0;
+  }
+};
+
+/** 选择订单 */
+const handleSelectOrder = () => {
+  if (!form.value.customerId) {
+    proxy?.$modal.msgWarning('请先选择客户');
+    return;
+  }
+  // 传递当前已选中的订单、产品列表、预加载的订单列表和总数
+  orderMainDrawerRef.value?.open(
+    form.value.customerId,
+    currentSelectedOrders.value,
+    form.value.productList,
+    preloadedOrders.value,
+    preloadedTotal.value
+  );
+};
+
+/** 获取员工列表 */
+const getStaffList = async () => {
+  try {
+    const res = await listComStaff();
+    staffOptions.value = res.rows || [];
+  } catch (error) {
+    console.error('获取员工列表失败:', error);
+  }
+};
+
+/** 员工变更 */
+const handleStaffChange = (staffId: string | number) => {
+  if (staffId) {
+    const staff = staffOptions.value.find((item) => item.staffId === staffId);
+    if (staff) {
+      form.value.statementSelf = staff.staffName || '';
+      form.value.statementSelfPhone = staff.phone || '';
+    }
+  } else {
+    form.value.statementSelf = undefined;
+    form.value.statementSelfPhone = '';
+  }
+};
+
+/** 订单选择成功回调 */
+const handleOrderSelected = (data: any) => {
+  const { orders, products } = data;
+
+  if (orders && orders.length > 0) {
+    let totalAmount = 0;
+
+    // 保存当前选中的订单
+    currentSelectedOrders.value = orders;
+
+    // 清空现有数据
+    form.value.detailList = [];
+    form.value.productList = [];
+
+    // 填充对账明细
+    orders.forEach((order: any) => {
+      totalAmount += Number(order.totalAmount || 0);
+
+      const detail: StatementDetailForm = {
+        id: undefined,
+        statementOrderId: undefined,
+        orderNo: order.orderNo,
+        orderTime: order.orderTime || '',
+        amount: order.totalAmount,
+        type: order.orderType || '',
+        orderId: order.id,
+        signingDate: order.receivingTime || order.signingDate || '',
+        userId: order.createBy,
+        userName: order.createName || '',
+        userDeptId: order.createDept,
+        userDept: order.createDeptName || ''
+      };
+      form.value.detailList?.push(detail);
+    });
+
+    // 设置总金额
+    form.value.amount = totalAmount;
+
+    // 直接使用传递过来的产品列表
+    if (products && products.length > 0) {
+      form.value.productList = products;
+      productPage.total = products.length;
+    }
+  }
+};
+
+/** 添加对账明细 */
+const handleAddDetail = () => {
+  if (!form.value.customerId) {
+    proxy?.$modal.msgWarning('请先选择客户');
+    return;
+  }
+  // 传递当前已选中的订单、产品列表、预加载的订单列表和总数
+  orderMainDrawerRef.value?.open(
+    form.value.customerId,
+    currentSelectedOrders.value,
+    form.value.productList,
+    preloadedOrders.value,
+    preloadedTotal.value
+  );
+};
+
+/** 产品分页大小改变 */
+const handleProductSizeChange = (val: number) => {
+  productPage.pageSize = val;
+};
+
+/** 产品分页页码改变 */
+const handleProductCurrentChange = (val: number) => {
+  productPage.pageNum = val;
+};
+
+/** 文件上传改变 */
+const handleFileChange = (file: any, uploadFileList: any[]) => {
+  // 更新文件列表
+  fileList.value = uploadFileList;
+  // 更新表单中的附件地址
+  form.value.annexAddress = uploadFileList.map((f) => f.name).join(',');
+};
+
+/** 删除文件 */
+const handleRemoveFile = (index: number) => {
+  fileList.value.splice(index, 1);
+  // 更新表单中的附件地址
+  form.value.annexAddress = fileList.value.map((f) => f.name).join(',');
+};
+
+/** 打开文件选择器 */
+const handleOpenFileSelector = () => {
+  fileSelectorVisible.value = true;
+};
+
+/** 处理文件选择完成 */
+const handleFileSelected = (files: any[]) => {
+  if (files && files.length > 0) {
+    // 将选中的文件添加到文件列表
+    files.forEach((file) => {
+      fileList.value.push({
+        name: file.fileName || file.name,
+        url: file.fileUrl || file.url,
+        id: file.id
+      });
+    });
+    // 更新表单中的附件地址
+    form.value.annexAddress = fileList.value.map((f) => f.url || f.name).join(',');
+  }
+  fileSelectorVisible.value = false;
+};
+
+/** 下载文件 */
+const handleDownloadFile = async (file: any) => {
+  if (!file.url) {
+    proxy?.$modal.msgWarning('文件地址不存在');
+    return;
+  }
+
+  try {
+    // 使用fetch获取文件
+    const response = await fetch(file.url);
+    const blob = await response.blob();
+
+    // 创建Blob URL
+    const blobUrl = window.URL.createObjectURL(blob);
+
+    // 创建下载链接
+    const link = document.createElement('a');
+    link.href = blobUrl;
+    link.download = file.name || '附件';
+    document.body.appendChild(link);
+    link.click();
+
+    // 清理
+    document.body.removeChild(link);
+    window.URL.revokeObjectURL(blobUrl);
+  } catch (error) {
+    console.error('下载失败:', error);
+    // 如果fetch失败,回退到直接打开链接的方式
+    const link = document.createElement('a');
+    link.href = file.url;
+    link.download = file.name || '附件';
+    link.target = '_blank';
+    document.body.appendChild(link);
+    link.click();
+    document.body.removeChild(link);
+  }
+};
+
+/** 预览文件 */
+const handlePreviewFile = (file: any) => {
+  if (!file.url) {
+    proxy?.$modal.msgWarning('文件地址不存在');
+    return;
+  }
+  // 在新窗口打开文件
+  window.open(file.url, '_blank');
+};
+
+/** 提交表单 */
+const handleSubmit = async () => {
+  if (!formRef.value) return;
+
+  await formRef.value.validate(async (valid) => {
+    if (valid) {
+      buttonLoading.value = true;
+      try {
+        if (isEdit.value) {
+          // 编辑模式
+          await updateStatementOrder(form.value);
+          proxy?.$modal.msgSuccess('修改成功');
+        } else {
+          // 新增模式
+          await addStatementOrder(form.value);
+          proxy?.$modal.msgSuccess('新增成功');
+        }
+        drawer.visible = false;
+        emit('success');
+      } catch (error) {
+        console.error(error);
+      } finally {
+        buttonLoading.value = false;
+      }
+    }
+  });
+};
+
+/** 关闭抽屉前的回调 */
+const handleDrawerClose = (done: () => void) => {
+  if (buttonLoading.value) {
+    return;
+  }
+  done();
+  reset();
+};
+
+defineExpose({
+  open
+});
+</script>
+
+<style scoped lang="scss">
+.drawer-header {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+
+  .order-title {
+    font-size: 16px;
+    font-weight: 600;
+    color: #303133;
+  }
+}
+
+.drawer-content {
+  padding: 0 20px 20px;
+  height: calc(100% - 60px);
+  overflow-y: auto;
+}
+
+.assign-dialog-content {
+  padding: 0 10px;
+}
+
+.assign-target-section {
+  margin-bottom: 20px;
+
+  .target-input-wrapper {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+
+    .required-mark {
+      color: #f56c6c;
+      font-size: 14px;
+    }
+
+    .label {
+      font-size: 14px;
+      color: #606266;
+      white-space: nowrap;
+    }
+  }
+
+  .or-divider {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    font-size: 14px;
+    color: #909399;
+  }
+}
+
+.product-list-section {
+  margin-top: 20px;
+}
+
+.detail-header {
+  padding: 15px 20px;
+  background-color: #f5f7fa;
+  border-radius: 4px;
+  margin-bottom: 20px;
+
+  .info-item {
+    display: flex;
+    flex-direction: column;
+    gap: 5px;
+
+    .label {
+      font-size: 12px;
+      color: #909399;
+    }
+
+    .value {
+      font-size: 14px;
+      color: #303133;
+      font-weight: 500;
+    }
+  }
+}
+
+.detail-tabs {
+  .tab-actions {
+    margin-bottom: 15px;
+  }
+}
+
+:deep(.el-alert__title) {
+  font-size: 13px;
+}
+
+.empty-data {
+  padding: 20px 0;
+  text-align: center;
+}
+
+.drawer-footer {
+  display: flex;
+  justify-content: flex-end;
+  gap: 10px;
+  padding: 15px 20px;
+  border-top: 1px solid #e4e7ed;
+}
+
+:deep(.el-upload__tip) {
+  margin-top: 8px;
+  font-size: 12px;
+  color: #909399;
+}
+</style>

+ 561 - 0
src/views/bill/statementOrder/detailDrawer.vue

@@ -0,0 +1,561 @@
+<template>
+  <!-- 对账详情抽屉 -->
+  <el-drawer v-model="drawer.visible" size="75%" direction="rtl" :before-close="handleDrawerClose" :close-on-click-modal="true">
+    <template #header>
+      <div class="drawer-header">
+        <span class="order-title">对账详情</span>
+      </div>
+    </template>
+
+    <div class="drawer-content">
+      <el-form ref="formRef" :model="form" :rules="rules" label-width="135px">
+        <!-- 基本信息 -->
+        <el-divider content-position="left">
+          <span style="color: #409eff; font-weight: 600">基本信息</span>
+        </el-divider>
+        <el-row :gutter="20">
+          <el-col :span="8">
+            <el-form-item label="对账单号:" prop="statementOrderNo">
+              <el-input v-model="form.statementOrderNo" disabled />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="对账状态:" prop="statementStatus">
+              <dict-tag :options="statement_status" :value="form.statementStatus" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="客户名称:" prop="customerName">
+              <el-input v-model="form.customerName" disabled />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <el-col :span="8">
+            <el-form-item label="对账单金额:" prop="amount">
+              <el-input v-model="form.amount" disabled />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="对账人:" prop="statementSelf">
+              <el-input v-model="form.statementSelf" disabled />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="对账人联系方式:" prop="statementSelfPhone">
+              <el-input v-model="form.statementSelfPhone" placeholder="请填写对账人联系方式" disabled />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <el-col :span="8">
+            <el-form-item label="对账日期:" prop="statementDate">
+              <el-date-picker
+                v-model="form.statementDate"
+                type="date"
+                placeholder="请选择对账日期"
+                style="width: 100%"
+                value-format="YYYY-MM-DD"
+                disabled
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="支付状态:" prop="isPaymentStatus">
+              <el-select v-model="form.isPaymentStatus" placeholder="请选择支付状态" clearable style="width: 100%" disabled>
+                <el-option v-for="dict in payment_status" :key="dict.value" :label="dict.label" :value="dict.value" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="发票状态:" prop="isInvoiceStatus">
+              <el-select v-model="form.isInvoiceStatus" placeholder="请选择发票状态" clearable style="width: 100%" disabled>
+                <el-option v-for="dict in invoice_issuance_status" :key="dict.value" :label="dict.label" :value="dict.value" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <el-col :span="24">
+            <el-form-item label="备注:" prop="remark">
+              <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注" maxlength="500" show-word-limit disabled />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <!-- 对账明细 -->
+        <el-divider content-position="left">
+          <span style="color: #409eff; font-weight: 600">对账明细</span>
+        </el-divider>
+
+        <el-table :data="form.detailList" border style="width: 100%; margin-bottom: 20px">
+          <el-table-column type="index" label="序号" width="60" align="center" />
+          <el-table-column prop="type" label="订单类型" min-width="100" align="center">
+            <template #default="scope">
+              <dict-tag :options="statement_order_type" :value="scope.row.type" />
+            </template>
+          </el-table-column>
+          <el-table-column prop="orderNo" label="订单编号" min-width="150" align="center" />
+          <el-table-column prop="amount" label="金额" min-width="120" align="center" />
+          <el-table-column prop="orderTime" label="下单日期" min-width="120" align="center" />
+          <el-table-column prop="signingDate" label="签收日期" min-width="120" align="center" />
+          <el-table-column prop="userName" label="下单人" min-width="100" align="center" />
+          <el-table-column prop="userDept" label="部门" min-width="120" align="center" />
+        </el-table>
+        <!-- 
+        <div v-if="form.detailList.length === 0" class="empty-data">
+          <el-empty description="暂无数据" :image-size="100" />
+        </div> -->
+
+        <!-- 商品清单 -->
+        <el-divider content-position="left">
+          <span style="color: #409eff; font-weight: 600">商品清单</span>
+        </el-divider>
+
+        <el-table :data="pagedProductList" border style="width: 100%; margin-bottom: 20px">
+          <el-table-column
+            type="index"
+            label="序号"
+            width="60"
+            align="center"
+            :index="(index) => (productPage.pageNum - 1) * productPage.pageSize + index + 1"
+          />
+          <el-table-column prop="orderNo" label="订单编号" min-width="120" align="center" />
+          <el-table-column prop="productNo" label="商品编号" min-width="120" align="center" />
+          <el-table-column prop="itemName" label="商品名称" min-width="180" align="center" />
+          <el-table-column prop="specifications" label="规格型号" min-width="120" align="center" />
+          <el-table-column prop="unitName" label="单位" width="80" align="center" />
+          <el-table-column prop="quantity" label="数量" width="100" align="center" />
+          <el-table-column prop="unitPrice" label="单价" width="100" align="center">
+            <template #default="scope">
+              {{ scope.row.unitPrice ? Number(scope.row.unitPrice).toFixed(2) : '0.00' }}
+            </template>
+          </el-table-column>
+          <el-table-column label="金额" width="120" align="center">
+            <template #default="scope">
+              {{ (Number(scope.row.quantity || 0) * Number(scope.row.unitPrice || 0)).toFixed(2) }}
+            </template>
+          </el-table-column>
+          <el-table-column label="小计" width="120" align="center">
+            <template #default="scope">
+              {{ (Number(scope.row.quantity || 0) * Number(scope.row.unitPrice || 0)).toFixed(2) }}
+            </template>
+          </el-table-column>
+        </el-table>
+
+        <!-- 分页 -->
+        <el-pagination
+          v-if="form.productList.length > 0"
+          v-model:current-page="productPage.pageNum"
+          v-model:page-size="productPage.pageSize"
+          :page-sizes="[10, 20, 50, 100]"
+          layout="total, sizes, prev, pager, next, jumpers"
+          :total="productPage.total"
+          @size-change="handleProductSizeChange"
+          @current-change="handleProductCurrentChange"
+          style="margin-top: 15px"
+        />
+
+        <!-- 对账附件 -->
+        <el-divider content-position="left">
+          <span style="color: #409eff; font-weight: 600">对账附件</span>
+        </el-divider>
+
+        <el-table :data="fileList" border style="width: 100%; margin-bottom: 20px">
+          <el-table-column type="index" label="序号" width="80" align="center" />
+          <el-table-column prop="name" label="文件名称" min-width="200" align="center" />
+          <el-table-column prop="url" label="文件地址" min-width="250" align="center">
+            <template #default="scope">
+              {{ scope.row.url || '暂无地址' }}
+            </template>
+          </el-table-column>
+          <el-table-column label="操作" width="160" align="center">
+            <template #default="scope">
+              <el-button type="primary" link @click="handleDownloadFile(scope.row)">下载</el-button>
+              <el-button type="primary" link @click="handlePreviewFile(scope.row)">预览</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+        <!-- 
+        <div v-if="fileList.length === 0" class="empty-data">
+          <el-empty description="暂无附件" :image-size="100" />
+        </div> -->
+      </el-form>
+    </div>
+
+    <template #footer>
+      <div class="drawer-footer">
+        <el-button @click="handleDrawerClose(() => (drawer.visible = false))">取消</el-button>
+        <el-button type="primary" :loading="buttonLoading" @click="handleDrawerClose(() => (drawer.visible = false))">提交</el-button>
+      </div>
+    </template>
+  </el-drawer>
+</template>
+
+<script setup name="AddDrawer" lang="ts">
+import { addStatementOrder, updateStatementOrder } from '@/api/bill/statementOrder';
+import { StatementOrderForm } from '@/api/bill/statementOrder/types';
+import { CustomerInfoVO } from '@/api/customer/customerFile/customerInfo/types';
+import { listOrderMain, getOrderMain } from '@/api/order/orderMain';
+import { OrderMainVO, OrderMainQuery, OrderMainForm } from '@/api/order/orderMain/types';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { statement_status, statement_order_type, invoice_issuance_status, payment_status } = toRefs<any>(
+  proxy?.useDict('statement_status', 'statement_order_type', 'invoice_issuance_status', 'payment_status')
+);
+
+const initFormData: StatementOrderForm = {
+  id: undefined,
+  statementOrderNo: undefined,
+  customerId: undefined,
+  customerNo: undefined,
+  customerName: undefined,
+  amount: undefined,
+  statementSelfId: undefined,
+  statementSelf: undefined,
+  statementSelfPhone: undefined,
+  statementStatus: undefined,
+  isPaymentStatus: undefined,
+  isInvoiceStatus: undefined,
+  statementDate: undefined,
+  annexAddress: undefined,
+  rejectRemark: undefined,
+  remark: undefined,
+  detailList: [],
+  productList: []
+};
+
+const emit = defineEmits(['success']);
+
+const formRef = ref<any>();
+const buttonLoading = ref(false);
+
+const form = ref<
+  StatementOrderForm & {
+    actualAmount?: string;
+    accountingVerification?: string;
+    skuProductName?: string;
+    skuProductModel?: string;
+    attachmentRemark?: string;
+  }
+>({ ...initFormData });
+
+const rules = reactive({
+  customerName: [{ required: true, message: '请输入客户名称', trigger: 'blur' }],
+  statementSelfId: [{ required: true, message: '请选择对账人', trigger: 'blur' }],
+  statementSelfPhone: [
+    { required: true, message: '请输入对账人手机号', trigger: 'blur' },
+    { pattern: /^1[3-9]\d{9}$/ as RegExp, message: '请输入正确的手机号格式', trigger: 'blur' }
+  ]
+} as any);
+
+const fileList = ref<any[]>([]);
+
+const productPage = reactive({
+  pageNum: 1,
+  pageSize: 10,
+  total: 0
+});
+
+const drawer = reactive<DialogOption>({
+  visible: false,
+  title: '对账详情'
+});
+
+const isEdit = ref(false); // 是否为编辑模式
+
+const customerOptions = ref<CustomerInfoVO[]>([]);
+const preloadedOrders = ref<OrderMainVO[]>([]); // 预加载的订单列表
+const preloadedTotal = ref(0); // 预加载的订单总数
+
+/** 计算当前页的商品列表 */
+const pagedProductList = computed(() => {
+  const start = (productPage.pageNum - 1) * productPage.pageSize;
+  const end = start + productPage.pageSize;
+  return form.value.productList?.slice(start, end) || [];
+});
+
+/** 打开新增/编辑对账详情抽屉 */
+const open = (id?: string | number, data?: StatementOrderForm) => {
+  reset();
+
+  if (id && data) {
+    // 查看模式
+    drawer.title = '对账详情';
+    Object.assign(form.value, data);
+
+    // 将客户信息添加到下拉选项中,以便回显
+    if (data.customerId && data.customerName) {
+      customerOptions.value = [
+        {
+          id: data.customerId,
+          customerName: data.customerName,
+          customerNo: data.customerNo || ''
+        } as CustomerInfoVO
+      ];
+    }
+
+    // 解析附件地址并回显附件列表
+    if (data.annexAddress) {
+      const urls = data.annexAddress.split(',').filter((url) => url.trim());
+      fileList.value = urls.map((url, index) => {
+        const fileName = url.split('/').pop() || `附件${index + 1}`;
+        return {
+          name: fileName,
+          url: url.trim(),
+          id: undefined
+        };
+      });
+    }
+
+    // 设置商品列表分页总数
+    if (data.productList && data.productList.length > 0) {
+      productPage.total = data.productList.length;
+    }
+  }
+
+  // 先打开抽屉
+  drawer.visible = true;
+};
+
+/** 表单重置 */
+const reset = () => {
+  form.value = { ...initFormData };
+  fileList.value = [];
+  productPage.pageNum = 1;
+  productPage.pageSize = 10;
+  productPage.total = 0;
+  customerOptions.value = [];
+  formRef.value?.clearValidate();
+};
+
+/** 预加载订单列表 */
+const preloadOrders = async (customerId: string | number) => {
+  try {
+    const params: OrderMainQuery = {
+      pageNum: 1,
+      pageSize: 10,
+      customerId: customerId
+    };
+    const res = await listOrderMain(params);
+    preloadedOrders.value = res.rows || [];
+    preloadedTotal.value = res.total || 0;
+  } catch (error) {
+    console.error('预加载订单列表失败:', error);
+    preloadedOrders.value = [];
+    preloadedTotal.value = 0;
+  }
+};
+
+/** 产品分页大小改变 */
+const handleProductSizeChange = (val: number) => {
+  productPage.pageSize = val;
+};
+
+/** 产品分页页码改变 */
+const handleProductCurrentChange = (val: number) => {
+  productPage.pageNum = val;
+};
+
+/** 下载文件 */
+const handleDownloadFile = async (file: any) => {
+  if (!file.url) {
+    proxy?.$modal.msgWarning('文件地址不存在');
+    return;
+  }
+
+  try {
+    // 使用fetch获取文件
+    const response = await fetch(file.url);
+    const blob = await response.blob();
+
+    // 创建Blob URL
+    const blobUrl = window.URL.createObjectURL(blob);
+
+    // 创建下载链接
+    const link = document.createElement('a');
+    link.href = blobUrl;
+    link.download = file.name || '附件';
+    document.body.appendChild(link);
+    link.click();
+
+    // 清理
+    document.body.removeChild(link);
+    window.URL.revokeObjectURL(blobUrl);
+  } catch (error) {
+    console.error('下载失败:', error);
+    // 如果fetch失败,回退到直接打开链接的方式
+    const link = document.createElement('a');
+    link.href = file.url;
+    link.download = file.name || '附件';
+    link.target = '_blank';
+    document.body.appendChild(link);
+    link.click();
+    document.body.removeChild(link);
+  }
+};
+
+/** 预览文件 */
+const handlePreviewFile = (file: any) => {
+  if (!file.url) {
+    proxy?.$modal.msgWarning('文件地址不存在');
+    return;
+  }
+  // 在新窗口打开文件
+  window.open(file.url, '_blank');
+};
+
+/** 提交表单 */
+const handleSubmit = async () => {
+  if (!formRef.value) return;
+
+  await formRef.value.validate(async (valid) => {
+    if (valid) {
+      buttonLoading.value = true;
+      try {
+        if (isEdit.value) {
+          // 编辑模式
+          await updateStatementOrder(form.value);
+          proxy?.$modal.msgSuccess('修改成功');
+        } else {
+          // 新增模式
+          await addStatementOrder(form.value);
+          proxy?.$modal.msgSuccess('新增成功');
+        }
+        drawer.visible = false;
+        emit('success');
+      } catch (error) {
+        console.error(error);
+      } finally {
+        buttonLoading.value = false;
+      }
+    }
+  });
+};
+
+/** 关闭抽屉前的回调 */
+const handleDrawerClose = (done: () => void) => {
+  if (buttonLoading.value) {
+    return;
+  }
+  done();
+  reset();
+};
+
+defineExpose({
+  open
+});
+</script>
+
+<style scoped lang="scss">
+.drawer-header {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+
+  .order-title {
+    font-size: 16px;
+    font-weight: 600;
+    color: #303133;
+  }
+}
+
+.drawer-content {
+  padding: 0 20px 20px;
+  height: calc(100% - 60px);
+  overflow-y: auto;
+}
+
+.assign-dialog-content {
+  padding: 0 10px;
+}
+
+.assign-target-section {
+  margin-bottom: 20px;
+
+  .target-input-wrapper {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+
+    .required-mark {
+      color: #f56c6c;
+      font-size: 14px;
+    }
+
+    .label {
+      font-size: 14px;
+      color: #606266;
+      white-space: nowrap;
+    }
+  }
+
+  .or-divider {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    font-size: 14px;
+    color: #909399;
+  }
+}
+
+.product-list-section {
+  margin-top: 20px;
+}
+
+.detail-header {
+  padding: 15px 20px;
+  background-color: #f5f7fa;
+  border-radius: 4px;
+  margin-bottom: 20px;
+
+  .info-item {
+    display: flex;
+    flex-direction: column;
+    gap: 5px;
+
+    .label {
+      font-size: 12px;
+      color: #909399;
+    }
+
+    .value {
+      font-size: 14px;
+      color: #303133;
+      font-weight: 500;
+    }
+  }
+}
+
+.detail-tabs {
+  .tab-actions {
+    margin-bottom: 15px;
+  }
+}
+
+:deep(.el-alert__title) {
+  font-size: 13px;
+}
+
+.empty-data {
+  padding: 20px 0;
+  text-align: center;
+}
+
+.drawer-footer {
+  display: flex;
+  justify-content: flex-end;
+  gap: 10px;
+  padding: 15px 20px;
+  border-top: 1px solid #e4e7ed;
+}
+
+:deep(.el-upload__tip) {
+  margin-top: 8px;
+  font-size: 12px;
+  color: #909399;
+}
+</style>

+ 300 - 0
src/views/bill/statementOrder/index.vue

@@ -0,0 +1,300 @@
+<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="statementOrderNo">
+              <el-input v-model="queryParams.statementOrderNo" placeholder="请输入对账单号" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="对账日期" prop="statementDate">
+              <el-date-picker v-model="dateRange" type="daterange" range-separator="至" start-placeholder="开始时间" end-placeholder="结束时间" />
+            </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="orderNo">
+              <el-input v-model="queryParams.orderNo" placeholder="请输入客户名称" clearable @keyup.enter="handleQuery" />
+            </el-form-item> -->
+            <el-form-item label="对账状态" prop="statementStatus">
+              <el-select v-model="queryParams.statementStatus" placeholder="请选择对账状态" clearable>
+                <el-option v-for="dict in statement_status" :key="dict.value" :label="dict.label" :value="dict.value" />
+              </el-select>
+            </el-form-item>
+            <el-form-item label="支付状态" prop="isPaymentStatus">
+              <el-select v-model="queryParams.isPaymentStatus" placeholder="请选择付款状态" clearable>
+                <el-option v-for="dict in payment_status" :key="dict.value" :label="dict.label" :value="dict.value" />
+              </el-select>
+            </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="22"> 对账管理信息列表 </el-col>
+          <el-col :span="1.5">
+            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['bill:statementOrder:add']">新增</el-button>
+          </el-col>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" border :data="statementOrderList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="对账单号" align="center" prop="statementOrderNo" />
+        <el-table-column label="对账日期" align="center" prop="statementDate" width="180">
+          <template #default="scope">
+            <span>{{ parseTime(scope.row.statementDate, '{y}-{m}-{d}') }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="对账金额" align="center" prop="amount" />
+        <el-table-column label="客户编号" align="center" prop="customerNo" />
+        <el-table-column label="客户名称" align="center" prop="customerName" />
+        <el-table-column label="对账状态" align="center" prop="statementStatus">
+          <template #default="scope">
+            <dict-tag :options="statement_status" :value="scope.row.statementStatus" />
+          </template>
+        </el-table-column>
+        <el-table-column label="开票状态" align="center" prop="isInvoiceStatus">
+          <template #default="scope">
+            <dict-tag :options="invoice_issuance_status" :value="scope.row.isInvoiceStatus" />
+          </template>
+        </el-table-column>
+        <el-table-column label="支付状态" align="center" prop="isPaymentStatus">
+          <template #default="scope">
+            <dict-tag :options="payment_status" :value="scope.row.isPaymentStatus" />
+          </template>
+        </el-table-column>
+
+        <el-table-column label="操作" align="center" width="300" fixed="right">
+          <template #default="scope">
+            <el-button link type="primary" @click="handleDetail(scope.row)" v-hasPermi="['bill:statementOrder:edit']">查看</el-button>
+            <el-button
+              link
+              type="primary"
+              @click="handleUpdate(scope.row)"
+              v-if="scope.row.statementStatus != 4"
+              v-hasPermi="['bill:statementOrder:edit']"
+              >编辑</el-button
+            >
+            <el-button
+              link
+              type="primary"
+              @click="handleSend(scope.row)"
+              v-if="scope.row.statementStatus != 4"
+              v-hasPermi="['bill:statementOrder:edit']"
+              >发送对账单</el-button
+            >
+            <el-button
+              link
+              type="primary"
+              @click="handleWithdraw(scope.row)"
+              v-if="scope.row.statementStatus != 4"
+              v-hasPermi="['bill:statementOrder:edit']"
+              >撤回</el-button
+            >
+            <el-button
+              link
+              type="primary"
+              @click="handleObsolete(scope.row)"
+              v-if="scope.row.statementStatus != 4"
+              v-hasPermi="['bill:statementOrder:remove']"
+              >作废</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>
+    <!--新增对账的抽屉 -->
+    <add-drawer ref="addDrawerRef" @success="getList" />
+    <!--查看对账的抽屉 -->
+    <detail-drawer ref="detailDrawerRef" @success="getList" />
+  </div>
+</template>
+
+<script setup name="StatementOrder" lang="ts">
+import { listStatementOrder, getStatementOrder, changeStatus } from '@/api/bill/statementOrder';
+import { StatementOrderVO, StatementOrderQuery, StatementOrderForm } from '@/api/bill/statementOrder/types';
+import AddDrawer from './addDrawer.vue';
+import DetailDrawer from './detailDrawer.vue';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { statement_status, statement_order_type, invoice_issuance_status, payment_status } = toRefs<any>(
+  proxy?.useDict('statement_status', 'statement_order_type', 'invoice_issuance_status', 'payment_status')
+);
+
+const statementOrderList = ref<StatementOrderVO[]>([]);
+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 addDrawerRef = ref<InstanceType<typeof AddDrawer>>();
+const detailDrawerRef = ref<InstanceType<typeof DetailDrawer>>();
+const queryFormRef = ref<ElFormInstance>();
+const statementOrderFormRef = ref<ElFormInstance>();
+const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: StatementOrderForm = {
+  id: undefined,
+  statementOrderNo: undefined,
+  customerId: undefined,
+  customerNo: undefined,
+  customerName: undefined,
+  amount: undefined,
+  statementSelf: undefined,
+  statementSelfPhone: undefined,
+  statementStatus: undefined,
+  isPaymentStatus: undefined,
+  isInvoiceStatus: undefined,
+  statementDate: undefined,
+  annexAddress: undefined,
+  rejectRemark: undefined,
+  remark: undefined,
+  detailList: [],
+  productList: []
+};
+const data = reactive<PageData<StatementOrderForm, StatementOrderQuery>>({
+  form: { ...initFormData },
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    statementOrderNo: undefined,
+    customerId: undefined,
+    customerNo: undefined,
+    customerName: undefined,
+    amount: undefined,
+    statementSelf: undefined,
+    statementSelfPhone: undefined,
+    statementStatus: undefined,
+    isPaymentStatus: undefined,
+    isInvoiceStatus: undefined,
+    statementDate: undefined,
+    annexAddress: undefined,
+    rejectRemark: undefined,
+    platformCode: undefined,
+    params: {}
+  },
+  rules: {}
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 查询对账单主列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listStatementOrder(proxy?.addDateRange(queryParams.value, dateRange.value));
+  statementOrderList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+};
+
+/** 表单重置 */
+const reset = () => {
+  form.value = { ...initFormData };
+  statementOrderFormRef.value?.resetFields();
+};
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+};
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  dateRange.value = ['', ''];
+  queryFormRef.value?.resetFields();
+  handleQuery();
+};
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection: StatementOrderVO[]) => {
+  ids.value = selection.map((item) => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+};
+
+/** 新增按钮操作 */
+const handleAdd = () => {
+  reset();
+  // 打开抽屉
+  addDrawerRef.value?.open();
+};
+
+/** 修改按钮操作 */
+const handleUpdate = async (row?: StatementOrderVO) => {
+  const _id = row?.id || ids.value[0];
+  const res = await getStatementOrder(_id);
+  // 打开抽屉,传递id和数据
+  addDrawerRef.value?.open(_id, res.data);
+};
+
+/** 查看按钮操作 */
+const handleDetail = async (row?: StatementOrderVO) => {
+  const _id = row?.id || ids.value[0];
+  const res = await getStatementOrder(_id);
+  // 打开抽屉,传递id和数据
+  detailDrawerRef.value?.open(_id, res.data);
+};
+
+/** 发送按钮操作 */
+const handleSend = async (row?: StatementOrderVO) => {
+  const _no = row?.statementOrderNo;
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('是否发送当前账单编号【' + _no + '】吗?').finally(() => (loading.value = false));
+  //todo 发送
+  proxy?.$modal.msgSuccess('发送成功');
+  await getList();
+};
+
+/** 撤回按钮操作 */
+const handleWithdraw = async (row?: StatementOrderVO) => {
+  const _no = row?.statementOrderNo;
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('是否撤回当前账单编号【' + _no + '】吗?').finally(() => (loading.value = false));
+  //todo 撤回
+  proxy?.$modal.msgSuccess('撤回成功');
+  await getList();
+};
+
+/** 作废按钮操作 */
+const handleObsolete = async (row?: StatementOrderVO) => {
+  const _no = row?.statementOrderNo;
+  const oldValue = row.statementStatus; // 保存旧值
+  await proxy?.$modal.confirm('是否作废当前账单编号【' + _no + '】吗?').finally(() => (loading.value = false));
+  await changeStatus(row.id, '4'); // 传新值 4 - 作废
+  proxy?.$modal.msgSuccess('作废成功');
+  await getList();
+};
+
+/** 导出按钮操作 */
+const handleExport = () => {
+  proxy?.download(
+    'bill/statementOrder/export',
+    {
+      ...queryParams.value
+    },
+    `statementOrder_${new Date().getTime()}.xlsx`
+  );
+};
+
+onMounted(() => {
+  getList();
+});
+</script>

+ 235 - 0
src/views/bill/statementOrder/orderMainDrawer.vue

@@ -0,0 +1,235 @@
+<template>
+  <!-- 客户订单抽屉 -->
+  <el-drawer v-model="drawer.visible" title="对账单信息" size="68%" direction="rtl" :before-close="handleDrawerClose" :close-on-click-modal="true">
+    <div class="drawer-content">
+      <el-table ref="tableRef" v-loading="loading" :data="orderList" border @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column prop="orderType" label="订单类型" min-width="120" />
+        <el-table-column prop="orderNo" label="订单编号" min-width="150" />
+        <el-table-column prop="totalAmount" label="金额" min-width="100" align="right">
+          <template #default="scope">
+            {{ scope.row.totalAmount ? Number(scope.row.totalAmount).toFixed(2) : '0.00' }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="orderTime" label="下单日期" min-width="120" />
+        <el-table-column prop="signingDate" label="签收日期" min-width="120" />
+        <el-table-column prop="createName" label="下单人" min-width="100" />
+        <el-table-column prop="createDeptName" label="部门" min-width="150" />
+      </el-table>
+
+      <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
+    </div>
+
+    <template #footer>
+      <div class="drawer-footer">
+        <el-button @click="handleDrawerClose(() => (drawer.visible = false))">取消</el-button>
+        <el-button type="primary" @click="handleConfirm">确定</el-button>
+      </div>
+    </template>
+  </el-drawer>
+</template>
+
+<script setup name="OrderMainDrawer" lang="ts">
+import { listOrderMain, getCustomerOrderProductList } from '@/api/order/orderMain';
+import { OrderMainVO, OrderMainQuery } from '@/api/order/orderMain/types';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const emit = defineEmits(['success']);
+
+const drawer = reactive<DialogOption>({
+  visible: false,
+  title: '对账单信息'
+});
+
+const loading = ref(false);
+const orderList = ref<OrderMainVO[]>([]);
+const selectedOrders = ref<OrderMainVO[]>([]);
+const mergedProductList = ref<any[]>([]);
+const total = ref(0);
+const tableRef = ref<any>();
+
+const queryParams = ref<OrderMainQuery>({
+  pageNum: 1,
+  pageSize: 10,
+  customerId: undefined
+});
+
+/** 打开抽屉 */
+const open = async (
+  customerId: string | number,
+  preSelectedOrders?: OrderMainVO[],
+  preProductList?: any[],
+  preloadedOrderList?: OrderMainVO[],
+  preloadedTotalCount?: number
+) => {
+  // 先保存预选数据
+  const tempSelectedOrders = preSelectedOrders || [];
+  const tempProductList = preProductList || [];
+
+  reset();
+  queryParams.value.customerId = customerId;
+  drawer.visible = true;
+
+  // 如果有预加载的订单列表,直接使用,否则请求API
+  if (preloadedOrderList && preloadedOrderList.length > 0) {
+    orderList.value = preloadedOrderList;
+    total.value = preloadedTotalCount || 0;
+    loading.value = false;
+  } else {
+    // 获取订单列表
+    await getList();
+  }
+
+  // 恢复之前选中的订单和产品列表
+  if (tempSelectedOrders.length > 0) {
+    selectedOrders.value = [...tempSelectedOrders];
+    mergedProductList.value = [...tempProductList];
+
+    // 恢复表格选中状态
+    await nextTick();
+    restoreSelection(tempSelectedOrders);
+  }
+};
+
+/** 获取订单列表 */
+const getList = async () => {
+  loading.value = true;
+  try {
+    const res = await listOrderMain(queryParams.value);
+    orderList.value = res.rows || [];
+    total.value = res.total || 0;
+  } catch (error) {
+    console.error(error);
+  } finally {
+    loading.value = false;
+  }
+};
+
+/** 恢复选中状态 */
+const restoreSelection = (preSelectedOrders: OrderMainVO[]) => {
+  if (!tableRef.value || !orderList.value.length) {
+    return;
+  }
+
+  // 根据订单ID恢复选中状态
+  preSelectedOrders.forEach((preOrder) => {
+    const row = orderList.value.find((order) => order.id === preOrder.id);
+    if (row) {
+      tableRef.value.toggleRowSelection(row, true);
+    }
+  });
+};
+
+/** 表格选择变化 */
+const handleSelectionChange = async (selection: OrderMainVO[]) => {
+  selectedOrders.value = selection;
+  // 合并选中订单的产品列表
+  await mergeProductList();
+};
+
+/** 合并选中订单的产品列表 */
+const mergeProductList = async () => {
+  if (selectedOrders.value.length === 0) {
+    mergedProductList.value = [];
+    return;
+  }
+
+  try {
+    // 提取所有选中订单的ID
+    const orderIds = selectedOrders.value.map((order) => order.id).filter((id) => id !== undefined);
+
+    if (orderIds.length === 0) {
+      mergedProductList.value = [];
+      return;
+    }
+
+    // 调用接口获取订单商品列表
+    const res = await getCustomerOrderProductList(orderIds);
+    const productList = res.rows || [];
+
+    // 构建产品列表
+    const newProductList: any[] = [];
+    productList.forEach((item: any) => {
+      const product: any = {
+        id: undefined,
+        statementOrderId: undefined,
+        orderNo: item.orderNo || '',
+        productNo: item.productNo || item.skuNo || item.productCode || '',
+        itemName: item.productName || item.skuName || item.itemName || item.productFullName || '',
+        specifications: item.specifications || item.spec || item.model || item.specModel || '',
+        quantity: item.quantity || item.num || item.orderQuantity,
+        unitPrice: item.price || item.unitPrice || item.salePrice || item.orderPrice,
+        productUnit: item.productUnit || item.unitName || '',
+        subtotal: item.subtotal,
+        unitName: item.productUnit || item.unitName || '',
+        type: item.productType || item.type || '',
+        orderId: item.orderId,
+        categoryId: item.categoryId,
+        categoryNo: item.categoryNo || '',
+        productId: item.productId || item.id,
+        unitId: item.productUnitId || item.unitId
+      };
+      newProductList.push(product);
+    });
+
+    // 更新产品列表
+    mergedProductList.value = newProductList;
+  } catch (error) {
+    console.error('获取订单产品列表失败:', error);
+    mergedProductList.value = [];
+  }
+};
+
+/** 确定选择 */
+const handleConfirm = () => {
+  if (selectedOrders.value.length === 0) {
+    proxy?.$modal.msgWarning('请至少选择一条订单');
+    return;
+  }
+
+  // 传递订单列表和产品列表
+  emit('success', { orders: selectedOrders.value, products: mergedProductList.value });
+  drawer.visible = false;
+};
+
+/** 重置 */
+const reset = () => {
+  orderList.value = [];
+  selectedOrders.value = [];
+  mergedProductList.value = [];
+  total.value = 0;
+  queryParams.value = {
+    pageNum: 1,
+    pageSize: 10,
+    customerId: undefined
+  };
+  tableRef.value?.clearSelection();
+};
+
+/** 关闭抽屉前的回调 */
+const handleDrawerClose = (done: () => void) => {
+  done();
+  reset();
+};
+
+defineExpose({
+  open
+});
+</script>
+
+<style scoped lang="scss">
+.drawer-content {
+  padding: 0 20px 20px;
+  height: calc(100% - 60px);
+  overflow-y: auto;
+}
+
+.drawer-footer {
+  display: flex;
+  justify-content: flex-end;
+  gap: 10px;
+  padding: 15px 20px;
+  border-top: 1px solid #e4e7ed;
+}
+</style>

+ 22 - 4
src/views/customer/customerFile/customerInfo/add.vue

@@ -19,7 +19,11 @@
           </el-col>
           <el-col :span="8">
             <el-form-item label="客户名称" prop="customerName">
-              <el-input v-model="form.customerName" placeholder="请输入客户名称" />
+              <el-input v-model="form.customerName" placeholder="请输入客户名称">
+                <template #append>
+                  <el-button @click="selectBusinessBtn">查询工商信息</el-button>
+                </template>
+              </el-input>
             </el-form-item>
           </el-col>
           <el-col :span="8">
@@ -379,7 +383,7 @@
 </template>
 
 <script setup lang="ts" name="CustomerInfoAdd">
-import { listCustomerInfo, getCustomerInfo, addCustomerInfo } from '@/api/customer/customerFile/customerInfo';
+import { getCustomerInfo, addCustomerInfo } from '@/api/customer/customerFile/customerInfo';
 import { CustomerInfoForm } from '@/api/customer/customerFile/customerInfo/types';
 import { listInvoiceType } from '@/api/customer/invoiceType';
 import { InvoiceTypeVO, InvoiceTypeQuery } from '@/api/customer/invoiceType/types';
@@ -394,6 +398,7 @@ import { generateCustomerNumber } from '@/utils/customerNumber';
 import type { CustomerContactForm } from '@/api/customer/customerFile/customerContact/types';
 import type { InvoiceInfoForm } from '@/api/customer/customerFile/invoiceInfo/types';
 import type { SalesInfoForm } from '@/api/customer/customerFile/salesInfo/types';
+import { getBusinessInfoBycustomerName } from '@/api/customer/customerFile/businessInfo';
 import type { BusinessInfoForm } from '@/api/customer/customerFile/businessInfo/types';
 import AddContactDialog from './components/addContactDialog.vue';
 import AddInvoiceDialog from './components/addInvoiceDialog.vue';
@@ -507,7 +512,7 @@ onMounted(async () => {
   await loadComDeptList();
 
   // 判断是新增还是编辑
-  const id = route.query.id;
+  const id = route.query.id as any;
   if (id) {
     // 编辑模式
     isEdit.value = true;
@@ -519,6 +524,19 @@ onMounted(async () => {
   }
 });
 
+const selectBusinessBtn = async () => {
+  try {
+    const res = await getBusinessInfoBycustomerName(form.customerName);
+    const data = res.data;
+    // 填充信息
+    Object.assign(businessForm, data);
+    form.businessCustomerName = data.businessCustomerName;
+  } catch (error) {
+    console.error('查询工商信息失败:', error);
+    ElMessage.error('查询工商信息失败');
+  }
+};
+
 // 加载客户数据(编辑模式)
 const loadCustomerData = async (id: string) => {
   try {
@@ -541,7 +559,7 @@ const loadCustomerData = async (id: string) => {
 
     // 填充联系人列表
     if (data.customerContactVoList) {
-      contactList.value = data.customerContactVoList;
+      contactList.value = data.customerContactVoList as any;
     }
 
     // 填充开票信息列表

+ 25 - 7
src/views/customer/customerFile/customerInfo/overview/baseInfo.vue

@@ -21,7 +21,11 @@
           </el-col>
           <el-col :span="8">
             <el-form-item label="客户名称" prop="customerName">
-              <el-input v-model="form.customerName" placeholder="请输入客户名称" />
+              <el-input v-model="form.customerName" placeholder="请输入客户名称">
+                <template #append>
+                  <el-button @click="selectBusinessBtn">查询工商信息</el-button>
+                </template>
+              </el-input>
             </el-form-item>
           </el-col>
           <el-col :span="8">
@@ -435,6 +439,7 @@ import { listCustomerLevel, getCustomerLevel } from '@/api/customer/customerLeve
 import { CustomerLevelVO, CustomerLevelQuery, CustomerLevelForm } from '@/api/customer/customerLevel/types';
 import { listCustomerType, getCustomerType } from '@/api/customer/customerType';
 import { CustomerTypeVO, CustomerTypeQuery, CustomerTypeForm } from '@/api/customer/customerType/types';
+import { getBusinessInfoBycustomerName } from '@/api/customer/customerFile/businessInfo';
 import { listCreditLevel } from '@/api/customer/creditLevel';
 import { CreditLevelVO, CreditLevelQuery } from '@/api/customer/creditLevel/types';
 import AddInvoiceDialog from '../components/addInvoiceDialog.vue';
@@ -468,7 +473,7 @@ const router = useRouter();
 const formRef = ref<any>(null);
 const salesFormRef = ref<any>(null);
 const submitLoading = ref(false);
-const customerId = ref<string | number>('');
+const custId = ref<string | number>('');
 const customerNumber = ref('');
 const codeArr = ref([]);
 
@@ -619,8 +624,8 @@ onMounted(async () => {
   // 优先使用props传递的customerId,否则从路由获取
   const id = props.customerId || route.query.id;
   if (id) {
-    customerId.value = id as string;
-    await loadCustomerData(id as string);
+    custId.value = id as string;
+    await loadCustomerData(id as any);
   }
 });
 
@@ -629,12 +634,25 @@ watch(
   () => props.customerId,
   (newId) => {
     if (newId) {
-      customerId.value = newId;
+      custId.value = newId;
       loadCustomerData(newId);
     }
   }
 );
 
+const selectBusinessBtn = async () => {
+  try {
+    const res = await getBusinessInfoBycustomerName(form.customerName);
+    const data = res.data;
+    // 填充信息
+    Object.assign(businessInfo, data);
+    form.businessCustomerName = data.businessCustomerName;
+  } catch (error) {
+    console.error('查询工商信息失败:', error);
+    ElMessage.error('查询工商信息失败');
+  }
+};
+
 // 加载企业规模列表
 const loadEnterpriseScaleList = async () => {
   try {
@@ -741,7 +759,7 @@ const loadComDeptList = async () => {
 };
 
 // 加载客户数据
-const loadCustomerData = async (id: string) => {
+const loadCustomerData = async (id: any) => {
   try {
     const res = await getCustomerInfo(id);
     const data = res.data;
@@ -1059,7 +1077,7 @@ const handleSave = async () => {
     ElMessage.success('保存成功');
 
     // 重新加载数据
-    await loadCustomerData(customerId.value);
+    await loadCustomerData(custId.value);
   } catch (error) {
     console.error('保存失败:', error);
     ElMessage.error('保存失败,请重试');

+ 4 - 0
src/views/customer/customerFile/customerInfo/overview/shippingAddress.vue

@@ -158,6 +158,10 @@ const data = reactive<PageData<ShippingAddressForm, ShippingAddressQuery>>({
   },
   rules: {
     consignee: [{ required: true, message: '收货人姓名不能为空', trigger: 'blur' }],
+    phone: [
+      { required: true, message: '收货人手机号不能为空', trigger: 'blur' },
+      { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
+    ],
     provincialCityCountry: [{ required: true, message: '详细地址不能为空', trigger: 'blur' }]
   }
 });

+ 1 - 1
src/views/order/expense/index.vue

@@ -167,7 +167,7 @@ const handleUpdate = async (row?: RevenueHeaderVO) => {
   reset();
   const _id = row?.id || ids.value[0];
   router.push({
-    path: '/order-center/order-revenue-add',
+    path: '/order-manage/order-revenue-add',
     query: { revenueType: row.revenueType, id: _id }
   });
 };

+ 1 - 1
src/views/order/orderMain/index.vue

@@ -671,7 +671,7 @@ const submitForm = () => {
         reset();
         proxy?.$modal.msgSuccess('操作成功');
         // 可以在这里添加跳转逻辑,比如返回列表页
-        router.push('/order-center/order-list');
+        router.push('/order-manage/order-list');
       } catch (error) {
         console.error('提交订单失败:', error);
         proxy?.$modal.msgError('提交订单失败,请检查数据后重试');

+ 1 - 1
src/views/order/revenueHeader/add.vue

@@ -478,7 +478,7 @@ const submitForm = async () => {
 
     ElMessage.success('保存成功');
     // 跳转回列表页
-    router.push('/order-center/order-revenue');
+    router.push('/order-manage/order-revenue');
   } catch (error) {
     console.error('保存失败:', error);
     if (error !== false) {

+ 19 - 8
src/views/order/saleOrder/deliverDialog.vue

@@ -14,8 +14,8 @@
           </el-form-item>
         </el-col>
         <el-col :span="12" v-if="form.deliverMethod === '1'">
-          <el-form-item label="物流公司名称" prop="logisticsCompany">
-            <el-select v-model="form.logisticsCompany" placeholder="请选择" style="width: 100%" filterable>
+          <el-form-item label="物流公司名称" prop="logisticsCompanyId">
+            <el-select v-model="form.logisticsCompanyId" placeholder="请选择" style="width: 100%" filterable @change="handleLogisticsCompanyChange">
               <el-option v-for="company in logisticsCompanyList" :key="company.id" :label="company.logisticsName" :value="company.id" />
             </el-select>
           </el-form-item>
@@ -168,7 +168,8 @@ const form = reactive<OrderDeliverForm>({
   logisticsStatus: '',
   deliverRemark: '',
   checklistRemark: '',
-  logisticsCompany: undefined,
+  logisticsCompanyId: undefined,
+  logisticsCompanyCode: '',
   logisticNo: '',
   logisticPackStatus: '',
   consigneePhone: '',
@@ -184,10 +185,10 @@ const rules = computed(() => {
 
   // 第三方物流(deliverMethod === '1')
   if (form.deliverMethod === '1') {
-    baseRules.logisticsCompany = [{ required: true, message: '请选择物流公司', trigger: 'change' }];
+    baseRules.logisticsCompanyId = [{ required: true, message: '请选择物流公司', trigger: 'change' }];
     baseRules.logisticNo = [{ required: true, message: '请输入物流单号', trigger: 'blur' }];
     baseRules.consigneePhone = [
-      { required: true, message: '请输入收货人手机', trigger: 'blur' },
+      { required: true, message: '请输入收货人手机号码', trigger: 'blur' },
       { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
     ];
   }
@@ -229,7 +230,8 @@ const handleClose = () => {
 const resetForm = () => {
   form.orderId = undefined;
   form.orderCode = '';
-  form.logisticsCompany = undefined;
+  form.logisticsCompanyId = undefined;
+  form.logisticsCompanyCode = '';
   form.logisticNo = '';
   form.deliverMethod = '1';
   form.deliverMan = '';
@@ -241,6 +243,13 @@ const resetForm = () => {
   formRef.value?.clearValidate();
 };
 
+const handleLogisticsCompanyChange = (val: any) => {
+  const selectedCompany = logisticsCompanyList.value.find((item) => item.id === val);
+  if (selectedCompany) {
+    form.logisticsCompanyCode = selectedCompany.logisticsCode;
+  }
+};
+
 // 加载商品列表
 const loadProductList = async () => {
   try {
@@ -253,7 +262,8 @@ const loadProductList = async () => {
       deliverNum: 0,
       productNo: item.productNo,
       productId: item.productId,
-      productUnitId: item.productUnit
+      productUnit: item.productUnit,
+      productUnitId: item.productUnitId
     }));
     total.value = res.data.orderProductList.length || 0;
   } catch (error) {
@@ -335,7 +345,8 @@ const handleSubmit = async () => {
     const deliveryData: OrderDeliverForm = {
       orderId: form.orderId,
       orderCode: form.orderCode,
-      logisticsCompany: form.logisticsCompany,
+      logisticsCompanyId: form.logisticsCompanyId,
+      logisticsCompanyCode: form.logisticsCompanyCode,
       logisticNo: form.logisticNo,
       deliverMethod: form.deliverMethod,
       deliverMan: form.deliverMan,

+ 26 - 4
src/views/order/saleOrder/index.vue

@@ -130,7 +130,13 @@
               @click="handleReview(scope.row)"
               >查看订单信息</el-button
             >
-            <el-button link type="primary" v-if="scope.row.orderStatus === '4' || scope.row.orderStatus === '5'">查看物流</el-button>
+            <el-button
+              link
+              type="primary"
+              v-if="scope.row.orderStatus === '4' || scope.row.orderStatus === '5'"
+              @click="handleViewLogistics(scope.row)"
+              >查看物流</el-button
+            >
             <el-button link type="primary" @click="handleCancel(scope.row)" v-if="scope.row.orderStatus != '7'">取消订单</el-button>
           </template>
         </el-table-column>
@@ -141,6 +147,9 @@
 
     <!-- 发货对话框 -->
     <DeliverDialog v-model="showDeliverDialog" :order-id="currentOrderId" :order-no="currentOrderNo" @success="handleDeliverSuccess" />
+
+    <!-- 物流详情对话框 -->
+    <LogisticsDetail v-model="showLogisticsDialog" :order-id="logisticsOrderId" />
   </div>
 </template>
 
@@ -157,8 +166,7 @@ import {
 } from '@/api/order/orderMain';
 import { OrderMainVO, OrderMainQuery, OrderMainForm } from '@/api/order/orderMain/types';
 import DeliverDialog from './deliverDialog.vue';
-import { ElMessageBox } from 'element-plus';
-import { h } from 'vue';
+import LogisticsDetail from './logisticsDetail.vue';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const { order_status, order_check_status, pay_method, order_source } = toRefs<any>(
@@ -184,6 +192,9 @@ const currentOrderId = ref<string | number>();
 const currentOrderNo = ref<string>();
 const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
 
+const showLogisticsDialog = ref(false);
+const logisticsOrderId = ref<string | number>();
+
 const dialog = reactive<DialogOption>({
   visible: false,
   title: ''
@@ -370,9 +381,20 @@ const handleSelectionChange = (selection: OrderMainVO[]) => {
   multiple.value = !selection.length;
 };
 
+/** 查看物流按钮操作 */
+const handleViewLogistics = (row?: OrderMainVO) => {
+  if (!row?.id) {
+    proxy?.$modal.msgWarning('订单ID不能为空');
+    return;
+  }
+  logisticsOrderId.value = row.id;
+
+  showLogisticsDialog.value = true;
+};
+
 const handleReview = (row?: OrderMainVO) => {
   router.push({
-    path: '/order-center/order-sendDetail',
+    path: '/order-manage/order-sendDetail',
     query: { id: row.id }
   });
 };

+ 196 - 0
src/views/order/saleOrder/logisticsDetail.vue

@@ -0,0 +1,196 @@
+<template>
+  <el-dialog v-model="visible" title="物流信息" width="700px" :before-close="handleClose">
+    <div class="logistics-detail">
+      <div class="section-title">单号查询</div>
+
+      <el-form :model="form" label-width="80px">
+        <el-form-item label="物流单号">
+          <el-select v-model="form.selectedLogisticNo" placeholder="请选择物流单号" @change="handleLogisticNoChange" style="width: 100%">
+            <el-option
+              v-for="item in logisticsList"
+              :key="item.id"
+              :label="`${item.logisticNo},${getDictLabel(deliver_method, item.deliverMethod)}`"
+              :value="item.logisticNo"
+            />
+          </el-select>
+        </el-form-item>
+      </el-form>
+
+      <div class="section-title">物流信息</div>
+
+      <el-timeline v-if="logisticsInfo.length > 0">
+        <el-timeline-item v-for="(item, index) in logisticsInfo" :key="index" :timestamp="item.time" placement="top">
+          <div class="timeline-content">
+            <div class="timeline-status">{{ index }}</div>
+            <div class="timeline-detail">
+              <div>{{ item.time }}</div>
+              <div>{{ item.location }}</div>
+              <div>{{ item.status }}</div>
+            </div>
+          </div>
+        </el-timeline-item>
+      </el-timeline>
+
+      <el-empty v-else description="暂无物流信息" />
+    </div>
+  </el-dialog>
+</template>
+
+<script setup lang="ts">
+import { listOrderDeliver, queryTrack } from '@/api/order/orderDeliver';
+import { OrderDeliverVO } from '@/api/order/orderDeliver/types';
+
+interface Props {
+  modelValue: boolean;
+  orderId?: string | number;
+}
+
+interface LogisticsInfo {
+  time: string;
+  location: string;
+  status: string;
+}
+
+const props = defineProps<Props>();
+const emit = defineEmits(['update:modelValue']);
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { deliver_method } = toRefs<any>(proxy?.useDict('deliver_method'));
+
+const visible = ref(false);
+const logisticsList = ref<OrderDeliverVO[]>([]);
+const form = ref({
+  selectedLogisticNo: ''
+});
+const logisticsInfo = ref<LogisticsInfo[]>([]);
+
+watch(
+  () => props.modelValue,
+  (val) => {
+    visible.value = val;
+    if (val && props.orderId) {
+      loadLogisticsList();
+    }
+  }
+);
+
+watch(visible, (val) => {
+  emit('update:modelValue', val);
+});
+
+const loadLogisticsList = async () => {
+  try {
+    const res = await listOrderDeliver({
+      orderId: props.orderId,
+      pageNum: 1,
+      pageSize: 100
+    });
+    logisticsList.value = res.rows || [];
+
+    if (logisticsList.value.length > 0) {
+      form.value.selectedLogisticNo = logisticsList.value[0].logisticNo;
+      handleLogisticNoChange(form.value.selectedLogisticNo);
+    }
+  } catch (error) {
+    console.error('Failed to load logistics list:', error);
+  }
+};
+
+const handleLogisticNoChange = async (logisticNo: string) => {
+  const selected = logisticsList.value.find((item) => item.logisticNo === logisticNo);
+  if (!selected) return;
+
+  try {
+    const res = await queryTrack({
+      logisticNo: logisticNo,
+      pageNum: 1,
+      pageSize: 100
+    });
+
+    if (res.data && Array.isArray(res.data) && res.data.length > 0) {
+      logisticsInfo.value = res.data.map((item: any) => ({
+        time: item.acceptTime || item.time || '',
+        location: selected.orderCode ? `${selected.orderCode}` : '',
+        status: item.context || ''
+      }));
+    } else {
+      logisticsInfo.value = [
+        {
+          time: (selected as any).createTime || '',
+          location: selected.orderCode ? `${selected.orderCode}` : '',
+          status: '已下单'
+        }
+      ];
+    }
+  } catch (error) {
+    console.error('Failed to query track:', error);
+    logisticsInfo.value = [
+      {
+        time: (selected as any).createTime || '',
+        location: selected.orderCode ? `${selected.orderCode}` : '',
+        status: '已下单'
+      }
+    ];
+  }
+};
+
+const getDictLabel = (dictOptions: any[], value: string) => {
+  if (!dictOptions || !value) return value;
+  const dict = dictOptions.find((item) => item.value === value);
+  return dict ? dict.label : value;
+};
+
+const handleClose = () => {
+  visible.value = false;
+  form.value.selectedLogisticNo = '';
+  logisticsInfo.value = [];
+};
+</script>
+
+<style scoped lang="scss">
+.logistics-detail {
+  .section-title {
+    font-size: 16px;
+    font-weight: 500;
+    margin-bottom: 16px;
+    color: #303133;
+  }
+
+  .timeline-content {
+    display: flex;
+    align-items: flex-start;
+    gap: 12px;
+
+    .timeline-status {
+      width: 24px;
+      height: 24px;
+      border-radius: 50%;
+      background-color: #409eff;
+      color: white;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      font-size: 12px;
+      flex-shrink: 0;
+    }
+
+    .timeline-detail {
+      flex: 1;
+
+      div {
+        margin-bottom: 4px;
+        color: #606266;
+        font-size: 14px;
+
+        &:last-child {
+          margin-bottom: 0;
+        }
+      }
+    }
+  }
+
+  :deep(.el-timeline-item__timestamp) {
+    display: none;
+  }
+}
+</style>