Browse Source

Merge remote-tracking branch 'origin/master' into master

肖路 1 week ago
parent
commit
5e29b42d41
56 changed files with 5422 additions and 360 deletions
  1. 2 2
      .env.production
  2. 76 0
      src/api/bill/invoiceApplication/index.ts
  3. 395 0
      src/api/bill/invoiceApplication/types.ts
  4. 63 0
      src/api/bill/reconApplication/index.ts
  5. 110 0
      src/api/bill/reconApplication/types.ts
  6. 63 0
      src/api/company/taxrate/index.ts
  7. 110 0
      src/api/company/taxrate/types.ts
  8. 4 0
      src/api/customer/customerFile/customerContact/types.ts
  9. 6 0
      src/api/customer/customerFile/customerInfo/types.ts
  10. 20 0
      src/api/customer/customerFile/salesInfo/types.ts
  11. 63 0
      src/api/erpData/erpCompany/index.ts
  12. 410 0
      src/api/erpData/erpCompany/types.ts
  13. 63 0
      src/api/erpData/erpDept/index.ts
  14. 295 0
      src/api/erpData/erpDept/types.ts
  15. 63 0
      src/api/erpData/erpStaff/index.ts
  16. 215 0
      src/api/erpData/erpStaff/types.ts
  17. 2 0
      src/api/order/orderDeliver/types.ts
  18. 6 0
      src/api/order/orderMain/types.ts
  19. 4 0
      src/api/order/orderProduct/types.ts
  20. 12 0
      src/api/order/orderReturn/index.ts
  21. 75 0
      src/api/order/orderStatusLog/index.ts
  22. 219 0
      src/api/order/orderStatusLog/types.ts
  23. 167 0
      src/views/bill/invoiceApplication/addAndEditDrawer.vue
  24. 287 0
      src/views/bill/invoiceApplication/index.vue
  25. 167 0
      src/views/bill/invoiceApplication/invoiceApplyDrawer.vue
  26. 189 0
      src/views/bill/reconApplication/index.vue
  27. 113 0
      src/views/bill/reconApplication/reconApplyDrawer.vue
  28. 52 57
      src/views/bill/statementInvoice/addInvoiceDialog.vue
  29. 11 4
      src/views/bill/statementInvoice/index.vue
  30. 215 42
      src/views/bill/statementOrder/addDrawer.vue
  31. 8 1
      src/views/bill/statementOrder/index.vue
  32. 461 0
      src/views/customer/customerFile/contactInfo/index.vue
  33. 64 13
      src/views/customer/customerFile/customerInfo/add.vue
  34. 15 2
      src/views/customer/customerFile/customerInfo/components/addContactDialog.vue
  35. 10 10
      src/views/customer/customerFile/customerInfo/edit.vue
  36. 44 38
      src/views/customer/customerFile/customerInfo/index.vue
  37. 220 60
      src/views/customer/customerFile/customerInfo/overview/baseInfo.vue
  38. 16 11
      src/views/customer/customerFile/customerInfo/overview/contactInfo.vue
  39. 1 1
      src/views/customer/customerFile/customerInfo/overview/contractManagement.vue
  40. 6 4
      src/views/customer/customerFile/customerInfo/overview/orgStructure.vue
  41. 2 2
      src/views/customer/customerFile/customerInfo/overview/shippingAddress.vue
  42. 4 23
      src/views/customer/maintainInfo/add.vue
  43. 2 2
      src/views/order/orderAssignment/index.vue
  44. 65 14
      src/views/order/orderAssignment/splitAssignDialog.vue
  45. 5 1
      src/views/order/orderMain/components/addressDialog.vue
  46. 1 1
      src/views/order/orderMain/components/chooseAddress.vue
  47. 22 8
      src/views/order/orderMain/index.vue
  48. 23 1
      src/views/order/orderReturn/index.vue
  49. 3 3
      src/views/order/orderReturn/returnDetail.vue
  50. 314 0
      src/views/order/saleOrder/addOrderStatusLogDrawer.vue
  51. 3 5
      src/views/order/saleOrder/deliverDialog.vue
  52. 436 0
      src/views/order/saleOrder/editDeliverDialog.vue
  53. 2 2
      src/views/order/saleOrder/index.vue
  54. 53 28
      src/views/order/saleOrder/logisticsDetail.vue
  55. 7 2
      src/views/order/saleOrder/orderAffirm.vue
  56. 158 23
      src/views/order/saleOrder/sendDetail.vue

+ 2 - 2
.env.production

@@ -1,5 +1,5 @@
 # 页面标题
-VITE_APP_TITLE = RuoYi-Vue-Plus多租户管理系统
+VITE_APP_TITLE = 优易商城-客户营销智能化管理平台
 VITE_APP_LOGO_TITLE = RuoYi-Vue-Plus
 
 # 生产环境配置
@@ -15,7 +15,7 @@ VITE_APP_MONITOR_ADMIN = '/admin/applications'
 VITE_APP_SNAILJOB_ADMIN = '/snail-job'
 
 # 生产环境
-VITE_APP_BASE_API = '/prod-api'
+VITE_APP_BASE_API = 'https://one.yoe365.com'
 
 # 是否在打包时开启压缩,支持 gzip 和 brotli
 VITE_BUILD_COMPRESS = gzip

+ 76 - 0
src/api/bill/invoiceApplication/index.ts

