客户余额记录
This commit is contained in:
+121
@@ -0,0 +1,121 @@
|
||||
package com.ruoyi.web.controller.customer.customermoney;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import com.ruoyi.common.annotation.Log;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import com.ruoyi.common.enums.BusinessType;
|
||||
import com.ruoyi.customer.customermoney.domain.CustomerBalanceFlow;
|
||||
import com.ruoyi.customer.customermoney.domain.CustomerReceipt;
|
||||
import com.ruoyi.customer.customermoney.service.ICustomerMoneyService;
|
||||
|
||||
/**
|
||||
* 客户余额/收款/流水 Controller
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-09-11
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/customersupplier/customermoney")
|
||||
public class CustomerMoneyController extends BaseController
|
||||
{
|
||||
@Autowired
|
||||
private ICustomerMoneyService customerMoneyService;
|
||||
|
||||
/**
|
||||
* 查询客户各币种账户余额
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:list')")
|
||||
@GetMapping("/account/list/{customerId}")
|
||||
public AjaxResult accountList(@PathVariable("customerId") Long customerId)
|
||||
{
|
||||
return success(customerMoneyService.selectAccountList(customerId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询收款记录列表
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:list')")
|
||||
@GetMapping("/receipt/list")
|
||||
public TableDataInfo receiptList(CustomerReceipt query)
|
||||
{
|
||||
// customerId 为空或为 0,直接返回空数据
|
||||
if (query == null || query.getCustomerId() == null || query.getCustomerId() == 0L) {
|
||||
return getDataTable(Collections.emptyList());
|
||||
}
|
||||
|
||||
startPage();
|
||||
List<CustomerReceipt> list = customerMoneyService.selectReceiptList(query);
|
||||
return getDataTable(list);
|
||||
}
|
||||
/**
|
||||
* 获取收款单详情(含收款附件)
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:query')")
|
||||
@GetMapping("/receipt/{id}")
|
||||
public AjaxResult getReceipt(@PathVariable("id") Long id)
|
||||
{
|
||||
return success(customerMoneyService.selectReceiptById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 登记收款(默认待确认,同时保存收款凭证附件)
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:add')")
|
||||
@Log(title = "客户收款", businessType = BusinessType.INSERT)
|
||||
@PostMapping("/receipt")
|
||||
public AjaxResult addReceipt(@RequestBody CustomerReceipt receipt)
|
||||
{
|
||||
receipt.setCreateBy(getUsername());
|
||||
return toAjax(customerMoneyService.insertReceipt(receipt));
|
||||
}
|
||||
|
||||
/**
|
||||
* 审核通过收款单:确认后变动客户余额并生成余额流水
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:edit')")
|
||||
@Log(title = "客户收款审核", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/receipt/confirm/{id}")
|
||||
public AjaxResult confirmReceipt(@PathVariable("id") Long id)
|
||||
{
|
||||
return toAjax(customerMoneyService.confirmReceipt(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消收款单(仅待确认可取消)
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:edit')")
|
||||
@Log(title = "客户收款取消", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/receipt/cancel/{id}")
|
||||
public AjaxResult cancelReceipt(@PathVariable("id") Long id)
|
||||
{
|
||||
return toAjax(customerMoneyService.cancelReceipt(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询余额流水列表
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:list')")
|
||||
@GetMapping("/flow/list")
|
||||
public TableDataInfo flowList(CustomerBalanceFlow query)
|
||||
{
|
||||
// customerId 为空或为 0,直接返回空数据
|
||||
if (query == null || query.getCustomerId() == null || query.getCustomerId() == 0L) {
|
||||
return getDataTable(Collections.emptyList());
|
||||
}
|
||||
startPage();
|
||||
List<CustomerBalanceFlow> list = customerMoneyService.selectFlowList(query);
|
||||
return getDataTable(list);
|
||||
}
|
||||
}
|
||||
+3
@@ -47,6 +47,9 @@ public class BmsCustomerAttachment extends BaseEntity
|
||||
@Excel(name = "附件类型", readConverterExp = "0=附件资料,1=客户Logo,2=合同文件,3=其他")
|
||||
private String attachmentType;
|
||||
|
||||
/** 关联业务ID(如收款单ID,attachment_type=3 时使用) */
|
||||
private Long bizId;
|
||||
|
||||
/** 是否Logo(0否 1是) */
|
||||
private String isLogo;
|
||||
|
||||
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package com.ruoyi.customer.customermoney.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 客户余额账户 customer_account
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-09-11
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CustomerAccount extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 主键 */
|
||||
private Long id;
|
||||
|
||||
/** 客户ID */
|
||||
private Long customerId;
|
||||
|
||||
/** 币种,如 CNY/USD */
|
||||
private String currency;
|
||||
|
||||
/** 账户余额 */
|
||||
private BigDecimal balance;
|
||||
|
||||
/** 冻结余额 */
|
||||
private BigDecimal frozenBalance;
|
||||
|
||||
/** 总额度 */
|
||||
private BigDecimal totalCredit;
|
||||
|
||||
/** 额外额度 */
|
||||
private BigDecimal extraCredit;
|
||||
|
||||
/** 已用额度 */
|
||||
private BigDecimal usedCredit;
|
||||
|
||||
/** 税金 */
|
||||
private BigDecimal tax;
|
||||
|
||||
/** 乐观锁版本号 */
|
||||
private Integer version;
|
||||
|
||||
/** 删除标志(0存在 1删除) */
|
||||
private String delFlag;
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
package com.ruoyi.customer.customermoney.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 客户余额流水 customer_balance_flow
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-09-11
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CustomerBalanceFlow extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 业务类型:收款 */
|
||||
public static final String BIZ_RECEIPT = "RECEIPT";
|
||||
/** 业务类型:账单支付 */
|
||||
public static final String BIZ_BILL_PAY = "BILL_PAY";
|
||||
/** 业务类型:退款 */
|
||||
public static final String BIZ_REFUND = "REFUND";
|
||||
/** 业务类型:手工调整 */
|
||||
public static final String BIZ_ADJUST = "ADJUST";
|
||||
/** 业务类型:冻结 */
|
||||
public static final String BIZ_FREEZE = "FREEZE";
|
||||
/** 业务类型:解冻 */
|
||||
public static final String BIZ_UNFREEZE = "UNFREEZE";
|
||||
|
||||
/** 主键 */
|
||||
private Long id;
|
||||
|
||||
/** 流水号 */
|
||||
private String flowNo;
|
||||
|
||||
/** 客户ID */
|
||||
private Long customerId;
|
||||
|
||||
/** 客户账户ID */
|
||||
private Long accountId;
|
||||
|
||||
/** 币种 */
|
||||
private String currency;
|
||||
|
||||
/** 业务类型:RECEIPT/BILL_PAY/REFUND/ADJUST/FREEZE/UNFREEZE */
|
||||
private String bizType;
|
||||
|
||||
/** 关联业务ID */
|
||||
private Long bizId;
|
||||
|
||||
/** 关联业务单号 */
|
||||
private String bizNo;
|
||||
|
||||
/** 变动方向:1增加,-1减少 */
|
||||
private Integer direction;
|
||||
|
||||
/** 变动金额 */
|
||||
private BigDecimal amount;
|
||||
|
||||
/** 变动前余额 */
|
||||
private BigDecimal beforeBalance;
|
||||
|
||||
/** 变动后余额 */
|
||||
private BigDecimal afterBalance;
|
||||
|
||||
/** 变动前冻结余额 */
|
||||
private BigDecimal beforeFrozenBalance;
|
||||
|
||||
/** 变动后冻结余额 */
|
||||
private BigDecimal afterFrozenBalance;
|
||||
|
||||
/** 操作人ID */
|
||||
private Long operatorId;
|
||||
|
||||
/** 删除标志(0存在 1删除) */
|
||||
private String delFlag;
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
package com.ruoyi.customer.customermoney.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import com.ruoyi.customer.customerinfo.domain.BmsCustomerAttachment;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 客户收款 customer_receipt
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-09-11
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CustomerReceipt extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 状态:待确认 */
|
||||
public static final String STATUS_PENDING = "PENDING";
|
||||
/** 状态:已确认 */
|
||||
public static final String STATUS_CONFIRMED = "CONFIRMED";
|
||||
/** 状态:已取消 */
|
||||
public static final String STATUS_CANCELLED = "CANCELLED";
|
||||
|
||||
/** 收款类型:预充值 */
|
||||
public static final String TYPE_PREPAY = "PREPAY";
|
||||
/** 收款类型:还款核销 */
|
||||
public static final String TYPE_REPAY = "REPAY";
|
||||
/** 收款类型:混合 */
|
||||
public static final String TYPE_MIXED = "MIXED";
|
||||
|
||||
/** 主键 */
|
||||
private Long id;
|
||||
|
||||
/** 收款单号 */
|
||||
private String receiptNo;
|
||||
|
||||
/** 客户ID */
|
||||
private Long customerId;
|
||||
|
||||
/** 客户账户ID(预留) */
|
||||
private Long accountId;
|
||||
|
||||
/** 收款类型:PREPAY预充值/REPAY还款核销/MIXED混合 */
|
||||
private String receiptType;
|
||||
|
||||
/** 收款金额 */
|
||||
private BigDecimal amount;
|
||||
|
||||
/** 已核销金额 */
|
||||
private BigDecimal allocatedAmount;
|
||||
|
||||
/** 待核销金额 */
|
||||
private BigDecimal unallocatedAmount;
|
||||
|
||||
/** 币种 */
|
||||
private String currency;
|
||||
|
||||
/** 收款方式:BANK_TRANSFER/ALIPAY/WECHAT/CASH/OTHER */
|
||||
private String payMethod;
|
||||
|
||||
/** 银行流水号/支付平台流水号 */
|
||||
private String bankTransactionNo;
|
||||
|
||||
/** 收款日期 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date receivedAt;
|
||||
|
||||
/** 状态:PENDING待确认/CONFIRMED已确认/CANCELLED已取消 */
|
||||
private String status;
|
||||
|
||||
/** 确认时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date confirmedAt;
|
||||
|
||||
/** 确认人 */
|
||||
private Long confirmedBy;
|
||||
|
||||
/** 开票状态:NONE未开/PART部分/ALL全部 */
|
||||
private String invoiceStatus;
|
||||
|
||||
/** 乐观锁版本号 */
|
||||
private Integer version;
|
||||
|
||||
/** 删除标志(0存在 1删除) */
|
||||
private String delFlag;
|
||||
|
||||
/** 收款附件列表(存 bms_customer_attachment,attachment_type='3',biz_id 关联本单) */
|
||||
private List<BmsCustomerAttachment> attachmentList;
|
||||
|
||||
/** 收款附件数量(列表查询统计) */
|
||||
private Integer attachmentCount;
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
package com.ruoyi.customer.customermoney.mapper;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import com.ruoyi.customer.customermoney.domain.CustomerAccount;
|
||||
import com.ruoyi.customer.customermoney.domain.CustomerBalanceFlow;
|
||||
import com.ruoyi.customer.customermoney.domain.CustomerReceipt;
|
||||
|
||||
/**
|
||||
* 客户余额/收款/流水 Mapper
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-09-11
|
||||
*/
|
||||
public interface CustomerMoneyMapper
|
||||
{
|
||||
/* ================= 客户余额账户 ================= */
|
||||
|
||||
/** 按客户+币种查询账户 */
|
||||
CustomerAccount selectAccountByCustomerCurrency(@Param("customerId") Long customerId,
|
||||
@Param("currency") String currency);
|
||||
|
||||
/** 按客户+币种查询账户(行锁,审核事务内使用) */
|
||||
CustomerAccount selectAccountForUpdate(@Param("customerId") Long customerId,
|
||||
@Param("currency") String currency);
|
||||
|
||||
/** 查询客户全部币种账户 */
|
||||
List<CustomerAccount> selectAccountListByCustomerId(@Param("customerId") Long customerId);
|
||||
|
||||
/** 新增账户 */
|
||||
int insertAccount(CustomerAccount account);
|
||||
|
||||
/** 乐观锁更新余额 */
|
||||
int updateAccountBalance(CustomerAccount account);
|
||||
|
||||
/* ================= 客户收款 ================= */
|
||||
|
||||
/** 查询收款单列表 */
|
||||
List<CustomerReceipt> selectReceiptList(CustomerReceipt query);
|
||||
|
||||
/** 按ID查询收款单 */
|
||||
CustomerReceipt selectReceiptById(@Param("id") Long id);
|
||||
|
||||
/** 新增收款单 */
|
||||
int insertReceipt(CustomerReceipt receipt);
|
||||
|
||||
/** 审核通过:PENDING -> CONFIRMED(带状态+乐观锁条件) */
|
||||
int confirmReceipt(CustomerReceipt receipt);
|
||||
|
||||
/** 取消收款单:PENDING -> CANCELLED */
|
||||
int cancelReceipt(@Param("id") Long id, @Param("updateBy") String updateBy);
|
||||
|
||||
/* ================= 余额流水 ================= */
|
||||
|
||||
/** 查询流水列表 */
|
||||
List<CustomerBalanceFlow> selectFlowList(CustomerBalanceFlow query);
|
||||
|
||||
/** 新增流水 */
|
||||
int insertFlow(CustomerBalanceFlow flow);
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package com.ruoyi.customer.customermoney.service;
|
||||
|
||||
import java.util.List;
|
||||
import com.ruoyi.customer.customermoney.domain.CustomerAccount;
|
||||
import com.ruoyi.customer.customermoney.domain.CustomerBalanceFlow;
|
||||
import com.ruoyi.customer.customermoney.domain.CustomerReceipt;
|
||||
|
||||
/**
|
||||
* 客户余额/收款/流水 Service接口
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-09-11
|
||||
*/
|
||||
public interface ICustomerMoneyService
|
||||
{
|
||||
/** 查询客户各币种账户 */
|
||||
List<CustomerAccount> selectAccountList(Long customerId);
|
||||
|
||||
/** 查询收款单列表 */
|
||||
List<CustomerReceipt> selectReceiptList(CustomerReceipt query);
|
||||
|
||||
/** 查询收款单详情(含附件) */
|
||||
CustomerReceipt selectReceiptById(Long id);
|
||||
|
||||
/** 登记收款(状态默认 PENDING,同时保存附件) */
|
||||
int insertReceipt(CustomerReceipt receipt);
|
||||
|
||||
/**
|
||||
* 审核通过收款单:PENDING -> CONFIRMED
|
||||
* 事务内:锁定账户(不存在则初始化)-> 变动余额/已用额度 -> 写余额流水
|
||||
*/
|
||||
int confirmReceipt(Long id);
|
||||
|
||||
/** 取消收款单(仅待确认可取消) */
|
||||
int cancelReceipt(Long id);
|
||||
|
||||
/** 查询余额流水列表 */
|
||||
List<CustomerBalanceFlow> selectFlowList(CustomerBalanceFlow query);
|
||||
}
|
||||
+253
@@ -0,0 +1,253 @@
|
||||
package com.ruoyi.customer.customermoney.service.impl;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
import com.ruoyi.customer.customerinfo.domain.BmsCustomerAttachment;
|
||||
import com.ruoyi.customer.customerinfo.mapper.BmsCustomerMapper;
|
||||
import com.ruoyi.customer.customermoney.domain.CustomerAccount;
|
||||
import com.ruoyi.customer.customermoney.domain.CustomerBalanceFlow;
|
||||
import com.ruoyi.customer.customermoney.domain.CustomerReceipt;
|
||||
import com.ruoyi.customer.customermoney.mapper.CustomerMoneyMapper;
|
||||
import com.ruoyi.customer.customermoney.service.ICustomerMoneyService;
|
||||
|
||||
/**
|
||||
* 客户余额/收款/流水 Service实现
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-09-11
|
||||
*/
|
||||
@Service
|
||||
public class CustomerMoneyServiceImpl implements ICustomerMoneyService
|
||||
{
|
||||
private static final DateTimeFormatter NO_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");
|
||||
|
||||
@Autowired
|
||||
private CustomerMoneyMapper customerMoneyMapper;
|
||||
|
||||
@Autowired
|
||||
private BmsCustomerMapper bmsCustomerMapper;
|
||||
|
||||
@Override
|
||||
public List<CustomerAccount> selectAccountList(Long customerId)
|
||||
{
|
||||
return customerMoneyMapper.selectAccountListByCustomerId(customerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CustomerReceipt> selectReceiptList(CustomerReceipt query)
|
||||
{
|
||||
return customerMoneyMapper.selectReceiptList(query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CustomerReceipt selectReceiptById(Long id)
|
||||
{
|
||||
CustomerReceipt receipt = customerMoneyMapper.selectReceiptById(id);
|
||||
if (receipt != null)
|
||||
{
|
||||
BmsCustomerAttachment query = new BmsCustomerAttachment();
|
||||
query.setBizId(id);
|
||||
receipt.setAttachmentList(bmsCustomerMapper.selectBmsCustomerAttachmentList(query));
|
||||
}
|
||||
return receipt;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int insertReceipt(CustomerReceipt receipt)
|
||||
{
|
||||
if (receipt.getCustomerId() == null)
|
||||
{
|
||||
throw new ServiceException("客户ID不能为空");
|
||||
}
|
||||
if (receipt.getAmount() == null || receipt.getAmount().compareTo(BigDecimal.ZERO) <= 0)
|
||||
{
|
||||
throw new ServiceException("收款金额必须大于0");
|
||||
}
|
||||
if (!StringUtils.hasText(receipt.getReceiptType()))
|
||||
{
|
||||
receipt.setReceiptType(CustomerReceipt.TYPE_PREPAY);
|
||||
}
|
||||
if (!StringUtils.hasText(receipt.getCurrency()))
|
||||
{
|
||||
receipt.setCurrency("CNY");
|
||||
}
|
||||
receipt.setReceiptNo(generateNo("SK"));
|
||||
receipt.setStatus(CustomerReceipt.STATUS_PENDING);
|
||||
receipt.setAllocatedAmount(BigDecimal.ZERO);
|
||||
receipt.setUnallocatedAmount(receipt.getAmount());
|
||||
if (!StringUtils.hasText(receipt.getInvoiceStatus()))
|
||||
{
|
||||
receipt.setInvoiceStatus("NONE");
|
||||
}
|
||||
receipt.setVersion(0);
|
||||
customerMoneyMapper.insertReceipt(receipt);
|
||||
|
||||
// 收款附件:复用客户附件表,类型挂"其他(3)",biz_id 关联收款单
|
||||
saveReceiptAttachments(receipt);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int confirmReceipt(Long id)
|
||||
{
|
||||
CustomerReceipt receipt = customerMoneyMapper.selectReceiptById(id);
|
||||
if (receipt == null)
|
||||
{
|
||||
throw new ServiceException("收款单不存在");
|
||||
}
|
||||
if (!CustomerReceipt.STATUS_PENDING.equals(receipt.getStatus()))
|
||||
{
|
||||
throw new ServiceException("仅待确认的收款单可以审核");
|
||||
}
|
||||
|
||||
String currency = StringUtils.hasText(receipt.getCurrency()) ? receipt.getCurrency() : "CNY";
|
||||
String username = SecurityUtils.getUsername();
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
|
||||
// 1. 行锁锁定客户币种账户,不存在则自动初始化
|
||||
CustomerAccount account = customerMoneyMapper.selectAccountForUpdate(receipt.getCustomerId(), currency);
|
||||
if (account == null)
|
||||
{
|
||||
account = new CustomerAccount();
|
||||
account.setCustomerId(receipt.getCustomerId());
|
||||
account.setCurrency(currency);
|
||||
account.setBalance(BigDecimal.ZERO);
|
||||
account.setFrozenBalance(BigDecimal.ZERO);
|
||||
account.setTotalCredit(BigDecimal.ZERO);
|
||||
account.setExtraCredit(BigDecimal.ZERO);
|
||||
account.setUsedCredit(BigDecimal.ZERO);
|
||||
account.setTax(BigDecimal.ZERO);
|
||||
account.setVersion(0);
|
||||
account.setCreateBy(username);
|
||||
customerMoneyMapper.insertAccount(account);
|
||||
}
|
||||
|
||||
// 2. 变动余额(预充值/混合增加余额;还款核销释放已用额度)
|
||||
BigDecimal amount = nz(receipt.getAmount());
|
||||
BigDecimal beforeBalance = nz(account.getBalance());
|
||||
BigDecimal afterBalance = beforeBalance.add(amount);
|
||||
BigDecimal frozenBalance = nz(account.getFrozenBalance());
|
||||
|
||||
account.setBalance(afterBalance);
|
||||
if (CustomerReceipt.TYPE_REPAY.equals(receipt.getReceiptType()))
|
||||
{
|
||||
account.setUsedCredit(nz(account.getUsedCredit()).subtract(amount).max(BigDecimal.ZERO));
|
||||
}
|
||||
account.setUpdateBy(username);
|
||||
|
||||
int accountRows = customerMoneyMapper.updateAccountBalance(account);
|
||||
if (accountRows == 0)
|
||||
{
|
||||
throw new ServiceException("账户余额已被其他操作变动,请刷新后重试");
|
||||
}
|
||||
|
||||
// 3. 写余额流水
|
||||
CustomerBalanceFlow flow = new CustomerBalanceFlow();
|
||||
flow.setFlowNo(generateNo("LS"));
|
||||
flow.setCustomerId(receipt.getCustomerId());
|
||||
flow.setAccountId(account.getId());
|
||||
flow.setCurrency(currency);
|
||||
flow.setBizType(CustomerBalanceFlow.BIZ_RECEIPT);
|
||||
flow.setBizId(receipt.getId());
|
||||
flow.setBizNo(receipt.getReceiptNo());
|
||||
flow.setDirection(1);
|
||||
flow.setAmount(amount);
|
||||
flow.setBeforeBalance(beforeBalance);
|
||||
flow.setAfterBalance(afterBalance);
|
||||
flow.setBeforeFrozenBalance(frozenBalance);
|
||||
flow.setAfterFrozenBalance(frozenBalance);
|
||||
flow.setOperatorId(userId);
|
||||
flow.setCreateBy(username);
|
||||
flow.setRemark("收款单审核通过:" + receipt.getReceiptNo());
|
||||
customerMoneyMapper.insertFlow(flow);
|
||||
|
||||
// 4. 收款单置为已确认
|
||||
receipt.setStatus(CustomerReceipt.STATUS_CONFIRMED);
|
||||
receipt.setConfirmedAt(new Date());
|
||||
receipt.setConfirmedBy(userId);
|
||||
receipt.setUpdateBy(username);
|
||||
int receiptRows = customerMoneyMapper.confirmReceipt(receipt);
|
||||
if (receiptRows == 0)
|
||||
{
|
||||
throw new ServiceException("收款单状态已变更,请刷新后重试");
|
||||
}
|
||||
return receiptRows;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int cancelReceipt(Long id)
|
||||
{
|
||||
CustomerReceipt receipt = customerMoneyMapper.selectReceiptById(id);
|
||||
if (receipt == null)
|
||||
{
|
||||
throw new ServiceException("收款单不存在");
|
||||
}
|
||||
if (!CustomerReceipt.STATUS_PENDING.equals(receipt.getStatus()))
|
||||
{
|
||||
throw new ServiceException("仅待确认的收款单可以取消");
|
||||
}
|
||||
return customerMoneyMapper.cancelReceipt(id, SecurityUtils.getUsername());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CustomerBalanceFlow> selectFlowList(CustomerBalanceFlow query)
|
||||
{
|
||||
return customerMoneyMapper.selectFlowList(query);
|
||||
}
|
||||
|
||||
/* ================= 私有方法 ================= */
|
||||
|
||||
/** 保存收款附件到客户附件表 */
|
||||
private void saveReceiptAttachments(CustomerReceipt receipt)
|
||||
{
|
||||
if (CollectionUtils.isEmpty(receipt.getAttachmentList()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
int sort = 0;
|
||||
for (BmsCustomerAttachment attachment : receipt.getAttachmentList())
|
||||
{
|
||||
attachment.setAttachmentId(null);
|
||||
attachment.setCustomerId(receipt.getCustomerId());
|
||||
attachment.setBizId(receipt.getId());
|
||||
attachment.setAttachmentType("3");
|
||||
if (!StringUtils.hasText(attachment.getIsLogo()))
|
||||
{
|
||||
attachment.setIsLogo("0");
|
||||
}
|
||||
if (attachment.getSortOrder() == null)
|
||||
{
|
||||
attachment.setSortOrder(sort);
|
||||
}
|
||||
attachment.setDelFlag("0");
|
||||
bmsCustomerMapper.insertBmsCustomerAttachment(attachment);
|
||||
sort++;
|
||||
}
|
||||
}
|
||||
|
||||
/** 生成业务单号:前缀 + 时间戳(毫秒) + 3位随机数 */
|
||||
private String generateNo(String prefix)
|
||||
{
|
||||
return prefix + LocalDateTime.now().format(NO_FORMATTER)
|
||||
+ ThreadLocalRandom.current().nextInt(100, 1000);
|
||||
}
|
||||
|
||||
private BigDecimal nz(BigDecimal value)
|
||||
{
|
||||
return value == null ? BigDecimal.ZERO : value;
|
||||
}
|
||||
}
|
||||
@@ -36,41 +36,50 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="delFlag" column="del_flag" />
|
||||
</resultMap>
|
||||
|
||||
<!-- 账户余额/外币余额统一取 customer_account 余额表(CNY 为本币,其余为外币),不取 bms_customer 冗余列 -->
|
||||
<sql id="selectBmsCustomerVo">
|
||||
select customer_id, customer_code, customer_name, customer_code_dw, credit_code, customer_type, is_enable, sales_user_id, sales_user, ed, zq, settlement_user_id, settlement_user, service_user_id, service_user, settlement_type, temp_ed, account_balance, foreign_currency_balance, weight_threshold, throw_ratio, credit_ed, product_ids, remark, create_by, create_time, update_by, update_time, del_flag from bms_customer
|
||||
select c.customer_id, c.customer_code, c.customer_name, c.customer_code_dw, c.credit_code, c.customer_type,
|
||||
c.is_enable, c.sales_user_id, c.sales_user, c.ed, c.zq, c.settlement_user_id, c.settlement_user,
|
||||
c.service_user_id, c.service_user, c.settlement_type, c.temp_ed,
|
||||
(select coalesce(sum(ca.balance), 0) from customer_account ca
|
||||
where ca.customer_id = c.customer_id and ca.currency = 'CNY' and ca.del_flag = '0') as account_balance,
|
||||
(select coalesce(sum(ca.balance), 0) from customer_account ca
|
||||
where ca.customer_id = c.customer_id and ca.currency != 'CNY' and ca.del_flag = '0') as foreign_currency_balance,
|
||||
c.weight_threshold, c.throw_ratio, c.credit_ed, c.product_ids, c.remark,
|
||||
c.create_by, c.create_time, c.update_by, c.update_time, c.del_flag
|
||||
from bms_customer c
|
||||
</sql>
|
||||
|
||||
<select id="selectBmsCustomerList" parameterType="BmsCustomer" resultMap="BmsCustomerResult">
|
||||
<include refid="selectBmsCustomerVo"/>
|
||||
<where>
|
||||
<if test="customerCode != null and customerCode != ''"> and customer_code = #{customerCode}</if>
|
||||
<if test="customerName != null and customerName != ''"> and customer_name like concat('%', #{customerName}, '%')</if>
|
||||
<if test="customerCodeDw != null and customerCodeDw != ''"> and customer_code_dw = #{customerCodeDw}</if>
|
||||
<if test="creditCode != null and creditCode != ''"> and credit_code = #{creditCode}</if>
|
||||
<if test="customerType != null and customerType != ''"> and customer_type = #{customerType}</if>
|
||||
<if test="isEnable != null and isEnable != ''"> and is_enable = #{isEnable}</if>
|
||||
<if test="salesUserId != null and salesUserId != ''"> and sales_user_id = #{salesUserId}</if>
|
||||
<if test="salesUser != null and salesUser != ''"> and sales_user = #{salesUser}</if>
|
||||
<if test="ed != null "> and ed = #{ed}</if>
|
||||
<if test="zq != null "> and zq = #{zq}</if>
|
||||
<if test="settlementUserId != null and settlementUserId != ''"> and settlement_user_id = #{settlementUserId}</if>
|
||||
<if test="settlementUser != null and settlementUser != ''"> and settlement_user = #{settlementUser}</if>
|
||||
<if test="serviceUserId != null and serviceUserId != ''"> and service_user_id = #{serviceUserId}</if>
|
||||
<if test="serviceUser != null and serviceUser != ''"> and service_user = #{serviceUser}</if>
|
||||
<if test="settlementType != null and settlementType != ''"> and settlement_type = #{settlementType}</if>
|
||||
<if test="tempEd != null "> and temp_ed = #{tempEd}</if>
|
||||
<if test="accountBalance != null "> and account_balance = #{accountBalance}</if>
|
||||
<if test="foreignCurrencyBalance != null "> and foreign_currency_balance = #{foreignCurrencyBalance}</if>
|
||||
<if test="weightThreshold != null "> and weight_threshold = #{weightThreshold}</if>
|
||||
<if test="throwRatio != null "> and throw_ratio = #{throwRatio}</if>
|
||||
<if test="creditEd != null "> and credit_ed = #{creditEd}</if>
|
||||
<if test="productIds != null and productIds != ''"> and product_ids = #{productIds}</if>
|
||||
<if test="customerCode != null and customerCode != ''"> and c.customer_code = #{customerCode}</if>
|
||||
<if test="customerName != null and customerName != ''"> and c.customer_name like concat('%', #{customerName}, '%')</if>
|
||||
<if test="customerCodeDw != null and customerCodeDw != ''"> and c.customer_code_dw = #{customerCodeDw}</if>
|
||||
<if test="creditCode != null and creditCode != ''"> and c.credit_code = #{creditCode}</if>
|
||||
<if test="customerType != null and customerType != ''"> and c.customer_type = #{customerType}</if>
|
||||
<if test="isEnable != null and isEnable != ''"> and c.is_enable = #{isEnable}</if>
|
||||
<if test="salesUserId != null and salesUserId != ''"> and c.sales_user_id = #{salesUserId}</if>
|
||||
<if test="salesUser != null and salesUser != ''"> and c.sales_user = #{salesUser}</if>
|
||||
<if test="ed != null "> and c.ed = #{ed}</if>
|
||||
<if test="zq != null "> and c.zq = #{zq}</if>
|
||||
<if test="settlementUserId != null and settlementUserId != ''"> and c.settlement_user_id = #{settlementUserId}</if>
|
||||
<if test="settlementUser != null and settlementUser != ''"> and c.settlement_user = #{settlementUser}</if>
|
||||
<if test="serviceUserId != null and serviceUserId != ''"> and c.service_user_id = #{serviceUserId}</if>
|
||||
<if test="serviceUser != null and serviceUser != ''"> and c.service_user = #{serviceUser}</if>
|
||||
<if test="settlementType != null and settlementType != ''"> and c.settlement_type = #{settlementType}</if>
|
||||
<if test="tempEd != null "> and c.temp_ed = #{tempEd}</if>
|
||||
<if test="weightThreshold != null "> and c.weight_threshold = #{weightThreshold}</if>
|
||||
<if test="throwRatio != null "> and c.throw_ratio = #{throwRatio}</if>
|
||||
<if test="creditEd != null "> and c.credit_ed = #{creditEd}</if>
|
||||
<if test="productIds != null and productIds != ''"> and c.product_ids = #{productIds}</if>
|
||||
</where>
|
||||
order by c.customer_id desc
|
||||
</select>
|
||||
|
||||
<select id="selectBmsCustomerByCustomerId" parameterType="Long" resultMap="BmsCustomerResult">
|
||||
<include refid="selectBmsCustomerVo"/>
|
||||
where customer_id = #{customerId} and del_flag = '0'
|
||||
where c.customer_id = #{customerId} and c.del_flag = '0'
|
||||
</select>
|
||||
|
||||
<resultMap type="com.ruoyi.customer.customerinfo.domain.BmsCustomerAttachment" id="BmsCustomerAttachmentResult">
|
||||
@@ -82,6 +91,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="fileType" column="file_type" />
|
||||
<result property="fileExtension" column="file_extension" />
|
||||
<result property="attachmentType" column="attachment_type" />
|
||||
<result property="bizId" column="biz_id" />
|
||||
<result property="isLogo" column="is_logo" />
|
||||
<result property="sortOrder" column="sort_order" />
|
||||
<result property="remark" column="remark" />
|
||||
@@ -93,7 +103,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectBmsCustomerAttachmentVo">
|
||||
select attachment_id, customer_id, file_name, file_path, file_size, file_type, file_extension, attachment_type, is_logo, sort_order, remark, create_by, create_time, update_by, update_time, del_flag from bms_customer_attachment
|
||||
select attachment_id, customer_id, file_name, file_path, file_size, file_type, file_extension, attachment_type, biz_id, is_logo, sort_order, remark, create_by, create_time, update_by, update_time, del_flag from bms_customer_attachment
|
||||
</sql>
|
||||
|
||||
<select id="selectBmsCustomerAttachmentList" parameterType="com.ruoyi.customer.customerinfo.domain.BmsCustomerAttachment" resultMap="BmsCustomerAttachmentResult">
|
||||
@@ -102,6 +112,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="customerId != null "> and customer_id = #{customerId}</if>
|
||||
<if test="fileName != null and fileName != ''"> and file_name like concat('%', #{fileName}, '%')</if>
|
||||
<if test="attachmentType != null and attachmentType != ''"> and attachment_type = #{attachmentType}</if>
|
||||
<if test="bizId != null"> and biz_id = #{bizId}</if>
|
||||
<if test="isLogo != null and isLogo != ''"> and is_logo = #{isLogo}</if>
|
||||
and del_flag = '0'
|
||||
</where>
|
||||
@@ -118,6 +129,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="fileType != null">file_type,</if>
|
||||
<if test="fileExtension != null">file_extension,</if>
|
||||
<if test="attachmentType != null">attachment_type,</if>
|
||||
<if test="bizId != null">biz_id,</if>
|
||||
<if test="isLogo != null">is_logo,</if>
|
||||
<if test="sortOrder != null">sort_order,</if>
|
||||
<if test="remark != null">remark,</if>
|
||||
@@ -133,6 +145,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="fileType != null">#{fileType},</if>
|
||||
<if test="fileExtension != null">#{fileExtension},</if>
|
||||
<if test="attachmentType != null">#{attachmentType},</if>
|
||||
<if test="bizId != null">#{bizId},</if>
|
||||
<if test="isLogo != null">#{isLogo},</if>
|
||||
<if test="sortOrder != null">#{sortOrder},</if>
|
||||
<if test="remark != null">#{remark},</if>
|
||||
@@ -152,6 +165,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="fileType != null">file_type = #{fileType},</if>
|
||||
<if test="fileExtension != null">file_extension = #{fileExtension},</if>
|
||||
<if test="attachmentType != null">attachment_type = #{attachmentType},</if>
|
||||
<if test="bizId != null">biz_id = #{bizId},</if>
|
||||
<if test="isLogo != null">is_logo = #{isLogo},</if>
|
||||
<if test="sortOrder != null">sort_order = #{sortOrder},</if>
|
||||
<if test="remark != null">remark = #{remark},</if>
|
||||
|
||||
@@ -0,0 +1,322 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ruoyi.customer.customermoney.mapper.CustomerMoneyMapper">
|
||||
|
||||
<!-- ================= 客户余额账户 ================= -->
|
||||
<resultMap type="com.ruoyi.customer.customermoney.domain.CustomerAccount" id="CustomerAccountResult">
|
||||
<id property="id" column="id" />
|
||||
<result property="customerId" column="customer_id" />
|
||||
<result property="currency" column="currency" />
|
||||
<result property="balance" column="balance" />
|
||||
<result property="frozenBalance" column="frozen_balance" />
|
||||
<result property="totalCredit" column="total_credit" />
|
||||
<result property="extraCredit" column="extra_credit" />
|
||||
<result property="usedCredit" column="used_credit" />
|
||||
<result property="tax" column="tax" />
|
||||
<result property="version" column="version" />
|
||||
<result property="createBy" column="create_by" />
|
||||
<result property="createTime" column="create_time" />
|
||||
<result property="updateBy" column="update_by" />
|
||||
<result property="updateTime" column="update_time" />
|
||||
<result property="remark" column="remark" />
|
||||
<result property="delFlag" column="del_flag" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectCustomerAccountVo">
|
||||
select id, customer_id, currency, balance, frozen_balance, total_credit, extra_credit, used_credit,
|
||||
tax, version, create_by, create_time, update_by, update_time, remark, del_flag
|
||||
from customer_account
|
||||
</sql>
|
||||
|
||||
<select id="selectAccountByCustomerCurrency" resultMap="CustomerAccountResult">
|
||||
<include refid="selectCustomerAccountVo"/>
|
||||
where customer_id = #{customerId} and currency = #{currency} and del_flag = '0'
|
||||
</select>
|
||||
|
||||
<select id="selectAccountForUpdate" resultMap="CustomerAccountResult">
|
||||
<include refid="selectCustomerAccountVo"/>
|
||||
where customer_id = #{customerId} and currency = #{currency} and del_flag = '0'
|
||||
for update
|
||||
</select>
|
||||
|
||||
<select id="selectAccountListByCustomerId" resultMap="CustomerAccountResult">
|
||||
<include refid="selectCustomerAccountVo"/>
|
||||
where customer_id = #{customerId} and del_flag = '0'
|
||||
</select>
|
||||
|
||||
<insert id="insertAccount" parameterType="com.ruoyi.customer.customermoney.domain.CustomerAccount"
|
||||
useGeneratedKeys="true" keyProperty="id">
|
||||
insert into customer_account
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
<if test="customerId != null">customer_id,</if>
|
||||
<if test="currency != null">currency,</if>
|
||||
<if test="balance != null">balance,</if>
|
||||
<if test="frozenBalance != null">frozen_balance,</if>
|
||||
<if test="totalCredit != null">total_credit,</if>
|
||||
<if test="extraCredit != null">extra_credit,</if>
|
||||
<if test="usedCredit != null">used_credit,</if>
|
||||
<if test="tax != null">tax,</if>
|
||||
<if test="version != null">version,</if>
|
||||
<if test="createBy != null">create_by,</if>
|
||||
<if test="remark != null">remark,</if>
|
||||
del_flag,
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="customerId != null">#{customerId},</if>
|
||||
<if test="currency != null">#{currency},</if>
|
||||
<if test="balance != null">#{balance},</if>
|
||||
<if test="frozenBalance != null">#{frozenBalance},</if>
|
||||
<if test="totalCredit != null">#{totalCredit},</if>
|
||||
<if test="extraCredit != null">#{extraCredit},</if>
|
||||
<if test="usedCredit != null">#{usedCredit},</if>
|
||||
<if test="tax != null">#{tax},</if>
|
||||
<if test="version != null">#{version},</if>
|
||||
<if test="createBy != null">#{createBy},</if>
|
||||
<if test="remark != null">#{remark},</if>
|
||||
'0',
|
||||
</trim>
|
||||
</insert>
|
||||
|
||||
<update id="updateAccountBalance">
|
||||
update customer_account
|
||||
set balance = #{balance},
|
||||
frozen_balance = #{frozenBalance},
|
||||
used_credit = #{usedCredit},
|
||||
version = version + 1,
|
||||
update_by = #{updateBy},
|
||||
update_time = now()
|
||||
where id = #{id} and version = #{version} and del_flag = '0'
|
||||
</update>
|
||||
|
||||
<!-- ================= 客户收款 ================= -->
|
||||
<resultMap type="com.ruoyi.customer.customermoney.domain.CustomerReceipt" id="CustomerReceiptResult">
|
||||
<id property="id" column="id" />
|
||||
<result property="receiptNo" column="receipt_no" />
|
||||
<result property="customerId" column="customer_id" />
|
||||
<result property="accountId" column="account_id" />
|
||||
<result property="receiptType" column="receipt_type" />
|
||||
<result property="amount" column="amount" />
|
||||
<result property="allocatedAmount" column="allocated_amount" />
|
||||
<result property="unallocatedAmount" column="unallocated_amount" />
|
||||
<result property="currency" column="currency" />
|
||||
<result property="payMethod" column="pay_method" />
|
||||
<result property="bankTransactionNo" column="bank_transaction_no" />
|
||||
<result property="receivedAt" column="received_at" />
|
||||
<result property="status" column="status" />
|
||||
<result property="confirmedAt" column="confirmed_at" />
|
||||
<result property="confirmedBy" column="confirmed_by" />
|
||||
<result property="invoiceStatus" column="invoice_status" />
|
||||
<result property="version" column="version" />
|
||||
<result property="createBy" column="create_by" />
|
||||
<result property="createTime" column="create_time" />
|
||||
<result property="updateBy" column="update_by" />
|
||||
<result property="updateTime" column="update_time" />
|
||||
<result property="remark" column="remark" />
|
||||
<result property="delFlag" column="del_flag" />
|
||||
<result property="attachmentCount" column="attachment_count" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectCustomerReceiptVo">
|
||||
select r.id, r.receipt_no, r.customer_id, r.account_id, r.receipt_type, r.amount, r.allocated_amount,
|
||||
r.unallocated_amount, r.currency, r.pay_method, r.bank_transaction_no, r.received_at, r.status,
|
||||
r.confirmed_at, r.confirmed_by, r.invoice_status, r.version, r.create_by, r.create_time,
|
||||
r.update_by, r.update_time, r.remark, r.del_flag,
|
||||
(select count(1) from bms_customer_attachment a
|
||||
where a.biz_id = r.id and a.attachment_type = '3' and a.del_flag = '0') as attachment_count
|
||||
from customer_receipt r
|
||||
</sql>
|
||||
|
||||
<select id="selectReceiptList" parameterType="com.ruoyi.customer.customermoney.domain.CustomerReceipt"
|
||||
resultMap="CustomerReceiptResult">
|
||||
<include refid="selectCustomerReceiptVo"/>
|
||||
<where>
|
||||
r.del_flag = '0'
|
||||
<if test="customerId != null"> and r.customer_id = #{customerId}</if>
|
||||
|
||||
<if test="receiptNo != null and receiptNo != ''"> and r.receipt_no like concat('%', #{receiptNo}, '%')</if>
|
||||
<if test="receiptType != null and receiptType != ''"> and r.receipt_type = #{receiptType}</if>
|
||||
<if test="status != null and status != ''"> and r.status = #{status}</if>
|
||||
<if test="currency != null and currency != ''"> and r.currency = #{currency}</if>
|
||||
<if test="payMethod != null and payMethod != ''"> and r.pay_method = #{payMethod}</if>
|
||||
<if test="bankTransactionNo != null and bankTransactionNo != ''">
|
||||
and r.bank_transaction_no like concat('%', #{bankTransactionNo}, '%')
|
||||
</if>
|
||||
<if test="params != null and params.beginTime != null and params.beginTime != ''">
|
||||
and date_format(r.received_at,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d')
|
||||
</if>
|
||||
<if test="params != null and params.endTime != null and params.endTime != ''">
|
||||
and date_format(r.received_at,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d')
|
||||
</if>
|
||||
</where>
|
||||
order by r.received_at desc, r.id desc
|
||||
</select>
|
||||
|
||||
<select id="selectReceiptById" resultMap="CustomerReceiptResult">
|
||||
<include refid="selectCustomerReceiptVo"/>
|
||||
where r.id = #{id} and r.del_flag = '0'
|
||||
</select>
|
||||
|
||||
<insert id="insertReceipt" parameterType="com.ruoyi.customer.customermoney.domain.CustomerReceipt"
|
||||
useGeneratedKeys="true" keyProperty="id">
|
||||
insert into customer_receipt
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
<if test="receiptNo != null">receipt_no,</if>
|
||||
<if test="customerId != null">customer_id,</if>
|
||||
<if test="accountId != null">account_id,</if>
|
||||
<if test="receiptType != null">receipt_type,</if>
|
||||
<if test="amount != null">amount,</if>
|
||||
<if test="allocatedAmount != null">allocated_amount,</if>
|
||||
<if test="unallocatedAmount != null">unallocated_amount,</if>
|
||||
<if test="currency != null">currency,</if>
|
||||
<if test="payMethod != null">pay_method,</if>
|
||||
<if test="bankTransactionNo != null">bank_transaction_no,</if>
|
||||
<if test="receivedAt != null">received_at,</if>
|
||||
<if test="status != null">status,</if>
|
||||
<if test="confirmedAt != null">confirmed_at,</if>
|
||||
<if test="confirmedBy != null">confirmed_by,</if>
|
||||
<if test="invoiceStatus != null">invoice_status,</if>
|
||||
<if test="version != null">version,</if>
|
||||
<if test="createBy != null">create_by,</if>
|
||||
<if test="remark != null">remark,</if>
|
||||
del_flag,
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="receiptNo != null">#{receiptNo},</if>
|
||||
<if test="customerId != null">#{customerId},</if>
|
||||
<if test="accountId != null">#{accountId},</if>
|
||||
<if test="receiptType != null">#{receiptType},</if>
|
||||
<if test="amount != null">#{amount},</if>
|
||||
<if test="allocatedAmount != null">#{allocatedAmount},</if>
|
||||
<if test="unallocatedAmount != null">#{unallocatedAmount},</if>
|
||||
<if test="currency != null">#{currency},</if>
|
||||
<if test="payMethod != null">#{payMethod},</if>
|
||||
<if test="bankTransactionNo != null">#{bankTransactionNo},</if>
|
||||
<if test="receivedAt != null">#{receivedAt},</if>
|
||||
<if test="status != null">#{status},</if>
|
||||
<if test="confirmedAt != null">#{confirmedAt},</if>
|
||||
<if test="confirmedBy != null">#{confirmedBy},</if>
|
||||
<if test="invoiceStatus != null">#{invoiceStatus},</if>
|
||||
<if test="version != null">#{version},</if>
|
||||
<if test="createBy != null">#{createBy},</if>
|
||||
<if test="remark != null">#{remark},</if>
|
||||
'0',
|
||||
</trim>
|
||||
</insert>
|
||||
|
||||
<update id="confirmReceipt" parameterType="com.ruoyi.customer.customermoney.domain.CustomerReceipt">
|
||||
update customer_receipt
|
||||
set status = 'CONFIRMED',
|
||||
confirmed_at = #{confirmedAt},
|
||||
confirmed_by = #{confirmedBy},
|
||||
version = version + 1,
|
||||
update_by = #{updateBy},
|
||||
update_time = now()
|
||||
where id = #{id} and status = 'PENDING' and version = #{version} and del_flag = '0'
|
||||
</update>
|
||||
|
||||
<update id="cancelReceipt">
|
||||
update customer_receipt
|
||||
set status = 'CANCELLED',
|
||||
update_by = #{updateBy},
|
||||
update_time = now()
|
||||
where id = #{id} and status = 'PENDING' and del_flag = '0'
|
||||
</update>
|
||||
|
||||
<!-- ================= 余额流水 ================= -->
|
||||
<resultMap type="com.ruoyi.customer.customermoney.domain.CustomerBalanceFlow" id="CustomerBalanceFlowResult">
|
||||
<id property="id" column="id" />
|
||||
<result property="flowNo" column="flow_no" />
|
||||
<result property="customerId" column="customer_id" />
|
||||
<result property="accountId" column="account_id" />
|
||||
<result property="currency" column="currency" />
|
||||
<result property="bizType" column="biz_type" />
|
||||
<result property="bizId" column="biz_id" />
|
||||
<result property="bizNo" column="biz_no" />
|
||||
<result property="direction" column="direction" />
|
||||
<result property="amount" column="amount" />
|
||||
<result property="beforeBalance" column="before_balance" />
|
||||
<result property="afterBalance" column="after_balance" />
|
||||
<result property="beforeFrozenBalance" column="before_frozen_balance" />
|
||||
<result property="afterFrozenBalance" column="after_frozen_balance" />
|
||||
<result property="operatorId" column="operator_id" />
|
||||
<result property="createBy" column="create_by" />
|
||||
<result property="createTime" column="create_time" />
|
||||
<result property="updateBy" column="update_by" />
|
||||
<result property="updateTime" column="update_time" />
|
||||
<result property="remark" column="remark" />
|
||||
<result property="delFlag" column="del_flag" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectCustomerBalanceFlowVo">
|
||||
select id, flow_no, customer_id, account_id, currency, biz_type, biz_id, biz_no, direction, amount,
|
||||
before_balance, after_balance, before_frozen_balance, after_frozen_balance, operator_id,
|
||||
create_by, create_time, update_by, update_time, remark, del_flag
|
||||
from customer_balance_flow
|
||||
</sql>
|
||||
|
||||
<select id="selectFlowList" parameterType="com.ruoyi.customer.customermoney.domain.CustomerBalanceFlow"
|
||||
resultMap="CustomerBalanceFlowResult">
|
||||
<include refid="selectCustomerBalanceFlowVo"/>
|
||||
<where>
|
||||
del_flag = '0'
|
||||
<if test="customerId != null"> and customer_id = #{customerId}</if>
|
||||
<if test="accountId != null"> and account_id = #{accountId}</if>
|
||||
<if test="bizType != null and bizType != ''"> and biz_type = #{bizType}</if>
|
||||
<if test="direction != null"> and direction = #{direction}</if>
|
||||
<if test="currency != null and currency != ''"> and currency = #{currency}</if>
|
||||
<if test="bizNo != null and bizNo != ''"> and biz_no like concat('%', #{bizNo}, '%')</if>
|
||||
<if test="params != null and params.beginTime != null and params.beginTime != ''">
|
||||
and date_format(create_time,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d')
|
||||
</if>
|
||||
<if test="params != null and params.endTime != null and params.endTime != ''">
|
||||
and date_format(create_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d')
|
||||
</if>
|
||||
</where>
|
||||
order by create_time desc, id desc
|
||||
</select>
|
||||
|
||||
<insert id="insertFlow" parameterType="com.ruoyi.customer.customermoney.domain.CustomerBalanceFlow"
|
||||
useGeneratedKeys="true" keyProperty="id">
|
||||
insert into customer_balance_flow
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
<if test="flowNo != null">flow_no,</if>
|
||||
<if test="customerId != null">customer_id,</if>
|
||||
<if test="accountId != null">account_id,</if>
|
||||
<if test="currency != null">currency,</if>
|
||||
<if test="bizType != null">biz_type,</if>
|
||||
<if test="bizId != null">biz_id,</if>
|
||||
<if test="bizNo != null">biz_no,</if>
|
||||
<if test="direction != null">direction,</if>
|
||||
<if test="amount != null">amount,</if>
|
||||
<if test="beforeBalance != null">before_balance,</if>
|
||||
<if test="afterBalance != null">after_balance,</if>
|
||||
<if test="beforeFrozenBalance != null">before_frozen_balance,</if>
|
||||
<if test="afterFrozenBalance != null">after_frozen_balance,</if>
|
||||
<if test="operatorId != null">operator_id,</if>
|
||||
<if test="createBy != null">create_by,</if>
|
||||
<if test="remark != null">remark,</if>
|
||||
del_flag,
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="flowNo != null">#{flowNo},</if>
|
||||
<if test="customerId != null">#{customerId},</if>
|
||||
<if test="accountId != null">#{accountId},</if>
|
||||
<if test="currency != null">#{currency},</if>
|
||||
<if test="bizType != null">#{bizType},</if>
|
||||
<if test="bizId != null">#{bizId},</if>
|
||||
<if test="bizNo != null">#{bizNo},</if>
|
||||
<if test="direction != null">#{direction},</if>
|
||||
<if test="amount != null">#{amount},</if>
|
||||
<if test="beforeBalance != null">#{beforeBalance},</if>
|
||||
<if test="afterBalance != null">#{afterBalance},</if>
|
||||
<if test="beforeFrozenBalance != null">#{beforeFrozenBalance},</if>
|
||||
<if test="afterFrozenBalance != null">#{afterFrozenBalance},</if>
|
||||
<if test="operatorId != null">#{operatorId},</if>
|
||||
<if test="createBy != null">#{createBy},</if>
|
||||
<if test="remark != null">#{remark},</if>
|
||||
'0',
|
||||
</trim>
|
||||
</insert>
|
||||
</mapper>
|
||||
@@ -0,0 +1,90 @@
|
||||
DROP TABLE IF EXISTS `customer_account`;
|
||||
CREATE TABLE `customer_account` (
|
||||
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`customer_id` BIGINT(20) NOT NULL COMMENT '客户ID',
|
||||
`currency` CHAR(3) NOT NULL DEFAULT 'CNY' COMMENT '币种,如 CNY/USD',
|
||||
`balance` DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '账户余额',
|
||||
`frozen_balance` DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '冻结余额',
|
||||
`total_credit` DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '总额度',
|
||||
`extra_credit` DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '额外额度',
|
||||
`used_credit` DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '已用额度',
|
||||
`tax` DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '税金',
|
||||
`version` INT(11) NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||||
`create_by` VARCHAR(64) DEFAULT '' COMMENT '创建者',
|
||||
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_by` VARCHAR(64) DEFAULT '' COMMENT '更新者',
|
||||
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`remark` VARCHAR(500) DEFAULT NULL COMMENT '备注',
|
||||
`del_flag` CHAR(1) DEFAULT '0' COMMENT '删除标志(0代表存在 1代表删除)',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_customer_currency` (`customer_id`, `currency`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='客户余额表';
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `customer_receipt`;
|
||||
CREATE TABLE `customer_receipt` (
|
||||
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`receipt_no` VARCHAR(64) NOT NULL COMMENT '收款单号',
|
||||
`customer_id` BIGINT(20) NOT NULL COMMENT '客户ID',
|
||||
`account_id` BIGINT(20) DEFAULT NULL COMMENT '客户账户ID',
|
||||
`receipt_type` VARCHAR(32) NOT NULL DEFAULT 'PREPAY' COMMENT '收款类型:PREPAY预充值/REPAY还款核销/MIXED混合',
|
||||
`amount` DECIMAL(18,2) NOT NULL COMMENT '收款金额',
|
||||
`allocated_amount` DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '已核销金额',
|
||||
`unallocated_amount` DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '待核销金额',
|
||||
`currency` CHAR(3) NOT NULL DEFAULT 'CNY' COMMENT '币种,如 CNY/USD',
|
||||
`pay_method` VARCHAR(32) DEFAULT NULL COMMENT '收款方式:BANK_TRANSFER/ALIPAY/WECHAT/CASH/OTHER',
|
||||
`bank_transaction_no` VARCHAR(64) DEFAULT NULL COMMENT '银行流水号/支付平台流水号',
|
||||
`received_at` DATETIME NOT NULL COMMENT '收款日期',
|
||||
`status` VARCHAR(16) NOT NULL DEFAULT 'PENDING' COMMENT '状态:PENDING待确认/CONFIRMED已确认/CANCELLED已取消',
|
||||
`confirmed_at` DATETIME DEFAULT NULL COMMENT '确认时间',
|
||||
`confirmed_by` BIGINT(20) DEFAULT NULL COMMENT '确认人',
|
||||
`invoice_status` VARCHAR(16) NOT NULL DEFAULT 'NONE' COMMENT '开票状态:NONE未开/PART部分/ALL全部',
|
||||
`version` INT(11) NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||||
`create_by` VARCHAR(64) DEFAULT '' COMMENT '创建者',
|
||||
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_by` VARCHAR(64) DEFAULT '' COMMENT '更新者',
|
||||
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`remark` VARCHAR(500) DEFAULT NULL COMMENT '备注',
|
||||
`del_flag` CHAR(1) DEFAULT '0' COMMENT '删除标志(0代表存在 1代表删除)',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_receipt_no` (`receipt_no`),
|
||||
KEY `idx_customer_time` (`customer_id`, `received_at`),
|
||||
KEY `idx_account` (`account_id`),
|
||||
KEY `idx_status` (`status`),
|
||||
KEY `idx_bank_transaction_no` (`bank_transaction_no`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='客户收款表';
|
||||
|
||||
|
||||
CREATE TABLE `customer_balance_flow` (
|
||||
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`flow_no` VARCHAR(64) NOT NULL COMMENT '流水号',
|
||||
`customer_id` BIGINT(20) NOT NULL COMMENT '客户ID',
|
||||
`account_id` BIGINT(20) NOT NULL COMMENT '客户账户ID',
|
||||
`currency` CHAR(3) NOT NULL DEFAULT 'CNY' COMMENT '币种',
|
||||
`biz_type` VARCHAR(32) NOT NULL COMMENT '业务类型:RECEIPT收款/BILL_PAY账单支付/REFUND退款/ADJUST手工调整/FREEZE冻结/UNFREEZE解冻',
|
||||
`biz_id` BIGINT(20) DEFAULT NULL COMMENT '关联业务ID,如收款单ID、账单ID',
|
||||
`biz_no` VARCHAR(64) DEFAULT NULL COMMENT '关联业务单号',
|
||||
`direction` TINYINT(1) NOT NULL COMMENT '变动方向:1增加,-1减少',
|
||||
`amount` DECIMAL(18,2) NOT NULL COMMENT '变动金额',
|
||||
`before_balance` DECIMAL(18,2) NOT NULL COMMENT '变动前余额',
|
||||
`after_balance` DECIMAL(18,2) NOT NULL COMMENT '变动后余额',
|
||||
`before_frozen_balance` DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '变动前冻结余额',
|
||||
`after_frozen_balance` DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '变动后冻结余额',
|
||||
`operator_id` BIGINT(20) DEFAULT NULL COMMENT '操作人',
|
||||
`create_by` VARCHAR(64) DEFAULT '' COMMENT '创建者',
|
||||
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_by` VARCHAR(64) DEFAULT '' COMMENT '更新者',
|
||||
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`remark` VARCHAR(500) DEFAULT NULL COMMENT '备注',
|
||||
`del_flag` CHAR(1) DEFAULT '0' COMMENT '删除标志(0代表存在 1代表删除)',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_flow_no` (`flow_no`),
|
||||
KEY `idx_customer_time` (`customer_id`, `create_time`),
|
||||
KEY `idx_account_time` (`account_id`, `create_time`),
|
||||
KEY `idx_biz` (`biz_type`, `biz_id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='余额流水表';
|
||||
|
||||
-- 收款单附件复用客户附件表:attachment_type='3'(其他),biz_id 关联 customer_receipt.id
|
||||
ALTER TABLE `bms_customer_attachment`
|
||||
ADD COLUMN `biz_id` BIGINT(20) DEFAULT NULL COMMENT '关联业务ID(如收款单ID,attachment_type=3时使用)' AFTER `attachment_type`,
|
||||
ADD KEY `idx_biz_id` (`biz_id`);
|
||||
@@ -0,0 +1,60 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
/** 查询客户各币种账户余额 */
|
||||
export function listAccount(customerId) {
|
||||
return request({
|
||||
url: '/customersupplier/customermoney/account/list/' + customerId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
/** 查询收款记录列表 */
|
||||
export function listReceipt(query) {
|
||||
return request({
|
||||
url: '/customersupplier/customermoney/receipt/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
/** 查询收款单详情(含附件) */
|
||||
export function getReceipt(id) {
|
||||
return request({
|
||||
url: '/customersupplier/customermoney/receipt/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
/** 登记收款 */
|
||||
export function addReceipt(data) {
|
||||
return request({
|
||||
url: '/customersupplier/customermoney/receipt',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/** 审核通过收款单 */
|
||||
export function confirmReceipt(id) {
|
||||
return request({
|
||||
url: '/customersupplier/customermoney/receipt/confirm/' + id,
|
||||
method: 'put'
|
||||
})
|
||||
}
|
||||
|
||||
/** 取消收款单 */
|
||||
export function cancelReceipt(id) {
|
||||
return request({
|
||||
url: '/customersupplier/customermoney/receipt/cancel/' + id,
|
||||
method: 'put'
|
||||
})
|
||||
}
|
||||
|
||||
/** 查询余额流水列表 */
|
||||
export function listFlow(query) {
|
||||
return request({
|
||||
url: '/customersupplier/customermoney/flow/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
@@ -158,6 +158,20 @@ export const dynamicRoutes = [
|
||||
meta: { title: '修改生成配置', activeMenu: '/tool/gen' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/customersupplier/customer-money',
|
||||
component: Layout,
|
||||
hidden: true,
|
||||
permissions: ['customersupplier:customerinfo:list'],
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
component: () => import('@/views/customersuppliermanagement/customerinfo/customermoney/index.vue'),
|
||||
name: 'CustomerMoney',
|
||||
meta: { title: '客户余额', activeMenu: '/customersupplier/customerinfo' }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -0,0 +1,838 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!-- 顶部:返回 + 标题 -->
|
||||
<el-card shadow="never" class="head-card">
|
||||
<div class="head-bar">
|
||||
<el-button icon="ArrowLeft" @click="goBack">返回</el-button>
|
||||
<div class="head-title">
|
||||
<span class="customer-name">{{ customer.customerName || route.query.customerName || '-' }}</span>
|
||||
<el-tag size="small" type="info">客户余额</el-tag>
|
||||
</div>
|
||||
<div class="head-code">客户编码:{{ customer.customerCode || route.query.customerCode || '-' }}</div>
|
||||
<div class="head-right">
|
||||
<span style="color:#909399;font-size:13px;">币种</span>
|
||||
<el-select v-model="currentCurrency" style="width: 110px;" @change="loadAccounts">
|
||||
<el-option v-for="acc in accountList" :key="acc.currency" :label="acc.currency" :value="acc.currency" />
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 余额概览(数据来源 customer_account) -->
|
||||
<el-row :gutter="12" class="balance-row">
|
||||
<el-col :xs="12" :sm="8" :md="4">
|
||||
<el-card shadow="hover" class="balance-card">
|
||||
<div class="balance-label">账户余额</div>
|
||||
<div class="balance-value primary">{{ formatMoney(currentAccount.balance) }}</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="12" :sm="8" :md="4">
|
||||
<el-card shadow="hover" class="balance-card">
|
||||
<div class="balance-label">冻结余额</div>
|
||||
<div class="balance-value warning">{{ formatMoney(currentAccount.frozenBalance) }}</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="12" :sm="8" :md="4">
|
||||
<el-card shadow="hover" class="balance-card">
|
||||
<div class="balance-label">总额度</div>
|
||||
<div class="balance-value">{{ formatMoney(currentAccount.totalCredit) }}</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="12" :sm="8" :md="4">
|
||||
<el-card shadow="hover" class="balance-card">
|
||||
<div class="balance-label">额外额度</div>
|
||||
<div class="balance-value">{{ formatMoney(currentAccount.extraCredit) }}</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="12" :sm="8" :md="4">
|
||||
<el-card shadow="hover" class="balance-card">
|
||||
<div class="balance-label">已用额度</div>
|
||||
<div class="balance-value danger">{{ formatMoney(currentAccount.usedCredit) }}</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="12" :sm="8" :md="4">
|
||||
<el-card shadow="hover" class="balance-card">
|
||||
<div class="balance-label">可用额度</div>
|
||||
<div class="balance-value success">{{ availableCredit }}</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 明细区域 -->
|
||||
<el-card shadow="never">
|
||||
<el-tabs v-model="activeTab">
|
||||
<!-- ====== 收款记录 ====== -->
|
||||
<el-tab-pane label="收款记录" name="receipt">
|
||||
<el-form :model="receiptQuery" :inline="true" class="search-form">
|
||||
<el-form-item label="收款类型" prop="receiptType">
|
||||
<el-select v-model="receiptQuery.receiptType" placeholder="全部" clearable style="width: 130px;">
|
||||
<el-option v-for="opt in receiptTypeOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="receiptQuery.status" placeholder="全部" clearable style="width: 120px;">
|
||||
<el-option v-for="opt in receiptStatusOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="收款日期">
|
||||
<el-date-picker
|
||||
v-model="receiptDateRange"
|
||||
value-format="YYYY-MM-DD"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="getReceiptList">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetReceiptQuery">重置</el-button>
|
||||
<el-button type="success" icon="Plus" @click="handleAddReceipt">登记收款</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table :data="receiptList" border v-loading="receiptLoading">
|
||||
<el-table-column label="收款单号" align="center" prop="receiptNo" width="190" />
|
||||
<el-table-column label="收款日期" align="center" prop="receivedAt" width="160">
|
||||
<template #default="scope">{{ formatDateTime(scope.row.receivedAt) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="收款类型" align="center" prop="receiptType" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="receiptTypeTag(scope.row.receiptType)">{{ formatOption(receiptTypeOptions, scope.row.receiptType) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="收款金额" align="center" width="130">
|
||||
<template #default="scope">
|
||||
<span class="money-in">{{ scope.row.currency }} {{ formatMoney(scope.row.amount) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="待核销" align="center" prop="unallocatedAmount" width="110" />
|
||||
<el-table-column label="收款方式" align="center" prop="payMethod" width="110">
|
||||
<template #default="scope">{{ formatOption(payMethodOptions, scope.row.payMethod) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="银行/平台流水号" align="center" prop="bankTransactionNo" width="170" show-overflow-tooltip />
|
||||
<el-table-column label="状态" align="center" prop="status" width="90">
|
||||
<template #default="scope">
|
||||
<el-tag :type="receiptStatusTag(scope.row.status)">{{ formatOption(receiptStatusOptions, scope.row.status) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="附件" align="center" width="70">
|
||||
<template #default="scope">
|
||||
<el-badge v-if="scope.row.attachmentCount > 0" :value="scope.row.attachmentCount" type="primary">
|
||||
<el-icon><Paperclip /></el-icon>
|
||||
</el-badge>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="登记人" align="center" prop="createBy" width="100" />
|
||||
<el-table-column label="操作" align="center" width="200" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button v-if="scope.row.status === 'PENDING'" link type="success" size="small" @click="handleConfirmReceipt(scope.row)">审核通过</el-button>
|
||||
<el-button v-if="scope.row.status === 'PENDING'" link type="warning" size="small" @click="handleCancelReceipt(scope.row)">取消</el-button>
|
||||
<el-button link type="primary" size="small" @click="handleViewReceipt(scope.row)">查看</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="receiptTotal > 0"
|
||||
:total="receiptTotal"
|
||||
v-model:page="receiptQuery.pageNum"
|
||||
v-model:limit="receiptQuery.pageSize"
|
||||
@pagination="getReceiptList"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- ====== 余额流水 ====== -->
|
||||
<el-tab-pane label="余额流水" name="flow">
|
||||
<el-form :model="flowQuery" :inline="true" class="search-form">
|
||||
<el-form-item label="业务类型" prop="bizType">
|
||||
<el-select v-model="flowQuery.bizType" placeholder="全部" clearable style="width: 130px;">
|
||||
<el-option v-for="opt in bizTypeOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="收支方向" prop="direction">
|
||||
<el-select v-model="flowQuery.direction" placeholder="全部" clearable style="width: 120px;">
|
||||
<el-option label="收入" :value="1" />
|
||||
<el-option label="支出" :value="-1" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="日期">
|
||||
<el-date-picker
|
||||
v-model="flowDateRange"
|
||||
value-format="YYYY-MM-DD"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="getFlowList">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetFlowQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table :data="flowList" border v-loading="flowLoading">
|
||||
<el-table-column label="流水号" align="center" prop="flowNo" width="190" />
|
||||
<el-table-column label="时间" align="center" prop="createTime" width="160">
|
||||
<template #default="scope">{{ formatDateTime(scope.row.createTime) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="业务类型" align="center" prop="bizType" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="bizTypeTag(scope.row.bizType)">{{ formatOption(bizTypeOptions, scope.row.bizType) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="收支" align="center" width="80">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.direction === 1" type="success">收入</el-tag>
|
||||
<el-tag v-else type="danger">支出</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="变动金额" align="center" width="130">
|
||||
<template #default="scope">
|
||||
<span :class="scope.row.direction === 1 ? 'money-in' : 'money-out'">
|
||||
{{ scope.row.direction === 1 ? '+' : '-' }}{{ formatMoney(scope.row.amount) }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="变动前余额" align="center" prop="beforeBalance" width="120" />
|
||||
<el-table-column label="变动后余额" align="center" prop="afterBalance" width="120" />
|
||||
<el-table-column label="币种" align="center" prop="currency" width="70" />
|
||||
<el-table-column label="关联单号" align="center" prop="bizNo" width="180" show-overflow-tooltip />
|
||||
<el-table-column label="操作人" align="center" prop="createBy" width="100" />
|
||||
<el-table-column label="备注" align="center" prop="remark" show-overflow-tooltip min-width="160" />
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="flowTotal > 0"
|
||||
:total="flowTotal"
|
||||
v-model:page="flowQuery.pageNum"
|
||||
v-model:limit="flowQuery.pageSize"
|
||||
@pagination="getFlowList"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-card>
|
||||
|
||||
<!-- ====== 登记收款弹窗 ====== -->
|
||||
<el-dialog title="登记收款" v-model="receiptDialog.visible" width="640px" append-to-body @closed="onReceiptDialogClosed">
|
||||
<el-form ref="receiptFormRef" :model="receiptDialog.form" :rules="receiptRules" label-width="110px">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="收款日期" prop="receivedAt">
|
||||
<el-date-picker
|
||||
v-model="receiptDialog.form.receivedAt"
|
||||
type="datetime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
placeholder="选择收款日期时间"
|
||||
style="width: 100%;"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="收款金额" prop="amount">
|
||||
<el-input v-model="receiptDialog.form.amount" placeholder="请输入金额" type="number">
|
||||
<template #prepend>¥</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="收款类型" prop="receiptType">
|
||||
<el-select v-model="receiptDialog.form.receiptType" placeholder="请选择" style="width: 100%;">
|
||||
<el-option v-for="opt in receiptTypeOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="币种" prop="currency">
|
||||
<el-select v-model="receiptDialog.form.currency" placeholder="请选择" style="width: 100%;">
|
||||
<el-option label="人民币 CNY" value="CNY" />
|
||||
<el-option label="美元 USD" value="USD" />
|
||||
<el-option label="欧元 EUR" value="EUR" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="收款方式" prop="payMethod">
|
||||
<el-select v-model="receiptDialog.form.payMethod" placeholder="请选择" style="width: 100%;" clearable>
|
||||
<el-option v-for="opt in payMethodOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="流水号" prop="bankTransactionNo">
|
||||
<el-input v-model="receiptDialog.form.bankTransactionNo" placeholder="银行/支付平台流水号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="receiptDialog.form.remark" type="textarea" :rows="2" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="收款凭证">
|
||||
<div style="margin-bottom: 8px;">
|
||||
<el-upload
|
||||
:show-file-list="false"
|
||||
:action="uploadFileUrl"
|
||||
:headers="uploadHeaders"
|
||||
:before-upload="handleBeforeUpload"
|
||||
:on-success="handleUploadSuccess"
|
||||
:on-error="handleUploadError"
|
||||
multiple
|
||||
>
|
||||
<el-button type="primary" icon="Upload">上传附件</el-button>
|
||||
</el-upload>
|
||||
</div>
|
||||
<el-table :data="receiptDialog.form.attachmentList" border size="small" max-height="220">
|
||||
<el-table-column label="序号" type="index" width="55" align="center" />
|
||||
<el-table-column label="文件名称" prop="fileName" align="center" show-overflow-tooltip min-width="180" />
|
||||
<el-table-column label="大小" align="center" width="100">
|
||||
<template #default="scope">{{ formatFileSize(scope.row.fileSize) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="类型" prop="fileExtension" align="center" width="80" />
|
||||
<el-table-column label="操作" align="center" width="80">
|
||||
<template #default="scope">
|
||||
<el-button link type="danger" icon="Delete" @click="handleRemoveUpload(scope.$index)" />
|
||||
<el-button link type="primary" icon="Download" @click="handleDownload(scope.row)">下载</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="upload-tip">附件分类挂在「其他」类型下,支持 PDF/图片/Office/压缩包,单个不超过 50MB</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button type="primary" :loading="submitLoading" @click="submitReceipt">确 定</el-button>
|
||||
<el-button @click="receiptDialog.visible = false">取 消</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- ====== 收款单查看弹窗 ====== -->
|
||||
<el-dialog title="收款单详情" v-model="viewDialog.visible" width="720px" append-to-body>
|
||||
<el-descriptions :column="2" border v-if="viewDialog.data" size="small">
|
||||
<el-descriptions-item label="收款单号">{{ viewDialog.data.receiptNo }}</el-descriptions-item>
|
||||
<el-descriptions-item label="状态">
|
||||
<el-tag :type="receiptStatusTag(viewDialog.data.status)">{{ formatOption(receiptStatusOptions, viewDialog.data.status) }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="收款日期">{{ formatDateTime(viewDialog.data.receivedAt) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="收款类型">
|
||||
<el-tag :type="receiptTypeTag(viewDialog.data.receiptType)">{{ formatOption(receiptTypeOptions, viewDialog.data.receiptType) }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="收款金额">
|
||||
<span class="money-in">{{ viewDialog.data.currency }} {{ formatMoney(viewDialog.data.amount) }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="待核销金额">{{ formatMoney(viewDialog.data.unallocatedAmount) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="收款方式">{{ formatOption(payMethodOptions, viewDialog.data.payMethod) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="银行流水号">{{ viewDialog.data.bankTransactionNo || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="确认时间">{{ formatDateTime(viewDialog.data.confirmedAt) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="登记人">{{ viewDialog.data.createBy || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="备注" :span="2">{{ viewDialog.data.remark || '-' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<div class="attach-title">收款凭证附件</div>
|
||||
<el-table :data="viewDialog.attachments" border size="small" max-height="240" v-loading="viewDialog.loading">
|
||||
<el-table-column label="序号" type="index" width="55" align="center" />
|
||||
<el-table-column label="文件名称" prop="fileName" align="center" show-overflow-tooltip min-width="200" />
|
||||
<el-table-column label="大小" align="center" width="100">
|
||||
<template #default="scope">{{ formatFileSize(scope.row.fileSize) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="类型" prop="fileExtension" align="center" width="80" />
|
||||
<el-table-column label="上传人" align="center" prop="createBy" width="100" />
|
||||
<el-table-column label="操作" align="center" width="100">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" icon="Download" @click="handleDownload(scope.row)">下载</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template #empty>
|
||||
<el-empty description="暂无收款凭证附件" :image-size="70" />
|
||||
</template>
|
||||
</el-table>
|
||||
|
||||
<template #footer>
|
||||
<el-button
|
||||
v-if="viewDialog.data && viewDialog.data.status === 'PENDING'"
|
||||
type="success"
|
||||
@click="handleConfirmFromView"
|
||||
>审核通过</el-button>
|
||||
<el-button @click="viewDialog.visible = false">关 闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="CustomerMoney">
|
||||
import axios from 'axios'
|
||||
import { saveAs } from 'file-saver'
|
||||
import { getCustomer } from "@/api/customersuppliermanagement/customerinfo/customer.js"
|
||||
import { listAccount, listReceipt, getReceipt, addReceipt, confirmReceipt, cancelReceipt, listFlow } from "@/api/customersuppliermanagement/customermoney/customermoney.js"
|
||||
import { getToken } from "@/utils/auth"
|
||||
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const { proxy } = getCurrentInstance()
|
||||
const baseUrl = import.meta.env.VITE_APP_BASE_API
|
||||
|
||||
const activeTab = ref('receipt')
|
||||
const customer = ref({})
|
||||
|
||||
/* ================= 枚举 ================= */
|
||||
const receiptTypeOptions = [
|
||||
{ value: 'PREPAY', label: '预充值' },
|
||||
{ value: 'REPAY', label: '还款核销' },
|
||||
{ value: 'MIXED', label: '混合' }
|
||||
]
|
||||
const receiptStatusOptions = [
|
||||
{ value: 'PENDING', label: '待确认' },
|
||||
{ value: 'CONFIRMED', label: '已确认' },
|
||||
{ value: 'CANCELLED', label: '已取消' }
|
||||
]
|
||||
const payMethodOptions = [
|
||||
{ value: 'BANK_TRANSFER', label: '银行转账' },
|
||||
{ value: 'ALIPAY', label: '支付宝' },
|
||||
{ value: 'WECHAT', label: '微信' },
|
||||
{ value: 'CASH', label: '现金' },
|
||||
{ value: 'OTHER', label: '其他' }
|
||||
]
|
||||
const bizTypeOptions = [
|
||||
{ value: 'RECEIPT', label: '收款' },
|
||||
{ value: 'BILL_PAY', label: '账单支付' },
|
||||
{ value: 'REFUND', label: '退款' },
|
||||
{ value: 'ADJUST', label: '手工调整' },
|
||||
{ value: 'FREEZE', label: '冻结' },
|
||||
{ value: 'UNFREEZE', label: '解冻' }
|
||||
]
|
||||
|
||||
|
||||
|
||||
function formatOption(options, value) {
|
||||
if (value === null || value === undefined || value === '') return '-'
|
||||
const opt = options.find(o => String(o.value) === String(value))
|
||||
return opt ? opt.label : value
|
||||
}
|
||||
function receiptTypeTag(v) {
|
||||
return { PREPAY: '', REPAY: 'warning', MIXED: 'info' }[v] || 'info'
|
||||
}
|
||||
function receiptStatusTag(v) {
|
||||
return { PENDING: 'warning', CONFIRMED: 'success', CANCELLED: 'danger' }[v] || 'info'
|
||||
}
|
||||
function bizTypeTag(v) {
|
||||
return { RECEIPT: 'success', BILL_PAY: 'danger', REFUND: 'warning', ADJUST: 'info', FREEZE: 'info', UNFREEZE: '' }[v] || 'info'
|
||||
}
|
||||
function formatMoney(val) {
|
||||
const num = Number(val)
|
||||
return isNaN(num) ? '0.00' : num.toFixed(2)
|
||||
}
|
||||
function formatDateTime(val) {
|
||||
if (!val) return '-'
|
||||
return String(val).length > 16 ? String(val).substring(0, 16) : val
|
||||
}
|
||||
|
||||
/* ================= 客户信息 & 账户 ================= */
|
||||
const accountList = ref([])
|
||||
const currentCurrency = ref('CNY')
|
||||
|
||||
const currentAccount = computed(() => {
|
||||
return accountList.value.find(a => a.currency === currentCurrency.value) || {}
|
||||
})
|
||||
|
||||
/** 可用额度 = 总额度 + 额外额度 - 已用额度 */
|
||||
const availableCredit = computed(() => {
|
||||
const a = currentAccount.value
|
||||
const total = Number(a.totalCredit) || 0
|
||||
const extra = Number(a.extraCredit) || 0
|
||||
const used = Number(a.usedCredit) || 0
|
||||
return formatMoney(total + extra - used)
|
||||
})
|
||||
|
||||
function loadCustomer() {
|
||||
const customerId = route.query.customerId
|
||||
if (!customerId) return
|
||||
getCustomer(customerId).then(res => {
|
||||
customer.value = res.data || {}
|
||||
})
|
||||
}
|
||||
|
||||
function loadAccounts() {
|
||||
const customerId = route.query.customerId
|
||||
if (!customerId) return
|
||||
listAccount(customerId).then(res => {
|
||||
accountList.value = res.data || []
|
||||
if (accountList.value.length > 0) {
|
||||
const exists = accountList.value.some(a => a.currency === currentCurrency.value)
|
||||
if (!exists) currentCurrency.value = accountList.value[0].currency
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function goBack() {
|
||||
router.push({ path: '/customersuppliermanagement/customerinfo' })
|
||||
}
|
||||
|
||||
/* ================= 收款记录 ================= */
|
||||
const receiptList = ref([])
|
||||
const receiptTotal = ref(0)
|
||||
const receiptLoading = ref(false)
|
||||
const receiptDateRange = ref([])
|
||||
const receiptQuery = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
customerId: route.query.customerId,
|
||||
receiptType: '',
|
||||
status: ''
|
||||
})
|
||||
|
||||
function getReceiptList() {
|
||||
receiptLoading.value = true
|
||||
const params = proxy.addDateRange({ ...receiptQuery }, receiptDateRange.value)
|
||||
listReceipt(params).then(res => {
|
||||
receiptList.value = res.rows || []
|
||||
receiptTotal.value = res.total || 0
|
||||
}).finally(() => {
|
||||
receiptLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function resetReceiptQuery() {
|
||||
receiptQuery.receiptType = ''
|
||||
receiptQuery.status = ''
|
||||
receiptDateRange.value = []
|
||||
receiptQuery.pageNum = 1
|
||||
getReceiptList()
|
||||
}
|
||||
|
||||
/** 审核通过 */
|
||||
function handleConfirmReceipt(row) {
|
||||
proxy.$modal.confirm(`是否确认审核通过收款单「${row.receiptNo}」?审核通过后金额将计入客户余额。`).then(() => {
|
||||
return confirmReceipt(row.id)
|
||||
}).then(() => {
|
||||
proxy.$modal.msgSuccess("审核通过,余额已更新")
|
||||
getReceiptList()
|
||||
loadAccounts()
|
||||
if (activeTab.value === 'flow') getFlowList()
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
/** 取消收款单 */
|
||||
function handleCancelReceipt(row) {
|
||||
proxy.$modal.confirm(`是否确认取消收款单「${row.receiptNo}」?取消后不可恢复。`).then(() => {
|
||||
return cancelReceipt(row.id)
|
||||
}).then(() => {
|
||||
proxy.$modal.msgSuccess("已取消")
|
||||
getReceiptList()
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
/* ============ 登记收款弹窗 ============ */
|
||||
const receiptFormRef = ref(null)
|
||||
const submitLoading = ref(false)
|
||||
const receiptDialog = reactive({
|
||||
visible: false,
|
||||
form: {}
|
||||
})
|
||||
const receiptRules = {
|
||||
receivedAt: [{ required: true, message: '收款日期不能为空', trigger: 'change' }],
|
||||
amount: [{ required: true, message: '收款金额不能为空', trigger: 'blur' }],
|
||||
receiptType: [{ required: true, message: '请选择收款类型', trigger: 'change' }],
|
||||
currency: [{ required: true, message: '请选择币种', trigger: 'change' }]
|
||||
}
|
||||
|
||||
function resetReceiptForm() {
|
||||
receiptDialog.form = {
|
||||
customerId: route.query.customerId || null,
|
||||
receivedAt: proxy.parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}'),
|
||||
amount: '',
|
||||
receiptType: 'PREPAY',
|
||||
currency: 'CNY',
|
||||
payMethod: 'BANK_TRANSFER',
|
||||
bankTransactionNo: '',
|
||||
remark: '',
|
||||
attachmentList: []
|
||||
}
|
||||
proxy.resetForm('receiptFormRef')
|
||||
}
|
||||
|
||||
function handleAddReceipt() {
|
||||
resetReceiptForm()
|
||||
receiptDialog.visible = true
|
||||
}
|
||||
|
||||
function submitReceipt() {
|
||||
receiptFormRef.value.validate(valid => {
|
||||
if (!valid) return
|
||||
submitLoading.value = true
|
||||
addReceipt(receiptDialog.form).then(() => {
|
||||
proxy.$modal.msgSuccess("登记成功,等待审核")
|
||||
receiptDialog.visible = false
|
||||
getReceiptList()
|
||||
}).finally(() => {
|
||||
submitLoading.value = false
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function onReceiptDialogClosed() {
|
||||
resetReceiptForm()
|
||||
}
|
||||
|
||||
/* ============ 收款单查看弹窗 ============ */
|
||||
const viewDialog = reactive({
|
||||
visible: false,
|
||||
data: null,
|
||||
attachments: [],
|
||||
loading: false
|
||||
})
|
||||
let viewReceiptId = null
|
||||
|
||||
function handleViewReceipt(row) {
|
||||
viewReceiptId = row.id
|
||||
viewDialog.visible = true
|
||||
viewDialog.loading = true
|
||||
viewDialog.data = null
|
||||
viewDialog.attachments = []
|
||||
getReceipt(row.id).then(res => {
|
||||
viewDialog.data = res.data || null
|
||||
viewDialog.attachments = res.data?.attachmentList || []
|
||||
}).finally(() => {
|
||||
viewDialog.loading = false
|
||||
})
|
||||
}
|
||||
|
||||
function handleConfirmFromView() {
|
||||
if (!viewReceiptId) return
|
||||
viewDialog.visible = false
|
||||
const row = { id: viewReceiptId, receiptNo: viewDialog.data?.receiptNo }
|
||||
handleConfirmReceipt(row)
|
||||
}
|
||||
|
||||
/* ================= 余额流水 ================= */
|
||||
const flowList = ref([])
|
||||
const flowTotal = ref(0)
|
||||
const flowLoading = ref(false)
|
||||
const flowDateRange = ref([])
|
||||
const flowQuery = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
customerId: route.query.customerId,
|
||||
bizType: '',
|
||||
direction: ''
|
||||
})
|
||||
|
||||
function getFlowList() {
|
||||
flowLoading.value = true
|
||||
const params = proxy.addDateRange({ ...flowQuery }, flowDateRange.value)
|
||||
listFlow(params).then(res => {
|
||||
flowList.value = res.rows || []
|
||||
flowTotal.value = res.total || 0
|
||||
}).finally(() => {
|
||||
flowLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function resetFlowQuery() {
|
||||
flowQuery.bizType = ''
|
||||
flowQuery.direction = ''
|
||||
flowDateRange.value = []
|
||||
flowQuery.pageNum = 1
|
||||
getFlowList()
|
||||
}
|
||||
|
||||
/* ================= 附件上传 ================= */
|
||||
const uploadFileUrl = ref(baseUrl + "/common/upload")
|
||||
const uploadHeaders = ref({ Authorization: "Bearer " + getToken() })
|
||||
const ATTACHMENT_SIZE_LIMIT = 50
|
||||
const ATTACHMENT_EXTENSIONS = ["pdf", "jpg", "jpeg", "png", "gif", "doc", "docx", "xls", "xlsx", "zip", "rar"]
|
||||
|
||||
function formatFileSize(bytes) {
|
||||
if (!bytes) return "0 B"
|
||||
const k = 1024
|
||||
const sizes = ["B", "KB", "MB", "GB"]
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]
|
||||
}
|
||||
|
||||
function handleBeforeUpload(file) {
|
||||
const ext = (file.name.split('.').pop() || "").toLowerCase()
|
||||
if (ATTACHMENT_EXTENSIONS.indexOf(ext) < 0) {
|
||||
proxy.$modal.msgError(`不支持的文件格式:${ext}`)
|
||||
return false
|
||||
}
|
||||
if (file.name.includes(',')) {
|
||||
proxy.$modal.msgError('文件名不正确,不能包含英文逗号!')
|
||||
return false
|
||||
}
|
||||
if (file.size / 1024 / 1024 >= ATTACHMENT_SIZE_LIMIT) {
|
||||
proxy.$modal.msgError(`上传文件大小不能超过 ${ATTACHMENT_SIZE_LIMIT} MB!`)
|
||||
return false
|
||||
}
|
||||
proxy.$modal.loading("正在上传文件,请稍候...")
|
||||
return true
|
||||
}
|
||||
|
||||
function handleUploadSuccess(res, file) {
|
||||
proxy.$modal.closeLoading()
|
||||
if (res.code === 200) {
|
||||
receiptDialog.form.attachmentList.push({
|
||||
attachmentId: null,
|
||||
customerId: route.query.customerId || null,
|
||||
bizId: null,
|
||||
fileName: file.name,
|
||||
filePath: res.fileName,
|
||||
fileSize: file.size,
|
||||
fileType: file.type || '',
|
||||
fileExtension: (file.name.split('.').pop() || "").toLowerCase(),
|
||||
attachmentType: "3",
|
||||
isLogo: "0",
|
||||
sortOrder: receiptDialog.form.attachmentList.length,
|
||||
remark: "",
|
||||
delFlag: "0"
|
||||
})
|
||||
} else {
|
||||
proxy.$modal.msgError(res.msg || "上传文件失败")
|
||||
}
|
||||
}
|
||||
|
||||
function handleUploadError() {
|
||||
proxy.$modal.closeLoading()
|
||||
proxy.$modal.msgError("上传文件失败")
|
||||
}
|
||||
|
||||
function handleRemoveUpload(index) {
|
||||
receiptDialog.form.attachmentList.splice(index, 1)
|
||||
}
|
||||
|
||||
/** 附件下载(携带鉴权头,并以原始文件名保存) */
|
||||
function handleDownload(file) {
|
||||
proxy.$modal.loading("正在下载文件,请稍候...")
|
||||
axios({
|
||||
method: 'get',
|
||||
url: baseUrl + "/common/download/resource?resource=" + encodeURIComponent(file.filePath),
|
||||
responseType: 'blob',
|
||||
headers: { Authorization: "Bearer " + getToken() }
|
||||
}).then(res => {
|
||||
const blob = new Blob([res.data])
|
||||
saveAs(blob, file.fileName)
|
||||
}).catch(() => {
|
||||
proxy.$modal.msgError("下载失败")
|
||||
}).finally(() => {
|
||||
proxy.$modal.closeLoading()
|
||||
})
|
||||
}
|
||||
|
||||
/* ================= 初始化(同路由切换客户时需整体重置) ================= */
|
||||
function initPage() {
|
||||
const customerId = route.query.customerId
|
||||
// 重置页面状态,避免上一个客户的数据残留
|
||||
customer.value = {}
|
||||
accountList.value = []
|
||||
currentCurrency.value = 'CNY'
|
||||
activeTab.value = 'receipt'
|
||||
receiptDialog.visible = false
|
||||
viewDialog.visible = false
|
||||
viewDialog.data = null
|
||||
viewDialog.attachments = []
|
||||
receiptDateRange.value = []
|
||||
flowDateRange.value = []
|
||||
receiptQuery.customerId = customerId
|
||||
receiptQuery.receiptType = ''
|
||||
receiptQuery.status = ''
|
||||
receiptQuery.pageNum = 1
|
||||
flowQuery.customerId = customerId
|
||||
flowQuery.bizType = ''
|
||||
flowQuery.direction = ''
|
||||
flowQuery.pageNum = 1
|
||||
receiptList.value = []
|
||||
receiptTotal.value = 0
|
||||
flowList.value = []
|
||||
flowTotal.value = 0
|
||||
if (!customerId) return
|
||||
loadCustomer()
|
||||
loadAccounts()
|
||||
getReceiptList()
|
||||
getFlowList()
|
||||
}
|
||||
|
||||
onMounted(initPage)
|
||||
|
||||
// 从客户列表点不同客户跳转时,路由路径相同仅 query 变化,组件会被复用,必须监听 customerId 重新初始化
|
||||
watch(() => route.query.customerId, (newId, oldId) => {
|
||||
if (newId && newId !== oldId) {
|
||||
initPage()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.head-card {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.head-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
.head-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.customer-name {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.head-code {
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 13px;
|
||||
}
|
||||
.head-right {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.balance-row {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.balance-card {
|
||||
margin-bottom: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
.balance-label {
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 13px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.balance-value {
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
color: var(--el-text-color-primary);
|
||||
}
|
||||
.balance-value.primary { color: var(--el-color-primary); }
|
||||
.balance-value.success { color: var(--el-color-success); }
|
||||
.balance-value.warning { color: var(--el-color-warning); }
|
||||
.balance-value.danger { color: var(--el-color-danger); }
|
||||
.search-form {
|
||||
margin-bottom: -4px;
|
||||
}
|
||||
.money-in {
|
||||
color: var(--el-color-success);
|
||||
font-weight: 600;
|
||||
}
|
||||
.money-out {
|
||||
color: var(--el-color-danger);
|
||||
font-weight: 600;
|
||||
}
|
||||
.upload-tip {
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.attach-title {
|
||||
font-weight: 600;
|
||||
margin: 12px 0 8px;
|
||||
}
|
||||
</style>
|
||||
@@ -115,7 +115,7 @@
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
type="warning"
|
||||
plain
|
||||
icon="Edit"
|
||||
:disabled="single"
|
||||
@@ -123,6 +123,16 @@
|
||||
v-hasPermi="['customersupplier:customerinfo:edit']"
|
||||
>修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
icon="Edit"
|
||||
:disabled="single"
|
||||
@click="handleCustomerMoney"
|
||||
|
||||
>客户余额</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
@@ -175,8 +185,12 @@
|
||||
<el-table-column label="客服姓名" align="center" prop="serviceUser" />
|
||||
<el-table-column label="结算方式" align="center" prop="settlementType" />
|
||||
<el-table-column label="临时额度" align="center" prop="tempEd" />
|
||||
<el-table-column label="账户余额" align="center" prop="accountBalance" />
|
||||
<el-table-column label="外币余额" align="center" prop="foreignCurrencyBalance" />
|
||||
<el-table-column label="账户余额" align="center" prop="accountBalance" width="110">
|
||||
<template #default="scope">{{ formatBalance(scope.row.accountBalance) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="外币余额" align="center" prop="foreignCurrencyBalance" width="110">
|
||||
<template #default="scope">{{ formatBalance(scope.row.foreignCurrencyBalance) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="重量阈值" align="center" prop="weightThreshold" />
|
||||
<el-table-column label="抛比" align="center" prop="throwRatio" />
|
||||
<el-table-column label="信用额度" align="center" prop="creditEd" />
|
||||
@@ -186,7 +200,7 @@
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['customersupplier:customerinfo:edit']">修改</el-button>
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['customersupplier:customerinfo:remove']">删除</el-button>
|
||||
|
||||
<el-button link type="primary" icon="Wallet" @click="handleCustomerMoney(scope.row)">客户余额</el-button>
|
||||
</template>
|
||||
|
||||
</el-table-column>
|
||||
@@ -356,13 +370,17 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="账户余额" prop="accountBalance">
|
||||
<el-input v-model="form.accountBalance" placeholder="请输入账户余额" />
|
||||
<el-form-item label="账户余额">
|
||||
<el-input :model-value="formatBalance(form.accountBalance)" disabled placeholder="0.00">
|
||||
<template #append>CNY</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="外币余额" prop="foreignCurrencyBalance">
|
||||
<el-input v-model="form.foreignCurrencyBalance" placeholder="请输入外币余额" />
|
||||
<el-form-item label="外币余额">
|
||||
<el-input :model-value="formatBalance(form.foreignCurrencyBalance)" disabled placeholder="0.00">
|
||||
<template #append>外币</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
@@ -798,6 +816,12 @@ function formatFileSize(bytes) {
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]
|
||||
}
|
||||
|
||||
/** 格式化余额(数据源 customer_account 余额表) */
|
||||
function formatBalance(val) {
|
||||
const num = Number(val)
|
||||
return isNaN(num) ? '0.00' : num.toFixed(2)
|
||||
}
|
||||
|
||||
/** 附件上传前校验 */
|
||||
function handleBeforeAttachmentUpload(file) {
|
||||
const ext = (file.name.split('.').pop() || "").toLowerCase()
|
||||
@@ -959,8 +983,10 @@ function resetQuery() {
|
||||
}
|
||||
|
||||
/** 多选框选中数据 */
|
||||
const selectedRows = ref([])
|
||||
function handleSelectionChange(selection) {
|
||||
ids.value = selection.map(item => item.customerId)
|
||||
selectedRows.value = selection
|
||||
single.value = selection.length != 1
|
||||
multiple.value = !selection.length
|
||||
}
|
||||
@@ -996,14 +1022,16 @@ function handleUpdate(row) {
|
||||
function submitForm() {
|
||||
proxy.$refs["customerRef"].validate(valid => {
|
||||
if (valid) {
|
||||
// 账户余额/外币余额以 customer_account 余额表为准(收款审核自动变动),提交时不写回主表冗余列
|
||||
const payload = { ...form.value, accountBalance: null, foreignCurrencyBalance: null }
|
||||
if (form.value.customerId != null) {
|
||||
updateCustomer(form.value).then(() => {
|
||||
updateCustomer(payload).then(() => {
|
||||
proxy.$modal.msgSuccess("修改成功")
|
||||
open.value = false
|
||||
getList()
|
||||
})
|
||||
} else {
|
||||
addCustomer(form.value).then(() => {
|
||||
addCustomer(payload).then(() => {
|
||||
proxy.$modal.msgSuccess("新增成功")
|
||||
open.value = false
|
||||
getList()
|
||||
@@ -1024,12 +1052,30 @@ function handleDelete(row) {
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
// /** 导出按钮操作 */
|
||||
// function handleExport() {
|
||||
// proxy.download('system/customer/export', {
|
||||
// ...queryParams.value
|
||||
// }, `customer_${new Date().getTime()}.xlsx`)
|
||||
// }
|
||||
/** 跳转客户余额页面(操作列传 row;工具栏按钮不传参时取勾选行) */
|
||||
function handleCustomerMoney(row) {
|
||||
const target = (row && row.customerId) ? row : selectedRows.value[0]
|
||||
if (!target || !target.customerId) {
|
||||
proxy.$modal.msgWarning("请先勾选一个客户")
|
||||
return
|
||||
}
|
||||
proxy.$router.push({
|
||||
path: '/customersupplier/customer-money/index',
|
||||
query: {
|
||||
customerId: target.customerId,
|
||||
customerName: target.customerName,
|
||||
customerCode: target.customerCode
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/** 导出按钮操作 */
|
||||
function handleExport() {
|
||||
proxy.download('/customersupplier/customerinfo/export', {
|
||||
...queryParams.value
|
||||
}, `customer_${new Date().getTime()}.xlsx`)
|
||||
}
|
||||
|
||||
/* ============ 公司主体子表 ============ */
|
||||
const companyDialogVisible = ref(false)
|
||||
|
||||
@@ -233,6 +233,25 @@
|
||||
</el-table>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="联系人" prop="contactList">
|
||||
<div style="margin-bottom: 8px;">
|
||||
<el-button type="primary" plain icon="Plus" size="small" @click="handleAddContact">新增联系人</el-button>
|
||||
</div>
|
||||
<el-table :data="form.contactList" border size="small" :max-height="240">
|
||||
<el-table-column label="序号" type="index" width="55" align="center" />
|
||||
<el-table-column label="联系人" align="center" prop="contactName" show-overflow-tooltip />
|
||||
<el-table-column label="职位" align="center" prop="position" show-overflow-tooltip />
|
||||
<el-table-column label="手机号" align="center" prop="phone" show-overflow-tooltip />
|
||||
<el-table-column label="操作" align="center" width="160">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" size="small" @click="handleEditContact(scope.row, scope.$index)">编辑</el-button>
|
||||
<el-button link type="danger" size="small" @click="handleDeleteContact(scope.$index)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="供应商类型" prop="supplierType">
|
||||
<el-select v-model="form.supplierType" placeholder="请选择供应商类型" clearable>
|
||||
@@ -615,6 +634,118 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 联系人编辑对话框 -->
|
||||
<el-dialog :title="contactDialogTitle" v-model="contactDialogVisible" width="800px" append-to-body>
|
||||
<el-form ref="contactRef" :model="contactForm" :rules="contactRules" label-width="120px">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="联系人姓名" prop="contactName">
|
||||
<el-input v-model="contactForm.contactName" placeholder="请输入联系人姓名" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="职位" prop="position">
|
||||
<el-input v-model="contactForm.position" placeholder="如 销售经理/财务总监" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="手机号" prop="phone">
|
||||
<el-input v-model="contactForm.phone" placeholder="请输入手机号" maxlength="20" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="所属部门" prop="department">
|
||||
<el-input v-model="contactForm.department" placeholder="如 销售部/财务部" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="座机" prop="landline">
|
||||
<el-input v-model="contactForm.landline" placeholder="含区号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="电子邮箱" prop="email">
|
||||
<el-input v-model="contactForm.email" placeholder="请输入电子邮箱" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="微信号" prop="wechat">
|
||||
<el-input v-model="contactForm.wechat" placeholder="请输入微信号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="QQ号" prop="qq">
|
||||
<el-input v-model="contactForm.qq" placeholder="请输入QQ号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="国家/地区" prop="country" label-width="90px">
|
||||
<el-input v-model="contactForm.country" placeholder="中国" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="省份" prop="province" label-width="60px">
|
||||
<el-input v-model="contactForm.province" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="城市" prop="city" label-width="60px">
|
||||
<el-input v-model="contactForm.city" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="区/县" prop="district" label-width="60px">
|
||||
<el-input v-model="contactForm.district" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="邮政编码" prop="postalCode">
|
||||
<el-input v-model="contactForm.postalCode" placeholder="请输入邮政编码" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="详细地址" prop="contactAddress">
|
||||
<el-input v-model="contactForm.contactAddress" placeholder="街道、门牌号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="联系人类型" prop="contactType">
|
||||
<el-select v-model="contactForm.contactType" placeholder="请选择" clearable>
|
||||
<el-option label="业务联系人" value="0" />
|
||||
<el-option label="财务联系人" value="1" />
|
||||
<el-option label="紧急联系人" value="2" />
|
||||
<el-option label="其他" value="3" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="是否默认" prop="isDefault">
|
||||
<el-radio-group v-model="contactForm.isDefault">
|
||||
<el-radio label="0">否</el-radio>
|
||||
<el-radio label="1">是</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="排序" prop="sortOrder">
|
||||
<el-input-number v-model="contactForm.sortOrder" :min="0" controls-position="right" style="width: 100%;" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="contactForm.remark" type="textarea" :rows="2" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitContactForm">确 定</el-button>
|
||||
<el-button @click="contactDialogVisible = false">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -848,6 +979,7 @@ function reset() {
|
||||
channelIds: null,
|
||||
attachmentList: [],
|
||||
companyList: [],
|
||||
contactList: [],
|
||||
remark: null,
|
||||
createBy: null,
|
||||
createTime: null,
|
||||
@@ -896,6 +1028,9 @@ function handleUpdate(row) {
|
||||
if (!form.value.companyList) {
|
||||
form.value.companyList = []
|
||||
}
|
||||
if (!form.value.contactList) {
|
||||
form.value.contactList = []
|
||||
}
|
||||
open.value = true
|
||||
title.value = "修改供应商"
|
||||
})
|
||||
@@ -1047,6 +1182,86 @@ function submitCompanyForm() {
|
||||
proxy.$modal.msgSuccess(companyEditingIndex.value >= 0 ? "修改成功" : "新增成功")
|
||||
}
|
||||
|
||||
/* ============ 联系人子表 ============ */
|
||||
const contactDialogVisible = ref(false)
|
||||
const contactDialogTitle = ref("")
|
||||
const contactForm = ref({})
|
||||
const contactEditingIndex = ref(-1)
|
||||
const contactRules = {
|
||||
contactName: [{ required: true, message: '联系人姓名不能为空', trigger: 'blur' }],
|
||||
phone: [{ required: true, message: '手机号不能为空', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
/** 重置联系人表单 */
|
||||
function resetContactForm() {
|
||||
contactForm.value = {
|
||||
contactId: null,
|
||||
supplierId: form.value.supplierId || null,
|
||||
contactName: '',
|
||||
position: '',
|
||||
department: '',
|
||||
phone: '',
|
||||
landline: '',
|
||||
email: '',
|
||||
wechat: '',
|
||||
qq: '',
|
||||
country: '中国',
|
||||
province: '',
|
||||
city: '',
|
||||
district: '',
|
||||
contactAddress: '',
|
||||
postalCode: '',
|
||||
isDefault: '0',
|
||||
contactType: '0',
|
||||
sortOrder: 0,
|
||||
remark: ''
|
||||
}
|
||||
proxy.resetForm("contactRef")
|
||||
}
|
||||
|
||||
/** 新增联系人(本地子表,跟随主表一起提交) */
|
||||
function handleAddContact() {
|
||||
resetContactForm()
|
||||
contactEditingIndex.value = -1
|
||||
contactDialogTitle.value = "新增联系人"
|
||||
contactDialogVisible.value = true
|
||||
}
|
||||
|
||||
/** 编辑联系人 */
|
||||
function handleEditContact(row, index) {
|
||||
resetContactForm()
|
||||
contactForm.value = { ...row }
|
||||
contactEditingIndex.value = index
|
||||
contactDialogTitle.value = "修改联系人"
|
||||
contactDialogVisible.value = true
|
||||
}
|
||||
|
||||
/** 删除联系人(仅从子表移除;未入库的不调后端,已入库的在主表保存后由后端增量处理) */
|
||||
function handleDeleteContact(index) {
|
||||
proxy.$modal.confirm('是否确认删除该条联系人记录?').then(() => {
|
||||
form.value.contactList.splice(index, 1)
|
||||
proxy.$modal.msgSuccess("删除成功")
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
/** 提交联系人表单(本地保存,跟随主表提交一起入库) */
|
||||
function submitContactForm() {
|
||||
if (!contactForm.value.contactName) {
|
||||
proxy.$modal.msgWarning("联系人姓名不能为空")
|
||||
return
|
||||
}
|
||||
if (!Array.isArray(form.value.contactList)) {
|
||||
form.value.contactList = []
|
||||
}
|
||||
if (contactEditingIndex.value >= 0) {
|
||||
form.value.contactList.splice(contactEditingIndex.value, 1, { ...contactForm.value })
|
||||
} else {
|
||||
form.value.contactList.push({ ...contactForm.value })
|
||||
}
|
||||
contactDialogVisible.value = false
|
||||
proxy.$modal.msgSuccess(contactEditingIndex.value >= 0 ? "修改成功" : "新增成功")
|
||||
}
|
||||
|
||||
getUserList()
|
||||
getList()
|
||||
</script>
|
||||
Reference in New Issue
Block a user