@@ -0,0 +1,76 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { InvoiceApplicationVO, InvoiceApplicationForm, InvoiceApplicationQuery } from '@/api/bill/invoiceApplication/types';
+
+/**
+ * 查询开票申请单列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listInvoiceApplication = (query?: InvoiceApplicationQuery): AxiosPromise<InvoiceApplicationVO[]> => {
+  return request({
+    url: '/bill/invoiceApplication/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询开票申请单详细
+ * @param id
+ */
+export const getInvoiceApplication = (id: string | number): AxiosPromise<InvoiceApplicationVO> => {
+  return request({
+    url: '/bill/invoiceApplication/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增开票申请单
+ * @param data
+ */
+export const addInvoiceApplication = (data: InvoiceApplicationForm) => {
+  return request({
+    url: '/bill/invoiceApplication',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改开票申请单
+ * @param data
+ */
+export const updateInvoiceApplication = (data: InvoiceApplicationForm) => {
+  return request({
+    url: '/bill/invoiceApplication',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除开票申请单
+ * @param id
+ */
+export const delInvoiceApplication = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/bill/invoiceApplication/' + id,
+    method: 'delete'
+  });
+};
+
+/**
+ * 审核开票申请
+ * @param id
+ * @param orderStatus
+ */
+export function auditApplication(data: InvoiceApplicationForm) {
+  return request({
+    url: '/bill/invoiceApplication/auditApplication',
+    method: 'put',
+    data: data
+  });
+}

+ 395 - 0
src/api/bill/invoiceApplication/types.ts

@@ -0,0 +1,395 @@
+export interface InvoiceApplicationVO {
+  /**
+   * 主键ID
+   */
+  id: string | number;
+
+  /**
+   * 开票申请单号
+   */
+  applicationNo: string;
+
+  /**
+   * 客户ID
+   */
+  customerId: string | number;
+
+  /**
+   * 客户名称
+   */
+  customerName: string;
+
+  /**
+   * 发票类型
+   */
+  invoiceType: string;
+
+  /**
+   * 发票抬头
+   */
+  invoiceTitle: string;
+
+  /**
+   * 纳税人识别号/统一社会信用代码
+   */
+  taxNumber: string;
+
+  /**
+   * 注册地址
+   */
+  regAddress: string;
+
+  /**
+   * 注册电话
+   */
+  regPhone: string;
+
+  /**
+   * 开户银行
+   */
+  bankName: string;
+
+  /**
+   * 银行账号
+   */
+  bankAccount: string;
+
+  /**
+   * 接收发票邮箱 (用于电票)
+   */
+  receiverEmail: string;
+
+  /**
+   * 接收短信手机号
+   */
+  receiverPhone: string;
+
+  /**
+   * 申请开票金额 (不含税)
+   */
+  applyAmount: number;
+
+  /**
+   * 申请税额
+   */
+  applyTaxAmount: number;
+
+  /**
+   * 价税合计总金额
+   */
+  totalAmount: number;
+
+  /**
+   * 关联的对账单ID集合
+   */
+  statementIds: string | number;
+
+  /**
+   * 申请备注/特殊要求
+   */
+  remark: string;
+
+  /**
+   * 申请状态: 0-待审核, 1-审核通过, 2-已驳回, 3-开票中, 4-已完成, 5-已取消
+   */
+  status: string;
+
+  /**
+   * 审核人ID
+   */
+  auditUserId: string | number;
+
+  /**
+   * 审核时间
+   */
+  auditTime: string;
+
+  /**
+   * 审核意见/驳回原因
+   */
+  auditRemark: string;
+
+  /**
+   * 审核通过后生成的正式发票单据ID (关联 statement_invoice.id)
+   */
+  finalInvoiceId: string | number;
+
+  /**
+   * 正式发票代码 (开票后回填)
+   */
+  invoiceCode: string;
+
+  /**
+   * 正式发票号码 (开票后回填)
+   */
+  invoiceNumber: string;
+
+  /**
+   * 正式开票日期
+   */
+  invoiceDate: string;
+}
+
+export interface InvoiceApplicationForm extends BaseEntity {
+  /**
+   * 主键ID
+   */
+  id?: string | number;
+
+  /**
+   * 开票申请单号
+   */
+  applicationNo?: string;
+
+  /**
+   * 客户ID
+   */
+  customerId?: string | number;
+
+  /**
+   * 客户名称
+   */
+  customerName?: string;
+
+  /**
+   * 发票类型
+   */
+  invoiceType?: string;
+
+  /**
+   * 发票抬头
+   */
+  invoiceTitle?: string;
+
+  /**
+   * 纳税人识别号/统一社会信用代码
+   */
+  taxNumber?: string;
+
+  /**
+   * 注册地址
+   */
+  regAddress?: string;
+
+  /**
+   * 注册电话
+   */
+  regPhone?: string;
+
+  /**
+   * 开户银行
+   */
+  bankName?: string;
+
+  /**
+   * 银行账号
+   */
+  bankAccount?: string;
+
+  /**
+   * 接收发票邮箱 (用于电票)
+   */
+  receiverEmail?: string;
+
+  /**
+   * 接收短信手机号
+   */
+  receiverPhone?: string;
+
+  /**
+   * 申请开票金额 (不含税)
+   */
+  applyAmount?: number;
+
+  /**
+   * 申请税额
+   */
+  applyTaxAmount?: number;
+
+  /**
+   * 价税合计总金额
+   */
+  totalAmount?: number;
+
+  /**
+   * 关联的对账单ID集合
+   */
+  statementIds?: string | number;
+
+  /**
+   * 申请备注/特殊要求
+   */
+  remark?: string;
+
+  /**
+   * 申请状态: 0-待审核, 1-审核通过, 2-已驳回, 3-开票中, 4-已完成, 5-已取消
+   */
+  status?: string;
+
+  /**
+   * 审核人ID
+   */
+  auditUserId?: string | number;
+
+  /**
+   * 审核时间
+   */
+  auditTime?: string;
+
+  /**
+   * 审核意见/驳回原因
+   */
+  auditRemark?: string;
+
+  /**
+   * 审核通过后生成的正式发票单据ID (关联 statement_invoice.id)
+   */
+  finalInvoiceId?: string | number;
+
+  /**
+   * 正式发票代码 (开票后回填)
+   */
+  invoiceCode?: string;
+
+  /**
+   * 正式发票号码 (开票后回填)
+   */
+  invoiceNumber?: string;
+
+  /**
+   * 正式开票日期
+   */
+  invoiceDate?: string;
+}
+
+export interface InvoiceApplicationQuery extends PageQuery {
+  /**
+   * 开票申请单号
+   */
+  applicationNo?: string;
+
+  /**
+   * 客户ID
+   */
+  customerId?: string | number;
+
+  /**
+   * 客户名称
+   */
+  customerName?: string;
+
+  /**
+   * 发票类型
+   */
+  invoiceType?: string;
+
+  /**
+   * 发票抬头
+   */
+  invoiceTitle?: string;
+
+  /**
+   * 纳税人识别号/统一社会信用代码
+   */
+  taxNumber?: string;
+
+  /**
+   * 注册地址
+   */
+  regAddress?: string;
+
+  /**
+   * 注册电话
+   */
+  regPhone?: string;
+
+  /**
+   * 开户银行
+   */
+  bankName?: string;
+
+  /**
+   * 银行账号
+   */
+  bankAccount?: string;
+
+  /**
+   * 接收发票邮箱 (用于电票)
+   */
+  receiverEmail?: string;
+
+  /**
+   * 接收短信手机号
+   */
+  receiverPhone?: string;
+
+  /**
+   * 申请开票金额 (不含税)
+   */
+  applyAmount?: number;
+
+  /**
+   * 申请税额
+   */
+  applyTaxAmount?: number;
+
+  /**
+   * 价税合计总金额
+   */
+  totalAmount?: number;
+
+  /**
+   * 关联的对账单ID集合
+   */
+  statementIds?: string | number;
+
+  /**
+   * 申请状态: 0-待审核, 1-审核通过, 2-已驳回, 3-开票中, 4-已完成, 5-已取消
+   */
+  status?: string;
+
+  /**
+   * 审核人ID
+   */
+  auditUserId?: string | number;
+
+  /**
+   * 审核时间
+   */
+  auditTime?: string;
+
+  /**
+   * 审核意见/驳回原因
+   */
+  auditRemark?: string;
+
+  /**
+   * 审核通过后生成的正式发票单据ID (关联 statement_invoice.id)
+   */
+  finalInvoiceId?: string | number;
+
+  /**
+   * 正式发票代码 (开票后回填)
+   */
+  invoiceCode?: string;
+
+  /**
+   * 正式发票号码 (开票后回填)
+   */
+  invoiceNumber?: string;
+
+  /**
+   * 正式开票日期
+   */
+  invoiceDate?: string;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+  /**
+   * 日期范围参数
+   */
+  params?: any;
+}

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

@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { ReconApplicationVO, ReconApplicationForm, ReconApplicationQuery } from '@/api/bill/reconApplication/types';
+
+/**
+ * 查询对账申请列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listReconApplication = (query?: ReconApplicationQuery): AxiosPromise<ReconApplicationVO[]> => {
+  return request({
+    url: '/bill/reconApplication/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询对账申请详细
+ * @param id
+ */
+export const getReconApplication = (id: string | number): AxiosPromise<ReconApplicationVO> => {
+  return request({
+    url: '/bill/reconApplication/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增对账申请
+ * @param data
+ */
+export const addReconApplication = (data: ReconApplicationForm) => {
+  return request({
+    url: '/bill/reconApplication',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改对账申请
+ * @param data
+ */
+export const updateReconApplication = (data: ReconApplicationForm) => {
+  return request({
+    url: '/bill/reconApplication',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除对账申请
+ * @param id
+ */
+export const delReconApplication = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/bill/reconApplication/' + id,
+    method: 'delete'
+  });
+};

+ 110 - 0
src/api/bill/reconApplication/types.ts

@@ -0,0 +1,110 @@
+export interface ReconApplicationVO {
+  /**
+   * 主键
+   */
+  id: string | number;
+
+  /**
+   * 对账单号
+   */
+  reconNo: string;
+
+  /**
+   * 总金额
+   */
+  totalAmount: number;
+
+  /**
+   * 申请日期
+   */
+  applyDate: string;
+
+  /**
+   * 申请对账订单id
+   */
+  orderIds: string | number;
+
+  /**
+   * 申请状态: 0-未开, 1-已开
+   */
+  status: string;
+
+  /**
+   * 备注
+   */
+  remark: string;
+}
+
+export interface ReconApplicationForm extends BaseEntity {
+  /**
+   * 主键
+   */
+  id?: string | number;
+
+  /**
+   * 对账单号
+   */
+  reconNo?: string;
+
+  /**
+   * 总金额
+   */
+  totalAmount?: number;
+
+  /**
+   * 申请日期
+   */
+  applyDate?: string;
+
+  /**
+   * 申请对账订单id
+   */
+  orderIds?: string | number;
+
+  /**
+   * 申请状态: 0-未开, 1-已开
+   */
+  status?: string;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+}
+
+export interface ReconApplicationQuery extends PageQuery {
+  /**
+   * 对账单号
+   */
+  reconNo?: string;
+
+  /**
+   * 总金额
+   */
+  totalAmount?: number;
+
+  /**
+   * 申请日期
+   */
+  applyDate?: string;
+
+  /**
+   * 申请对账订单id
+   */
+  orderIds?: string | number;
+
+  /**
+   * 申请状态: 0-未开, 1-已开
+   */
+  status?: string;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+  /**
+   * 日期范围参数
+   */
+  params?: any;
+}

+ 63 - 0
src/api/company/taxrate/index.ts

@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { TaxrateVO, TaxrateForm, TaxrateQuery } from '@/api/company/taxrate/types';
+
+/**
+ * 查询产品税率配置列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listTaxrate = (query?: TaxrateQuery): AxiosPromise<TaxrateVO[]> => {
+  return request({
+    url: '/system/taxrate/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询产品税率配置详细
+ * @param id
+ */
+export const getTaxrate = (id: string | number): AxiosPromise<TaxrateVO> => {
+  return request({
+    url: '/system/taxrate/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增产品税率配置
+ * @param data
+ */
+export const addTaxrate = (data: TaxrateForm) => {
+  return request({
+    url: '/system/taxrate',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改产品税率配置
+ * @param data
+ */
+export const updateTaxrate = (data: TaxrateForm) => {
+  return request({
+    url: '/system/taxrate',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除产品税率配置
+ * @param id
+ */
+export const delTaxrate = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/system/taxrate/' + id,
+    method: 'delete'
+  });
+};

+ 110 - 0
src/api/company/taxrate/types.ts

@@ -0,0 +1,110 @@
+export interface TaxrateVO {
+  /**
+   * 主键,自增ID
+   */
+  id: string | number;
+
+  /**
+   * 税率编号
+   */
+  taxrateNo: string;
+
+  /**
+   * 税率名称
+   */
+  taxrateName: string;
+
+  /**
+   * 税率值)
+   */
+  taxrate: number;
+
+  /**
+   * 数据来源
+   */
+  dataSource: string;
+
+  /**
+   * 是否显示:1=是,0=否
+   */
+  isShow: string;
+
+  /**
+   * 备注
+   */
+  remark: string;
+}
+
+export interface TaxrateForm extends BaseEntity {
+  /**
+   * 主键,自增ID
+   */
+  id?: string | number;
+
+  /**
+   * 税率编号
+   */
+  taxrateNo?: string;
+
+  /**
+   * 税率名称
+   */
+  taxrateName?: string;
+
+  /**
+   * 税率值)
+   */
+  taxrate?: number;
+
+  /**
+   * 数据来源
+   */
+  dataSource?: string;
+
+  /**
+   * 是否显示:1=是,0=否
+   */
+  isShow?: string;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+}
+
+export interface TaxrateQuery extends PageQuery {
+  /**
+   * 税率编号
+   */
+  taxrateNo?: string;
+
+  /**
+   * 税率名称
+   */
+  taxrateName?: string;
+
+  /**
+   * 税率值)
+   */
+  taxrate?: number;
+
+  /**
+   * 数据来源
+   */
+  dataSource?: string;
+
+  /**
+   * 是否显示:1=是,0=否
+   */
+  isShow?: string;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+  /**
+   * 日期范围参数
+   */
+  params?: any;
+}

+ 4 - 0
src/api/customer/customerFile/customerContact/types.ts

@@ -199,6 +199,10 @@ export interface CustomerContactQuery extends PageQuery {
    */
   contactName?: string;
 
+  contactNo?: string;
+
+  customerName?: string;
+
   /**
    * 手机号码
    */

+ 6 - 0
src/api/customer/customerFile/customerInfo/types.ts

@@ -138,6 +138,12 @@ export interface CustomerInfoVO {
    */
   remark: string;
 
+  salesPersonName: string;
+
+  serviceStaffName: string;
+
+  belongingDepartmentName: string;
+
   /**
    * 工商信息
    */

+ 20 - 0
src/api/customer/customerFile/salesInfo/types.ts

@@ -64,11 +64,15 @@ export interface SalesInfoVO {
    */
   salesPersonId: string | number;
 
+  salesPerson: string;
+
   /**
    * 服务人员
    */
   serviceStaffId: string | number;
 
+  serviceStaff: string;
+
   /**
    * 所属部门
    */
@@ -88,6 +92,13 @@ export interface SalesInfoVO {
    * 备注
    */
   remark: string;
+  customerSource: string;
+  unitPrice: string;
+  sellChannel: string;
+  creditLimit: string;
+  creditTimeLimit: string;
+  dealCurrencyId: string | number;
+  rateId: string | number;
 }
 
 export interface SalesInfoForm extends BaseEntity {
@@ -180,6 +191,15 @@ export interface SalesInfoForm extends BaseEntity {
    * 备注
    */
   remark?: string;
+
+  customerSource?: string;
+  unitPrice?: string;
+  sellChannel?: string;
+  creditLimit?: string;
+  creditTimeLimit?: string;
+  dealCurrencyId?: string | number;
+  rateId?: string | number;
+  settlementMethod?: string | number;
 }
 
 export interface SalesInfoQuery extends PageQuery {

+ 63 - 0
src/api/erpData/erpCompany/index.ts

@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { ErpCompanyVO, ErpCompanyForm, ErpCompanyQuery } from '@/api/erpData/erpCompany/types';
+
+/**
+ * 查询erp公司信息列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listErpCompany = (query?: ErpCompanyQuery): AxiosPromise<ErpCompanyVO[]> => {
+  return request({
+    url: '/system/erpCompany/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询erp公司信息详细
+ * @param id
+ */
+export const getErpCompany = (id: string | number): AxiosPromise<ErpCompanyVO> => {
+  return request({
+    url: '/system/erpCompany/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增erp公司信息
+ * @param data
+ */
+export const addErpCompany = (data: ErpCompanyForm) => {
+  return request({
+    url: '/system/erpCompany',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改erp公司信息
+ * @param data
+ */
+export const updateErpCompany = (data: ErpCompanyForm) => {
+  return request({
+    url: '/system/erpCompany',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除erp公司信息
+ * @param id
+ */
+export const delErpCompany = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/system/erpCompany/' + id,
+    method: 'delete'
+  });
+};

+ 410 - 0
src/api/erpData/erpCompany/types.ts

@@ -0,0 +1,410 @@
+export interface ErpCompanyVO {
+  /**
+   * 主键ID
+   */
+  id: string | number;
+
+  /**
+   * 会计主体ID
+   */
+  accBnId: string | number;
+
+  /**
+   * 会计主体编号
+   */
+  accBnNo: string;
+
+  /**
+   * 公司地址
+   */
+  address: string;
+
+  /**
+   * 开始日期
+   */
+  begDate: string;
+
+  /**
+   * 经营范围
+   */
+  busScp: string;
+
+  /**
+   * 注册资本金额
+   */
+  capAmt: number;
+
+  /**
+   * 公司全名
+   */
+  companyFullName: string;
+
+  /**
+   * 法人代表
+   */
+  corporation: string;
+
+  /**
+   * 电子邮箱
+   */
+  email: string;
+
+  /**
+   * 结束日期
+   */
+  endDate: string;
+
+  /**
+   * 成立日期
+   */
+  foundDate: string;
+
+  /**
+   * 内部客户ID
+   */
+  inCustId: string | number;
+
+  /**
+   * 内部供应商ID
+   */
+  inSupId: string | number;
+
+  /**
+   * 法律代表人
+   */
+  lelPer: string;
+
+  /**
+   * 联系电话
+   */
+  phone: string;
+
+  /**
+   * 负责人/经办人
+   */
+  principal: string;
+
+  /**
+   * 注册地址
+   */
+  regAddr: string;
+
+  /**
+   * 注册日期
+   */
+  regDate: string;
+
+  /**
+   * 注册机关
+   */
+  regOrg: string;
+
+  /**
+   * 税务登记号
+   */
+  taxNo: string;
+
+  /**
+   * 公司编号
+   */
+  companyCode: string;
+
+  /**
+   * 公司名称
+   */
+  companyName: string;
+
+  /**
+   * 是否显示(0-是,1-否)
+   */
+  isShow: string;
+
+  /**
+   * 数据来源
+   */
+  dataSource: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status: string;
+
+  /**
+   * 备注
+   */
+  remark: string;
+}
+
+export interface ErpCompanyForm extends BaseEntity {
+  /**
+   * 主键ID
+   */
+  id?: string | number;
+
+  /**
+   * 会计主体ID
+   */
+  accBnId?: string | number;
+
+  /**
+   * 会计主体编号
+   */
+  accBnNo?: string;
+
+  /**
+   * 公司地址
+   */
+  address?: string;
+
+  /**
+   * 开始日期
+   */
+  begDate?: string;
+
+  /**
+   * 经营范围
+   */
+  busScp?: string;
+
+  /**
+   * 注册资本金额
+   */
+  capAmt?: number;
+
+  /**
+   * 公司全名
+   */
+  companyFullName?: string;
+
+  /**
+   * 法人代表
+   */
+  corporation?: string;
+
+  /**
+   * 电子邮箱
+   */
+  email?: string;
+
+  /**
+   * 结束日期
+   */
+  endDate?: string;
+
+  /**
+   * 成立日期
+   */
+  foundDate?: string;
+
+  /**
+   * 内部客户ID
+   */
+  inCustId?: string | number;
+
+  /**
+   * 内部供应商ID
+   */
+  inSupId?: string | number;
+
+  /**
+   * 法律代表人
+   */
+  lelPer?: string;
+
+  /**
+   * 联系电话
+   */
+  phone?: string;
+
+  /**
+   * 负责人/经办人
+   */
+  principal?: string;
+
+  /**
+   * 注册地址
+   */
+  regAddr?: string;
+
+  /**
+   * 注册日期
+   */
+  regDate?: string;
+
+  /**
+   * 注册机关
+   */
+  regOrg?: string;
+
+  /**
+   * 税务登记号
+   */
+  taxNo?: string;
+
+  /**
+   * 公司编号
+   */
+  companyCode?: string;
+
+  /**
+   * 公司名称
+   */
+  companyName?: string;
+
+  /**
+   * 是否显示(0-是,1-否)
+   */
+  isShow?: string;
+
+  /**
+   * 数据来源
+   */
+  dataSource?: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+}
+
+export interface ErpCompanyQuery extends PageQuery {
+  /**
+   * 会计主体ID
+   */
+  accBnId?: string | number;
+
+  /**
+   * 会计主体编号
+   */
+  accBnNo?: string;
+
+  /**
+   * 公司地址
+   */
+  address?: string;
+
+  /**
+   * 开始日期
+   */
+  begDate?: string;
+
+  /**
+   * 经营范围
+   */
+  busScp?: string;
+
+  /**
+   * 注册资本金额
+   */
+  capAmt?: number;
+
+  /**
+   * 公司全名
+   */
+  companyFullName?: string;
+
+  /**
+   * 法人代表
+   */
+  corporation?: string;
+
+  /**
+   * 电子邮箱
+   */
+  email?: string;
+
+  /**
+   * 结束日期
+   */
+  endDate?: string;
+
+  /**
+   * 成立日期
+   */
+  foundDate?: string;
+
+  /**
+   * 内部客户ID
+   */
+  inCustId?: string | number;
+
+  /**
+   * 内部供应商ID
+   */
+  inSupId?: string | number;
+
+  /**
+   * 法律代表人
+   */
+  lelPer?: string;
+
+  /**
+   * 联系电话
+   */
+  phone?: string;
+
+  /**
+   * 负责人/经办人
+   */
+  principal?: string;
+
+  /**
+   * 注册地址
+   */
+  regAddr?: string;
+
+  /**
+   * 注册日期
+   */
+  regDate?: string;
+
+  /**
+   * 注册机关
+   */
+  regOrg?: string;
+
+  /**
+   * 税务登记号
+   */
+  taxNo?: string;
+
+  /**
+   * 公司编号
+   */
+  companyCode?: string;
+
+  /**
+   * 公司名称
+   */
+  companyName?: string;
+
+  /**
+   * 是否显示(0-是,1-否)
+   */
+  isShow?: string;
+
+  /**
+   * 数据来源
+   */
+  dataSource?: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+  /**
+   * 日期范围参数
+   */
+  params?: any;
+}

+ 63 - 0
src/api/erpData/erpDept/index.ts

@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { ErpDeptVO, ErpDeptForm, ErpDeptQuery } from '@/api/erpData/erpDept/types';
+
+/**
+ * 查询erp部门列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listErpDept = (query?: ErpDeptQuery): AxiosPromise<ErpDeptVO[]> => {
+  return request({
+    url: '/system/erpDept/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询erp部门详细
+ * @param deptId
+ */
+export const getErpDept = (deptId: string | number): AxiosPromise<ErpDeptVO> => {
+  return request({
+    url: '/system/erpDept/' + deptId,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增erp部门
+ * @param data
+ */
+export const addErpDept = (data: ErpDeptForm) => {
+  return request({
+    url: '/system/erpDept',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改erp部门
+ * @param data
+ */
+export const updateErpDept = (data: ErpDeptForm) => {
+  return request({
+    url: '/system/erpDept',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除erp部门
+ * @param deptId
+ */
+export const delErpDept = (deptId: string | number | Array<string | number>) => {
+  return request({
+    url: '/system/erpDept/' + deptId,
+    method: 'delete'
+  });
+};

+ 295 - 0
src/api/erpData/erpDept/types.ts

@@ -0,0 +1,295 @@
+export interface ErpDeptVO {
+  /**
+   * 部门id
+   */
+  deptId: string | number;
+
+  /**
+   * 部门编号
+   */
+  deptNo: string;
+
+  /**
+   * 父部门id
+   */
+  parentId: string | number;
+
+  /**
+   * 公司id
+   */
+  companyId: string | number;
+
+  /**
+   * 祖级列表
+   */
+  ancestors: string;
+
+  /**
+   * 部门名称
+   */
+  deptName: string;
+
+  /**
+   * 部门类别编码
+   */
+  deptCategory: string;
+
+  /**
+   * 显示顺序
+   */
+  orderNum: number;
+
+  /**
+   * 负责人
+   */
+  leader: number;
+
+  /**
+   * 联系电话
+   */
+  phone: string;
+
+  /**
+   * 邮箱
+   */
+  email: string;
+
+  /**
+   * 部门状态(0正常 1停用)
+   */
+  status: string;
+
+  /**
+   * 是否是公司(0是  1否)
+   */
+  isCompanyFlag: string;
+
+  /**
+   * 部门经理id
+   */
+  managerId: string | number;
+
+  /**
+   * 部门主管id
+   */
+  chargeId: string | number;
+
+  /**
+   * 部门经理编号
+   */
+  managerNo: string;
+
+  /**
+   * 部门主管编号
+   */
+  chargeNo: string;
+
+  /**
+   * 部门经理
+   */
+  managerName: string;
+
+  /**
+   * 部门主管
+   */
+  chargeName: string;
+}
+
+export interface ErpDeptForm extends BaseEntity {
+  /**
+   * 部门id
+   */
+  deptId?: string | number;
+
+  /**
+   * 部门编号
+   */
+  deptNo?: string;
+
+  /**
+   * 父部门id
+   */
+  parentId?: string | number;
+
+  /**
+   * 公司id
+   */
+  companyId?: string | number;
+
+  /**
+   * 祖级列表
+   */
+  ancestors?: string;
+
+  /**
+   * 部门名称
+   */
+  deptName?: string;
+
+  /**
+   * 部门类别编码
+   */
+  deptCategory?: string;
+
+  /**
+   * 显示顺序
+   */
+  orderNum?: number;
+
+  /**
+   * 负责人
+   */
+  leader?: number;
+
+  /**
+   * 联系电话
+   */
+  phone?: string;
+
+  /**
+   * 邮箱
+   */
+  email?: string;
+
+  /**
+   * 部门状态(0正常 1停用)
+   */
+  status?: string;
+
+  /**
+   * 是否是公司(0是  1否)
+   */
+  isCompanyFlag?: string;
+
+  /**
+   * 部门经理id
+   */
+  managerId?: string | number;
+
+  /**
+   * 部门主管id
+   */
+  chargeId?: string | number;
+
+  /**
+   * 部门经理编号
+   */
+  managerNo?: string;
+
+  /**
+   * 部门主管编号
+   */
+  chargeNo?: string;
+
+  /**
+   * 部门经理
+   */
+  managerName?: string;
+
+  /**
+   * 部门主管
+   */
+  chargeName?: string;
+}
+
+export interface ErpDeptQuery extends PageQuery {
+  /**
+   * 部门编号
+   */
+  deptNo?: string;
+
+  /**
+   * 父部门id
+   */
+  parentId?: string | number;
+
+  /**
+   * 公司id
+   */
+  companyId?: string | number;
+
+  /**
+   * 祖级列表
+   */
+  ancestors?: string;
+
+  /**
+   * 部门名称
+   */
+  deptName?: string;
+
+  /**
+   * 部门类别编码
+   */
+  deptCategory?: string;
+
+  /**
+   * 显示顺序
+   */
+  orderNum?: number;
+
+  /**
+   * 负责人
+   */
+  leader?: number;
+
+  /**
+   * 联系电话
+   */
+  phone?: string;
+
+  /**
+   * 邮箱
+   */
+  email?: string;
+
+  /**
+   * 部门状态(0正常 1停用)
+   */
+  status?: string;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+  /**
+   * 是否是公司(0是  1否)
+   */
+  isCompanyFlag?: string;
+
+  /**
+   * 部门经理id
+   */
+  managerId?: string | number;
+
+  /**
+   * 部门主管id
+   */
+  chargeId?: string | number;
+
+  /**
+   * 部门经理编号
+   */
+  managerNo?: string;
+
+  /**
+   * 部门主管编号
+   */
+  chargeNo?: string;
+
+  /**
+   * 部门经理
+   */
+  managerName?: string;
+
+  /**
+   * 部门主管
+   */
+  chargeName?: string;
+
+  /**
+   * 日期范围参数
+   */
+  params?: any;
+}

+ 63 - 0
src/api/erpData/erpStaff/index.ts

@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { ErpStaffVO, ErpStaffForm, ErpStaffQuery } from '@/api/erpData/erpStaff/types';
+
+/**
+ * 查询erp人员信息列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listErpStaff = (query?: ErpStaffQuery): AxiosPromise<ErpStaffVO[]> => {
+  return request({
+    url: '/system/erpStaff/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询erp人员信息详细
+ * @param staffId
+ */
+export const getErpStaff = (staffId: string | number): AxiosPromise<ErpStaffVO> => {
+  return request({
+    url: '/system/erpStaff/' + staffId,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增erp人员信息
+ * @param data
+ */
+export const addErpStaff = (data: ErpStaffForm) => {
+  return request({
+    url: '/system/erpStaff',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改erp人员信息
+ * @param data
+ */
+export const updateErpStaff = (data: ErpStaffForm) => {
+  return request({
+    url: '/system/erpStaff',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除erp人员信息
+ * @param staffId
+ */
+export const delErpStaff = (staffId: string | number | Array<string | number>) => {
+  return request({
+    url: '/system/erpStaff/' + staffId,
+    method: 'delete'
+  });
+};

+ 215 - 0
src/api/erpData/erpStaff/types.ts

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

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

@@ -82,6 +82,8 @@ export interface OrderDeliverVO {
   remark: string;
 
   deliverProductList: any[];
+
+  deliverCode?: string;
 }
 
 export interface OrderDeliverForm extends BaseEntity {

+ 6 - 0
src/api/order/orderMain/types.ts

@@ -96,6 +96,8 @@ export interface OrderMainVO {
    */
   userDept: string;
 
+  userDeptName: string;
+
   /**
    * 商品总数量
    */
@@ -339,6 +341,10 @@ export interface OrderMainForm extends BaseEntity {
    */
   creditLimit?: number;
 
+  temporaryQuota?: number;
+
+  remainingQuota?: number;
+
   /**
    * 预计送达时间
    */

+ 4 - 0
src/api/order/orderProduct/types.ts

@@ -52,6 +52,8 @@ export interface OrderProductVO {
    */
   productImage: string;
 
+  categoryName: string;
+
   /**
    * 产品图片URLUrl
    */
@@ -359,6 +361,8 @@ export interface OrderProductQuery extends PageQuery {
    */
   productImage?: string;
 
+  categoryName?: string;
+
   /**
    * 平台价格(元)
    */

+ 12 - 0
src/api/order/orderReturn/index.ts

@@ -61,3 +61,15 @@ export const delOrderReturn = (id: string | number | Array<string | number>) =>
     method: 'delete'
   });
 };
+
+export function changeReturnStatus(id: string | number, returnStatus: string) {
+  const data = {
+    id,
+    returnStatus
+  };
+  return request({
+    url: '/order/orderReturn/changeReturnStatus',
+    method: 'put',
+    data: data
+  });
+}

+ 75 - 0
src/api/order/orderStatusLog/index.ts

@@ -0,0 +1,75 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { OrderStatusLogVO, OrderStatusLogForm, OrderStatusLogQuery } from '@/api/order/orderStatusLog/types';
+
+/**
+ * 查询订单状态流转记录列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listOrderStatusLog = (query?: OrderStatusLogQuery): AxiosPromise<OrderStatusLogVO[]> => {
+  return request({
+    url: '/order/orderStatusLog/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询订单状态流转记录详细
+ * @param id
+ */
+export const getOrderStatusLog = (id: string | number): AxiosPromise<OrderStatusLogVO> => {
+  return request({
+    url: '/order/orderStatusLog/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增订单状态流转记录
+ * @param data
+ */
+export const addOrderStatusLog = (data: OrderStatusLogForm) => {
+  return request({
+    url: '/order/orderStatusLog',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改订单状态流转记录
+ * @param data
+ */
+export const updateOrderStatusLog = (data: OrderStatusLogForm) => {
+  return request({
+    url: '/order/orderStatusLog',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除订单状态流转记录
+ * @param id
+ */
+export const delOrderStatusLog = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/order/orderStatusLog/' + id,
+    method: 'delete'
+  });
+};
+
+/**
+ * 查询最新状态流转记录
+ * @param data
+ */
+export const selectNewOneLog = (data: OrderStatusLogForm) => {
+  return request({
+    url: '/order/orderStatusLog/selectNewOne',
+    method: 'post',
+    data: data
+  });
+};

+ 219 - 0
src/api/order/orderStatusLog/types.ts

@@ -0,0 +1,219 @@
+export interface OrderStatusLogVO {
+  /**
+   * 主键ID
+   */
+  id: string | number;
+
+  /**
+   * 订单编号
+   */
+  orderNo: string;
+
+  /**
+   * 订单ID (关联订单主表)
+   */
+  orderId: string | number;
+
+  /**
+   * 客户编号
+   */
+  customerNo: string;
+
+  /**
+   * 客户ID (关联客户主表)
+   */
+  customerId: string | number;
+
+  /**
+   * 状态类型(发货方式)
+   */
+  deliverMethod: string;
+
+  /**
+   * 状态名称
+   */
+  statusName: string;
+
+  /**
+   * 驳回原因
+   */
+  turnDown: string;
+
+  /**
+   * 图片路径 (多张逗号分隔或JSON)
+   */
+  images: string;
+
+  /**
+   * 图片路径 (多张逗号分隔或JSON)Url
+   */
+  imagesUrl: string;
+  /**
+   * 物流单号
+   */
+  logisticNos: string;
+
+  /**
+   * 下一审批人
+   */
+  nextApprover: string;
+
+  /**
+   * 是否审批流程中 (1:否, 0:是)
+   */
+  isApprovalProcess: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status: string;
+
+  /**
+   * 备注
+   */
+  remark: string;
+}
+
+export interface OrderStatusLogForm extends BaseEntity {
+  /**
+   * 主键ID
+   */
+  id?: string | number;
+
+  /**
+   * 订单编号
+   */
+  orderNo?: string;
+
+  /**
+   * 订单ID (关联订单主表)
+   */
+  orderId?: string | number;
+
+  /**
+   * 客户编号
+   */
+  customerNo?: string;
+
+  /**
+   * 客户ID (关联客户主表)
+   */
+  customerId?: string | number;
+
+  /**
+   * 状态类型(发货方式)
+   */
+  deliverMethod?: string;
+
+  /**
+   * 状态名称
+   */
+  statusName?: string;
+
+  /**
+   * 驳回原因
+   */
+  turnDown?: string;
+
+  /**
+   * 图片路径 (多张逗号分隔或JSON)
+   */
+  images?: string;
+
+  /**
+   * 物流单号
+   */
+  logisticNos?: string;
+
+  /**
+   * 下一审批人
+   */
+  nextApprover?: string;
+
+  /**
+   * 是否审批流程中 (1:否, 0:是)
+   */
+  isApprovalProcess?: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+}
+
+export interface OrderStatusLogQuery extends PageQuery {
+  /**
+   * 订单编号
+   */
+  orderNo?: string;
+
+  /**
+   * 订单ID (关联订单主表)
+   */
+  orderId?: string | number;
+
+  /**
+   * 客户编号
+   */
+  customerNo?: string;
+
+  /**
+   * 客户ID (关联客户主表)
+   */
+  customerId?: string | number;
+
+  /**
+   * 状态类型(发货方式)
+   */
+  deliverMethod?: string;
+
+  /**
+   * 状态名称
+   */
+  statusName?: string;
+
+  /**
+   * 驳回原因
+   */
+  turnDown?: string;
+
+  /**
+   * 图片路径 (多张逗号分隔或JSON)
+   */
+  images?: string;
+
+  /**
+   * 物流单号
+   */
+  logisticNos?: string;
+
+  /**
+   * 下一审批人
+   */
+  nextApprover?: string;
+
+  /**
+   * 是否审批流程中 (1:否, 0:是)
+   */
+  isApprovalProcess?: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+  /**
+   * 日期范围参数
+   */
+  params?: any;
+}

+ 167 - 0
src/views/bill/invoiceApplication/addAndEditDrawer.vue

@@ -0,0 +1,167 @@
+<template>
+  <el-drawer v-model="visible" :title="title" :size="'70%'" :before-close="handleClose" destroy-on-close :close-on-click-modal="true">
+    <el-form ref="formRef" :model="form" :rules="rules" label-width="180px">
+      <el-form-item label="发票类型" prop="invoiceType">
+        <el-select v-model="form.invoiceType" placeholder="请选择发票类型">
+          <el-option v-for="dict in invoice_type" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="发票抬头" prop="invoiceTitle">
+        <el-input v-model="form.invoiceTitle" placeholder="请输入发票抬头" />
+      </el-form-item>
+      <el-form-item label="纳税人识别号" prop="taxNumber">
+        <el-input v-model="form.taxNumber" placeholder="请输入纳税人识别号/统一社会信用代码" />
+      </el-form-item>
+      <el-form-item label="注册地址" prop="regAddress">
+        <el-input v-model="form.regAddress" placeholder="请输入注册地址" />
+      </el-form-item>
+      <el-form-item label="注册电话" prop="regPhone">
+        <el-input v-model="form.regPhone" placeholder="请输入注册电话" />
+      </el-form-item>
+      <el-form-item label="开户银行" prop="bankName">
+        <el-input v-model="form.bankName" placeholder="请输入开户银行" />
+      </el-form-item>
+      <el-form-item label="银行账号" prop="bankAccount">
+        <el-input v-model="form.bankAccount" placeholder="请输入银行账号" />
+      </el-form-item>
+      <el-form-item label="接收短信手机号" prop="receiverPhone">
+        <el-input v-model="form.receiverPhone" placeholder="请输入接收短信手机号" />
+      </el-form-item>
+      <el-form-item label="申请开票金额 (不含税)" prop="applyAmount">
+        <el-input v-model="form.applyAmount" placeholder="请输入申请开票金额 (不含税)" />
+      </el-form-item>
+      <el-form-item label="申请税额" prop="applyTaxAmount">
+        <el-input v-model="form.applyTaxAmount" placeholder="请输入申请税额" />
+      </el-form-item>
+      <el-form-item label="价税合计总金额" prop="totalAmount">
+        <el-input v-model="form.totalAmount" placeholder="请输入价税合计总金额" />
+      </el-form-item>
+      <el-form-item label="申请备注" prop="remark">
+        <el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <div class="drawer-footer">
+        <el-button @click="handleClose">取 消</el-button>
+        <el-button :loading="buttonLoading" type="primary" @click="handleSubmit">确 定</el-button>
+      </div>
+    </template>
+  </el-drawer>
+</template>
+
+<script setup lang="ts">
+import { ref, reactive, toRefs, getCurrentInstance, ComponentInternalInstance, watch } from 'vue';
+import { InvoiceApplicationForm } from '@/api/bill/invoiceApplication/types';
+import { addInvoiceApplication, updateInvoiceApplication } from '@/api/bill/invoiceApplication';
+import { ElMessage } from 'element-plus';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { invoice_application_status, invoice_type } = toRefs<any>(proxy?.useDict('invoice_application_status', 'invoice_type'));
+
+interface Props {
+  modelValue: boolean;
+  title?: string;
+  formData?: InvoiceApplicationForm;
+}
+
+interface Emits {
+  (e: 'update:modelValue', value: boolean): void;
+  (e: 'success'): void;
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  title: '添加开票申请单'
+});
+
+const emit = defineEmits<Emits>();
+
+const formRef = ref();
+const buttonLoading = ref(false);
+
+const visible = ref(false);
+
+const initFormData: InvoiceApplicationForm = {
+  id: undefined,
+  applicationNo: undefined,
+  customerId: undefined,
+  customerName: undefined,
+  invoiceType: undefined,
+  invoiceTitle: undefined,
+  taxNumber: undefined,
+  regAddress: undefined,
+  regPhone: undefined,
+  bankName: undefined,
+  bankAccount: undefined,
+  receiverEmail: undefined,
+  receiverPhone: undefined,
+  applyAmount: undefined,
+  applyTaxAmount: undefined,
+  totalAmount: undefined,
+  statementIds: undefined,
+  remark: undefined,
+  status: undefined,
+  auditUserId: undefined,
+  auditTime: undefined,
+  auditRemark: undefined,
+  finalInvoiceId: undefined,
+  invoiceCode: undefined,
+  invoiceNumber: undefined,
+  invoiceDate: undefined
+};
+
+const form = ref<InvoiceApplicationForm>({ ...initFormData });
+
+const rules = {
+  invoiceType: [{ required: true, message: '发票类型不能为空', trigger: 'change' }]
+};
+
+// 监听modelValue变化
+watch(
+  () => props.modelValue,
+  (val) => {
+    visible.value = val;
+    if (val && props.formData) {
+      form.value = { ...props.formData };
+    } else if (val) {
+      form.value = { ...initFormData };
+    }
+  }
+);
+
+watch(visible, (val) => {
+  emit('update:modelValue', val);
+});
+
+const handleClose = () => {
+  visible.value = false;
+  formRef.value?.resetFields();
+};
+
+const handleSubmit = async () => {
+  await formRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      try {
+        if (form.value.id) {
+          await updateInvoiceApplication(form.value);
+        } else {
+          await addInvoiceApplication(form.value);
+        }
+        ElMessage.success('操作成功');
+        visible.value = false;
+        emit('success');
+      } finally {
+        buttonLoading.value = false;
+      }
+    }
+  });
+};
+</script>
+
+<style scoped lang="scss">
+.drawer-footer {
+  display: flex;
+  justify-content: flex-end;
+  gap: 10px;
+}
+</style>

+ 287 - 0
src/views/bill/invoiceApplication/index.vue

@@ -0,0 +1,287 @@
+<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" label-width="110px">
+            <el-form-item label="开票申请单号" prop="applicationNo">
+              <el-input v-model="queryParams.applicationNo" placeholder="请输入开票申请单号" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <!-- <el-form-item label="客户ID" prop="customerId">
+              <el-input v-model="queryParams.customerId" placeholder="请输入客户ID" clearable @keyup.enter="handleQuery" />
+            </el-form-item> -->
+
+            <el-form-item label="发票类型" prop="invoiceType">
+              <el-select v-model="queryParams.invoiceType" placeholder="请选择发票类型" clearable>
+                <el-option v-for="dict in invoice_type" :key="dict.value" :label="dict.label" :value="dict.value" />
+              </el-select>
+            </el-form-item>
+            <el-form-item label="发票抬头" prop="invoiceTitle">
+              <el-input v-model="queryParams.invoiceTitle" placeholder="请输入发票抬头" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+
+            <el-form-item label="申请状态" prop="status">
+              <el-select v-model="queryParams.status" placeholder="请选择申请状态" clearable>
+                <el-option v-for="dict in invoice_application_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="1.5">
+            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['bill:invoiceApplication:add']">新增</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['bill:invoiceApplication:edit']"
+              >修改</el-button
+            >
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['bill:invoiceApplication:remove']"
+              >删除</el-button
+            >
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['bill:invoiceApplication:export']">导出</el-button>
+          </el-col>
+          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" border :data="invoiceApplicationList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="开票申请单号" align="center" prop="applicationNo" />
+        <el-table-column label="客户名称" align="center" prop="customerName" />
+        <el-table-column label="发票类型" align="center" prop="invoiceType">
+          <template #default="scope">
+            <dict-tag :options="invoice_type" :value="scope.row.invoiceType" />
+          </template>
+        </el-table-column>
+        <el-table-column label="发票抬头" align="center" prop="invoiceTitle" />
+        <el-table-column label="纳税人识别号" align="center" prop="taxNumber" />
+        <el-table-column label="注册地址" align="center" prop="regAddress" />
+        <el-table-column label="注册电话" align="center" prop="regPhone" />
+        <el-table-column label="开户银行" align="center" prop="bankName" />
+        <el-table-column label="银行账号" align="center" prop="bankAccount" />
+        <el-table-column label="申请开票金额" align="center" prop="applyAmount" />
+        <el-table-column label="申请税额" align="center" prop="applyTaxAmount" />
+        <el-table-column label="总金额" align="center" prop="totalAmount" />
+        <el-table-column label="申请备注" align="center" prop="remark" />
+        <el-table-column label="申请状态" align="center" prop="status">
+          <template #default="scope">
+            <dict-tag :options="invoice_application_status" :value="scope.row.status" />
+          </template>
+        </el-table-column>
+        <el-table-column label="审核人ID" align="center" prop="auditUserId" />
+        <el-table-column label="审核时间" align="center" prop="auditTime" width="180">
+          <template #default="scope">
+            <span>{{ parseTime(scope.row.auditTime, '{y}-{m}-{d}') }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="审核意见" align="center" prop="auditRemark" />
+
+        <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <!-- <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['bill:invoiceApplication:edit']">编辑</el-button> -->
+            <el-button link type="primary" icon="Edit" @click="handleAudit(scope.row)" v-hasPermi="['bill:invoiceApplication:edit']">审核</el-button>
+            <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['bill:invoiceApplication: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>
+    <!-- 添加或修改开票申请单抽屉 -->
+    <AddAndEditDrawer v-model="drawerVisible" :title="drawerTitle" :form-data="currentFormData" @success="handleDrawerSuccess" />
+
+    <!-- 审核对话框 -->
+    <el-dialog v-model="auditDialogVisible" title="审核开票申请" width="500px" append-to-body>
+      <el-form ref="auditFormRef" :model="auditForm" label-width="100px">
+        <el-form-item label="审核结果" prop="status">
+          <el-select v-model="auditForm.status" placeholder="请选择审核结果">
+            <el-option v-for="dict in invoice_application_status" :key="dict.value" :label="dict.label" :value="dict.value" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="审核意见" prop="auditRemark">
+          <el-input v-model="auditForm.auditRemark" type="textarea" :rows="4" placeholder="请输入审核意见" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="auditDialogVisible = false">取 消</el-button>
+        <el-button type="primary" @click="handleAuditSubmit">确 定</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="InvoiceApplication" lang="ts">
+import { listInvoiceApplication, getInvoiceApplication, delInvoiceApplication, auditApplication } from '@/api/bill/invoiceApplication';
+import AddAndEditDrawer from './invoiceApplyDrawer.vue';
+import { InvoiceApplicationVO, InvoiceApplicationQuery, InvoiceApplicationForm } from '@/api/bill/invoiceApplication/types';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { invoice_application_status, invoice_type } = toRefs<any>(proxy?.useDict('invoice_application_status', 'invoice_type'));
+
+const invoiceApplicationList = ref<InvoiceApplicationVO[]>([]);
+const drawerVisible = ref(false);
+const drawerTitle = ref('');
+const currentFormData = ref<InvoiceApplicationForm>();
+const auditDialogVisible = ref(false);
+const auditForm = ref<InvoiceApplicationForm>({});
+const auditFormRef = ref<ElFormInstance>();
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+
+const queryFormRef = ref<ElFormInstance>();
+
+const data = reactive<PageData<InvoiceApplicationForm, InvoiceApplicationQuery>>({
+  form: {},
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    applicationNo: undefined,
+    customerId: undefined,
+    customerName: undefined,
+    invoiceType: undefined,
+    invoiceTitle: undefined,
+    taxNumber: undefined,
+    regAddress: undefined,
+    regPhone: undefined,
+    bankName: undefined,
+    bankAccount: undefined,
+    receiverEmail: undefined,
+    receiverPhone: undefined,
+    applyAmount: undefined,
+    applyTaxAmount: undefined,
+    totalAmount: undefined,
+    statementIds: undefined,
+    status: undefined,
+    auditUserId: undefined,
+    auditTime: undefined,
+    auditRemark: undefined,
+    finalInvoiceId: undefined,
+    invoiceCode: undefined,
+    invoiceNumber: undefined,
+    invoiceDate: undefined,
+    platformCode: undefined,
+    params: {}
+  },
+  rules: {}
+});
+
+const { queryParams } = toRefs(data);
+
+/** 查询开票申请单列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listInvoiceApplication(queryParams.value);
+  invoiceApplicationList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+};
+
+/** 抽屉成功回调 */
+const handleDrawerSuccess = () => {
+  getList();
+};
+
+/** 审核按钮操作 */
+const handleAudit = (row: InvoiceApplicationVO) => {
+  auditForm.value = {
+    id: row.id,
+    status: undefined,
+    auditRemark: ''
+  };
+  auditDialogVisible.value = true;
+};
+
+/** 审核提交 */
+const handleAuditSubmit = async () => {
+  if (!auditForm.value.status) {
+    proxy?.$modal.msgWarning('请选择审核结果');
+    return;
+  }
+  try {
+    await auditApplication(auditForm.value);
+    proxy?.$modal.msgSuccess('审核成功');
+    auditDialogVisible.value = false;
+    await getList();
+  } catch (error) {
+    console.error('审核失败:', error);
+  }
+};
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+};
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+};
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection: InvoiceApplicationVO[]) => {
+  ids.value = selection.map((item) => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+};
+
+/** 新增按钮操作 */
+const handleAdd = () => {
+  currentFormData.value = undefined;
+  drawerTitle.value = '添加开票申请单';
+  drawerVisible.value = true;
+};
+
+/** 修改按钮操作 */
+const handleUpdate = async (row?: InvoiceApplicationVO) => {
+  const _id = row?.id || ids.value[0];
+  const res = await getInvoiceApplication(_id);
+  currentFormData.value = res.data;
+  drawerTitle.value = '修改开票申请单';
+  drawerVisible.value = true;
+};
+
+/** 删除按钮操作 */
+const handleDelete = async (row?: InvoiceApplicationVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('是否确认删除开票申请单编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
+  await delInvoiceApplication(_ids);
+  proxy?.$modal.msgSuccess('删除成功');
+  await getList();
+};
+
+/** 导出按钮操作 */
+const handleExport = () => {
+  proxy?.download(
+    'bill/invoiceApplication/export',
+    {
+      ...queryParams.value
+    },
+    `invoiceApplication_${new Date().getTime()}.xlsx`
+  );
+};
+
+onMounted(() => {
+  getList();
+});
+</script>

+ 167 - 0
src/views/bill/invoiceApplication/invoiceApplyDrawer.vue

@@ -0,0 +1,167 @@
+<template>
+  <el-drawer v-model="visible" :title="title" :size="'70%'" :before-close="handleClose" destroy-on-close :close-on-click-modal="true">
+    <el-form ref="formRef" :model="form" :rules="rules" label-width="180px">
+      <el-form-item label="发票类型" prop="invoiceType">
+        <el-select v-model="form.invoiceType" placeholder="请选择发票类型">
+          <el-option v-for="dict in invoice_type" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="发票抬头" prop="invoiceTitle">
+        <el-input v-model="form.invoiceTitle" placeholder="请输入发票抬头" />
+      </el-form-item>
+      <el-form-item label="纳税人识别号" prop="taxNumber">
+        <el-input v-model="form.taxNumber" placeholder="请输入纳税人识别号/统一社会信用代码" />
+      </el-form-item>
+      <el-form-item label="注册地址" prop="regAddress">
+        <el-input v-model="form.regAddress" placeholder="请输入注册地址" />
+      </el-form-item>
+      <el-form-item label="注册电话" prop="regPhone">
+        <el-input v-model="form.regPhone" placeholder="请输入注册电话" />
+      </el-form-item>
+      <el-form-item label="开户银行" prop="bankName">
+        <el-input v-model="form.bankName" placeholder="请输入开户银行" />
+      </el-form-item>
+      <el-form-item label="银行账号" prop="bankAccount">
+        <el-input v-model="form.bankAccount" placeholder="请输入银行账号" />
+      </el-form-item>
+      <el-form-item label="接收短信手机号" prop="receiverPhone">
+        <el-input v-model="form.receiverPhone" placeholder="请输入接收短信手机号" />
+      </el-form-item>
+      <el-form-item label="申请开票金额 (不含税)" prop="applyAmount">
+        <el-input v-model="form.applyAmount" placeholder="请输入申请开票金额 (不含税)" />
+      </el-form-item>
+      <el-form-item label="申请税额" prop="applyTaxAmount">
+        <el-input v-model="form.applyTaxAmount" placeholder="请输入申请税额" />
+      </el-form-item>
+      <el-form-item label="价税合计总金额" prop="totalAmount">
+        <el-input v-model="form.totalAmount" placeholder="请输入价税合计总金额" />
+      </el-form-item>
+      <el-form-item label="申请备注/特殊要求" prop="remark">
+        <el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <div class="drawer-footer">
+        <el-button @click="handleClose">取 消</el-button>
+        <el-button :loading="buttonLoading" type="primary" @click="handleSubmit">确 定</el-button>
+      </div>
+    </template>
+  </el-drawer>
+</template>
+
+<script setup lang="ts">
+import { ref, reactive, toRefs, getCurrentInstance, ComponentInternalInstance, watch } from 'vue';
+import { InvoiceApplicationForm } from '@/api/bill/invoiceApplication/types';
+import { addInvoiceApplication, updateInvoiceApplication } from '@/api/bill/invoiceApplication';
+import { ElMessage } from 'element-plus';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { invoice_application_status, invoice_type } = toRefs<any>(proxy?.useDict('invoice_application_status', 'invoice_type'));
+
+interface Props {
+  modelValue: boolean;
+  title?: string;
+  formData?: InvoiceApplicationForm;
+}
+
+interface Emits {
+  (e: 'update:modelValue', value: boolean): void;
+  (e: 'success'): void;
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  title: '添加开票申请单'
+});
+
+const emit = defineEmits<Emits>();
+
+const formRef = ref();
+const buttonLoading = ref(false);
+
+const visible = ref(false);
+
+const initFormData: InvoiceApplicationForm = {
+  id: undefined,
+  applicationNo: undefined,
+  customerId: undefined,
+  customerName: undefined,
+  invoiceType: undefined,
+  invoiceTitle: undefined,
+  taxNumber: undefined,
+  regAddress: undefined,
+  regPhone: undefined,
+  bankName: undefined,
+  bankAccount: undefined,
+  receiverEmail: undefined,
+  receiverPhone: undefined,
+  applyAmount: undefined,
+  applyTaxAmount: undefined,
+  totalAmount: undefined,
+  statementIds: undefined,
+  remark: undefined,
+  status: undefined,
+  auditUserId: undefined,
+  auditTime: undefined,
+  auditRemark: undefined,
+  finalInvoiceId: undefined,
+  invoiceCode: undefined,
+  invoiceNumber: undefined,
+  invoiceDate: undefined
+};
+
+const form = ref<InvoiceApplicationForm>({ ...initFormData });
+
+const rules = {
+  invoiceType: [{ required: true, message: '发票类型不能为空', trigger: 'change' }]
+};
+
+// 监听modelValue变化
+watch(
+  () => props.modelValue,
+  (val) => {
+    visible.value = val;
+    if (val && props.formData) {
+      form.value = { ...props.formData };
+    } else if (val) {
+      form.value = { ...initFormData };
+    }
+  }
+);
+
+watch(visible, (val) => {
+  emit('update:modelValue', val);
+});
+
+const handleClose = () => {
+  visible.value = false;
+  formRef.value?.resetFields();
+};
+
+const handleSubmit = async () => {
+  await formRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      try {
+        if (form.value.id) {
+          await updateInvoiceApplication(form.value);
+        } else {
+          await addInvoiceApplication(form.value);
+        }
+        ElMessage.success('操作成功');
+        visible.value = false;
+        emit('success');
+      } finally {
+        buttonLoading.value = false;
+      }
+    }
+  });
+};
+</script>
+
+<style scoped lang="scss">
+.drawer-footer {
+  display: flex;
+  justify-content: flex-end;
+  gap: 10px;
+}
+</style>

+ 189 - 0
src/views/bill/reconApplication/index.vue

@@ -0,0 +1,189 @@
+<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="reconNo">
+              <el-input v-model="queryParams.reconNo" placeholder="请输入对账单号" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="申请日期" prop="applyDate">
+              <el-date-picker clearable v-model="queryParams.applyDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择申请日期" />
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
+              <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+            </el-form-item>
+          </el-form>
+        </el-card>
+      </div>
+    </transition>
+
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['bill:reconApplication:add']">新增</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['bill:reconApplication:edit']"
+              >修改</el-button
+            >
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['bill:reconApplication:remove']"
+              >删除</el-button
+            >
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['bill:reconApplication:export']">导出</el-button>
+          </el-col>
+          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" border :data="reconApplicationList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="主键" align="center" prop="id" v-if="true" />
+        <el-table-column label="对账单号" align="center" prop="reconNo" />
+        <el-table-column label="总金额" align="center" prop="totalAmount" />
+        <el-table-column label="申请日期" align="center" prop="applyDate" width="180">
+          <template #default="scope">
+            <span>{{ parseTime(scope.row.applyDate, '{y}-{m}-{d}') }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="申请状态" align="center" prop="status" />
+        <el-table-column label="备注" align="center" prop="remark" />
+        <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-button link type="primary" icon="Edit" @click="handleAudit(scope.row)" v-hasPermi="['bill:reconApplication:edit']">审核</el-button>
+            <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['bill:reconApplication: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>
+    <!-- 添加或修改对账申请抽屉 -->
+    <ReconApplyDrawer v-model="drawerVisible" :title="drawerTitle" :form-data="currentFormData" @success="handleDrawerSuccess" />
+  </div>
+</template>
+
+<script setup name="ReconApplication" lang="ts">
+import { listReconApplication, getReconApplication, delReconApplication } from '@/api/bill/reconApplication';
+import ReconApplyDrawer from './reconApplyDrawer.vue';
+import { ReconApplicationVO, ReconApplicationQuery, ReconApplicationForm } from '@/api/bill/reconApplication/types';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const reconApplicationList = ref<ReconApplicationVO[]>([]);
+const drawerVisible = ref(false);
+const drawerTitle = ref('');
+const currentFormData = ref<ReconApplicationForm>();
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+
+const queryFormRef = ref<ElFormInstance>();
+
+const data = reactive<PageData<ReconApplicationForm, ReconApplicationQuery>>({
+  form: {},
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    reconNo: undefined,
+    totalAmount: undefined,
+    applyDate: undefined,
+    orderIds: undefined,
+    status: undefined,
+    platformCode: undefined,
+    params: {}
+  },
+  rules: {}
+});
+
+const { queryParams } = toRefs(data);
+
+/** 查询对账申请列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listReconApplication(queryParams.value);
+  reconApplicationList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+};
+
+/** 抽屉成功回调 */
+const handleDrawerSuccess = () => {
+  getList();
+};
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+};
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+};
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection: ReconApplicationVO[]) => {
+  ids.value = selection.map((item) => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+};
+
+/** 新增按钮操作 */
+const handleAdd = () => {
+  currentFormData.value = undefined;
+  drawerTitle.value = '添加对账申请';
+  drawerVisible.value = true;
+};
+
+/** 修改按钮操作 */
+const handleUpdate = async (row?: ReconApplicationVO) => {
+  const _id = row?.id || ids.value[0];
+  const res = await getReconApplication(_id);
+  currentFormData.value = res.data;
+  drawerTitle.value = '修改对账申请';
+  drawerVisible.value = true;
+};
+
+/** 修改按钮操作 */
+const handleAudit = async (row?: ReconApplicationVO) => {
+  const _id = row?.id || ids.value[0];
+};
+
+/** 删除按钮操作 */
+const handleDelete = async (row?: ReconApplicationVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('是否确认删除对账申请编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
+  await delReconApplication(_ids);
+  proxy?.$modal.msgSuccess('删除成功');
+  await getList();
+};
+
+/** 导出按钮操作 */
+const handleExport = () => {
+  proxy?.download(
+    'bill/reconApplication/export',
+    {
+      ...queryParams.value
+    },
+    `reconApplication_${new Date().getTime()}.xlsx`
+  );
+};
+
+onMounted(() => {
+  getList();
+});
+</script>

+ 113 - 0
src/views/bill/reconApplication/reconApplyDrawer.vue

@@ -0,0 +1,113 @@
+<template>
+  <el-drawer v-model="visible" :title="title" :size="'70%'" :before-close="handleClose" destroy-on-close :close-on-click-modal="true">
+    <el-form ref="formRef" :model="form" :rules="rules" label-width="150px">
+      <el-form-item label="总金额" prop="totalAmount">
+        <el-input v-model="form.totalAmount" placeholder="请输入总金额" />
+      </el-form-item>
+      <el-form-item label="申请日期" prop="applyDate">
+        <el-date-picker clearable v-model="form.applyDate" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" placeholder="请选择申请日期">
+        </el-date-picker>
+      </el-form-item>
+      <el-form-item label="备注" prop="remark">
+        <el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <div class="drawer-footer">
+        <el-button @click="handleClose">取 消</el-button>
+        <el-button :loading="buttonLoading" type="primary" @click="handleSubmit">确 定</el-button>
+      </div>
+    </template>
+  </el-drawer>
+</template>
+
+<script setup lang="ts">
+import { ref, watch } from 'vue';
+import { ReconApplicationForm } from '@/api/bill/reconApplication/types';
+import { addReconApplication, updateReconApplication } from '@/api/bill/reconApplication';
+import { ElMessage } from 'element-plus';
+
+interface Props {
+  modelValue: boolean;
+  title?: string;
+  formData?: ReconApplicationForm;
+}
+
+interface Emits {
+  (e: 'update:modelValue', value: boolean): void;
+  (e: 'success'): void;
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  title: '添加对账申请'
+});
+
+const emit = defineEmits<Emits>();
+
+const formRef = ref();
+const buttonLoading = ref(false);
+const visible = ref(false);
+
+const initFormData: ReconApplicationForm = {
+  id: undefined,
+  reconNo: undefined,
+  totalAmount: undefined,
+  applyDate: undefined,
+  orderIds: undefined,
+  status: undefined,
+  remark: undefined
+};
+
+const form = ref<ReconApplicationForm>({ ...initFormData });
+
+const rules = {};
+
+watch(
+  () => props.modelValue,
+  (val) => {
+    visible.value = val;
+    if (val && props.formData) {
+      form.value = { ...props.formData };
+    } else if (val) {
+      form.value = { ...initFormData };
+    }
+  }
+);
+
+watch(visible, (val) => {
+  emit('update:modelValue', val);
+});
+
+const handleClose = () => {
+  visible.value = false;
+  formRef.value?.resetFields();
+};
+
+const handleSubmit = async () => {
+  await formRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      try {
+        if (form.value.id) {
+          await updateReconApplication(form.value);
+        } else {
+          await addReconApplication(form.value);
+        }
+        ElMessage.success('操作成功');
+        visible.value = false;
+        emit('success');
+      } finally {
+        buttonLoading.value = false;
+      }
+    }
+  });
+};
+</script>
+
+<style scoped lang="scss">
+.drawer-footer {
+  display: flex;
+  justify-content: flex-end;
+  gap: 10px;
+}
+</style>

+ 52 - 57
src/views/bill/statementInvoice/addInvoiceDialog.vue

@@ -52,29 +52,22 @@
 
       <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>
+          <el-form-item label="发票附件" prop="invoiceAnnex">
+            <el-upload
+              :action="uploadAction"
+              :on-success="handleUploadSuccess"
+              :before-upload="beforeUpload"
+              :on-remove="handleRemoveUploadFile"
+              :on-preview="handlePreviewUploadFile"
+              :file-list="fileList"
+              multiple
+            >
+              <el-button type="primary" icon="Upload">点击上传</el-button>
+              <template #tip>
+                <div class="el-upload__tip">支持jpg/png/xlsx等文件,单个文件不超过50MB</div>
               </template>
-            </el-table-column>
-          </el-table>
+            </el-upload>
+          </el-form-item>
         </el-col>
       </el-row>
     </el-form>
@@ -86,15 +79,9 @@
       </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 {
@@ -104,7 +91,7 @@ interface InvoiceForm {
   invoiceAmount?: number;
   invoiceDate?: string;
   remark?: string;
-  invoiceAttachment?: string;
+  invoiceAnnex?: string;
 }
 
 const initFormData: InvoiceForm = {
@@ -114,14 +101,14 @@ const initFormData: InvoiceForm = {
   invoiceAmount: undefined,
   remark: undefined,
   invoiceDate: undefined,
-  invoiceAttachment: undefined
+  invoiceAnnex: undefined
 };
 
 const formRef = ref<ElFormInstance>();
 const buttonLoading = ref(false);
 const form = ref<InvoiceForm>({ ...initFormData });
 const fileList = ref<any[]>([]);
-const fileSelectorVisible = ref(false);
+const uploadAction = import.meta.env.VITE_APP_BASE_API + '/resource/oss/upload';
 
 const dialog = reactive<DialogOption>({
   visible: false,
@@ -152,14 +139,15 @@ const open = (data?: InvoiceForm) => {
     Object.assign(form.value, data);
 
     // 解析附件地址并回显附件列表
-    if (data.invoiceAttachment) {
-      const urls = data.invoiceAttachment.split(',').filter((url) => url.trim());
+    if (data.invoiceAnnex) {
+      const urls = data.invoiceAnnex.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
+          uid: Date.now() + index,
+          status: 'success'
         };
       });
     }
@@ -175,39 +163,46 @@ const reset = () => {
   formRef.value?.clearValidate();
 };
 
-/** 打开文件选择器 */
-const handleOpenFileSelector = () => {
-  fileSelectorVisible.value = true;
+/** 上传前校验 */
+const beforeUpload = (file: any) => {
+  const isLt50M = file.size / 1024 / 1024 < 50;
+  if (!isLt50M) {
+    proxy?.$modal.msgWarning('上传文件大小不能超过 50MB!');
+  }
+  return isLt50M;
 };
 
-/** 文件选择完成 */
-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(',');
+function handleUploadSuccess(response: any, file: any, fileListParam: any[]) {
+  if (response.code === 200) {
+    // 更新 fileList
+    fileList.value = fileListParam;
+    // 收集所有已上传成功的文件URL
+    const urls = fileListParam
+      .filter((f: any) => f.response?.code === 200 || f.url)
+      .map((f: any) => f.response?.data?.url || f.url)
+      .filter(Boolean);
+    form.value.invoiceAnnex = urls.join(',');
+    proxy?.$modal.msgSuccess('文件上传成功');
+  } else {
+    proxy?.$modal.msgError(response.msg || '文件上传失败');
   }
-  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 handleRemoveUploadFile = (uploadFile: any) => {
+  form.value.invoiceAnnex = fileList.value
+    .map((f) => f.url)
+    .filter(Boolean)
+    .join(',');
 };
 
 /** 预览文件 */
-const handlePreviewFile = (file: any) => {
-  if (!file.url) {
+const handlePreviewUploadFile = (uploadFile: any) => {
+  if (uploadFile.url) {
+    window.open(uploadFile.url, '_blank');
+  } else {
     proxy?.$modal.msgWarning('文件地址不存在');
-    return;
   }
-  window.open(file.url, '_blank');
 };
 
 /** 提交表单 */

+ 11 - 4
src/views/bill/statementInvoice/index.vue

@@ -8,7 +8,14 @@
               <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-date-picker
+                v-model="dateRange"
+                type="daterange"
+                range-separator="至"
+                start-placeholder="开始时间"
+                end-placeholder="结束时间"
+                value-format="YYYY-MM-DD"
+              />
             </el-form-item>
             <el-form-item label="客户名称" prop="customerId">
               <el-select
@@ -268,10 +275,10 @@ const handleSend = async (row?: StatementInvoiceVO) => {
   const _no = row?.statementInvoiceNo;
   const _ids = row?.id || ids.value;
   const oldValue = row.invoiceStatus; // 保存旧值
-  await proxy?.$modal.confirm('是否发送当前发票编号【' + _no + '】吗?').finally(() => (loading.value = false));
+  await proxy?.$modal.confirm('是否确认当前发票编号【' + _no + '】吗?').finally(() => (loading.value = false));
   //todo 发送
   try {
-    await changeStatus(row.id, '1'); // 传新值 1 待开票
+    await changeStatus(row.id, '2'); // 传新值 1 待开票
   } catch (e) {
     // 恢复旧值
     row.invoiceStatus = oldValue;
@@ -312,7 +319,7 @@ const buttonConfigMap: Record<number, ActionButton[]> = {
   [InvoiceStatus.DRAFT]: [
     { action: 'detail', label: '查看', handler: handleDetail, permission: 'bill:statementInvoice:edit' },
     { action: 'edit', label: '编辑', handler: handleUpdate, permission: 'bill:statementInvoice:edit' },
-    { action: 'send', label: '发送开票信息', handler: handleSend, permission: 'bill:statementInvoice:edit' },
+    { action: 'send', label: '确认', handler: handleSend, permission: 'bill:statementInvoice:edit' },
     { action: 'withdraw', label: '撤回', handler: handleWithdraw, permission: 'bill:statementInvoice:edit' },
     { action: 'obsolete', label: '作废', handler: handleObsolete, permission: 'bill:statementInvoice:remove' }
   ],

+ 215 - 42
src/views/bill/statementOrder/addDrawer.vue

@@ -48,7 +48,7 @@
         <el-row :gutter="20">
           <el-col :span="8">
             <el-form-item label="对账单金额" prop="amount">
-              <el-input v-model="form.amount" disabled />
+              <el-input :model-value="amountDisplay" disabled />
             </el-form-item>
           </el-col>
           <el-col :span="8">
@@ -130,7 +130,26 @@
           <span style="color: #409eff; font-weight: 600">商品清单</span>
         </el-divider>
 
-        <el-table :data="pagedProductList" border style="width: 100%; margin-bottom: 20px">
+        <div class="table-actions" style="margin-bottom: 15px; display: flex; justify-content: space-between; align-items: center">
+          <span v-if="isProductSelectMode">已选 {{ selectedProductCount }} / {{ form.productList?.length || 0 }} 个商品</span>
+          <span v-else>共 {{ form.productList?.length || 0 }} 个商品</span>
+          <div>
+            <template v-if="isProductSelectMode">
+              <el-button @click="handleCancelProductSelection">取消</el-button>
+            </template>
+            <el-button v-else type="primary" icon="Plus" @click="handleSelectProducts">选择商品</el-button>
+          </div>
+        </div>
+
+        <el-table
+          ref="productTableRef"
+          :data="pagedProductList"
+          border
+          style="width: 100%; margin-bottom: 20px"
+          :row-key="(row: any) => row.id || `${row.orderNo}_${row.productNo}_${row.quantity || 0}`"
+          @selection-change="handleProductSelectionChange"
+        >
+          <el-table-column v-if="isProductSelectMode" type="selection" width="50" align="center" :reserve-selection="true" />
           <el-table-column
             type="index"
             label="序号"
@@ -173,9 +192,16 @@
           <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-upload
+          :action="uploadAction"
+          :on-success="handleUploadSuccess"
+          :before-upload="beforeUpload"
+          :show-file-list="false"
+          multiple
+          style="margin-bottom: 10px"
+        >
+          <el-button type="primary" icon="Upload">点击上传</el-button>
+        </el-upload>
 
         <el-table :data="fileList" border style="width: 100%; margin-bottom: 20px">
           <el-table-column type="index" label="序号" width="80" align="center" />
@@ -185,10 +211,11 @@
               {{ scope.row.url || '暂无地址' }}
             </template>
           </el-table-column>
-          <el-table-column label="操作" width="200" align="center">
+          <el-table-column label="操作" width="250" 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>
+              <el-button type="danger" link @click="handleRemoveFile(scope.$index)">删除</el-button>
             </template>
           </el-table-column>
         </el-table>
@@ -209,9 +236,6 @@
 
   <!-- 客户订单抽屉 -->
   <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">
@@ -226,7 +250,6 @@ import { OrderDeliverVO } from '@/api/order/orderDeliver/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 { getCustomerDeliverOrders } from '@/api/order/orderDeliver';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@@ -271,7 +294,7 @@ const form = ref<
 >({ ...initFormData });
 
 const rules = reactive({
-  customerName: [{ required: true, message: '请输入客户名称', trigger: 'blur' }],
+  customerId: [{ required: true, message: '请输入客户名称', trigger: 'blur' }],
   statementSelfId: [{ required: true, message: '请选择对账人', trigger: 'blur' }],
   statementSelfPhone: [
     { required: true, message: '请输入对账人手机号', trigger: 'blur' },
@@ -298,10 +321,19 @@ 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 uploadAction = import.meta.env.VITE_APP_BASE_API + '/resource/oss/upload';
 const currentSelectedOrders = ref<OrderDeliverVO[]>([]);
 const preloadedOrders = ref<OrderDeliverVO[]>([]); // 预加载的订单列表
+const productTableRef = ref<any>(); // 商品表格引用
+const selectedProductIds = ref<Set<string>>(new Set()); // 选中的商品ID集合
+const isProductSelectMode = ref(false); // 是否处于商品选择模式
+const productsToSubmit = ref<any[]>([]); // 最终要提交的商品列表
+
+/** 已选商品数量 */
+const selectedProductCount = computed(() => selectedProductIds.value.size);
+
+/** 生成商品唯一键 */
+const getProductKey = (item: any) => item.id || `${item.orderNo}_${item.productNo}_${item.quantity || 0}`;
 
 /** 计算当前页的商品列表 */
 const pagedProductList = computed(() => {
@@ -309,6 +341,35 @@ const pagedProductList = computed(() => {
   const end = start + productPage.pageSize;
   return form.value.productList?.slice(start, end) || [];
 });
+
+/** 计算对账单金额(根据选中状态) */
+const calculatedAmount = computed(() => {
+  let total = 0;
+  if (form.value.productList && form.value.productList.length > 0) {
+    if (isProductSelectMode.value && selectedProductIds.value.size > 0) {
+      // 选择模式:只计算选中的商品
+      form.value.productList.forEach((item: any) => {
+        if (selectedProductIds.value.has(getProductKey(item))) {
+          total += Number(item.quantity || 0) * Number(item.unitPrice || 0);
+        }
+      });
+    } else {
+      // 非选择模式:计算所有商品
+      form.value.productList.forEach((item: any) => {
+        total += Number(item.quantity || 0) * Number(item.unitPrice || 0);
+      });
+    }
+  }
+  return total.toFixed(2);
+});
+
+/** 金额显示文本 */
+const amountDisplay = computed(() => {
+  if (isProductSelectMode.value && selectedProductIds.value.size > 0) {
+    return `${calculatedAmount.value}`;
+  }
+  return calculatedAmount.value;
+});
 const preloadedTotal = ref(0); // 预加载的订单总数
 
 /** 打开新增/编辑对账详情抽屉 */
@@ -512,6 +573,23 @@ const handleOrderSelected = (data: any) => {
     if (products && products.length > 0) {
       form.value.productList = products;
       productPage.total = products.length;
+
+      // 默认全选所有商品
+      nextTick(() => {
+        selectedProductIds.value.clear();
+        products.forEach((item: any) => {
+          selectedProductIds.value.add(item.id || item.productNo);
+        });
+        // 更新表格选中状态
+        productTableRef.value?.clearSelection?.();
+        nextTick(() => {
+          pagedProductList.value.forEach((item: any) => {
+            if (selectedProductIds.value.has(item.id || item.productNo)) {
+              productTableRef.value?.toggleRowSelection?.(item, true);
+            }
+          });
+        });
+      });
     }
   }
 };
@@ -542,41 +620,114 @@ 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 handleProductSelectionChange = (selection: any[]) => {
+  // 只在非初始化时更新已选ID集合
+  if (!isInitializingSelection.value) {
+    selectedProductIds.value.clear();
+    selection.forEach((item) => {
+      selectedProductIds.value.add(getProductKey(item));
+    });
+  }
 };
 
-/** 删除文件 */
-const handleRemoveFile = (index: number) => {
-  fileList.value.splice(index, 1);
-  // 更新表单中的附件地址
-  form.value.annexAddress = fileList.value.map((f) => f.name).join(',');
+const isInitializingSelection = ref(false); // 是否正在初始化选中状态
+
+/** 全选所有商品 */
+const handleSelectAllProducts = () => {
+  if (form.value.productList && form.value.productList.length > 0) {
+    // 选中所有商品
+    productTableRef.value?.toggleAllSelection?.();
+  }
 };
 
-/** 打开文件选择器 */
-const handleOpenFileSelector = () => {
-  fileSelectorVisible.value = true;
+/** 清空选择 */
+const handleClearSelection = () => {
+  selectedProductIds.value.clear();
+  productTableRef.value?.clearSelection?.();
 };
 
-/** 处理文件选择完成 */
-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
-      });
+/** 进入商品选择模式 */
+const handleSelectProducts = () => {
+  if (!form.value.productList || form.value.productList.length === 0) {
+    proxy?.$modal.msgWarning('请先选择订单');
+    return;
+  }
+  isProductSelectMode.value = true;
+  selectedProductIds.value.clear();
+  // 默认全部选中
+  form.value.productList.forEach((item: any) => {
+    selectedProductIds.value.add(getProductKey(item));
+  });
+  // 刷新表格选中状态(避免触发 handleProductSelectionChange)
+  isInitializingSelection.value = true;
+  nextTick(() => {
+    productTableRef.value?.clearSelection?.();
+    pagedProductList.value.forEach((item: any) => {
+      if (selectedProductIds.value.has(getProductKey(item))) {
+        productTableRef.value?.toggleRowSelection?.(item, true);
+      }
+    });
+    isInitializingSelection.value = false;
+  });
+};
+
+/** 确认商品选择 */
+const handleConfirmProductSelection = () => {
+  if (selectedProductIds.value.size === 0) {
+    proxy?.$modal.msgWarning('请至少选择一个商品');
+    return;
+  }
+  // 保存要提交的商品列表
+  productsToSubmit.value = form.value.productList.filter((item: any) => selectedProductIds.value.has(getProductKey(item)));
+  isProductSelectMode.value = false;
+  productTableRef.value?.clearSelection?.();
+  proxy?.$modal.msgSuccess(`已选择 ${selectedProductIds.value.size} 个商品`);
+};
+
+/** 取消商品选择 */
+const handleCancelProductSelection = () => {
+  isProductSelectMode.value = false;
+  selectedProductIds.value.clear();
+  productTableRef.value?.clearSelection?.();
+};
+
+/** 上传前校验 */
+const beforeUpload = (file: any) => {
+  const isLt50M = file.size / 1024 / 1024 < 50;
+  if (!isLt50M) {
+    proxy?.$modal.msgWarning('上传文件大小不能超过 50MB!');
+  }
+  return isLt50M;
+};
+
+/** 上传成功回调 */
+const handleUploadSuccess = (response: any, file: any) => {
+  if (response.code === 200) {
+    fileList.value.push({
+      name: file.name,
+      url: response.data.url,
+      id: undefined
     });
     // 更新表单中的附件地址
-    form.value.annexAddress = fileList.value.map((f) => f.url || f.name).join(',');
+    form.value.annexAddress = fileList.value
+      .map((f) => f.url)
+      .filter(Boolean)
+      .join(',');
+    proxy?.$modal.msgSuccess('上传成功');
+  } else {
+    proxy?.$modal.msgError(response.msg || '上传失败');
   }
-  fileSelectorVisible.value = false;
+};
+
+/** 删除文件 */
+const handleRemoveFile = (index: number) => {
+  fileList.value.splice(index, 1);
+  // 更新表单中的附件地址
+  form.value.annexAddress = fileList.value
+    .map((f) => f.url)
+    .filter(Boolean)
+    .join(',');
 };
 
 /** 下载文件 */
@@ -630,19 +781,41 @@ const handlePreviewFile = (file: any) => {
 /** 提交表单 */
 const handleSubmit = async () => {
   if (!formRef.value) return;
-  console.log(form.value);
 
   await formRef.value.validate(async (valid) => {
     if (valid) {
       buttonLoading.value = true;
       try {
+        const submitData = { ...form.value };
+
+        // 根据是否处于商品选择模式来决定提交哪些商品
+        if (submitData.productList && submitData.productList.length > 0) {
+          if (isProductSelectMode.value) {
+            // 选择模式:根据 selectedProductIds 过滤选中的商品
+            if (selectedProductIds.value.size === 0) {
+              proxy?.$modal.msgWarning('请先选择商品');
+              buttonLoading.value = false;
+              return;
+            }
+            submitData.productList = submitData.productList.filter((item: any) => selectedProductIds.value.has(getProductKey(item)));
+          }
+          // 非选择模式:提交所有商品
+
+          // 重新计算对账金额
+          let totalAmount = 0;
+          submitData.productList.forEach((item: any) => {
+            totalAmount += Number(item.quantity || 0) * Number(item.unitPrice || 0);
+          });
+          submitData.amount = totalAmount;
+        }
+
         if (isEdit.value) {
           // 编辑模式
-          await updateStatementOrder(form.value);
+          await updateStatementOrder(submitData);
           proxy?.$modal.msgSuccess('修改成功');
         } else {
           // 新增模式
-          await addStatementOrder(form.value);
+          await addStatementOrder(submitData);
           proxy?.$modal.msgSuccess('新增成功');
         }
         drawer.visible = false;

+ 8 - 1
src/views/bill/statementOrder/index.vue

@@ -8,7 +8,14 @@
               <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-date-picker
+                v-model="dateRange"
+                type="daterange"
+                range-separator="至"
+                start-placeholder="开始时间"
+                end-placeholder="结束时间"
+                value-format="YYYY-MM-DD"
+              />
             </el-form-item>
             <el-form-item label="客户名称" prop="customerName">
               <el-input v-model="queryParams.customerName" placeholder="请输入客户名称" clearable @keyup.enter="handleQuery" />

+ 461 - 0
src/views/customer/customerFile/contactInfo/index.vue

@@ -0,0 +1,461 @@
+<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="contactNo">
+              <el-input v-model="queryParams.contactNo" placeholder="请输入用户编码" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="姓名" prop="contactName">
+              <el-input v-model="queryParams.contactName" placeholder="请输入姓名" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="联系电话" prop="phone">
+              <el-input v-model="queryParams.phone" placeholder="请输入联系电话" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="所属客户" prop="customerName">
+              <el-input v-model="queryParams.customerName" placeholder="请输入所属客户" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item>
+              <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="19">
+            <span>员工信息列表</span>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">批量删除</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="primary" plain :disabled="single">修改密码</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="warning" plain icon="Download" @click="handleExport">导出数据</el-button>
+          </el-col>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" border :data="customerContactList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="员工编号" align="center" prop="contactNo" />
+        <el-table-column label="姓名" align="center" prop="contactName" />
+        <el-table-column label="手机号" align="center" prop="phone" />
+        <el-table-column label="采购角色" align="center" prop="roleName" />
+        <el-table-column label="职位" align="center" prop="postName" />
+        <el-table-column label="所属客户" align="center" prop="customerName" />
+        <el-table-column label="注册时间" align="center" prop="createTime" width="180">
+          <template #default="scope">
+            <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="状态" align="center" prop="status">
+          <template #default="scope">
+            <!-- <dict-tag :options="sys_normal_disable" :value="scope.row.status" /> -->
+            {{ scope.row.status === '0' ? '启用' : '停用' }}
+          </template>
+        </el-table-column>
+
+        <!-- <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-button link type="primary" @click="handleUpdate(scope.row)">编辑</el-button>
+            <el-button link type="primary">查看</el-button>
+          </template>
+        </el-table-column> -->
+      </el-table>
+
+      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+    </el-card>
+    <!-- 添加或修改客户联系人信息对话框 -->
+    <el-dialog :title="dialog.title" v-model="dialog.visible" width="900px" append-to-body>
+      <el-form ref="customerContactFormRef" :model="form" :rules="rules" label-width="120px">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="联系人姓名" prop="contactName">
+              <el-input v-model="form.contactName" placeholder="请输入联系人姓名" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="手机号码" prop="phone">
+              <el-input v-model="form.phone" placeholder="请输入手机号码" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="办公电话" prop="officePhone">
+              <el-input v-model="form.officePhone" placeholder="请输入办公电话" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="自定义登录名" prop="customLoginName">
+              <el-input v-model="form.customLoginName" placeholder="请输入自定义登录名" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="所属部门" prop="deptId">
+              <el-tree-select
+                v-model="form.deptId"
+                :data="customerDeptList"
+                :props="{ value: 'deptId', label: 'deptName', children: 'children' }"
+                value-key="deptId"
+                placeholder="请选择"
+                check-strictly
+                :render-after-expand="false"
+                @change="handleDeptChange"
+                filterable
+                style="width: 100%"
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="邮箱" prop="email">
+              <el-input v-model="form.email" placeholder="请输入邮箱" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="采购角色" prop="roleId">
+              <el-select v-model="form.roleId" placeholder="请选择采购角色" clearable filterable style="width: 100%" @change="handleRoleChange">
+                <el-option v-for="role in roleOptions" :key="role.roleId" :label="role.roleName" :value="role.roleId" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="生日" prop="birthday">
+              <el-date-picker v-model="form.birthday" type="date" placeholder="请选择" class="w-full" value-format="YYYY-MM-DD" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="性别" prop="gender">
+              <el-radio-group v-model="form.gender">
+                <el-radio v-for="dict in sys_user_sex" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="启用状态" prop="status">
+              <el-switch v-model="form.status" active-value="0" inactive-value="1" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="24">
+            <el-form-item label="详细地址" prop="codeArr">
+              <el-cascader v-model="codeArr" :options="regionOptions" placeholder="请选择" @change="handleChange" style="width: 100%"></el-cascader>
+            </el-form-item>
+            <el-form-item prop="addressDetail">
+              <el-input v-model="form.addressDetail" type="textarea" :rows="3" placeholder="请输入详细地址" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" :loading="buttonLoading" @click="submitForm">确认</el-button>
+          <el-button @click="cancel">取消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="CustomerContact" lang="ts">
+import {
+  listCustomerContact,
+  getCustomerContact,
+  changeIsPrimary,
+  delCustomerContact,
+  addCustomerContact,
+  updateCustomerContact
+} from '@/api/customer/customerFile/customerContact';
+import { CustomerContactVO, CustomerContactQuery, CustomerContactForm } from '@/api/customer/customerFile/customerContact/types';
+import { listCustomerDept, getCustomerDept } from '@/api/customer/customerFile/customerDept';
+import { CustomerDeptVO, CustomerDeptQuery } from '@/api/customer/customerFile/customerDept/types';
+import { listRole } from '@/api/system/role';
+import type { RoleVO } from '@/api/system/role/types';
+import { regionData } from 'element-china-area-data';
+
+const regionOptions = regionData as any;
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { sys_platform_yes_no, sys_user_sex, sys_normal_disable } = toRefs<any>(
+  proxy?.useDict('sys_platform_yes_no', 'sys_user_sex', 'sys_normal_disable')
+);
+
+const customerContactList = ref<CustomerContactVO[]>([]);
+const customerDeptList = ref<CustomerDeptVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+
+const queryFormRef = ref<ElFormInstance>();
+const customerContactFormRef = ref<ElFormInstance>();
+const codeArr = ref([]);
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const roleOptions = ref<any>([]);
+
+const initFormData: CustomerContactForm = {
+  id: undefined,
+  customerId: undefined,
+  contactName: undefined,
+  phone: undefined,
+  customLoginName: undefined,
+  officePhone: undefined,
+  officePhoneTwo: undefined,
+  gender: '2',
+  roleId: undefined,
+  deptId: undefined,
+  email: undefined,
+  birthday: undefined,
+  isPrimary: undefined,
+  addressDetail: undefined,
+  addressProvince: undefined,
+  addressCity: undefined,
+  addressCounty: undefined,
+  provincialCityCounty: undefined,
+  status: '0',
+  remark: undefined
+};
+const data = reactive<PageData<CustomerContactForm, CustomerContactQuery>>({
+  form: { ...initFormData },
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    customerId: undefined,
+    contactName: undefined,
+    phone: undefined,
+    officePhone: undefined,
+    officePhoneTwo: undefined,
+    gender: undefined,
+    roleId: undefined,
+    isPrimary: undefined,
+    addressDetail: undefined,
+    addressProvince: undefined,
+    addressCity: undefined,
+    addressCounty: undefined,
+    provincialCityCounty: undefined,
+    status: undefined,
+    platformCode: undefined,
+    params: {}
+  },
+  rules: {
+    contactName: [{ required: true, message: '联系人姓名不能为空', trigger: 'blur' }],
+    phone: [
+      { required: true, message: '手机号码不能为空', trigger: 'blur' },
+      { pattern: /^1[3-9]\d{9}$/ as RegExp, message: '请输入正确的手机号格式', trigger: 'blur' }
+    ]
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 查询客户联系人信息列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listCustomerContact(queryParams.value);
+  customerContactList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+};
+
+/** 加载角色列表 */
+const loadRoleOptions = async () => {
+  try {
+    // const res: any = await listRole({
+    //   pageNum: 1,
+    //   pageSize: 9999,
+    //   roleName: '',
+    //   roleKey: '',
+    //   status: ''
+    // } as any);
+    // roleOptions.value = res?.rows || res?.data?.rows || res?.data || [];
+    roleOptions.value = [
+      { roleId: 1, roleName: '管理角色' },
+      { roleId: 2, roleName: '采购角色' },
+      { roleId: 3, roleName: '查询角色' },
+      { roleId: 4, roleName: '审核角色' }
+    ];
+  } catch (error) {
+    console.error('加载角色列表失败:', error);
+    roleOptions.value = [];
+  }
+};
+
+const handleDeptChange = (deptId: string | number) => {
+  const dept = customerDeptList.value.find((d) => d.deptId === deptId);
+  form.value.deptName = dept?.deptName;
+};
+
+const handleRoleChange = (roleId: string | number) => {
+  const role = roleOptions.value.find((r) => r.roleId === roleId);
+  form.value.roleName = role?.roleName;
+};
+
+/** 加载客户部门列表 */
+const loadCustomerDeptList = async () => {
+  try {
+    const query: CustomerDeptQuery = {};
+    const res = await listCustomerDept(query);
+    // 将数据转换为树形结构
+    customerDeptList.value = proxy?.handleTree<CustomerDeptVO>(res.data, 'id', 'parentId') || [];
+  } catch (error) {
+    console.error('加载部门列表失败:', error);
+  }
+};
+
+/** 状态修改  */
+const handlePrimaryChange = async (row: any) => {
+  //   const text = row.status === '0' ? '是' : '否';
+  try {
+    // await proxy?.$modal.confirm('确认要"' + text + '"吗?');
+    await changeIsPrimary(row.id, row.isPrimary);
+    proxy?.$modal.msgSuccess('操作成功');
+    getList();
+  } catch (err) {
+    row.status = row.status === '0' ? '1' : '0';
+  }
+};
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+};
+
+/** 表单重置 */
+const reset = () => {
+  form.value = { ...initFormData };
+  codeArr.value = [];
+  customerContactFormRef.value?.resetFields();
+};
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+};
+
+/** 处理区域选择变化 */
+const handleChange = (val: string[]) => {
+  // 保存编码
+  form.value.addressProvince = val[0];
+  form.value.addressCity = val[1];
+  form.value.addressCounty = val[2];
+
+  // 根据编码获取名称
+  const names: string[] = [];
+  if (val[0]) {
+    const province = regionData.find((item: any) => item.value === val[0]);
+    if (province) {
+      names.push(province.label);
+
+      if (val[1] && province.children) {
+        const city = province.children.find((item: any) => item.value === val[1]);
+        if (city) {
+          names.push(city.label);
+
+          if (val[2] && city.children) {
+            const county = city.children.find((item: any) => item.value === val[2]);
+            if (county) {
+              names.push(county.label);
+            }
+          }
+        }
+      }
+    }
+  }
+
+  // 将省市区名称用斜杠连接
+  form.value.provincialCityCounty = names.join('/');
+};
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+};
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection: CustomerContactVO[]) => {
+  ids.value = selection.map((item) => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+};
+
+/** 修改按钮操作 */
+const handleUpdate = async (row?: CustomerContactVO) => {
+  reset();
+  // 加载部门列表
+  await loadCustomerDeptList();
+  const _id = row?.id || ids.value[0];
+  const res = await getCustomerContact(_id);
+  // 如果有省市区编码,回显到级联选择器
+  if (res.data.addressProvince && res.data.addressCity && res.data.addressCounty) {
+    codeArr.value = [res.data.addressProvince, res.data.addressCity, res.data.addressCounty] as any;
+  }
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+  dialog.title = '修改客户联系人信息';
+};
+
+/** 提交按钮 */
+const submitForm = () => {
+  customerContactFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.id) {
+        await updateCustomerContact(form.value).finally(() => (buttonLoading.value = false));
+      } else {
+        await addCustomerContact(form.value).finally(() => (buttonLoading.value = false));
+      }
+      proxy?.$modal.msgSuccess('操作成功');
+      dialog.visible = false;
+      await getList();
+    }
+  });
+};
+
+/** 删除按钮操作 */
+const handleDelete = async (row?: CustomerContactVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('是否确认删除客户联系人信息编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
+  await delCustomerContact(_ids);
+  proxy?.$modal.msgSuccess('删除成功');
+  await getList();
+};
+
+/** 导出按钮操作 */
+const handleExport = () => {
+  proxy?.download(
+    'customer/customerContact/export',
+    {
+      ...queryParams.value
+    },
+    `customerContact_${new Date().getTime()}.xlsx`
+  );
+};
+
+onMounted(() => {
+  loadRoleOptions();
+  getList();
+});
+</script>

+ 64 - 13
src/views/customer/customerFile/customerInfo/add.vue

@@ -3,16 +3,17 @@
     <!-- 企业基本信息 -->
     <el-card shadow="never" class="mb-4">
       <template #header>
-        <span class="font-medium"
+        <span class="font-medium">企业基本信息</span>
+        <!-- <span class="font-medium"
           >企业基本信息/ <span style="color: #ff0033"> 客户编号:{{ customerNumber }} </span></span
-        >
+        > -->
         <el-button style="float: right" type="primary" @click="handleSubmit" :loading="submitLoading">保存</el-button>
       </template>
       <el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
         <el-row :gutter="20">
           <el-col :span="8">
             <el-form-item label="所属公司" prop="belongCompanyId">
-              <el-select v-model="form.belongCompanyId" placeholder="请选择所属公司" class="w-full" filterable>
+              <el-select v-model="form.belongCompanyId" placeholder="请选择所属公司" class="w-full" filterable @change="handCompanyChange">
                 <el-option v-for="item in companyList" :key="item.id" :label="`${item.companyCode} , ${item.companyName}`" :value="item.id" />
               </el-select>
             </el-form-item>
@@ -134,11 +135,11 @@
         </el-row>
 
         <el-row :gutter="20">
-          <el-col :span="8">
+          <!-- <el-col :span="8">
             <el-form-item label="发票抬头" prop="invoiceTop">
               <el-input v-model="form.invoiceTop" placeholder="请输入发票抬头" disabled />
             </el-form-item>
-          </el-col>
+          </el-col> -->
           <el-col :span="8">
             <el-form-item label="详细地址" prop="address">
               <el-cascader
@@ -151,7 +152,7 @@
             </el-form-item>
           </el-col>
           <el-col :span="8">
-            <el-form-item>
+            <el-form-item label-width="0">
               <el-input v-model="form.address" placeholder="请输入详细地址" />
             </el-form-item>
           </el-col>
@@ -405,13 +406,16 @@ import type { EnterpriseScaleVO } from '@/api/customer/customerCategory/enterpri
 import type { IndustryCategoryVO } from '@/api/customer/customerCategory/industryCategory/types';
 import { listComStaff, getComStaff } from '@/api/company/comStaff';
 import { ComStaffVO, ComStaffQuery, ComStaffForm } from '@/api/company/comStaff/types';
+import { listErpStaff } from '@/api/erpData/erpStaff';
+import { ErpStaffVO } from '@/api/erpData/erpStaff/types';
 import { listDept, getDept, listDeptExcludeChild } from '@/api/system/dept';
 import { DeptVO } from '@/api/system/dept/types';
+import { listErpDept, getErpDept } from '@/api/erpData/erpDept';
+import { ErpDeptVO } from '@/api/erpData/erpDept/types';
 
 import { listCompany } from '@/api/company/company';
 import { CompanyVO } from '@/api/company/company/types';
 import { Plus, Delete } from '@element-plus/icons-vue';
-import { log } from 'console';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const { customer_type, customer_level } = toRefs<any>(proxy?.useDict('customer_type', 'customer_level'));
@@ -437,8 +441,8 @@ const companyList = ref<CompanyVO[]>([]);
 const settlementMethodList = ref<SettlementMethodVO[]>([]);
 const customerLevelList = ref<CustomerLevelVO[]>([]);
 const customerTypeList = ref<CustomerTypeVO[]>([]);
-const comStaffList = ref<ComStaffVO[]>([]);
-const comDeptList = ref<DeptVO[]>([]);
+const comStaffList = ref<ErpStaffVO[]>([]);
+const comDeptList = ref<ErpDeptVO[]>([]);
 
 // 企业基本信息表单
 const form = reactive<CustomerInfoForm>({
@@ -558,6 +562,28 @@ const handleLogoSelected = (files: any[]) => {
   }
 };
 
+const handCompanyChange = async (val) => {
+  try {
+    try {
+      // 1. 处理清空情况
+      if (!val) {
+        form.companyName = '';
+        return;
+      }
+      // 2. 在本地列表中查找完整对象
+      const selectedCompany = companyList.value.find((item) => item.id === val);
+
+      if (selectedCompany) {
+        // 3. 赋值操作
+        form.companyName = selectedCompany.companyName;
+      } else {
+        // 如果本地列表没找到(可能是数据不同步),可以选择清空或调用接口查询
+        form.companyName = '';
+      }
+    } catch (error) {}
+  } catch (error) {}
+};
+
 // 营业执照选择处理
 const handleBusinessLicenseSelected = (files: any[]) => {
   if (files && files.length > 0) {
@@ -745,7 +771,7 @@ const loadCustomerTypeList = async () => {
 const loadComStaffList = async () => {
   try {
     const query: any = { status: '0' };
-    const res = await listComStaff(query);
+    const res = await listErpStaff(query);
     comStaffList.value = res.rows || [];
   } catch (error) {
     console.error('加载员工列表失败:', error);
@@ -755,7 +781,7 @@ const loadComStaffList = async () => {
 // 加载部门列表
 const loadComDeptList = async () => {
   try {
-    const res = await listDept();
+    const res = await listErpDept();
     // 处理可能的不同返回结构
     comDeptList.value = res.rows || res.data || [];
   } catch (error) {
@@ -834,7 +860,7 @@ watch(
 
     // 如果列表中没有,从API获取
     try {
-      const res = await getDept(newDeptId);
+      const res = await getErpDept(newDeptId);
       if (res.data) {
         deptName.value = res.data.deptName;
         comDeptList.value.push(res.data);
@@ -856,6 +882,13 @@ const handleEditContact = (row: CustomerContactForm, index: number) => {
 
 // 确认添加/编辑联系人
 const handleContactConfirm = (data: CustomerContactForm) => {
+  // 如果新增/编辑的是主联系人(isPrimary === '0'),将其他所有联系人设置为非主联系人
+  if (data.isPrimary === '0') {
+    contactList.value.forEach((item) => {
+      item.isPrimary = '1';
+    });
+  }
+
   if (currentContactIndex.value >= 0) {
     // 编辑
     contactList.value[currentContactIndex.value] = data;
@@ -881,7 +914,9 @@ const removeContact = (index: number) => {
 
 // 打开添加开票信息对话框
 const handleAddInvoice = () => {
-  currentInvoice.value = undefined;
+  currentInvoice.value = {};
+  currentInvoice.value.taxId = businessForm.socialCreditCode;
+  currentInvoice.value.address = businessForm.businessAddress;
   currentInvoiceIndex.value = -1;
   invoiceDialogVisible.value = true;
 };
@@ -895,6 +930,13 @@ const handleEditInvoice = (row: InvoiceInfoForm, index: number) => {
 
 // 确认添加/编辑开票信息
 const handleInvoiceConfirm = (data: InvoiceInfoForm) => {
+  // 如果新增/编辑的是主账号(isPrimaryAccount === '0'),将其他所有账号设置为非主账号
+  if (data.isPrimaryAccount === '0') {
+    invoiceList.value.forEach((item) => {
+      item.isPrimaryAccount = '1';
+    });
+  }
+
   if (currentInvoiceIndex.value >= 0) {
     // 编辑
     invoiceList.value[currentInvoiceIndex.value] = data;
@@ -927,6 +969,15 @@ const handleSubmit = async () => {
       return;
     }
 
+    if (!salesForm.salesPersonId) {
+      ElMessage.warning('请选择业务人员!');
+      return;
+    }
+    if (!salesForm.serviceStaffId) {
+      ElMessage.warning('请选择客服人员!');
+      return;
+    }
+
     // 2. 表单字段校验
     await formRef.value?.validate();
     await salesFormRef.value?.validate();

+ 15 - 2
src/views/customer/customerFile/customerInfo/components/addContactDialog.vue

@@ -92,7 +92,7 @@ interface Emits {
   (e: 'confirm', data: CustomerContactForm): void;
 }
 
-const roleOptions = ref<RoleVO[]>([]);
+const roleOptions = ref<any>([]);
 
 const props = withDefaults(defineProps<Props>(), {
   modelValue: false,
@@ -149,6 +149,12 @@ watch(
   (newVal) => {
     if (newVal) {
       Object.assign(form, newVal);
+      // 回显地址选择器
+      if (newVal.addressProvince && newVal.addressCity && newVal.addressCounty) {
+        codeArr.value = [newVal.addressProvince, newVal.addressCity, newVal.addressCounty] as any;
+      } else {
+        codeArr.value = [];
+      }
     }
   },
   { immediate: true, deep: true }
@@ -199,7 +205,13 @@ const loadRoleOptions = async () => {
       status: ''
     } as any);
 
-    roleOptions.value = res?.rows || res?.data?.rows || res?.data || [];
+    // roleOptions.value = res?.rows || res?.data?.rows || res?.data || [];
+    roleOptions.value = [
+      { roleId: 1, roleName: '管理角色' },
+      { roleId: 2, roleName: '采购角色' },
+      { roleId: 3, roleName: '查询角色' },
+      { roleId: 4, roleName: '审核角色' }
+    ];
   } catch (error) {
     console.error('加载角色列表失败:', error);
     roleOptions.value = [];
@@ -224,6 +236,7 @@ onMounted(() => {
 // 关闭
 const handleClose = () => {
   formRef.value?.resetFields();
+  codeArr.value = [];
   Object.assign(form, {
     contactName: '',
     phone: '',

+ 10 - 10
src/views/customer/customerFile/customerInfo/edit.vue

@@ -10,14 +10,14 @@
 
     <!-- 菜单导航 -->
     <el-card shadow="never" class="mb-4">
-      <el-menu :default-active="activeMenu" class="el-menu-demo" mode="horizontal" @select="handleSelect">
+      <el-menu :default-active="activeMenu" class="el-menu-demo" mode="horizontal" @select="handleSelect" :ellipsis="false">
         <el-menu-item index="baseInfo">基本信息</el-menu-item>
-        <el-menu-item index="orgStructure">企业组织结构</el-menu-item>
-        <el-menu-item index="contactInfo">联系人信息</el-menu-item>
+        <el-menu-item index="orgStructure">组织结构</el-menu-item>
+        <el-menu-item index="contactInfo">联系人</el-menu-item>
         <el-menu-item index="shippingAddress">收货地址</el-menu-item>
         <el-menu-item index="contractManagement">合同管理</el-menu-item>
-        <el-menu-item index="procurementProfile">采购画像</el-menu-item>
-        <el-menu-item index="operationLog">日志管理</el-menu-item>
+        <!-- <el-menu-item index="procurementProfile">采购画像</el-menu-item>
+        <el-menu-item index="operationLog">日志管理</el-menu-item> -->
       </el-menu>
       <!-- 动态组件显示区域 -->
       <component :is="currentComponent" :customer-id="customerId" :customer-no="customerNo" :customer-name="customerName" />
@@ -40,8 +40,8 @@ const OrgStructure = defineAsyncComponent(() => import('@/views/customer/custome
 const ContactInfo = defineAsyncComponent(() => import('@/views/customer/customerFile/customerInfo/overview/contactInfo.vue'));
 const ShippingAddress = defineAsyncComponent(() => import('@/views/customer/customerFile/customerInfo/overview/shippingAddress.vue'));
 const ContractManagement = defineAsyncComponent(() => import('@/views/customer/customerFile/customerInfo/overview/contractManagement.vue'));
-const ProcurementProfile = defineAsyncComponent(() => import('@/views/customer/customerFile/customerInfo/overview/procurementProfile.vue'));
-const OperationLog = defineAsyncComponent(() => import('@/views/customer/customerFile/customerInfo/overview/operationLog.vue'));
+// const ProcurementProfile = defineAsyncComponent(() => import('@/views/customer/customerFile/customerInfo/overview/procurementProfile.vue'));
+// const OperationLog = defineAsyncComponent(() => import('@/views/customer/customerFile/customerInfo/overview/operationLog.vue'));
 
 // 组件映射
 const componentMap: Record<string, any> = {
@@ -49,9 +49,9 @@ const componentMap: Record<string, any> = {
   orgStructure: OrgStructure,
   contactInfo: ContactInfo,
   shippingAddress: ShippingAddress,
-  contractManagement: ContractManagement,
-  procurementProfile: ProcurementProfile,
-  operationLog: OperationLog
+  contractManagement: ContractManagement
+  // procurementProfile: ProcurementProfile,
+  // operationLog: OperationLog
 };
 
 const currentComponent = ref(componentMap['baseInfo']); // 默认显示基本信息

+ 44 - 38
src/views/customer/customerFile/customerInfo/index.vue

@@ -74,7 +74,7 @@
               <el-col :span="6">
                 <el-form-item label="归属部门" prop="belongingDepartmentId" style="width: 100%; margin-right: 0">
                   <el-select v-model="queryParams.belongingDepartmentId" placeholder="请选择归属部门" clearable filterable style="width: 100%">
-                    <el-option v-for="item in comDeptList" :key="item.id" :label="item.deptName" :value="item.id" />
+                    <el-option v-for="item in comDeptList" :key="item.deptId" :label="item.deptName" :value="item.deptId" />
                   </el-select>
                 </el-form-item>
               </el-col>
@@ -100,34 +100,35 @@
 
     <el-card shadow="never">
       <template #header>
-        <el-row :gutter="10" class="mb8">
-          <el-col :span="17"><span>客户信息列表</span> </el-col>
-          <el-col :span="1.5">
+        <!-- 1. 添加 type="flex" 和 justify="space-between" -->
+        <!-- 2. align="middle" 可以让文字和按钮垂直居中 -->
+        <el-row :gutter="10" class="mb8" type="flex" justify="space-between" align="middle">
+          <!-- 左侧标题 -->
+          <span style="font-size: 16px; font-weight: 500">客户信息列表</span>
+
+          <!-- 右侧按钮组 -->
+          <!-- 这里的 flex-wrap: nowrap 保证了按钮挤在一起也不换行 -->
+          <div style="display: flex; flex-wrap: nowrap; gap: 10px">
             <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['customer:customerInfo:add']">新增</el-button>
-          </el-col>
-          <el-col :span="1.5">
-            <el-button type="primary" plain :disabled="single" @click="handleTransferSalesPerson" v-hasPermi="['customer:customerInfo:edit']"
-              >转移业务员</el-button
-            >
-          </el-col>
-          <el-col :span="1.5">
-            <el-button type="primary" plain :disabled="single" @click="handleTransferServiceStaff" v-hasPermi="['customer:customerInfo:edit']"
-              >转移客服人员</el-button
-            >
-          </el-col>
-          <el-col :span="1.5">
-            <el-button type="primary" plain :disabled="single" @click="updateCreditAmountBtn" v-hasPermi="['customer:customerInfo:edit']"
-              >修改临时额度</el-button
-            >
-          </el-col>
-          <el-col :span="1.5">
-            <el-button type="primary" plain :disabled="single" @click="setCustomerInfoTagBtn" v-hasPermi="['customer:customerInfo:edit']"
-              >设置标签</el-button
-            >
-          </el-col>
-          <el-col :span="1.5">
+
+            <el-button type="primary" plain :disabled="single" @click="handleTransferSalesPerson" v-hasPermi="['customer:customerInfo:edit']">
+              转移业务负责人
+            </el-button>
+
+            <el-button type="primary" plain :disabled="single" @click="handleTransferServiceStaff" v-hasPermi="['customer:customerInfo:edit']">
+              转移客服支持
+            </el-button>
+
+            <el-button type="primary" plain :disabled="single" @click="updateCreditAmountBtn" v-hasPermi="['customer:customerInfo:edit']">
+              修改临时额度
+            </el-button>
+
+            <el-button type="primary" plain :disabled="single" @click="setCustomerInfoTagBtn" v-hasPermi="['customer:customerInfo:edit']">
+              设置标签
+            </el-button>
+
             <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['customer:customerInfo:export']">导出</el-button>
-          </el-col>
+          </div>
         </el-row>
       </template>
 
@@ -137,19 +138,19 @@
         <el-table-column label="归属公司" align="left" prop="companyName" min-width="150" />
         <el-table-column label="客户编号" align="center" prop="customerNo" min-width="120" />
         <el-table-column label="行业" align="center" prop="industryCategory" min-width="120"> </el-table-column>
-        <el-table-column label="业务" align="center" min-width="100">
+        <el-table-column label="业务负责人" align="center" min-width="100">
           <template #default="scope">
-            <span>{{ scope.row.customerSalesInfoVo?.salesPerson }}</span>
+            <span>{{ scope.row.salesPersonName }}</span>
           </template>
         </el-table-column>
-        <el-table-column label="客服人员" align="center" min-width="100">
+        <el-table-column label="客服支持" align="center" min-width="100">
           <template #default="scope">
-            <span>{{ scope.row.customerSalesInfoVo?.serviceStaff }}</span>
+            <span>{{ scope.row.serviceStaffName }}</span>
           </template>
         </el-table-column>
-        <el-table-column label="归属部门" align="center" prop="belongingDepartmentId" min-width="120">
+        <el-table-column label="部门" align="center" min-width="120">
           <template #default="scope">
-            <span>{{ scope.row.customerSalesInfoVo?.belongingDepartment }}</span>
+            <span>{{ scope.row.belongingDepartmentName }}</span>
           </template>
         </el-table-column>
         <el-table-column label="信用额" align="center" prop="creditAmount" min-width="80">
@@ -157,6 +158,11 @@
             <span>{{ scope.row.customerSalesInfoVo?.creditAmount }}</span>
           </template>
         </el-table-column>
+        <!-- <el-table-column label="临时额度" align="center" prop="temporaryQuota" min-width="80">
+          <template #default="scope">
+            <span>{{ scope.row.customerSalesInfoVo?.temporaryQuota }}</span>
+          </template>
+        </el-table-column> -->
         <el-table-column label="状态" align="center" prop="status" min-width="80">
           <template #default="scope">
             <dict-tag :options="sys_check_status" :value="scope.row.status" />
@@ -164,6 +170,7 @@
         </el-table-column>
         <el-table-column label="操作" align="center" width="150" fixed="right">
           <template #default="scope">
+            <el-button link type="primary" @click="handleUpdate(scope.row)" v-hasPermi="['customer:customerInfo:edit']">查看</el-button>
             <el-button link type="primary" @click="handleUpdate(scope.row)" v-hasPermi="['customer:customerInfo:edit']">编辑</el-button>
             <el-button link type="primary" v-if="scope.row.status == '0'" @click="handleCheck(scope.row)" v-hasPermi="['customer:customerInfo:edit']"
               >审核</el-button
@@ -316,8 +323,8 @@ import { CustomerLevelVO, CustomerLevelQuery } from '@/api/customer/customerLeve
 import { CompanyVO } from '@/api/company/company/types';
 import { listComStaff, getComStaff } from '@/api/company/comStaff';
 import { ComStaffVO, ComStaffQuery, ComStaffForm } from '@/api/company/comStaff/types';
-import { listComDept, getComDept } from '@/api/company/comDept';
-import { ComDeptVO } from '@/api/company/comDept/types';
+import { listDept, getDept } from '@/api/system/dept';
+import { DeptVO } from '@/api/system/dept/types';
 import { WarningFilled } from '@element-plus/icons-vue';
 const router = useRouter();
 const customerInfoList = ref<CustomerInfoVO[]>([]);
@@ -326,7 +333,7 @@ const customerLevelList = ref<CustomerLevelVO[]>([]);
 const industryCategoryList = ref<IndustryCategoryVO[]>([]);
 const companyList = ref<CompanyVO[]>([]);
 const comStaffList = ref<ComStaffVO[]>([]);
-const comDeptList = ref<ComDeptVO[]>([]);
+const comDeptList = ref<DeptVO[]>([]);
 const buttonLoading = ref(false);
 const loading = ref(true);
 const transferConfirmLoading = ref(false);
@@ -827,7 +834,6 @@ const loadComStaffList = async () => {
   try {
     const query: ComStaffQuery = { status: '0' } as any;
     const res = await listComStaff(query);
-    console.log(res);
 
     comStaffList.value = res.rows || [];
   } catch (error) {
@@ -838,7 +844,7 @@ const loadComStaffList = async () => {
 /** 加载部门列表 */
 const loadComDeptList = async () => {
   try {
-    const res = await listComDept();
+    const res = await listDept();
     comDeptList.value = res.rows || res.data || [];
   } catch (error) {
     console.error('加载部门列表失败:', error);

+ 220 - 60
src/views/customer/customerFile/customerInfo/overview/baseInfo.vue

@@ -4,17 +4,18 @@
     <el-card shadow="never" class="mb-4">
       <template #header>
         <div class="flex justify-between items-center">
-          <span class="font-medium"
+          <span class="font-medium">企业基本信息 </span>
+          <el-button type="primary" style="float: right" @click="handleSave">保存</el-button>
+          <!-- <span class="font-medium"
             >企业基本信息 / <span style="color: #ff0033">客户编号:{{ customerNumber }}</span></span
-          >
-          <el-button type="primary" @click="handleSave">保存</el-button>
+          > -->
         </div>
       </template>
       <el-form ref="formRef" :model="form" :rules="rules" label-width="140px">
         <el-row :gutter="20">
           <el-col :span="8">
             <el-form-item label="所属公司" prop="belongCompanyId">
-              <el-select v-model="form.belongCompanyId" placeholder="请选择所属公司" class="w-full" filterable>
+              <el-select v-model="form.belongCompanyId" placeholder="请选择所属公司" class="w-full" filterable @change="handCompanyChange">
                 <el-option v-for="item in companyList" :key="item.id" :label="`${item.companyCode} , ${item.companyName}`" :value="item.id" />
               </el-select>
             </el-form-item>
@@ -144,11 +145,11 @@
         </el-row>
 
         <el-row :gutter="20">
-          <el-col :span="8">
+          <!-- <el-col :span="8">
             <el-form-item label="发票抬头" prop="invoiceTop">
               <el-input v-model="form.invoiceTop" placeholder="请输入发票抬头" disabled />
             </el-form-item>
-          </el-col>
+          </el-col> -->
           <el-col :span="8">
             <el-form-item label="详细地址" prop="address">
               <el-cascader
@@ -161,7 +162,7 @@
             </el-form-item>
           </el-col>
           <el-col :span="8">
-            <el-form-item>
+            <el-form-item label-width="0">
               <el-input v-model="form.address" placeholder="请输入详细地址" />
             </el-form-item>
           </el-col>
@@ -242,7 +243,7 @@
         </el-row>
 
         <el-row :gutter="20">
-          <el-col :span="24">
+          <!-- <el-col :span="24">
             <el-form-item label="营业执照">
               <div
                 v-if="!businessInfo.businessLicense"
@@ -284,17 +285,42 @@
                 />
               </div>
             </el-form-item>
-          </el-col>
+          </el-col> -->
         </el-row>
       </el-form>
     </el-card>
+    <!-- 公司开票信息 -->
+    <el-card shadow="never" class="mb-4">
+      <template #header>
+        <div class="flex justify-between items-center">
+          <span class="font-medium">企业开票信息</span>
+          <el-button type="primary" @click="handleAddInvoice">新增</el-button>
+        </div>
+      </template>
+      <el-table :data="invoiceList" border>
+        <el-table-column type="index" label="序号" align="center" width="60" />
+        <el-table-column label="是否主账号" align="center" prop="isPrimaryAccount" min-width="120">
+          <template #default="{ row }">
+            <span>{{ row.isPrimaryAccount === '0' ? '是' : '否' }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="开户行名称" align="center" prop="bankName" min-width="180" />
+        <el-table-column label="银行账户" align="center" prop="bankAccount" min-width="180" />
+        <el-table-column label="操作" align="center" width="150" fixed="right">
+          <template #default="{ row, $index }">
+            <el-button link type="primary" @click="handleEditInvoice(row, $index)">编辑</el-button>
+            <el-button link type="danger" @click="removeInvoice(row)">删除</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-card>
 
-    <!-- 销售信息 -->
+    <!-- 销售信息 --改为 ERP数据-->
     <el-card shadow="never" class="mb-4">
       <template #header>
         <div class="flex justify-between items-center">
-          <span class="font-medium">销售信息</span>
-          <el-button type="primary" @click="handleSave">保存</el-button>
+          <span class="font-medium">ERP销售信息</span>
+          <!-- <el-button type="primary" @click="handleSave">保存</el-button> -->
         </div>
       </template>
       <el-form ref="salesFormRef" :model="salesForm" :rules="salesRules" label-width="140px">
@@ -365,53 +391,85 @@
               <el-input v-model="salesForm.creditPaymentPassword" type="password" placeholder="请输入支付密码" show-password />
             </el-form-item>
           </el-col>
-          <el-col :span="8">
+          <!-- <el-col :span="8">
             <el-form-item label="收款条件" prop="accountPeriod">
               <el-select v-model="salesForm.accountPeriod" placeholder="请选择收款条件" class="w-full" filterable>
                 <el-option v-for="item in settlementMethodList" :key="item.id" :label="item.settlementName" :value="item.id" />
               </el-select>
             </el-form-item>
-          </el-col>
+          </el-col> -->
         </el-row>
 
         <el-row :gutter="20">
           <el-col :span="8">
-            <el-form-item label="结算方式" prop="payDays">
-              <el-select v-model="salesForm.payDays" placeholder="请选择结算方式" class="w-full" filterable>
+            <el-form-item label="结算方式" prop="settlementMethod">
+              <el-select v-model="salesForm.settlementMethod" placeholder="请选择结算方式" class="w-full" filterable>
                 <el-option v-for="item in settlementMethodList" :key="item.id" :label="item.settlementName" :value="item.id" />
               </el-select>
             </el-form-item>
           </el-col>
+          <el-col :span="8">
+            <el-form-item label="客户来源" prop="payDays">
+              <el-select v-model="salesForm.customerSource" class="w-full" filterable>
+                <el-option v-for="dict in customer_source" :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="payDays">
+              <el-select v-model="salesForm.sellChannel" class="w-full" filterable>
+                <el-option v-for="dict in sell_channel" :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="8">
+            <el-form-item label="税码" prop="rateId">
+              <el-select v-model="salesForm.rateId" placeholder="税码" class="w-full" filterable>
+                <el-option v-for="item in taxrateList" :key="item.id" :label="item.taxrateNo" :value="item.id" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="交易币别" prop="dealCurrencyId">
+              <el-select v-model="salesForm.dealCurrencyId" placeholder="请选择交易币别" class="w-full" filterable>
+                <el-option v-for="item in currencyList" :key="item.id" :label="item.currencyName" :value="item.id" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="账款归属" prop="payDays">
+              <el-input v-model="form.customerNo" disabled />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="8">
+            <el-form-item label="单价含税" prop="payDays">
+              <el-select v-model="salesForm.unitPrice" placeholder="单价含税" class="w-full" filterable>
+                <el-option v-for="item in unitPriceArr" :key="item.value" :label="item.label" :value="item.value" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="账款额度超限" prop="creditLimit">
+              <el-select v-model="salesForm.creditLimit" placeholder="请选择账款额度超限" class="w-full" filterable>
+                <el-option v-for="dict in erp_is_enabled" :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="creditTimeLimit">
+              <el-select v-model="salesForm.creditTimeLimit" placeholder="请选择账款超期" class="w-full" filterable>
+                <el-option v-for="dict in erp_is_enabled" :key="dict.value" :label="dict.label" :value="dict.value" />
+              </el-select>
+            </el-form-item>
+          </el-col>
         </el-row>
       </el-form>
     </el-card>
 
-    <!-- 公司开票信息 -->
-    <el-card shadow="never" class="mb-4">
-      <template #header>
-        <div class="flex justify-between items-center">
-          <span class="font-medium">企业开票信息</span>
-          <el-button type="primary" @click="handleAddInvoice">新增</el-button>
-        </div>
-      </template>
-      <el-table :data="invoiceList" border>
-        <el-table-column type="index" label="序号" align="center" width="60" />
-        <el-table-column label="是否主账号" align="center" prop="isPrimaryAccount" min-width="120">
-          <template #default="{ row }">
-            <span>{{ row.isPrimaryAccount === '0' ? '是' : '否' }}</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="开户行名称" align="center" prop="bankName" min-width="180" />
-        <el-table-column label="银行账户" align="center" prop="bankAccount" min-width="180" />
-        <el-table-column label="操作" align="center" width="150" fixed="right">
-          <template #default="{ row, $index }">
-            <el-button link type="primary" @click="handleEditInvoice(row, $index)">编辑</el-button>
-            <el-button link type="danger" @click="removeInvoice(row)">删除</el-button>
-          </template>
-        </el-table-column>
-      </el-table>
-    </el-card>
-
     <!-- 添加/编辑开票信息对话框 -->
     <add-invoice-dialog v-model="invoiceDialogVisible" :edit-data="currentInvoice" @confirm="handleInvoiceConfirm" />
 
@@ -454,14 +512,22 @@ import type { InvoiceTypeVO } from '@/api/customer/invoiceType/types';
 import { listCompany } from '@/api/company/company';
 import { CompanyVO } from '@/api/company/company/types';
 import { listComStaff } from '@/api/company/comStaff';
+import { listErpStaff } from '@/api/erpData/erpStaff';
 import { ComStaffVO, ComStaffQuery } from '@/api/company/comStaff/types';
+import { ErpStaffVO } from '@/api/erpData/erpStaff/types';
 import { listDept, getDept } from '@/api/system/dept';
 import { DeptVO } from '@/api/system/dept/types';
+import { listErpDept, getErpDept } from '@/api/erpData/erpDept';
+import { ErpDeptVO } from '@/api/erpData/erpDept/types';
 import FileSelector from '@/components/FileSelector/index.vue';
 import { Plus, Delete } from '@element-plus/icons-vue';
-
+import { listComCurrency } from '@/api/company/comCurrency';
+import { listTaxrate } from '@/api/company/taxrate';
+import { ComCurrencyVO } from '@/api/company/comCurrency/types';
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
-const { order_check_way, customer_type, customer_level } = toRefs<any>(proxy?.useDict('order_check_way', 'customer_type', 'customer_level'));
+const { order_check_way, customer_type, customer_level, customer_source, sell_channel, erp_is_enabled } = toRefs<any>(
+  proxy?.useDict('order_check_way', 'customer_type', 'customer_level', 'customer_source', 'sell_channel', 'erp_is_enabled')
+);
 // 接收父组件传递的props
 const props = defineProps<{
   customerId?: string | number;
@@ -470,6 +536,10 @@ const props = defineProps<{
 
 const route = useRoute();
 const router = useRouter();
+const unitPriceArr = ref([
+  { label: '含税', value: 'True' },
+  { label: '不含税', value: 'False' }
+]);
 
 const formRef = ref<any>(null);
 const salesFormRef = ref<any>(null);
@@ -487,9 +557,10 @@ const settlementMethodList = ref<SettlementMethodVO[]>([]);
 const creditLevelList = ref<CreditLevelVO[]>([]);
 const customerLevelList = ref<CustomerLevelVO[]>([]);
 const customerTypeList = ref<CustomerTypeVO[]>([]);
-const comStaffList = ref<ComStaffVO[]>([]);
-const comDeptList = ref<DeptVO[]>([]);
-
+const comStaffList = ref<ErpStaffVO[]>([]);
+const comDeptList = ref<ErpDeptVO[]>([]);
+const currencyList = ref<ComCurrencyVO[]>([]);
+const taxrateList = ref<any[]>([]);
 // 企业基本信息(用于显示)
 const customerInfo = reactive<any>({
   customerNo: '',
@@ -570,6 +641,13 @@ const salesForm = reactive<SalesInfoForm>({
   creditPaymentPassword: '',
   accountPeriod: '',
   payDays: undefined,
+  customerSource: '1',
+  unitPrice: 'True',
+  sellChannel: '1',
+  creditLimit: '1',
+  creditTimeLimit: '1',
+  dealCurrencyId: undefined,
+  settlementMethod: undefined,
   status: '0'
 });
 
@@ -617,6 +695,33 @@ const salesRules = {
   serviceStaffId: [{ required: true, message: '请选择客服人员', trigger: 'change' }]
 };
 
+// 加载币种列表
+const loadCurrencyList = async () => {
+  try {
+    const res = await listComCurrency({
+      isShow: '0',
+      pageNum: 1,
+      pageSize: 1000
+    });
+    currencyList.value = res.rows || [];
+  } catch (error) {
+    console.error('加载币种列表失败:', error);
+    currencyList.value = [];
+  }
+};
+
+// 加载税码列表
+const loadTaxrateList = async () => {
+  try {
+    const res = await listTaxrate({
+      isShow: '0',
+      pageNum: 1,
+      pageSize: 1000
+    });
+    taxrateList.value = res.rows || [];
+  } catch (error) {}
+};
+
 // 初始化
 onMounted(async () => {
   // 加载下拉框数据
@@ -630,6 +735,8 @@ onMounted(async () => {
   await loadCustomerTypeList();
   await loadComStaffList();
   await loadComDeptList();
+  await loadCurrencyList();
+  await loadTaxrateList();
 
   // 优先使用props传递的customerId,否则从路由获取
   const id = props.customerId || route.query.id;
@@ -639,6 +746,28 @@ onMounted(async () => {
   }
 });
 
+const handCompanyChange = async (val) => {
+  try {
+    try {
+      // 1. 处理清空情况
+      if (!val) {
+        form.companyName = '';
+        return;
+      }
+      // 2. 在本地列表中查找完整对象
+      const selectedCompany = companyList.value.find((item) => item.id === val);
+
+      if (selectedCompany) {
+        // 3. 赋值操作
+        form.companyName = selectedCompany.companyName;
+      } else {
+        // 如果本地列表没找到(可能是数据不同步),可以选择清空或调用接口查询
+        form.companyName = '';
+      }
+    } catch (error) {}
+  } catch (error) {}
+};
+
 // 监听props变化
 watch(
   () => props.customerId,
@@ -751,8 +880,8 @@ const loadCustomerTypeList = async () => {
 // 加载员工列表
 const loadComStaffList = async () => {
   try {
-    const query: any = { status: '0' };
-    const res = await listComStaff(query);
+    const query: any = {};
+    const res = await listErpStaff(query);
     comStaffList.value = res.rows || [];
   } catch (error) {
     console.error('加载员工列表失败:', error);
@@ -762,7 +891,7 @@ const loadComStaffList = async () => {
 // 加载部门列表
 const loadComDeptList = async () => {
   try {
-    const res = await listDept();
+    const res = await listErpDept();
     // 处理可能的不同返回结构
     comDeptList.value = res.rows || res.data || [];
   } catch (error) {
@@ -820,7 +949,7 @@ const loadCustomerData = async (id: any) => {
         const deptExists = comDeptList.value.find((d) => String(d.deptId) === String(salesForm.belongingDepartmentId));
         if (!deptExists) {
           try {
-            const deptRes = await getDept(salesForm.belongingDepartmentId);
+            const deptRes = await getErpDept(salesForm.belongingDepartmentId);
             if (deptRes.data) {
               comDeptList.value.push(deptRes.data);
             }
@@ -892,7 +1021,7 @@ watch(
       const res = await getDept(newDeptId);
       if (res.data) {
         deptName.value = res.data.deptName;
-        comDeptList.value.push(res.data);
+        comDeptList.value.push(res.data as any);
       }
     } catch (error) {
       console.error('获取部门信息失败:', error);
@@ -950,7 +1079,7 @@ const handleSalesPersonChange = async (staffId: any) => {
       try {
         const res = await getDept(selectedStaff.deptId);
         if (res.data) {
-          comDeptList.value.push(res.data);
+          comDeptList.value.push(res.data as any);
         }
       } catch (error) {
         console.error('获取部门信息失败:', error);
@@ -1096,15 +1225,28 @@ const removeInvoice = (data: any) => {
 
 // 保存按钮
 const handleSave = async () => {
+  // 1. 先检查 ref 是否存在,避免静默失败
+  if (!formRef.value || !salesFormRef.value) {
+    ElMessage.error('表单组件未加载,请刷新页面');
+    return;
+  }
+
   try {
-    // 验证基本信息表单
-    await formRef.value?.validate();
-    // 验证销售信息表单
-    await salesFormRef.value?.validate();
+    if (!salesForm.salesPersonId) {
+      ElMessage.warning('请选择业务人员!');
+      return;
+    }
+    if (!salesForm.serviceStaffId) {
+      ElMessage.warning('请选择客服人员!');
+      return;
+    }
+    // 2. 串行验证,任何一步失败都会抛出异常,进入 catch
+    // 注意:ElementPlus 的 validate 失败时会 reject 一个包含错误信息的对象
+    await formRef.value.validate();
+    await salesFormRef.value.validate();
 
     submitLoading.value = true;
 
-    // 组装提交数据
     const submitData: CustomerInfoForm = {
       ...form,
       customerBusinessBo: businessInfo,
@@ -1113,13 +1255,31 @@ const handleSave = async () => {
     };
 
     await updateCustomerInfo(submitData);
+
     ElMessage.success('保存成功');
 
     // 重新加载数据
     await loadCustomerData(custId.value);
+
+    // 可选:保存成功后重置表单或关闭弹窗
+    // emit('update:visible', false);
   } catch (error) {
-    console.error('保存失败:', error);
-    ElMessage.error('保存失败,请重试');
+    // 3. 关键优化:区分验证错误和系统错误
+
+    // ElementPlus 的 validate 失败时,error 通常是一个包含 fields 信息的对象
+    // 或者你可以判断 error 是否包含特定的验证失败标记
+    const isValidationError = error && typeof error === 'object' && 'fields' in error;
+    // 有些版本可能是 error instanceof Error 且 message 包含特定字样,具体视 EP 版本而定
+    // 最简单的判断:如果是因为 validate 抛出的,通常不需要报全局错误,因为表单上已经红字提示了
+
+    if (isValidationError) {
+      console.warn('表单验证未通过', error);
+      // 让用户专注于表单上的红色提示,不要弹全局 Toast 干扰用户
+      return;
+    }
+
+    // 如果是真正的系统错误(如网络断开、后端报错)
+    ElMessage.error('保存失败,请稍后重试或联系管理员');
   } finally {
     submitLoading.value = false;
   }

+ 16 - 11
src/views/customer/customerFile/customerInfo/overview/contactInfo.vue

@@ -3,7 +3,7 @@
     <el-card shadow="never">
       <template #header>
         <el-row :gutter="10" class="mb8">
-          <el-col :span="22">
+          <el-col :span="20">
             <span>联系人信息列表</span>
           </el-col>
           <el-col :span="1.5">
@@ -189,7 +189,7 @@ const dialog = reactive<DialogOption>({
   title: ''
 });
 
-const roleOptions = ref<RoleVO[]>([]);
+const roleOptions = ref<any>([]);
 
 const initFormData: CustomerContactForm = {
   id: undefined,
@@ -262,15 +262,20 @@ const getList = async () => {
 /** 加载角色列表 */
 const loadRoleOptions = async () => {
   try {
-    const res: any = await listRole({
-      pageNum: 1,
-      pageSize: 9999,
-      roleName: '',
-      roleKey: '',
-      status: ''
-    } as any);
-
-    roleOptions.value = res?.rows || res?.data?.rows || res?.data || [];
+    // const res: any = await listRole({
+    //   pageNum: 1,
+    //   pageSize: 9999,
+    //   roleName: '',
+    //   roleKey: '',
+    //   status: ''
+    // } as any);
+    // roleOptions.value = res?.rows || res?.data?.rows || res?.data || [];
+    roleOptions.value = [
+      { roleId: 1, roleName: '管理角色' },
+      { roleId: 2, roleName: '采购角色' },
+      { roleId: 3, roleName: '查询角色' },
+      { roleId: 4, roleName: '审核角色' }
+    ];
   } catch (error) {
     console.error('加载角色列表失败:', error);
     roleOptions.value = [];

+ 1 - 1
src/views/customer/customerFile/customerInfo/overview/contractManagement.vue

@@ -32,7 +32,7 @@
     <el-card shadow="never">
       <template #header>
         <el-row :gutter="10" class="mb8">
-          <el-col :span="22">
+          <el-col :span="20">
             <span>合同管理信息列表</span>
           </el-col>
           <el-col :span="1.5">

+ 6 - 4
src/views/customer/customerFile/customerInfo/overview/orgStructure.vue

@@ -228,7 +228,7 @@ const data = reactive<PageData<CustomerDeptForm, CustomerDeptQuery>>({
     params: {}
   },
   rules: {
-    parentId: [{ required: true, message: '上级部门不能为空', trigger: 'change' }],
+    // parentId: [{ required: true, message: '上级部门不能为空', trigger: 'change' }],
     deptName: [{ required: true, message: '部门名称不能为空', trigger: 'blur' }],
     deptManage: [{ required: true, message: '部门主管不能为空', trigger: 'blur' }],
     customerId: [{ required: true, message: '客户编号不能为空', trigger: 'blur' }]
@@ -369,11 +369,13 @@ const toggleExpandAll = (data: CustomerDeptVO[], status: boolean) => {
 /** 修改按钮操作 */
 const handleUpdate = async (row: CustomerDeptVO) => {
   const res = await getCustomerDept(row.deptId);
-  Object.assign(form.value, res.data);
   // 修正数据:如果 parentId 等于自己的 id,说明是顶级部门,将 parentId 设为 0
-  if (form.value.parentId === form.value.deptId) {
-    form.value.parentId = 0;
+
+  if (res.data.parentId === 100) {
+    res.data.parentId = undefined;
   }
+  Object.assign(form.value, res.data);
+
   dialog.visible = true;
   dialog.title = '修改部门';
 };

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

@@ -3,11 +3,11 @@
     <el-card shadow="never">
       <template #header>
         <el-row :gutter="10" class="mb8">
-          <el-col :span="22">
+          <el-col :span="20">
             <span>收货地址信息列表</span>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="primary" plain icon="Add" @click="handleAdd" v-hasPermi="['customer:shippingAddress:add']">添加地址</el-button>
+            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['customer:shippingAddress:add']">添加地址</el-button>
           </el-col>
         </el-row>
       </template>

+ 4 - 23
src/views/customer/maintainInfo/add.vue

@@ -309,7 +309,7 @@ onMounted(async () => {
   const _id = route.query.id;
   if (_id) {
     isEdit.value = true;
-    await loadMaintainData(_id);
+    await loadMaintainData(_id as any);
   }
 });
 
@@ -345,8 +345,7 @@ const handleCustomerChange = async (customerId: string | number) => {
 
     // 填充企业基本信息 - 使用API获取真实数据
     customerInfo.customerNo = data.customerNo;
-    customerInfo.cooperationLevel = getCustomerLevelName(data.customerLevelId);
-    customerInfo.memberLevel = getCustomerLevelName(data.customerLevelId);
+    // customerInfo.memberLevel = getCustomerLevelName(data.customerLevelId);
     customerInfo.phone = data.landline || '';
     customerInfo.address = data.address || '';
     customerInfo.provincialCityCounty = data.provincialCityCounty || '';
@@ -355,31 +354,14 @@ const handleCustomerChange = async (customerId: string | number) => {
 
     // 填充销售信息
     if (data.customerSalesInfoVo) {
-      customerInfo.salesPerson = getSalesPersonName(data.customerSalesInfoVo.salesPersonId);
-      customerInfo.servicePerson = getServiceStaffName(data.customerSalesInfoVo.serviceStaffId);
+      customerInfo.salesPerson = res.data.customerSalesInfoVo?.salesPerson;
+      customerInfo.servicePerson = res.data.customerSalesInfoVo?.serviceStaff;
     }
   } catch (error) {
-    console.error('加载企业信息失败:', error);
     ElMessage.error('加载企业信息失败');
   }
 };
 
-// 格式化方法
-const getCustomerLevelName = (id: string | number | undefined) => {
-  const map: Record<string, string> = { '1': 'A级', '2': 'B级', '3': 'C级', '4': 'D级' };
-  return map[String(id)] || '-';
-};
-
-const getSalesPersonName = (id: string | number | undefined) => {
-  const map: Record<string, string> = { '1': '张三', '2': '李四', '3': '王五', '4': '赵六' };
-  return map[String(id)] || '-';
-};
-
-const getServiceStaffName = (id: string | number | undefined) => {
-  const map: Record<string, string> = { '1': '客服A', '2': '客服B', '3': '客服C', '4': '客服D' };
-  return map[String(id)] || '-';
-};
-
 // 加载维保数据
 const loadMaintainData = async (id: string | number) => {
   try {
@@ -398,7 +380,6 @@ const loadMaintainData = async (id: string | number) => {
       await handleCustomerChange(form.customerId);
     }
   } catch (error) {
-    console.error('加载维保数据失败:', error);
     ElMessage.error('加载维保数据失败');
   }
 };

+ 2 - 2
src/views/order/orderAssignment/index.vue

@@ -90,7 +90,7 @@
           <template #default="scope">
             <el-button link type="primary" @click="handleReview(scope.row)">详情</el-button>
             <el-button link type="primary" v-if="scope.row.assignmentStatus != '1'" @click="handleAssignment(scope.row)">分配</el-button>
-            <!-- <el-button link type="primary" v-if="scope.row.assignmentStatus == '1'" @click="handleAssignment(scope.row)">重新分配</el-button> -->
+            <el-button link type="primary" v-if="scope.row.assignmentStatus == '1'" @click="handleAssignment(scope.row)">分配记录</el-button>
           </template>
         </el-table-column>
       </el-table>
@@ -282,7 +282,7 @@ const handleAssignment = async (row?: OrderMainVO | null) => {
 
   if (row) {
     // 单个订单分配 - 直接打开统一的分配抽屉
-    splitAssignDialogRef.value?.open(row.id);
+    splitAssignDialogRef.value?.open(row.id, row.assignmentStatus);
   } else {
     // 批量分配 - 只支持整单分配
     if (!ids.value || ids.value.length === 0) {

+ 65 - 14
src/views/order/orderAssignment/splitAssignDialog.vue

@@ -45,7 +45,7 @@
       </div>
 
       <el-tabs v-model="activeTab" class="detail-tabs">
-        <el-tab-pane label="待分配" name="pending">
+        <el-tab-pane label="待分配" name="pending" v-if="assignmentStatus != '1'">
           <div class="tab-actions">
             <el-button type="primary" @click="handleBatchAssign">+ 批量分配</el-button>
           </div>
@@ -73,7 +73,7 @@
               </template>
             </el-table-column>
             <el-table-column label="商品编号" prop="productNo" align="center" />
-            <el-table-column label="商品名称" prop="productName" min-width="200" show-overflow-tooltip align="center" />
+            <el-table-column label="商品名称" prop="productName" min-width="200" show-overflow-tooltip align="left" />
             <el-table-column label="数量" prop="orderQuantity" align="center" />
             <el-table-column label="单价" prop="orderPrice" align="center" />
             <el-table-column label="小计" align="center">
@@ -109,7 +109,7 @@
               </template>
             </el-table-column>
             <el-table-column label="商品编号" prop="productNo" align="center" />
-            <el-table-column label="商品名称" prop="productName" min-width="200" show-overflow-tooltip align="center" />
+            <el-table-column label="商品名称" prop="productName" min-width="200" show-overflow-tooltip align="left" />
             <el-table-column label="数量" prop="orderQuantity" align="center" />
             <el-table-column label="单价" prop="orderPrice" align="center" />
             <el-table-column label="小计" align="center">
@@ -120,6 +120,7 @@
                 <dict-tag :options="order_assignment_status" :value="scope.row.assignmentStatus" />
               </template>
             </el-table-column>
+            <el-table-column label="分配人" prop="assigneeName" min-width="200" align="left" />
           </el-table>
         </el-tab-pane>
 
@@ -141,17 +142,26 @@
       <!-- 分配目标选择 -->
       <div class="assign-target-section">
         <el-row :gutter="20">
-          <!-- <el-col :span="12">
+          <el-col :span="12">
             <div class="target-input-wrapper">
               <span class="required-mark">*</span>
               <span class="label">分配对象类型:</span>
               <el-select v-model="assignForm.targetType" placeholder="请选择分配对象类型" clearable style="flex: 1" @change="handleTargetTypeChange">
-                <el-option label="SRM供应商" value="srm" />
-                <el-option label="BP伙伴商" value="bp" />
+                <el-option label="自营" value="zy" />
+                <el-option label="供应商" value="srm" />
+              </el-select>
+            </div>
+          </el-col>
+          <!-- <el-col :span="12" v-show="assignForm.targetType === 'zy'">
+            <div class="target-input-wrapper">
+              <span class="required-mark">*</span>
+              <span class="label">自营客户: </span>
+              <el-select v-model="assignForm.targetId" :placeholder="'请选择自营客户'" clearable filterable style="flex: 1">
+                <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" />
               </el-select>
             </div>
           </el-col> -->
-          <el-col :span="12">
+          <el-col :span="12" v-show="assignForm.targetType === 'srm'">
             <div class="target-input-wrapper">
               <span class="required-mark">*</span>
               <span class="label">供应商: </span>
@@ -222,7 +232,7 @@ import { OrderSplitAssignForm, OrderProductAssignRule, OrderAssignmentForm } fro
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const { sys_platform_code, order_assignment_status } = toRefs<any>(proxy?.useDict('sys_platform_code', 'order_assignment_status'));
-
+import { listCustomerInfo } from '@/api/customer/customerFile/customerInfo';
 const emit = defineEmits(['success']);
 
 const buttonLoading = ref(false);
@@ -271,6 +281,10 @@ const cachedSupplierList = ref<any[]>([]);
 // 伙伴商列表
 const partnerList = ref<any[]>([]);
 
+// 自营客户列表
+const customerList = ref<any[]>([]);
+const cachedCustomerList = ref<any[]>([]);
+
 // 标签页
 const activeTab = ref('pending');
 
@@ -283,11 +297,16 @@ const assignedProducts = ref<any[]>([]);
 // 分配记录
 const assignRecords = ref<any[]>([]);
 
+const assignmentStatus = ref('0');
+
 /** 打开订单分配详情抽屉 */
-const open = async (orderId: string | number) => {
+const open = async (orderId: string | number, status: string) => {
   reset();
   drawer.visible = true;
-
+  assignmentStatus.value = status;
+  if (status == '1') {
+    activeTab.value = 'assigned';
+  }
   try {
     // 获取订单详情
     const res = await getOrderMain(orderId);
@@ -328,11 +347,43 @@ const open = async (orderId: string | number) => {
   }
 };
 
+/** 分配对象类型变化 */
+const handleTargetTypeChange = () => {
+  // 切换类型时清空已选择的目标
+  assignForm.targetId = '';
+
+  if (assignForm.targetType === 'zy') {
+    loadCustomerData();
+  } else if (assignForm.targetType === 'srm') {
+    supplierList.value = cachedSupplierList.value;
+  }
+};
+
+/** 加载自营客户数据 */
+const loadCustomerData = async () => {
+  if (cachedCustomerList.value.length > 0) {
+    customerList.value = cachedCustomerList.value;
+    return;
+  }
+  try {
+    const res: any = await listCustomerInfo({ status: '1', pageNum: 1, pageSize: 1000 });
+    const list = res.rows || res.data || [];
+    cachedCustomerList.value = list.map((item: any) => ({
+      ...item,
+      id: item.id,
+      name: item.customerName
+    }));
+    customerList.value = cachedCustomerList.value;
+  } catch (error) {
+    console.error('获取自营客户列表失败', error);
+  }
+};
+
 /** 预加载供应商数据 */
 const loadSupplierData = async () => {
   if (cachedSupplierList.value.length > 0) return;
   try {
-    const res: any = await listInfo({ pageNum: 1, pageSize: 1000 });
+    const res: any = await listInfo({ supplyStatus: 1, pageNum: 1, pageSize: 1000 });
     const list = res.rows || res.data || [];
     cachedSupplierList.value = list.map((item: any) => ({
       ...item,
@@ -465,8 +516,8 @@ const submitAssign = async () => {
   }
 
   // 验证分配目标
-  if (!assignForm.targetId) {
-    proxy?.$modal.msgWarning(`请选择${assignForm.targetType === 'srm' ? '供应商' : '伙伴商'}`);
+  if (assignForm.targetType == 'srm' && !assignForm.targetId) {
+    proxy?.$modal.msgWarning(`请选择${assignForm.targetType === 'srm' ? '供应商' : ''}`);
     return;
   }
 
@@ -494,7 +545,7 @@ const submitAssign = async () => {
     selectedAssignProducts.value = [];
 
     // 刷新抽屉数据
-    await open(orderInfo.value.id);
+    await open(orderInfo.value.id, null);
     emit('success');
   } catch (error) {
     console.error('分配失败:', error);

+ 5 - 1
src/views/order/orderMain/components/addressDialog.vue

@@ -72,7 +72,11 @@ const form = ref<ShippingAddressForm>({ ...initForm });
 
 const rules: FormRules = {
   consignee: [{ required: true, message: '收货人姓名不能为空', trigger: 'blur' }],
-  provincialCityCountry: [{ required: true, message: '详细地址不能为空', trigger: 'blur' }]
+  provincialCityCountry: [{ required: true, message: '详细地址不能为空', trigger: 'blur' }],
+  phone: [
+    { required: true, message: '收货人手机号不能为空', trigger: 'blur' },
+    { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
+  ]
 };
 
 // 监听 modelValue 变化

+ 1 - 1
src/views/order/orderMain/components/chooseAddress.vue

@@ -22,7 +22,7 @@
       </el-table-column>
     </el-table>
 
-    <div v-if="addressList.length === 0" class="empty-data">暂无数据</div>
+    <!-- <div v-if="addressList.length === 0" class="empty-data">暂无数据</div> -->
 
     <template #footer>
       <div class="dialog-footer">

+ 22 - 8
src/views/order/orderMain/index.vue

@@ -55,15 +55,19 @@
 
         <el-row :gutter="20">
           <!-- 第二行 -->
-          <el-col :span="8"> </el-col>
           <el-col :span="8">
-            <el-form-item label="信用额度" prop="creditLimit">
+            <el-form-item label="信用额度">
               <el-input v-model="form.creditLimit" placeholder="0" disabled />
             </el-form-item>
           </el-col>
+          <el-col :span="8">
+            <el-form-item label="临时额度">
+              <el-input v-model="form.temporaryQuota" placeholder="0" disabled />
+            </el-form-item>
+          </el-col>
           <el-col :span="8">
             <el-form-item label="剩余额度">
-              <el-input value="0" disabled />
+              <el-input v-model="form.remainingQuota" placeholder="0" disabled />
             </el-form-item>
           </el-col>
         </el-row>
@@ -480,6 +484,8 @@ const initFormData: OrderMainForm = {
   payType: '0',
   warehouseId: undefined,
   creditLimit: undefined,
+  temporaryQuota: undefined,
+  remainingQuota: undefined,
   expectedDeliveryTime: undefined,
   businessStaff: undefined,
   customerService: undefined,
@@ -563,6 +569,8 @@ watch(
       customerList.value = [];
       customerDeptList.value = [];
       form.value.creditLimit = undefined;
+      form.value.temporaryQuota = undefined;
+      form.value.remainingQuota = undefined;
       form.value.shippingAddressId = undefined;
       form.value.userDept = undefined;
       form.value.businessStaff = undefined;
@@ -658,6 +666,7 @@ const submitForm = () => {
           productUnitId: product.productUnitId,
           productUnit: product.unitName, // 产品单位
           productImage: product.productImage, // 产品图片
+          categoryName: product.categoryName,
           platformPrice: product.price, // 平台价格(单价)
           marketPrice: product.marketPrice,
           minOrderQuantity: product.minOrderQuantity, // 最小起订量
@@ -766,6 +775,7 @@ const handleProductConfirm = (product: any) => {
     id: product.id,
     productCode: product.productNo, // 产品编码
     productImage: product.productImage, // 商品图片
+    categoryName: product.categoryName,
     productName: product.itemName, // 产品信息
     taxRate: product.taxRate || 0, // 税率
     marketPrice: product.marketPrice || 0,
@@ -935,18 +945,22 @@ const handleCustomerChange = async (customerId: string | number) => {
     // 填充客户相关信息
     if (customerInfo.customerSalesInfoVo) {
       const salesInfo = customerInfo.customerSalesInfoVo as any;
-      form.value.creditLimit = salesInfo.remainingQuota || salesInfo.creditAmount;
+      form.value.creditLimit = salesInfo.creditAmount;
+      form.value.remainingQuota = salesInfo.remainingQuota;
+      form.value.temporaryQuota = salesInfo.temporaryQuota;
       // 填充业务人员、客服人员、业务部门
-      form.value.businessStaff = salesInfo.salesPerson;
-      form.value.customerService = salesInfo.serviceStaff;
-      form.value.businessDept = salesInfo.belongingDepartment;
+      form.value.businessStaff = customerInfo.salesPersonName;
+      form.value.customerService = customerInfo.serviceStaffName;
+      form.value.businessDept = customerInfo.belongingDepartmentName;
     }
 
     // 获取客户部门列表
     await getCustomerDeptList(customerId);
 
     // 获取客户开票类型信息
-    getCustmerInvoiceType(customerInfo.invoiceTypeId);
+    if (customerInfo.invoiceTypeId) {
+      getCustmerInvoiceType(customerInfo.invoiceTypeId);
+    }
 
     // 获取客户收货地址列表
     await loadAddressList(customerId);

+ 23 - 1
src/views/order/orderReturn/index.vue

@@ -73,6 +73,12 @@
         <el-table-column label="退货原因" align="center" prop="returnReason" />
         <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
           <template #default="scope">
+            <el-button link type="primary" v-if="scope.row.returnStatus == '0'" icon="Edit" @click="handleChangeReturnStatus(scope.row, '1')"
+              >确认退货</el-button
+            >
+            <el-button link type="primary" v-if="scope.row.returnStatus == '0'" icon="Edit" @click="handleChangeReturnStatus(scope.row, '2')"
+              >拒绝退货</el-button
+            >
             <el-button link type="primary" icon="Edit" @click="handleReview(scope.row)">查看退货信息</el-button>
           </template>
         </el-table-column>
@@ -84,7 +90,7 @@
 </template>
 
 <script setup name="OrderReturn" lang="ts">
-import { listOrderReturn, getOrderReturn, delOrderReturn, addOrderReturn, updateOrderReturn } from '@/api/order/orderReturn';
+import { listOrderReturn, getOrderReturn, delOrderReturn, addOrderReturn, updateOrderReturn, changeReturnStatus } from '@/api/order/orderReturn';
 import { OrderReturnVO, OrderReturnQuery, OrderReturnForm } from '@/api/order/orderReturn/types';
 import { listReturnReason } from '@/api/order/returnReason';
 import { ReturnReasonVO } from '@/api/order/returnReason/types';
@@ -280,6 +286,22 @@ const loadReturnReasonList = async () => {
     returnReasonList.value = [];
   }
 };
+const handleChangeReturnStatus = async (row?: OrderReturnVO, returnStatus?: string) => {
+  const _no = row?.returnNo;
+  const _ids = row?.id || ids.value;
+  const oldValue = row.returnStatus; // 保存旧值
+  const text = returnStatus === '1' ? '确认退货' : returnStatus === '2' ? '拒绝退货' : '';
+  await proxy?.$modal.confirm('是否' + text + '当前退货单编号【' + _no + '】吗?').finally(() => (loading.value = false));
+  //todo 发送
+  try {
+    await changeReturnStatus(row.id, returnStatus); // 传新值
+  } catch (e) {
+    // 恢复旧值
+    row.returnStatus = oldValue;
+  }
+  proxy?.$modal.msgSuccess('操作成功');
+  await getList();
+};
 
 onMounted(() => {
   getList();

+ 3 - 3
src/views/order/orderReturn/returnDetail.vue

@@ -31,15 +31,15 @@
         </el-descriptions-item>
 
         <el-descriptions-item label="凭证照片" :span="2">
-          <div v-if="orderDetail.voucherPhoto && orderDetail.voucherPhoto.length > 0">
+          <!-- 确保 split(',') 将字符串转为数组进行遍历 -->
+          <div v-if="orderDetail.voucherPhoto">
             <el-image
-              v-for="(url, index) in orderDetail.voucherPhoto"
+              v-for="(url, index) in orderDetail.voucherPhoto.split(',')"
               :key="index"
               :src="url"
               :preview-src-list="orderDetail.voucherPhoto.split(',')"
               style="width: 80px; height: 80px; margin-right: 8px; object-fit: cover"
               fit="cover"
-              :initial-index="index"
             />
           </div>
           <span v-else>无</span>

+ 314 - 0
src/views/order/saleOrder/addOrderStatusLogDrawer.vue

@@ -0,0 +1,314 @@
+<template>
+  <!-- 订单状态日志抽屉 -->
+  <el-drawer v-model="drawerVisible" title="变更物流状态" size="38%" :destroy-on-close="true" :close-on-click-modal="true">
+    <div class="status-log-container">
+      <el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
+        <el-form-item label="物流单号" prop="logisticNos">
+          <el-input v-model="form.logisticNos" disabled />
+        </el-form-item>
+
+        <el-form-item label="包裹单号" prop="packageNo">
+          <el-input v-model="form.packageNo" disabled />
+        </el-form-item>
+
+        <el-form-item label="所在状态" prop="statusName">
+          <el-select v-model="form.statusName" placeholder="请选择状态" style="width: 100%" @change="handleStatusChange">
+            <el-option
+              v-for="item in statusOptions"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+              :disabled="isStatusDisabled(item.value)"
+            />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="发货人" prop="shipper">
+          <el-input v-model="form.shipper" disabled />
+        </el-form-item>
+
+        <el-form-item label="操作时间" prop="operateTime">
+          <el-date-picker v-model="form.operateTime" type="date" placeholder="请选择操作时间" style="width: 100%" />
+        </el-form-item>
+
+        <el-form-item label="快递员手机号" prop="courierPhone">
+          <el-input v-model="form.courierPhone" disabled />
+        </el-form-item>
+
+        <el-form-item label="上传图片" prop="images" v-if="form.statusName === '已签收'">
+          <div class="image-selector-wrapper">
+            <!-- 修改点 1: 使用 !selectedImages.length 替代 length === 0,并确保样式一致 -->
+            <div
+              v-if="!selectedImages || selectedImages.length === 0"
+              class="upload-box"
+              @click="showFileSelector = true"
+              style="
+                width: 150px;
+                height: 150px;
+                border: 2px dashed #d9d9d9;
+                border-radius: 4px;
+                display: flex;
+                align-items: center;
+                justify-content: center;
+                cursor: pointer;
+                transition: all 0.3s;
+              "
+            >
+              <div style="text-align: center; color: #8c939d">
+                <el-icon :size="40" style="margin-bottom: 8px">
+                  <Plus />
+                </el-icon>
+                <div style="font-size: 14px">点击上传</div>
+              </div>
+            </div>
+
+            <!-- 修改点 2: 列表渲染区域 -->
+            <div v-else class="selected-images-list">
+              <div
+                v-for="(img, index) in selectedImages"
+                :key="img.url || index"
+                style="position: relative; display: inline-block; margin: 0 8px 8px 0"
+              >
+                <!-- 确保 img.url 存在才渲染,防止报错 -->
+                <el-image
+                  v-if="img.url"
+                  :src="img.url"
+                  style="width: 150px; height: 150px"
+                  fit="cover"
+                  :preview-src-list="selectedImages.filter((i) => i.url).map((i) => i.url)"
+                />
+                <el-button
+                  type="danger"
+                  :icon="Delete"
+                  circle
+                  size="small"
+                  style="position: absolute; top: 5px; right: 5px; z-index: 10"
+                  @click="removeImage(index)"
+                />
+              </div>
+
+              <!-- 修改点 3: 如果未满 3 张,显示“继续添加”按钮 -->
+              <div
+                v-if="selectedImages.length < 3"
+                class="upload-box-more"
+                @click="showFileSelector = true"
+                style="
+                  width: 150px;
+                  height: 150px;
+                  border: 2px dashed #d9d9d9;
+                  border-radius: 4px;
+                  display: inline-flex;
+                  align-items: center;
+                  justify-content: center;
+                  cursor: pointer;
+                  transition: all 0.3s;
+                  vertical-align: top;
+                  background-color: #f5f7fa; /* 加个背景色区分 */
+                "
+              >
+                <el-icon :size="30" color="#8c939d">
+                  <Plus />
+                </el-icon>
+              </div>
+            </div>
+          </div>
+        </el-form-item>
+
+        <!-- 文件选择器 -->
+        <FileSelector v-model="showFileSelector" :multiple="true" :allowed-types="[1]" title="选择图片" @confirm="handleImagesSelected" />
+      </el-form>
+    </div>
+
+    <template #footer>
+      <div class="drawer-footer">
+        <el-button @click="closeDrawer">取消</el-button>
+        <el-button type="primary" @click="handleSubmit" :loading="submitLoading">保存</el-button>
+      </div>
+    </template>
+  </el-drawer>
+</template>
+
+<script setup name="AddOrderStatusLogDrawer" lang="ts">
+import { ref, reactive } from 'vue';
+import { ElMessage } from 'element-plus';
+import { Plus, Delete } from '@element-plus/icons-vue';
+import { addOrderStatusLog } from '@/api/order/orderStatusLog';
+import FileSelector from '@/components/FileSelector/index.vue';
+
+const drawerVisible = ref(false);
+const submitLoading = ref(false);
+const formRef = ref<any>(null);
+const showFileSelector = ref(false);
+const selectedImages = ref<any[]>([]);
+
+// 表单数据
+const form = reactive({
+  orderNo: '',
+  orderId: undefined as string | number | undefined,
+  customerNo: '',
+  customerId: undefined as string | number | undefined,
+  deliverMethod: '0',
+  statusName: '',
+  logisticNos: '',
+  packageNo: '',
+  shipper: '',
+  operateTime: '',
+  courierPhone: '',
+  images: '',
+  status: '0'
+});
+
+// 状态顺序定义
+const statusOrder = ['已下单', '已揽件', '运输中', '派件中', '已签收'];
+
+// 当前订单状态
+const currentStatus = ref('');
+
+// 状态选项
+const statusOptions = [
+  { label: '已下单', value: '已下单' },
+  { label: '已揽件', value: '已揽件' },
+  { label: '运输中', value: '运输中' },
+  { label: '派件中', value: '派件中' },
+  { label: '已签收', value: '已签收' }
+];
+const rules = {
+  statusName: [{ required: true, message: '请选择状态名称', trigger: 'change' }]
+};
+
+// 打开抽屉
+const openDrawer = (orderData?: any, currentOrderStatus?: string) => {
+  if (orderData) {
+    form.orderNo = orderData.orderNo || '';
+    form.orderId = orderData.orderId || '';
+    form.shipper = orderData.deliverMan || '';
+    form.courierPhone = orderData.phone || '';
+    form.packageNo = orderData.deliverCode || '';
+    form.logisticNos = orderData.deliverCode || '';
+    form.customerNo = orderData.customerNo || '';
+    form.customerId = orderData.customerId;
+    form.operateTime = orderData.createTime || '';
+    form.deliverMethod = orderData.deliverMethod || '0';
+    form.statusName = currentOrderStatus || '';
+    form.images = orderData.images || '';
+
+    selectedImages.value = [];
+    if (form.images) {
+      const urls = form.images.split(',');
+      selectedImages.value = urls.filter((u) => u && u.trim()).map((u) => ({ url: u.trim(), name: 'Image' }));
+    }
+
+    currentStatus.value = currentOrderStatus || '';
+  }
+  drawerVisible.value = true;
+};
+
+// 关闭抽屉
+const closeDrawer = () => {
+  drawerVisible.value = false;
+  resetForm();
+};
+
+// 判断是否禁用某个状态选项
+const isStatusDisabled = (statusValue: string) => {
+  if (!currentStatus.value) return false;
+  const currentIndex = statusOrder.indexOf(currentStatus.value);
+  const targetIndex = statusOrder.indexOf(statusValue);
+  // 如果目标状态在当前状态之前(索引更小),则禁用
+  return targetIndex < currentIndex;
+};
+
+// 重置表单
+const resetForm = () => {
+  form.statusName = '';
+  form.logisticNos = '';
+  form.packageNo = '';
+  form.orderId = undefined;
+  form.shipper = '';
+  form.operateTime = '';
+  form.courierPhone = '';
+  form.images = '';
+  selectedImages.value = [];
+  showFileSelector.value = false;
+  currentStatus.value = '';
+};
+
+// 处理选择的图片 (优化健壮性)
+const handleImagesSelected = (files: any[]) => {
+  if (!files || files.length === 0) {
+    showFileSelector.value = false;
+    return;
+  }
+
+  const remainingSlots = 3 - selectedImages.value.length;
+  if (remainingSlots <= 0) {
+    ElMessage.warning('最多上传 3 张图片');
+    showFileSelector.value = false;
+    return;
+  }
+
+  const filesToAdd = files.slice(0, remainingSlots);
+
+  // 映射为标准格式 { url, name }
+  const newItems = filesToAdd
+    .map((f) => ({
+      url: f.fileUrl || f.url || f.path, // 兼容多种返回字段
+      name: f.fileName || 'Image'
+    }))
+    .filter((item) => item.url); // 过滤掉没有 url 的无效项
+
+  if (newItems.length > 0) {
+    // 重新赋值数组以触发视图更新
+    selectedImages.value = [...selectedImages.value, ...newItems];
+    form.images = selectedImages.value.map((img) => img.url).join(',');
+  }
+
+  showFileSelector.value = false;
+};
+
+// 移除已选择的图片
+const removeImage = (index: number) => {
+  selectedImages.value.splice(index, 1);
+  form.images = selectedImages.value.map((img) => img.url).join(',');
+};
+
+// 状态变化处理
+const handleStatusChange = () => {
+  // 当状态变化时,如果不是已签收,清空图片
+  if (form.statusName !== '已签收') {
+    form.images = '';
+    selectedImages.value = [];
+  }
+};
+const handleSubmit = async () => {
+  await formRef.value?.validate();
+  submitLoading.value = true;
+  try {
+    await addOrderStatusLog(form);
+    ElMessage.success('保存成功');
+    closeDrawer();
+  } catch (error) {
+    console.error('保存失败:', error);
+    ElMessage.error('保存失败');
+  } finally {
+    submitLoading.value = false;
+  }
+};
+
+// 暴露方法给父组件
+defineExpose({
+  openDrawer
+});
+</script>
+
+<style scoped>
+.status-log-container {
+  padding: 20px;
+}
+
+.drawer-footer {
+  display: flex;
+  justify-content: flex-end;
+  gap: 12px;
+}
+</style>

+ 3 - 5
src/views/order/saleOrder/deliverDialog.vue

@@ -1,5 +1,5 @@
 <template>
-  <el-dialog :model-value="modelValue" title="发货" width="1300px" @update:model-value="handleDialogChange" @open="handleOpen" @close="handleClose">
+  <el-dialog :model-value="modelValue" title="发货" width="50%" @update:model-value="handleDialogChange" @open="handleOpen" @close="handleClose">
     <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
       <el-row :gutter="20">
         <el-col :span="12">
@@ -255,10 +255,8 @@ const handleLogisticsCompanyChange = (val: any) => {
 const loadProductList = async () => {
   try {
     const res = await getOrderMain(props.orderId);
-    console.log(res.data.orderProductList);
-
     // 为每个商品添加发货数量字段,默认为未发货数量
-    productList.value = (res.data.orderProductList || []).map((item: OrderProductVO) => ({
+    productList.value = (res.data.orderProductList.filter((item) => item.unsentQuantity != 0) || []).map((item: OrderProductVO) => ({
       ...item,
       deliverNum: 0,
       productNo: item.productNo,
@@ -267,7 +265,7 @@ const loadProductList = async () => {
       productUnit: item.productUnit,
       productUnitId: item.productUnitId
     }));
-    total.value = res.data.orderProductList.length || 0;
+    total.value = res.data.orderProductList.filter((item) => item.unsentQuantity != 0).length || 0;
   } catch (error) {
     console.error('加载商品列表失败:', error);
     ElMessage.error('加载商品列表失败');

+ 436 - 0
src/views/order/saleOrder/editDeliverDialog.vue

@@ -0,0 +1,436 @@
+<template>
+  <el-dialog
+    :model-value="modelValue"
+    title="编辑发货信息"
+    width="50%"
+    @update:model-value="handleDialogChange"
+    @open="handleOpen"
+    @close="handleClose"
+  >
+    <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
+      <el-row :gutter="20">
+        <el-col :span="12">
+          <el-form-item label="订单编号" prop="orderCode">
+            <el-input v-model="form.orderCode" disabled />
+          </el-form-item>
+        </el-col>
+
+        <el-col :span="12" v-if="form.deliverMethod === '0'">
+          <el-form-item label="送货人" prop="deliverMan">
+            <el-input v-model="form.deliverMan" placeholder="请输入送货人姓名" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12" v-if="form.deliverMethod === '1'">
+          <el-form-item label="物流公司名称" prop="logisticsCompanyId">
+            <el-select
+              v-model="form.logisticsCompanyId"
+              placeholder="请选择"
+              style="width: 100%"
+              filterable
+              @change="handleLogisticsCompanyChange"
+              disabled
+            >
+              <el-option v-for="company in logisticsCompanyList" :key="company.id" :label="company.logisticsName" :value="company.id" />
+            </el-select>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="20">
+        <el-col :span="12">
+          <el-form-item v-if="form.deliverMethod === '1'" label="物流单号" prop="logisticNo">
+            <el-input v-model="form.logisticNo" placeholder="请输入物流单号" disabled />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="发货方式" prop="deliverMethod">
+            <el-radio-group v-model="form.deliverMethod">
+              <el-radio v-for="dict in deliver_method" :key="dict.value" :value="dict.value" disabled>{{ dict.label }}</el-radio>
+            </el-radio-group>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="20">
+        <el-col :span="12" v-if="form.deliverMethod === '1'">
+          <el-form-item label="收货人手机" prop="consigneePhone">
+            <el-input v-model="form.consigneePhone" placeholder="请输入收货人手机" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12" v-if="form.deliverMethod === '0'">
+          <el-form-item label="手机号码" prop="phone">
+            <el-input v-model="form.phone" placeholder="请输入手机号码" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="20">
+        <el-col :span="24">
+          <el-form-item label="发货备注" prop="deliverRemark">
+            <el-input v-model="form.deliverRemark" type="textarea" :rows="3" placeholder="请输入内容" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+
+    <!-- 商品列表 -->
+    <el-table :data="productList" border style="width: 100%" max-height="600px">
+      <el-table-column prop="productNo" label="产品编号" width="100" />
+      <el-table-column label="产品图片" width="100">
+        <template #default="scope">
+          <el-image v-if="scope.row.productImage" :src="scope.row.productImage" style="width: 60px; height: 60px" fit="cover" />
+          <span v-else>暂无图片</span>
+        </template>
+      </el-table-column>
+      <el-table-column prop="productName" label="产品名称" min-width="200" show-overflow-tooltip />
+      <el-table-column prop="productUnit" label="单位" width="120" />
+      <el-table-column prop="orderPrice" label="商品单价" width="160" />
+      <el-table-column label="发货数量">
+        <template #default="scope">
+          <el-input-number
+            v-model="scope.row.deliverNum"
+            :min="0"
+            :max="scope.row.unsentQuantity"
+            :precision="0"
+            size="small"
+            disabled
+            :controls="false"
+            style="width: 100%"
+            @change="handleDeliveryQuantityChange(scope.$index)"
+          />
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button @click="handleCancel">取消</el-button>
+        <el-button type="primary" :loading="submitLoading" @click="handleSubmit">确认</el-button>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup lang="ts">
+import { ref, reactive, computed, onMounted } from 'vue';
+import { getOrderMain } from '@/api/order/orderMain';
+import { OrderMainVO } from '@/api/order/orderMain/types';
+import { listOrderProduct } from '@/api/order/orderProduct';
+import { OrderProductVO } from '@/api/order/orderProduct/types';
+import { listLogisticsCompany } from '@/api/company/logisticsCompany';
+import { LogisticsCompanyVO } from '@/api/company/logisticsCompany/types';
+import { getOrderDeliver, addOrderDeliver, updateOrderDeliver } from '@/api/order/orderDeliver';
+import { OrderDeliverVO, OrderDeliverForm } from '@/api/order/orderDeliver/types';
+import { ElMessage } from 'element-plus';
+import { de } from 'element-plus/es/locale/index.mjs';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { deliver_method } = toRefs<any>(proxy?.useDict('deliver_method'));
+
+interface Props {
+  modelValue: boolean;
+  orderId?: string | number;
+  orderNo?: string;
+  operateType?: string;
+  deliverId?: string | number;
+}
+
+interface Emits {
+  (e: 'update:modelValue', value: boolean): void;
+  (e: 'success'): void;
+}
+
+const props = defineProps<Props>();
+const emit = defineEmits<Emits>();
+
+const formRef = ref();
+const submitLoading = ref(false);
+const productList = ref<any[]>([]);
+const total = ref(0);
+
+// 物流公司列表
+const logisticsCompanyList = ref<LogisticsCompanyVO[]>([]);
+
+// 查询参数
+const queryParams = ref({
+  pageNum: 1,
+  pageSize: 20,
+  orderId: undefined as string | number | undefined
+});
+
+// 表单数据
+const form = reactive<OrderDeliverForm>({
+  orderId: undefined,
+  orderCode: '',
+  logisticPackNo: '',
+  deliverMethod: '1',
+  deliverMan: '',
+  phone: '',
+  logisticsStatus: '',
+  deliverRemark: '',
+  checklistRemark: '',
+  logisticsCompanyId: undefined,
+  logisticsCompanyCode: '',
+  logisticNo: '',
+  logisticPackStatus: '',
+  consigneePhone: '',
+  remark: '',
+  orderDeliverProducts: []
+});
+
+// 动态校验规则
+// 动态校验规则
+const rules = computed(() => {
+  const baseRules: any = {
+    deliverMethod: [{ required: true, message: '请选择发货方式', trigger: 'change' }]
+  };
+
+  // 第三方物流(deliverMethod === '1')
+  if (form.deliverMethod === '1') {
+    baseRules.logisticsCompanyId = [{ required: true, message: '请选择物流公司', trigger: 'change' }];
+    baseRules.logisticNo = [{ required: true, message: '请输入物流单号', trigger: 'blur' }];
+    baseRules.consigneePhone = [
+      { required: true, message: '请输入收货人手机号码', trigger: 'blur' },
+      { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
+    ];
+  }
+
+  // 自有物流(deliverMethod === '0')
+  if (form.deliverMethod === '0') {
+    baseRules.deliverMan = [{ required: true, message: '请输入送货人姓名', trigger: 'blur' }];
+    baseRules.phone = [
+      { required: true, message: '请输入手机号码', trigger: 'blur' },
+      { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
+    ];
+  }
+
+  return baseRules;
+});
+
+// 对话框状态变化
+const handleDialogChange = (val: boolean) => {
+  emit('update:modelValue', val);
+};
+
+// 对话框打开时触发
+const handleOpen = async () => {
+  resetForm();
+
+  // 如果传入deliverId,直接根据deliverId查询回显
+  if (props.deliverId) {
+    await loadDeliverInfoById(props.deliverId);
+  } else if (props.orderId) {
+    form.orderId = props.orderId;
+    form.orderCode = props.orderNo || '';
+    queryParams.value.orderId = props.orderId;
+    await loadProductList();
+  }
+};
+
+// 对话框关闭时触发
+const handleClose = () => {
+  resetForm();
+};
+
+// 重置表单
+const resetForm = () => {
+  form.orderId = undefined;
+  form.orderCode = '';
+  form.logisticsCompanyId = undefined;
+  form.logisticsCompanyCode = '';
+  form.logisticNo = '';
+  form.deliverMethod = '1';
+  form.deliverMan = '';
+  form.phone = '';
+  form.consigneePhone = '';
+  form.deliverRemark = '';
+  productList.value = [];
+  queryParams.value.pageNum = 1;
+  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 {
+    const res = await getOrderMain(props.orderId);
+    // 为每个商品添加发货数量字段,默认为未发货数量
+    productList.value = (res.data.orderProductList || []).map((item: OrderProductVO) => ({
+      ...item,
+      deliverNum: 0,
+      productNo: item.productNo,
+      productId: item.productId,
+      orderPrice: item.orderPrice,
+      productUnit: item.productUnit,
+      productUnitId: item.productUnitId
+    }));
+    total.value = res.data.orderProductList.length || 0;
+  } catch (error) {
+    console.error('加载商品列表失败:', error);
+    ElMessage.error('加载商品列表失败');
+    productList.value = [];
+    total.value = 0;
+  }
+};
+
+// 加载物流公司列表
+const loadLogisticsCompanyList = async () => {
+  try {
+    const res = await listLogisticsCompany({
+      isShow: '0',
+      pageNum: 1,
+      pageSize: 1000
+    });
+    logisticsCompanyList.value = res.rows || [];
+  } catch (error) {
+    console.error('加载物流公司列表失败:', error);
+    logisticsCompanyList.value = [];
+  }
+};
+
+// 加载发货信息
+const loadDeliverInfo = async () => {
+  try {
+    const res = await getOrderDeliver(props.orderId);
+    if (res.code === 200 && res.data) {
+      const deliverData = res.data;
+      form.logisticsCompanyId = deliverData.logisticsCompanyId;
+      form.logisticsCompanyCode = deliverData.logisticsCompanyCode;
+      form.logisticNo = deliverData.logisticNo;
+      form.deliverMethod = deliverData.deliverMethod || '1';
+      form.consigneePhone = deliverData.consigneePhone;
+      form.deliverRemark = deliverData.deliverRemark;
+    }
+  } catch (error) {
+    console.error('加载发货信息失败:', error);
+  }
+};
+
+// 根据deliverId加载发货信息
+const loadDeliverInfoById = async (deliverId: string | number) => {
+  try {
+    const res = await getOrderDeliver(deliverId);
+    if (res.code === 200 && res.data) {
+      const deliverData = res.data;
+      form.orderId = deliverData.orderId;
+      form.orderCode = deliverData.orderCode;
+      form.logisticsCompanyId = deliverData.logisticsCompanyId;
+      form.logisticsCompanyCode = deliverData.logisticsCompanyCode;
+      form.logisticNo = deliverData.logisticNo;
+      form.deliverMan = deliverData.deliverMan;
+      form.phone = deliverData.phone;
+      form.deliverMethod = deliverData.deliverMethod || '1';
+      form.consigneePhone = deliverData.consigneePhone;
+      form.deliverRemark = deliverData.deliverRemark;
+      productList.value = deliverData.deliverProductList;
+    }
+  } catch (error) {
+    console.error('加载发货信息失败:', error);
+    ElMessage.error('加载发货信息失败');
+  }
+};
+
+// 发货数量变化
+const handleDeliveryQuantityChange = (index: number) => {
+  const product = productList.value[index];
+  if (product) {
+    // 确保发货数量不超过未发货数量
+    if (product.deliverNum > product.unsentQuantity) {
+      product.deliverNum = product.unsentQuantity;
+      ElMessage.warning('发货数量不能大于未发货数量');
+    }
+    // 确保发货数量不小于0
+    if (product.deliverNum < 0) {
+      product.deliverNum = 0;
+    }
+  }
+};
+
+// 删除商品
+const handleDeleteProduct = (index: number) => {
+  productList.value.splice(index, 1);
+};
+
+// 分页大小变化
+const handleSizeChange = () => {
+  queryParams.value.pageNum = 1;
+  loadProductList();
+};
+
+// 页码变化
+const handleCurrentChange = () => {
+  loadProductList();
+};
+
+// 取消
+const handleCancel = () => {
+  emit('update:modelValue', false);
+};
+
+// 提交
+const handleSubmit = async () => {
+  try {
+    // 验证表单
+    await formRef.value?.validate();
+
+    // 验证是否有发货商品
+    const deliveryProducts = productList.value.filter((item) => item.deliverNum > 0);
+    if (deliveryProducts.length === 0) {
+      ElMessage.warning('请至少选择一个商品进行发货');
+      return;
+    }
+
+    submitLoading.value = true;
+
+    // 组装发货数据
+    const deliveryData: OrderDeliverForm = {
+      id: props.deliverId,
+      orderId: form.orderId,
+      orderCode: form.orderCode,
+      logisticsCompanyId: form.logisticsCompanyId,
+      logisticsCompanyCode: form.logisticsCompanyCode,
+      logisticNo: form.logisticNo,
+      deliverMethod: form.deliverMethod,
+      deliverMan: form.deliverMan,
+      phone: form.phone,
+      consigneePhone: form.consigneePhone,
+      deliverRemark: form.deliverRemark
+    };
+
+    // 调用发货API
+    await updateOrderDeliver(deliveryData);
+
+    ElMessage.success('编辑成功');
+    emit('success');
+    emit('update:modelValue', false);
+  } catch (error) {
+    console.error('发货失败:', error);
+    if (error !== false) {
+      // 不是表单验证失败
+      ElMessage.error('发货失败,请重试');
+    }
+  } finally {
+    submitLoading.value = false;
+  }
+};
+
+// 组件挂载时加载物流公司列表
+onMounted(() => {
+  loadLogisticsCompanyList();
+});
+</script>
+
+<style scoped lang="scss">
+.mt-4 {
+  margin-top: 16px;
+}
+
+.dialog-footer {
+  display: flex;
+  justify-content: flex-end;
+  gap: 10px;
+}
+</style>

+ 2 - 2
src/views/order/saleOrder/index.vue

@@ -623,12 +623,12 @@ const getButtonsByStatus = (orderStatus: string, checkStatus: string): ActionBut
   }
 
   // 发货完成或已完成:显示查看物流按钮
-  if (orderStatus === OrderStatus.SHIPMENT_COMPLETED || orderStatus === OrderStatus.COMPLETED) {
+  if (orderStatus === OrderStatus.PARTIAL_SHIPMENT || orderStatus === OrderStatus.SHIPMENT_COMPLETED || orderStatus === OrderStatus.COMPLETED) {
     buttons.push({ label: '查看物流', handler: handleViewLogistics });
   }
 
   // 非已取消和已关闭状态:显示取消订单按钮
-  if (orderStatus !== OrderStatus.CANCELLED && orderStatus !== OrderStatus.CLOSED) {
+  if (orderStatus !== OrderStatus.CANCELLED && orderStatus !== OrderStatus.CLOSED && orderStatus !== OrderStatus.SHIPMENT_COMPLETED) {
     buttons.push({ label: '取消订单', handler: handleCancel });
   }
 

+ 53 - 28
src/views/order/saleOrder/logisticsDetail.vue

@@ -1,5 +1,5 @@
 <template>
-  <el-dialog v-model="visible" title="物流信息" width="700px" :before-close="handleClose">
+  <el-drawer v-model="visible" title="物流信息" size="38%" direction="rtl" :before-close="handleClose" :close-on-click-modal="true">
     <div class="logistics-detail">
       <div class="section-title">单号查询</div>
 
@@ -9,8 +9,8 @@
             <el-option
               v-for="item in logisticsList"
               :key="item.id"
-              :label="`${item.logisticNo},${getDictLabel(deliver_method, item.deliverMethod)}`"
-              :value="item.logisticNo"
+              :label="`${item.logisticNo || item.deliverCode},${getDictLabel(deliver_method, item.deliverMethod)}`"
+              :value="item.logisticNo || item.deliverCode"
             />
           </el-select>
         </el-form-item>
@@ -21,7 +21,7 @@
       <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-status">{{ index + 1 }}</div>
             <div class="timeline-detail">
               <div>{{ item.time }}</div>
               <div>{{ item.location }}</div>
@@ -33,13 +33,13 @@
 
       <el-empty v-else description="暂无物流信息" />
     </div>
-  </el-dialog>
+  </el-drawer>
 </template>
 
 <script setup lang="ts">
 import { listOrderDeliver, queryTrack } from '@/api/order/orderDeliver';
 import { OrderDeliverVO } from '@/api/order/orderDeliver/types';
-
+import { listOrderStatusLog } from '@/api/order/orderStatusLog';
 interface Props {
   modelValue: boolean;
   orderId?: string | number;
@@ -88,7 +88,7 @@ const loadLogisticsList = async () => {
     logisticsList.value = res.rows || [];
 
     if (logisticsList.value.length > 0) {
-      form.value.selectedLogisticNo = logisticsList.value[0].logisticNo;
+      form.value.selectedLogisticNo = logisticsList.value[0].logisticNo || logisticsList.value[0].deliverCode;
       handleLogisticNoChange(form.value.selectedLogisticNo);
     }
   } catch (error) {
@@ -98,29 +98,54 @@ const loadLogisticsList = async () => {
 
 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 || ''
-      }));
+    if (selected) {
+      const res = await queryTrack({
+        logisticNo: logisticNo,
+        pageNum: 1,
+        pageSize: 100
+      });
+      // 1. 兼容处理:有些接口返回在 res.data,有些可能直接是 res
+      const dataList = res.data || [];
+      if (Array.isArray(dataList) && dataList.length > 0) {
+        logisticsInfo.value = dataList.map((item: any) => {
+          // 2. 核心修复:精准匹配时间字段
+          // 顺丰用 'time',韵达用 'ftime'。
+          // 优先取 ftime (韵达标准),如果没有则取 time (顺丰标准)
+          const displayTime = item.ftime || item.time || item.acceptTime || '';
+
+          return {
+            time: displayTime,
+            // 3. 建议:保留原始状态字段,方便后续筛选(如“已签收”)
+            status: item.context || item.content || '',
+            // 4. 建议:如果有地址字段,也可以映射进来,没有则保持订单号
+            location: item.location || (selected.orderCode ? `${selected.orderCode}` : '')
+          };
+        });
+      }
     } else {
-      logisticsInfo.value = [
-        {
-          time: (selected as any).createTime || '',
-          location: selected.orderCode ? `${selected.orderCode}` : '',
-          status: '已下单'
-        }
-      ];
+      await listOrderStatusLog({
+        orderId: props.orderId,
+        logisticNos: form.value.selectedLogisticNo,
+        pageNum: 1,
+        pageSize: 100
+      }).then((res) => {
+        logisticsInfo.value = res.rows.map((item: any) => {
+          return {
+            time: item.createTime,
+            location: item.orderCode ? `${item.orderCode}` : '',
+            status: item.statusName
+          };
+        });
+      });
+
+      // logisticsInfo.value = [
+      //   {
+      //     time: (selected as any).createTime || '',
+      //     location: selected.orderCode ? `${selected.orderCode}` : '',
+      //     status: '已下单'
+      //   }
+      // ];
     }
   } catch (error) {
     console.error('Failed to query track:', error);

+ 7 - 2
src/views/order/saleOrder/orderAffirm.vue

@@ -33,15 +33,19 @@
 
         <el-row :gutter="20">
           <!-- 第二行 -->
-          <el-col :span="8"> </el-col>
           <el-col :span="8">
             <el-form-item label="信用额度">
               <el-input v-model="orderInfo.creditLimit" placeholder="0" disabled />
             </el-form-item>
           </el-col>
+          <el-col :span="8">
+            <el-form-item label="临时额度">
+              <el-input v-model="orderInfo.temporaryQuota" placeholder="0" disabled />
+            </el-form-item>
+          </el-col>
           <el-col :span="8">
             <el-form-item label="剩余额度">
-              <el-input value="0" disabled />
+              <el-input v-model="orderInfo.remainingQuota" placeholder="0" disabled />
             </el-form-item>
           </el-col>
         </el-row>
@@ -210,6 +214,7 @@
               :min="1"
               :precision="0"
               :controls="false"
+              disabled
               @change="handleQuantityChange(scope.$index)"
             />
           </template>

+ 158 - 23
src/views/order/saleOrder/sendDetail.vue

@@ -36,7 +36,7 @@
           </div>
           <div class="detail-item">
             <span class="label">发票类型:</span>
-            <span>{{ invoiceTypeInfo.invoiceTypeNo || '--' }},{{ invoiceTypeInfo.invoiceTypeName || '--' }}</span>
+            <span>{{ orderDetail.invoiceType }}</span>
           </div>
           <div class="detail-item">
             <span class="label">发货仓库:</span>
@@ -84,7 +84,7 @@
           </div>
           <div class="detail-item">
             <span class="label">下单部门:</span>
-            <span>{{ orderDetail.userDept }}</span>
+            <span>{{ orderDetail.userDeptName }}</span>
           </div>
           <div class="detail-item">
             <span class="label">订单状态:</span>
@@ -133,9 +133,9 @@
         <el-table-column prop="unsentQuantity" label="未发货数量" />
       </el-table>
     </el-card>
-    <div class="mt-2 text-right">
+    <div class="mt-2 text-right" style="margin-right: 8px; margin-bottom: 10px">
       <span
-        >商品数:{{ totalQuantity }}, 其中有<span style="color: #f56c6c">{{ totalQuantitySent }}已经发货</span></span
+        >商品数:{{ totalQuantity }} , 其中有 <span style="color: #f56c6c">{{ totalQuantitySent }} 已经发货</span></span
       >
     </div>
     <!-- 信息汇总 -->
@@ -154,32 +154,56 @@
       </el-table>
     </el-card>
     <!-- 发货对话框 -->
-    <DeliverDialog v-model="showDeliverDialog" :order-id="currentOrderId" :order-no="currentOrderNo" @success="handleDeliverSuccess" />
+    <DeliverDialog
+      v-model="showDeliverDialog"
+      :order-id="currentOrderId"
+      :order-no="currentOrderNo"
+      :operate-type="operateType"
+      @success="handleDeliverSuccess"
+    />
+
+    <!-- 编辑发货信息对话框 -->
+    <EditDeliverDialog v-model="showEditDeliverDialog" :deliver-id="editDeliverId" @success="handleDeliverSuccess" />
     <!-- 发货信息 -->
     <el-card shadow="never" class="mb-2" v-show="orderDetail.orderStatus != '0'">
       <template #header>
         <div class="card-header">
           <span>发货信息:共{{ orderDeliverList.length }}个包裹</span>
-          <el-button type="primary" style="float: right" @click="handleAddDeliver(orderDetail)">添加发货信息</el-button>
+          <el-button type="primary" v-if="orderDetail.orderStatus == '2' || orderDetail.orderStatus == '3'" @click="handleAddDeliver(orderDetail)"
+            >添加发货信息</el-button
+          >
         </div>
       </template>
-      <div v-for="deliver in orderDeliverList" :key="deliver.id" class="mb-4">
-        <div style="white-space: nowrap" class="mb-2">
-          <span style="margin-right: 16px">发货单号:{{ deliver.orderCode || '--' }}</span>
-          <span style="margin-right: 16px">发货时间:{{ (deliver as any).createTime || '--' }}</span>
-          <span style="margin-right: 16px">发货方式:{{ getDictLabel(deliver_method, deliver.deliverMethod || '--') }}</span>
-          <span style="margin-right: 16px">送货人:{{ deliver.deliverMan || '--' }}</span>
-          <span style="margin-right: 16px">手机:{{ deliver.phone || '--' }}</span>
-          <span style="margin-right: 16px">物流状态:{{ deliver.logisticsStatus || '--' }}</span>
-          <span>发货备注:{{ deliver.deliverRemark || '--' }}</span>
+      <el-card v-for="deliver in orderDeliverList" :key="deliver.id">
+        <div class="mb-2" style="background: #f3f3f3; padding: 10px">
+          <el-row :gutter="20" justify="space-between" align="middle">
+            <el-col :span="18">
+              <div style="display: flex; flex-wrap: wrap; gap: 16px">
+                <span>发货单号:{{ (deliver as any).deliverCode || '--' }}</span>
+                <span>发货时间:{{ (deliver as any).createTime || '--' }}</span>
+                <span>发货方式:{{ getDictLabel(deliver_method, deliver.deliverMethod || '--') }}</span>
+                <span v-if="deliver.deliverMethod == '0'">收货人:{{ deliver.deliverMan || '--' }}</span>
+                <span>手机:{{ deliver.phone || deliver.consigneePhone }}</span>
+                <span style="margin-left: 30px" v-if="deliver.deliverMethod == '1'">物流单号:{{ deliver.logisticNo || '--' }}</span>
+                <span style="margin-left: 20px">发货备注:{{ deliver.deliverRemark || '--' }}</span>
+              </div>
+            </el-col>
+            <el-col :span="6">
+              <div style="text-align: right">
+                <el-button type="primary" @click="handleEditDeliver(deliver)">编辑发货信息</el-button>
+                <el-button type="primary" v-if="deliver.deliverMethod == '0'" @click="handleEditLogistics(deliver)">变更物流状态</el-button>
+                <el-button type="primary" @click="handleViewLogistics(orderDetail)">查看物流</el-button>
+              </div>
+            </el-col>
+          </el-row>
         </div>
-        <el-table :data="deliverProductList" border style="width: 100%">
+        <el-table :data="deliver.deliverProductList" border style="width: 100%">
           <el-table-column label="产品编号" prop="productNo" align="center" />
           <el-table-column label="商品名称" prop="productName" align="center" />
           <el-table-column label="单位" prop="productUnit" align="center" />
           <el-table-column label="发货数量" prop="deliverNum" align="center" />
         </el-table>
-      </div>
+      </el-card>
     </el-card>
 
     <!-- A10备货信息 -->
@@ -207,12 +231,18 @@
     <div class="text-center mt-4">
       <el-button @click="goBack">返回</el-button>
     </div>
+    <!-- 物流详情对话框 -->
+    <LogisticsDetail v-model="showLogisticsDialog" :order-id="logisticsOrderId" />
+    <!-- 订单状态日志抽屉 -->
+    <AddOrderStatusLogDrawer ref="statusLogDrawerRef" />
   </div>
 </template>
 
 <script setup name="SendDetail" lang="ts">
 import { ref, computed, onMounted } from 'vue';
 import DeliverDialog from './deliverDialog.vue';
+import EditDeliverDialog from './editDeliverDialog.vue';
+import AddOrderStatusLogDrawer from './addOrderStatusLogDrawer.vue';
 import { useRoute, useRouter } from 'vue-router';
 import { getOrderMain } from '@/api/order/orderMain';
 import { OrderMainVO } from '@/api/order/orderMain/types';
@@ -221,6 +251,7 @@ import { OrderProductVO } from '@/api/order/orderProduct/types';
 import { DeliverProductVO } from '@/api/order/deliverProduct/types';
 import { listOrderDeliver } from '@/api/order/orderDeliver';
 import { OrderDeliverVO } from '@/api/order/orderDeliver/types';
+import { listDeliverProduct } from '@/api/order/deliverProduct';
 import { getShippingAddress } from '@/api/customer/customerFile/shippingAddress';
 import { ShippingAddressVO } from '@/api/customer/customerFile/shippingAddress/types';
 import { getWarehouse } from '@/api/company/warehouse';
@@ -231,7 +262,9 @@ import { getCustomerInfo } from '@/api/customer/customerFile/customerInfo';
 import { CustomerInfoVO } from '@/api/customer/customerFile/customerInfo/types';
 import { getInvoiceType } from '@/api/customer/invoiceType';
 import { InvoiceTypeVO } from '@/api/customer/invoiceType/types';
-
+import { selectNewOneLog } from '@/api/order/orderStatusLog';
+import { getDept } from '@/api/system/dept';
+import LogisticsDetail from './logisticsDetail.vue';
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const { order_status, payment_status, fee_type, pay_method, deliver_method } = toRefs<any>(
   proxy?.useDict('order_status', 'payment_status', 'fee_type', 'pay_method', 'deliver_method')
@@ -264,13 +297,27 @@ const deliverProductList = ref<DeliverProductVO[]>([]);
 // 收货地址信息
 const shippingAddress = ref<ShippingAddressVO>({} as ShippingAddressVO);
 
+const operateType = ref('add');
+
 // 发货对话框
 const showDeliverDialog = ref(false);
 const currentOrderId = ref<string | number>();
 const currentOrderNo = ref<string>();
 
+// 编辑发货对话框
+const showEditDeliverDialog = ref(false);
+const editDeliverId = ref<string | number>();
+
+const showLogisticsDialog = ref(false);
+const logisticsOrderId = ref<string | number>();
+
+// 订单状态日志抽屉
+const statusLogDrawerRef = ref<any>(null);
+
 /** 发货成功回调 */
-const handleDeliverSuccess = () => {};
+const handleDeliverSuccess = () => {
+  getOrderDetail();
+};
 
 // 计算商品总数(所有商品的采购数量之和)
 const totalQuantity = computed(() => {
@@ -286,6 +333,16 @@ const totalQuantitySent = computed(() => {
   }, 0);
 });
 
+// 获取部门详细信息
+const getDeptDetail = async (deptId: string) => {
+  try {
+    const res = await getDept(deptId);
+    return res.data?.deptName || deptId;
+  } catch (error) {
+    return deptId;
+  }
+};
+
 // 获取订单详情
 const getOrderDetail = async () => {
   const orderId = route.query.id || route.params.id;
@@ -336,11 +393,14 @@ const getOrderDetail = async () => {
     if (orderDetail.value.customerId) {
       await getCustomerDetail(orderDetail.value.customerId);
     }
+    if (orderDetail.value.userDept) {
+      orderDetail.value.userDeptName = await getDeptDetail(orderDetail.value.userDept);
+    }
 
     // 获取发票类型信息
-    if (orderDetail.value.invoiceType) {
-      await getInvoiceTypeDetail(orderDetail.value.invoiceType);
-    }
+    // if (orderDetail.value.invoiceType) {
+    //   await getInvoiceTypeDetail(orderDetail.value.invoiceType);
+    // }
   } catch (error) {
     console.error('获取订单详情失败:', error);
     proxy?.$modal.msgError('获取订单详情失败');
@@ -353,11 +413,64 @@ const handleAddDeliver = (row?: OrderMainVO) => {
     proxy?.$modal.msgWarning('订单ID不能为空');
     return;
   }
+  operateType.value = 'add';
   currentOrderId.value = row.id;
   currentOrderNo.value = row.orderNo;
   showDeliverDialog.value = true;
 };
 
+/** 编辑发货信息操作 */
+const handleEditDeliver = (deliver: any) => {
+  if (!deliver?.id) {
+    proxy?.$modal.msgWarning('发货信息ID不能为空');
+    return;
+  }
+  editDeliverId.value = deliver.id;
+  showEditDeliverDialog.value = true;
+};
+
+const handleViewLogistics = (row?: OrderMainVO) => {
+  if (!row?.id) {
+    proxy?.$modal.msgWarning('订单ID不能为空');
+    return;
+  }
+  logisticsOrderId.value = row.id;
+
+  showLogisticsDialog.value = true;
+};
+
+/** 变更物流状态 */
+const handleEditLogistics = async (row?: any) => {
+  if (!row?.orderId) {
+    proxy?.$modal.msgWarning('订单ID不能为空');
+    return;
+  }
+
+  // 补充客户信息
+  row.customerId = orderDetail.value.customerId;
+  row.customerNo = orderDetail.value.customerCode;
+
+  let currentOrderStatus = '';
+
+  try {
+    const res = await selectNewOneLog({
+      orderId: row.orderId,
+      customerId: row.customerId,
+      logisticNos: row.deliverCode
+    });
+
+    if (res.code == 200 && res.data) {
+      row.images = res.data.images;
+      currentOrderStatus = res.data.statusName || '';
+    }
+  } catch (error) {
+    console.error('查询物流失败:', error);
+  }
+
+  // 4. 使用获取到的状态打开弹窗
+  statusLogDrawerRef.value?.openDrawer(row, currentOrderStatus);
+};
+
 // 获取商品明细列表
 const getProductList = async (orderDetail: OrderMainVO) => {
   try {
@@ -380,7 +493,22 @@ const getDeliverProductList = async (orderDetail: OrderMainVO) => {
 const getOrderDeliverListData = async (orderId: string | number) => {
   try {
     const res = await listOrderDeliver({ orderId, pageNum: 1, pageSize: 100 });
-    orderDeliverList.value = (res as any).rows || res.data || [];
+    const deliverList = (res as any).rows || res.data || [];
+
+    // 为每条发货记录获取商品明细
+    for (const deliver of deliverList) {
+      if (deliver.id) {
+        try {
+          const productRes = await listDeliverProduct({ deliverId: deliver.id, pageNum: 1, pageSize: 100 });
+          deliver.deliverProductList = (productRes as any).rows || productRes.data || [];
+        } catch (error) {
+          console.error(`获取发货单${deliver.id}的商品明细失败:`, error);
+          deliver.deliverProductList = [];
+        }
+      }
+    }
+
+    orderDeliverList.value = deliverList;
   } catch (error) {
     console.error('获取发货单列表失败:', error);
   }
@@ -515,6 +643,13 @@ onMounted(() => {
   flex: 1;
 }
 
+.card-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  min-height: 40px;
+}
+
 @media print {
   .el-button {
     display: none;