客户余额,额度记录流水
This commit is contained in:
+29
@@ -17,6 +17,7 @@ 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.CustomerCreditFlow;
|
||||
import com.ruoyi.customer.customermoney.domain.CustomerReceipt;
|
||||
import com.ruoyi.customer.customermoney.service.ICustomerMoneyService;
|
||||
|
||||
@@ -118,4 +119,32 @@ public class CustomerMoneyController extends BaseController
|
||||
List<CustomerBalanceFlow> list = customerMoneyService.selectFlowList(query);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询额度流水列表
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:list')")
|
||||
@GetMapping("/credit/flow/list")
|
||||
public TableDataInfo creditFlowList(CustomerCreditFlow query)
|
||||
{
|
||||
if (query == null || query.getCustomerId() == null || query.getCustomerId() == 0L) {
|
||||
return getDataTable(Collections.emptyList());
|
||||
}
|
||||
startPage();
|
||||
List<CustomerCreditFlow> list = customerMoneyService.selectCreditFlowList(query);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 调整客户额度(信用额度/临时额度),调整过程留额度流水
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('customer:credit:adjust')")
|
||||
@Log(title = "客户额度调整", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/credit/adjust")
|
||||
public AjaxResult adjustCredit(@RequestBody CustomerCreditFlow flow)
|
||||
{
|
||||
flow.setCreateBy(getUsername());
|
||||
flow.setOperatorId(getUserId());
|
||||
return toAjax(customerMoneyService.adjustCredit(flow));
|
||||
}
|
||||
}
|
||||
|
||||
+23
@@ -23,6 +23,15 @@ public interface BmsCustomerMapper
|
||||
*/
|
||||
public BmsCustomer selectBmsCustomerByCustomerId(Long customerId);
|
||||
|
||||
/**
|
||||
* 只取客户档案物理额度列(credit_ed 在通用查询里是账户可用额度的子查询别名,
|
||||
* 账户首次初始化时必须读取档案原始值)
|
||||
*
|
||||
* @param customerId 客户ID
|
||||
* @return 仅含 creditEd/tempEd 的客户对象
|
||||
*/
|
||||
public BmsCustomer selectRawCreditByCustomerId(Long customerId);
|
||||
|
||||
/**
|
||||
* 查询客户主列表
|
||||
*
|
||||
@@ -103,6 +112,20 @@ public interface BmsCustomerMapper
|
||||
*/
|
||||
public int deleteBmsCustomerProfileAttachmentByCustomerId(Long customerId);
|
||||
|
||||
/**
|
||||
* 同步客户档案额度字段(额度以客户余额账户为准,调整后回写档案)
|
||||
*
|
||||
* @param customerId 客户ID
|
||||
* @param creditEd 信用额度
|
||||
* @param tempEd 临时额度
|
||||
* @param updateBy 更新人
|
||||
* @return 结果
|
||||
*/
|
||||
public int updateCustomerCredit(@org.apache.ibatis.annotations.Param("customerId") Long customerId,
|
||||
@org.apache.ibatis.annotations.Param("creditEd") java.math.BigDecimal creditEd,
|
||||
@org.apache.ibatis.annotations.Param("tempEd") java.math.BigDecimal tempEd,
|
||||
@org.apache.ibatis.annotations.Param("updateBy") String updateBy);
|
||||
|
||||
/**
|
||||
* 批量删除客户附件
|
||||
*
|
||||
|
||||
+6
@@ -14,6 +14,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import com.ruoyi.customer.customerinfo.mapper.BmsCustomerMapper;
|
||||
import com.ruoyi.customer.customerinfo.domain.BmsCustomer;
|
||||
import com.ruoyi.customer.customerinfo.service.IBmsCustomerService;
|
||||
import com.ruoyi.customer.customermoney.service.ICustomerMoneyService;
|
||||
|
||||
/**
|
||||
* 客户主Service业务层处理
|
||||
@@ -27,6 +28,9 @@ public class BmsCustomerServiceImpl implements IBmsCustomerService
|
||||
@Autowired
|
||||
private BmsCustomerMapper bmsCustomerMapper;
|
||||
|
||||
@Autowired
|
||||
private ICustomerMoneyService customerMoneyService;
|
||||
|
||||
/**
|
||||
* 查询客户主
|
||||
*
|
||||
@@ -226,6 +230,8 @@ public class BmsCustomerServiceImpl implements IBmsCustomerService
|
||||
saveBmsCustomerAttachments(bmsCustomer.getCustomerId(), bmsCustomer.getAttachmentList());
|
||||
saveBmsCustomerCompanies(bmsCustomer.getCustomerId(), bmsCustomer.getCompanyList());
|
||||
saveBmsCustomerContacts(bmsCustomer.getCustomerId(), bmsCustomer.getContactList());
|
||||
// 新建客户即初始化默认CNY账户:余额0、总额度1000 CNY,写INIT流水
|
||||
customerMoneyService.initDefaultCnyAccount(bmsCustomer.getCustomerId());
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
+11
-1
@@ -1,6 +1,8 @@
|
||||
package com.ruoyi.customer.customermoney.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
@@ -35,9 +37,17 @@ public class CustomerAccount extends BaseEntity
|
||||
/** 总额度 */
|
||||
private BigDecimal totalCredit;
|
||||
|
||||
/** 额外额度 */
|
||||
/** 额外额度(临时额度) */
|
||||
private BigDecimal extraCredit;
|
||||
|
||||
/** 临时额度生效日期 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date extraEffectiveDate;
|
||||
|
||||
/** 临时额度失效日期 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date extraExpireDate;
|
||||
|
||||
/** 已用额度 */
|
||||
private BigDecimal usedCredit;
|
||||
|
||||
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
package com.ruoyi.customer.customermoney.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 客户额度流水 customer_credit_flow
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2026-09-16
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CustomerCreditFlow extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 额度类型:信用额度(长期) */
|
||||
public static final String CREDIT_TYPE_CREDIT = "CREDIT";
|
||||
/** 额度类型:临时额度 */
|
||||
public static final String CREDIT_TYPE_TEMP = "TEMP";
|
||||
|
||||
/** 变动类型:初始化 */
|
||||
public static final String CHANGE_INIT = "INIT";
|
||||
/** 变动类型:手工调整 */
|
||||
public static final String CHANGE_ADJUST = "ADJUST";
|
||||
/** 变动类型:到期自动失效 */
|
||||
public static final String CHANGE_EXPIRE = "EXPIRE";
|
||||
|
||||
/** 调整方式:设置为 */
|
||||
public static final String MODE_SET = "SET";
|
||||
/** 调整方式:增加 */
|
||||
public static final String MODE_ADD = "ADD";
|
||||
/** 调整方式:减少 */
|
||||
public static final String MODE_SUB = "SUB";
|
||||
|
||||
/** 主键 */
|
||||
private Long id;
|
||||
|
||||
/** 流水号 */
|
||||
private String flowNo;
|
||||
|
||||
/** 客户ID */
|
||||
private Long customerId;
|
||||
|
||||
/** 客户账户ID */
|
||||
private Long accountId;
|
||||
|
||||
/** 额度类型:CREDIT信用额度 TEMP临时额度 */
|
||||
private String creditType;
|
||||
|
||||
/** 变动类型:INIT初始化 ADJUST调整 EXPIRE到期失效 */
|
||||
private String changeType;
|
||||
|
||||
/** 变动方向:1增加 -1减少 0持平 */
|
||||
private Integer direction;
|
||||
|
||||
/** 变动前额度 */
|
||||
private BigDecimal beforeAmount;
|
||||
|
||||
/** 变动额度(恒为非负数,方向看 direction) */
|
||||
private BigDecimal changeAmount;
|
||||
|
||||
/** 变动后额度 */
|
||||
private BigDecimal afterAmount;
|
||||
|
||||
/** 变动时已用额度快照 */
|
||||
private BigDecimal usedCredit;
|
||||
|
||||
/** 生效日期 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date effectiveDate;
|
||||
|
||||
/** 失效日期 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date expireDate;
|
||||
|
||||
/** 调整原因 */
|
||||
private String reason;
|
||||
|
||||
/** 调整方式(仅ADJUST流水有值):SET/ADD/SUB */
|
||||
private String adjustMode;
|
||||
|
||||
/** 操作人ID */
|
||||
private Long operatorId;
|
||||
|
||||
/** 删除标志(0存在 1删除) */
|
||||
private String delFlag;
|
||||
}
|
||||
+12
@@ -4,6 +4,7 @@ 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.CustomerCreditFlow;
|
||||
import com.ruoyi.customer.customermoney.domain.CustomerReceipt;
|
||||
|
||||
/**
|
||||
@@ -33,6 +34,17 @@ public interface CustomerMoneyMapper
|
||||
/** 乐观锁更新余额 */
|
||||
int updateAccountBalance(CustomerAccount account);
|
||||
|
||||
/** 乐观锁更新额度(信用额度/临时额度/有效期) */
|
||||
int updateAccountCredit(CustomerAccount account);
|
||||
|
||||
/* ================= 额度流水 ================= */
|
||||
|
||||
/** 查询额度流水 */
|
||||
List<CustomerCreditFlow> selectCreditFlowList(CustomerCreditFlow query);
|
||||
|
||||
/** 新增额度流水 */
|
||||
int insertCreditFlow(CustomerCreditFlow flow);
|
||||
|
||||
/* ================= 客户收款 ================= */
|
||||
|
||||
/** 查询收款单列表 */
|
||||
|
||||
+20
@@ -3,6 +3,7 @@ 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.CustomerCreditFlow;
|
||||
import com.ruoyi.customer.customermoney.domain.CustomerReceipt;
|
||||
|
||||
/**
|
||||
@@ -16,6 +17,15 @@ public interface ICustomerMoneyService
|
||||
/** 查询客户各币种账户 */
|
||||
List<CustomerAccount> selectAccountList(Long customerId);
|
||||
|
||||
/**
|
||||
* 新建客户时初始化默认CNY账户(余额0,默认总额度1000 CNY),并写 INIT 额度流水。
|
||||
* 已存在CNY账户时不重复创建。
|
||||
*
|
||||
* @param customerId 客户ID
|
||||
* @return 1=已初始化或已存在
|
||||
*/
|
||||
int initDefaultCnyAccount(Long customerId);
|
||||
|
||||
/** 查询收款单列表 */
|
||||
List<CustomerReceipt> selectReceiptList(CustomerReceipt query);
|
||||
|
||||
@@ -36,4 +46,14 @@ public interface ICustomerMoneyService
|
||||
|
||||
/** 查询余额流水列表 */
|
||||
List<CustomerBalanceFlow> selectFlowList(CustomerBalanceFlow query);
|
||||
|
||||
/** 查询额度流水列表 */
|
||||
List<CustomerCreditFlow> selectCreditFlowList(CustomerCreditFlow query);
|
||||
|
||||
/**
|
||||
* 调整客户额度(信用额度/临时额度)
|
||||
* 事务内:锁定CNY账户(不存在则初始化)-> 校验(不能为负、不能低于已用额度)
|
||||
* -> 乐观锁更新额度 -> 回写客户档案额度字段 -> 写额度流水
|
||||
*/
|
||||
int adjustCredit(CustomerCreditFlow flow);
|
||||
}
|
||||
|
||||
+352
-18
@@ -2,6 +2,7 @@ package com.ruoyi.customer.customermoney.service.impl;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Date;
|
||||
@@ -14,10 +15,12 @@ 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.BmsCustomer;
|
||||
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.CustomerCreditFlow;
|
||||
import com.ruoyi.customer.customermoney.domain.CustomerReceipt;
|
||||
import com.ruoyi.customer.customermoney.mapper.CustomerMoneyMapper;
|
||||
import com.ruoyi.customer.customermoney.service.ICustomerMoneyService;
|
||||
@@ -37,6 +40,8 @@ public class CustomerMoneyServiceImpl implements ICustomerMoneyService
|
||||
|
||||
/** 本位币币种 */
|
||||
private static final String BASE_CURRENCY = BmsExchangeRate.BASE_CURRENCY;
|
||||
/** 新客户默认总额度(CNY) */
|
||||
private static final BigDecimal DEFAULT_CREDIT = new BigDecimal("1000");
|
||||
/** 金额保留位数 */
|
||||
private static final int MONEY_SCALE = 2;
|
||||
|
||||
@@ -50,9 +55,25 @@ public class CustomerMoneyServiceImpl implements ICustomerMoneyService
|
||||
private IBmsExchangeRateService exchangeRateService;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public List<CustomerAccount> selectAccountList(Long customerId)
|
||||
{
|
||||
return customerMoneyMapper.selectAccountListByCustomerId(customerId);
|
||||
List<CustomerAccount> list = customerMoneyMapper.selectAccountListByCustomerId(customerId);
|
||||
// 查询时顺带处理临时额度到期自动失效(仅CNY账户),保证卡片展示为最新值
|
||||
for (int i = 0; i < list.size(); i++)
|
||||
{
|
||||
CustomerAccount account = list.get(i);
|
||||
if (BASE_CURRENCY.equals(account.getCurrency()) && isTempCreditExpired(account))
|
||||
{
|
||||
CustomerAccount locked = customerMoneyMapper.selectAccountForUpdate(customerId, BASE_CURRENCY);
|
||||
expireTempCreditIfNeeded(locked);
|
||||
if (locked != null)
|
||||
{
|
||||
list.set(i, locked);
|
||||
}
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -167,23 +188,8 @@ public class CustomerMoneyServiceImpl implements ICustomerMoneyService
|
||||
? receipt.getBaseAmount()
|
||||
: originalAmount.multiply(rate).setScale(MONEY_SCALE, RoundingMode.HALF_UP);
|
||||
|
||||
// 1. 行锁锁定客户本位币(CNY)账户,不存在则自动初始化。无论收什么币种,一律折算 CNY 入账
|
||||
CustomerAccount account = customerMoneyMapper.selectAccountForUpdate(receipt.getCustomerId(), BASE_CURRENCY);
|
||||
if (account == null)
|
||||
{
|
||||
account = new CustomerAccount();
|
||||
account.setCustomerId(receipt.getCustomerId());
|
||||
account.setCurrency(BASE_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);
|
||||
}
|
||||
// 1. 行锁锁定客户本位币(CNY)账户,不存在则按客户档案额度自动初始化。无论收什么币种,一律折算 CNY 入账
|
||||
CustomerAccount account = getOrInitCnyAccount(receipt.getCustomerId(), username);
|
||||
|
||||
// 2. 本位币余额变动(预充值/混合增加余额;还款核销释放已用额度)
|
||||
BigDecimal beforeBalance = nz(account.getBalance());
|
||||
@@ -260,6 +266,334 @@ public class CustomerMoneyServiceImpl implements ICustomerMoneyService
|
||||
return customerMoneyMapper.selectFlowList(query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CustomerCreditFlow> selectCreditFlowList(CustomerCreditFlow query)
|
||||
{
|
||||
return customerMoneyMapper.selectCreditFlowList(query);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int adjustCredit(CustomerCreditFlow cmd)
|
||||
{
|
||||
if (cmd == null || cmd.getCustomerId() == null)
|
||||
{
|
||||
throw new ServiceException("客户ID不能为空");
|
||||
}
|
||||
Long customerId = cmd.getCustomerId();
|
||||
String creditType = cmd.getCreditType();
|
||||
if (!CustomerCreditFlow.CREDIT_TYPE_CREDIT.equals(creditType)
|
||||
&& !CustomerCreditFlow.CREDIT_TYPE_TEMP.equals(creditType))
|
||||
{
|
||||
throw new ServiceException("额度类型不正确");
|
||||
}
|
||||
String adjustMode = cmd.getAdjustMode();
|
||||
if (!CustomerCreditFlow.MODE_SET.equals(adjustMode)
|
||||
&& !CustomerCreditFlow.MODE_ADD.equals(adjustMode)
|
||||
&& !CustomerCreditFlow.MODE_SUB.equals(adjustMode))
|
||||
{
|
||||
throw new ServiceException("调整方式不正确");
|
||||
}
|
||||
BigDecimal input = nz(cmd.getChangeAmount());
|
||||
if (input.compareTo(BigDecimal.ZERO) <= 0)
|
||||
{
|
||||
throw new ServiceException("调整金额必须大于0");
|
||||
}
|
||||
if (!StringUtils.hasText(cmd.getReason()))
|
||||
{
|
||||
throw new ServiceException("请填写调整原因");
|
||||
}
|
||||
String reason = cmd.getReason().trim();
|
||||
if (reason.length() > 255)
|
||||
{
|
||||
throw new ServiceException("调整原因不能超过255个字");
|
||||
}
|
||||
String username = SecurityUtils.getUsername();
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
boolean isTemp = CustomerCreditFlow.CREDIT_TYPE_TEMP.equals(creditType);
|
||||
|
||||
// 1. 行锁锁定CNY账户(不存在则按客户档案额度初始化创建);先处理临时额度过期
|
||||
CustomerAccount account = getOrInitCnyAccount(customerId, username);
|
||||
expireTempCreditIfNeeded(account);
|
||||
|
||||
// 2. 计算调整结果
|
||||
BigDecimal before = isTemp ? nz(account.getExtraCredit()) : nz(account.getTotalCredit());
|
||||
BigDecimal otherCredit = isTemp ? nz(account.getTotalCredit()) : nz(account.getExtraCredit());
|
||||
BigDecimal after;
|
||||
switch (adjustMode)
|
||||
{
|
||||
case CustomerCreditFlow.MODE_SET:
|
||||
after = input;
|
||||
break;
|
||||
case CustomerCreditFlow.MODE_ADD:
|
||||
after = before.add(input);
|
||||
break;
|
||||
default:
|
||||
after = before.subtract(input);
|
||||
break;
|
||||
}
|
||||
after = after.setScale(MONEY_SCALE, RoundingMode.HALF_UP);
|
||||
if (after.compareTo(BigDecimal.ZERO) < 0)
|
||||
{
|
||||
throw new ServiceException("调整后额度不能为负数");
|
||||
}
|
||||
if (otherCredit.add(after).compareTo(nz(account.getUsedCredit())) < 0)
|
||||
{
|
||||
throw new ServiceException("调整后授信总额度不能小于已用额度:"
|
||||
+ nz(account.getUsedCredit()).toPlainString());
|
||||
}
|
||||
if (after.compareTo(before) == 0)
|
||||
{
|
||||
throw new ServiceException("额度未发生变化,无需调整");
|
||||
}
|
||||
|
||||
// 3. 临时额度有效期校验(不允许预约未来生效,失效日必须晚于生效日)
|
||||
Date effectiveDate = null;
|
||||
Date expireDate = null;
|
||||
if (isTemp)
|
||||
{
|
||||
LocalDate today = LocalDate.now();
|
||||
LocalDate eff = cmd.getEffectiveDate() != null ? toLocalDate(cmd.getEffectiveDate()) : today;
|
||||
if (eff.isAfter(today))
|
||||
{
|
||||
throw new ServiceException("生效日期不能晚于今天,需要未来生效请在生效当天再设置");
|
||||
}
|
||||
effectiveDate = toSqlDate(eff);
|
||||
if (cmd.getExpireDate() != null)
|
||||
{
|
||||
LocalDate exp = toLocalDate(cmd.getExpireDate());
|
||||
if (!exp.isAfter(eff))
|
||||
{
|
||||
throw new ServiceException("失效日期必须晚于生效日期");
|
||||
}
|
||||
expireDate = toSqlDate(exp);
|
||||
}
|
||||
account.setExtraCredit(after);
|
||||
account.setExtraEffectiveDate(effectiveDate);
|
||||
account.setExtraExpireDate(expireDate);
|
||||
}
|
||||
else
|
||||
{
|
||||
account.setTotalCredit(after);
|
||||
}
|
||||
account.setUpdateBy(username);
|
||||
|
||||
// 4. 乐观锁更新额度
|
||||
int rows = customerMoneyMapper.updateAccountCredit(account);
|
||||
if (rows == 0)
|
||||
{
|
||||
throw new ServiceException("账户额度已被其他操作变动,请刷新后重试");
|
||||
}
|
||||
account.setVersion(account.getVersion() + 1);
|
||||
|
||||
// 5. 回写客户档案额度字段(额度唯一数据源为账户,档案仅做同步展示)
|
||||
bmsCustomerMapper.updateCustomerCredit(customerId,
|
||||
nz(account.getTotalCredit()), nz(account.getExtraCredit()), username);
|
||||
|
||||
// 6. 写额度流水
|
||||
CustomerCreditFlow flow = new CustomerCreditFlow();
|
||||
flow.setFlowNo(generateNo("EDLS"));
|
||||
flow.setCustomerId(customerId);
|
||||
flow.setAccountId(account.getId());
|
||||
flow.setCreditType(creditType);
|
||||
flow.setChangeType(CustomerCreditFlow.CHANGE_ADJUST);
|
||||
int direction = after.compareTo(before) > 0 ? 1 : -1;
|
||||
flow.setDirection(direction);
|
||||
flow.setBeforeAmount(before);
|
||||
flow.setChangeAmount(after.subtract(before).abs());
|
||||
flow.setAfterAmount(after);
|
||||
flow.setUsedCredit(nz(account.getUsedCredit()));
|
||||
flow.setEffectiveDate(effectiveDate);
|
||||
flow.setExpireDate(expireDate);
|
||||
flow.setReason(reason);
|
||||
flow.setAdjustMode(adjustMode);
|
||||
flow.setOperatorId(userId);
|
||||
flow.setCreateBy(username);
|
||||
flow.setRemark(cmd.getRemark());
|
||||
flow.setDelFlag("0");
|
||||
customerMoneyMapper.insertCreditFlow(flow);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ================= 额度私有方法 ================= */
|
||||
|
||||
/**
|
||||
* 新建客户时默认初始化CNY账户:余额0,总额度默认1000 CNY
|
||||
* (客户档案已填信用额度且大于0时按档案值),并写 INIT 流水;已存在则跳过。
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int initDefaultCnyAccount(Long customerId)
|
||||
{
|
||||
if (customerId == null)
|
||||
{
|
||||
throw new ServiceException("客户ID不能为空");
|
||||
}
|
||||
String username = SecurityUtils.getUsername();
|
||||
CustomerAccount account = customerMoneyMapper.selectAccountForUpdate(customerId, BASE_CURRENCY);
|
||||
if (account != null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
BmsCustomer customer = bmsCustomerMapper.selectRawCreditByCustomerId(customerId);
|
||||
if (customer == null)
|
||||
{
|
||||
throw new ServiceException("客户不存在");
|
||||
}
|
||||
BigDecimal initCredit = nz(customer.getCreditEd());
|
||||
if (initCredit.compareTo(BigDecimal.ZERO) <= 0)
|
||||
{
|
||||
initCredit = DEFAULT_CREDIT;
|
||||
}
|
||||
BigDecimal initTemp = nz(customer.getTempEd());
|
||||
account = createCnyAccount(customerId, initCredit, initTemp, username);
|
||||
// 档案额度与实际初始额度保持一致
|
||||
bmsCustomerMapper.updateCustomerCredit(customerId, initCredit, initTemp, username);
|
||||
writeInitFlow(account, CustomerCreditFlow.CREDIT_TYPE_CREDIT,
|
||||
initCredit, null, null, username, "新客户默认初始化额度1000 CNY");
|
||||
if (initTemp.compareTo(BigDecimal.ZERO) > 0)
|
||||
{
|
||||
writeInitFlow(account, CustomerCreditFlow.CREDIT_TYPE_TEMP,
|
||||
initTemp, null, null, username, "新客户初始化临时额度");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** 行锁获取客户CNY账户,不存在则按客户档案当前额度初始化创建 */
|
||||
private CustomerAccount getOrInitCnyAccount(Long customerId, String username)
|
||||
{
|
||||
CustomerAccount account = customerMoneyMapper.selectAccountForUpdate(customerId, BASE_CURRENCY);
|
||||
if (account == null)
|
||||
{
|
||||
BmsCustomer customer = bmsCustomerMapper.selectRawCreditByCustomerId(customerId);
|
||||
if (customer == null)
|
||||
{
|
||||
throw new ServiceException("客户不存在");
|
||||
}
|
||||
BigDecimal initCredit = nz(customer.getCreditEd());
|
||||
BigDecimal initTemp = nz(customer.getTempEd());
|
||||
account = createCnyAccount(customerId, initCredit, initTemp, username);
|
||||
writeInitFlow(account, CustomerCreditFlow.CREDIT_TYPE_CREDIT,
|
||||
initCredit, null, null, username, "首次创建CNY账户,从客户档案同步初始额度");
|
||||
if (initTemp.compareTo(BigDecimal.ZERO) > 0)
|
||||
{
|
||||
writeInitFlow(account, CustomerCreditFlow.CREDIT_TYPE_TEMP,
|
||||
initTemp, null, null, username, "首次创建CNY账户,从客户档案同步临时额度");
|
||||
}
|
||||
}
|
||||
return account;
|
||||
}
|
||||
|
||||
/** 创建CNY账户(余额0,已用0) */
|
||||
private CustomerAccount createCnyAccount(Long customerId, BigDecimal totalCredit,
|
||||
BigDecimal extraCredit, String username)
|
||||
{
|
||||
CustomerAccount account = new CustomerAccount();
|
||||
account.setCustomerId(customerId);
|
||||
account.setCurrency(BASE_CURRENCY);
|
||||
account.setBalance(BigDecimal.ZERO);
|
||||
account.setFrozenBalance(BigDecimal.ZERO);
|
||||
account.setTotalCredit(totalCredit);
|
||||
account.setExtraCredit(extraCredit);
|
||||
account.setUsedCredit(BigDecimal.ZERO);
|
||||
account.setTax(BigDecimal.ZERO);
|
||||
account.setVersion(0);
|
||||
account.setCreateBy(username);
|
||||
customerMoneyMapper.insertAccount(account);
|
||||
return account;
|
||||
}
|
||||
|
||||
/**
|
||||
* 账户首次初始化时写额度 INIT 流水(从客户档案额度同步)
|
||||
*/
|
||||
private void writeInitFlow(CustomerAccount account, String creditType,
|
||||
BigDecimal initAmount, Date effectiveDate, Date expireDate,
|
||||
String username, String remark)
|
||||
{
|
||||
CustomerCreditFlow flow = new CustomerCreditFlow();
|
||||
flow.setFlowNo(generateNo("EDLS"));
|
||||
flow.setCustomerId(account.getCustomerId());
|
||||
flow.setAccountId(account.getId());
|
||||
flow.setCreditType(creditType);
|
||||
flow.setChangeType(CustomerCreditFlow.CHANGE_INIT);
|
||||
flow.setDirection(initAmount.compareTo(BigDecimal.ZERO) > 0 ? 1 : 0);
|
||||
flow.setBeforeAmount(BigDecimal.ZERO);
|
||||
flow.setChangeAmount(initAmount);
|
||||
flow.setAfterAmount(initAmount);
|
||||
flow.setUsedCredit(nz(account.getUsedCredit()));
|
||||
flow.setEffectiveDate(effectiveDate);
|
||||
flow.setExpireDate(expireDate);
|
||||
flow.setReason("账户初始化");
|
||||
flow.setOperatorId(SecurityUtils.getUserId());
|
||||
flow.setCreateBy(username);
|
||||
flow.setRemark(remark);
|
||||
flow.setDelFlag("0");
|
||||
customerMoneyMapper.insertCreditFlow(flow);
|
||||
}
|
||||
|
||||
/** 临时额度是否已过期:有失效日期且今天已晚于失效日(失效日当天仍有效) */
|
||||
private boolean isTempCreditExpired(CustomerAccount account)
|
||||
{
|
||||
return account != null
|
||||
&& nz(account.getExtraCredit()).compareTo(BigDecimal.ZERO) > 0
|
||||
&& account.getExtraExpireDate() != null
|
||||
&& LocalDate.now().isAfter(toLocalDate(account.getExtraExpireDate()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 临时额度到期自动失效:额度清零 -> 回写客户档案 -> 写EXPIRE流水。
|
||||
* 入参必须是事务内行锁查出的账户。
|
||||
*/
|
||||
private void expireTempCreditIfNeeded(CustomerAccount account)
|
||||
{
|
||||
if (account == null || !isTempCreditExpired(account))
|
||||
{
|
||||
return;
|
||||
}
|
||||
String username = SecurityUtils.getUsername();
|
||||
BigDecimal before = nz(account.getExtraCredit());
|
||||
account.setExtraCredit(BigDecimal.ZERO);
|
||||
account.setUpdateBy(username);
|
||||
int rows = customerMoneyMapper.updateAccountCredit(account);
|
||||
if (rows == 0)
|
||||
{
|
||||
throw new ServiceException("账户额度已被其他操作变动,请刷新后重试");
|
||||
}
|
||||
account.setVersion(account.getVersion() + 1);
|
||||
bmsCustomerMapper.updateCustomerCredit(account.getCustomerId(),
|
||||
nz(account.getTotalCredit()), BigDecimal.ZERO, username);
|
||||
|
||||
CustomerCreditFlow flow = new CustomerCreditFlow();
|
||||
flow.setFlowNo(generateNo("EDLS"));
|
||||
flow.setCustomerId(account.getCustomerId());
|
||||
flow.setAccountId(account.getId());
|
||||
flow.setCreditType(CustomerCreditFlow.CREDIT_TYPE_TEMP);
|
||||
flow.setChangeType(CustomerCreditFlow.CHANGE_EXPIRE);
|
||||
flow.setDirection(-1);
|
||||
flow.setBeforeAmount(before);
|
||||
flow.setChangeAmount(before);
|
||||
flow.setAfterAmount(BigDecimal.ZERO);
|
||||
flow.setUsedCredit(nz(account.getUsedCredit()));
|
||||
flow.setEffectiveDate(account.getExtraEffectiveDate());
|
||||
flow.setExpireDate(account.getExtraExpireDate());
|
||||
flow.setReason("临时额度到期自动失效");
|
||||
flow.setOperatorId(SecurityUtils.getUserId());
|
||||
flow.setCreateBy(username);
|
||||
flow.setRemark("系统在访问账户时检测到临时额度已过失效日期,自动置零");
|
||||
flow.setDelFlag("0");
|
||||
customerMoneyMapper.insertCreditFlow(flow);
|
||||
}
|
||||
|
||||
private LocalDate toLocalDate(Date date)
|
||||
{
|
||||
return date.toInstant().atZone(java.time.ZoneId.systemDefault()).toLocalDate();
|
||||
}
|
||||
|
||||
private Date toSqlDate(LocalDate localDate)
|
||||
{
|
||||
return java.sql.Date.valueOf(localDate);
|
||||
}
|
||||
|
||||
/* ================= 私有方法 ================= */
|
||||
|
||||
/** 保存收款附件到客户附件表 */
|
||||
|
||||
@@ -198,6 +198,25 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
and (biz_type = 'customer' or biz_type is null or biz_type = '')
|
||||
</update>
|
||||
|
||||
<!-- 额度调整后回写客户档案额度字段(数据源为客户余额账户) -->
|
||||
<update id="updateCustomerCredit">
|
||||
update bms_customer
|
||||
set credit_ed = #{creditEd},
|
||||
temp_ed = #{tempEd},
|
||||
update_by = #{updateBy},
|
||||
update_time = now()
|
||||
where customer_id = #{customerId}
|
||||
</update>
|
||||
|
||||
<!-- 只取档案物理额度列(列表查询里的 credit_ed 是账户可用额度子查询) -->
|
||||
<select id="selectRawCreditByCustomerId" resultType="BmsCustomer">
|
||||
select customer_id as customerId,
|
||||
credit_ed as creditEd,
|
||||
temp_ed as tempEd
|
||||
from bms_customer
|
||||
where customer_id = #{customerId}
|
||||
</select>
|
||||
|
||||
<delete id="deleteBmsCustomerAttachmentByAttachmentIds" parameterType="String">
|
||||
update bms_customer_attachment set del_flag = '2' where attachment_id in
|
||||
<foreach item="attachmentId" collection="attachmentIds" open="(" separator="," close=")">
|
||||
@@ -287,12 +306,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
service_user_id = #{serviceUserId},
|
||||
service_user = #{serviceUser},
|
||||
<if test="settlementType != null">settlement_type = #{settlementType},</if>
|
||||
<if test="tempEd != null">temp_ed = #{tempEd},</if>
|
||||
<if test="accountBalance != null">account_balance = #{accountBalance},</if>
|
||||
<if test="foreignCurrencyBalance != null">foreign_currency_balance = #{foreignCurrencyBalance},</if>
|
||||
<if test="weightThreshold != null">weight_threshold = #{weightThreshold},</if>
|
||||
<if test="throwRatio != null">throw_ratio = #{throwRatio},</if>
|
||||
<if test="creditEd != null">credit_ed = #{creditEd},</if>
|
||||
<if test="productIds != null">product_ids = #{productIds},</if>
|
||||
<if test="remark != null">remark = #{remark},</if>
|
||||
<if test="createBy != null">create_by = #{createBy},</if>
|
||||
|
||||
@@ -13,6 +13,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="frozenBalance" column="frozen_balance" />
|
||||
<result property="totalCredit" column="total_credit" />
|
||||
<result property="extraCredit" column="extra_credit" />
|
||||
<result property="extraEffectiveDate" column="extra_effective_date" />
|
||||
<result property="extraExpireDate" column="extra_expire_date" />
|
||||
<result property="usedCredit" column="used_credit" />
|
||||
<result property="tax" column="tax" />
|
||||
<result property="version" column="version" />
|
||||
@@ -25,7 +27,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectCustomerAccountVo">
|
||||
select id, customer_id, currency, balance, frozen_balance, total_credit, extra_credit, used_credit,
|
||||
select id, customer_id, currency, balance, frozen_balance, total_credit, extra_credit,
|
||||
extra_effective_date, extra_expire_date, used_credit,
|
||||
tax, version, create_by, create_time, update_by, update_time, remark, del_flag
|
||||
from customer_account
|
||||
</sql>
|
||||
@@ -90,6 +93,112 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
where id = #{id} and version = #{version} and del_flag = '0'
|
||||
</update>
|
||||
|
||||
<!-- 乐观锁更新额度(信用/临时额度及临时额度有效期) -->
|
||||
<update id="updateAccountCredit">
|
||||
update customer_account
|
||||
set total_credit = #{totalCredit},
|
||||
extra_credit = #{extraCredit},
|
||||
extra_effective_date = #{extraEffectiveDate},
|
||||
extra_expire_date = #{extraExpireDate},
|
||||
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.CustomerCreditFlow" id="CustomerCreditFlowResult">
|
||||
<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="creditType" column="credit_type" />
|
||||
<result property="changeType" column="change_type" />
|
||||
<result property="direction" column="direction" />
|
||||
<result property="beforeAmount" column="before_amount" />
|
||||
<result property="changeAmount" column="change_amount" />
|
||||
<result property="afterAmount" column="after_amount" />
|
||||
<result property="usedCredit" column="used_credit" />
|
||||
<result property="effectiveDate" column="effective_date" />
|
||||
<result property="expireDate" column="expire_date" />
|
||||
<result property="reason" column="reason" />
|
||||
<result property="adjustMode" column="adjust_mode" />
|
||||
<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="selectCustomerCreditFlowVo">
|
||||
select id, flow_no, customer_id, account_id, credit_type, change_type, direction,
|
||||
before_amount, change_amount, after_amount, used_credit,
|
||||
effective_date, expire_date, reason, adjust_mode, operator_id,
|
||||
create_by, create_time, update_by, update_time, remark, del_flag
|
||||
from customer_credit_flow
|
||||
</sql>
|
||||
|
||||
<select id="selectCreditFlowList" parameterType="com.ruoyi.customer.customermoney.domain.CustomerCreditFlow"
|
||||
resultMap="CustomerCreditFlowResult">
|
||||
<include refid="selectCustomerCreditFlowVo"/>
|
||||
<where>
|
||||
<if test="customerId != null"> and customer_id = #{customerId}</if>
|
||||
<if test="creditType != null and creditType != ''"> and credit_type = #{creditType}</if>
|
||||
<if test="changeType != null and changeType != ''"> and change_type = #{changeType}</if>
|
||||
<if test="params.beginTime != null and params.beginTime != ''"> and date(create_time) >= #{params.beginTime}</if>
|
||||
<if test="params.endTime != null and params.endTime != ''"> and date(create_time) <= #{params.endTime}</if>
|
||||
and del_flag = '0'
|
||||
</where>
|
||||
order by create_time desc, id desc
|
||||
</select>
|
||||
|
||||
<insert id="insertCreditFlow" parameterType="com.ruoyi.customer.customermoney.domain.CustomerCreditFlow"
|
||||
useGeneratedKeys="true" keyProperty="id">
|
||||
insert into customer_credit_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="creditType != null">credit_type,</if>
|
||||
<if test="changeType != null">change_type,</if>
|
||||
<if test="direction != null">direction,</if>
|
||||
<if test="beforeAmount != null">before_amount,</if>
|
||||
<if test="changeAmount != null">change_amount,</if>
|
||||
<if test="afterAmount != null">after_amount,</if>
|
||||
<if test="usedCredit != null">used_credit,</if>
|
||||
<if test="effectiveDate != null">effective_date,</if>
|
||||
<if test="expireDate != null">expire_date,</if>
|
||||
<if test="reason != null">reason,</if>
|
||||
<if test="adjustMode != null">adjust_mode,</if>
|
||||
<if test="operatorId != null">operator_id,</if>
|
||||
<if test="createBy != null">create_by,</if>
|
||||
<if test="remark != null">remark,</if>
|
||||
<if test="delFlag != null">del_flag,</if>
|
||||
</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="creditType != null">#{creditType},</if>
|
||||
<if test="changeType != null">#{changeType},</if>
|
||||
<if test="direction != null">#{direction},</if>
|
||||
<if test="beforeAmount != null">#{beforeAmount},</if>
|
||||
<if test="changeAmount != null">#{changeAmount},</if>
|
||||
<if test="afterAmount != null">#{afterAmount},</if>
|
||||
<if test="usedCredit != null">#{usedCredit},</if>
|
||||
<if test="effectiveDate != null">#{effectiveDate},</if>
|
||||
<if test="expireDate != null">#{expireDate},</if>
|
||||
<if test="reason != null">#{reason},</if>
|
||||
<if test="adjustMode != null">#{adjustMode},</if>
|
||||
<if test="operatorId != null">#{operatorId},</if>
|
||||
<if test="createBy != null">#{createBy},</if>
|
||||
<if test="remark != null">#{remark},</if>
|
||||
<if test="delFlag != null">#{delFlag},</if>
|
||||
</trim>
|
||||
</insert>
|
||||
|
||||
<!-- ================= 客户收款 ================= -->
|
||||
<resultMap type="com.ruoyi.customer.customermoney.domain.CustomerReceipt" id="CustomerReceiptResult">
|
||||
<id property="id" column="id" />
|
||||
|
||||
@@ -58,3 +58,21 @@ export function listFlow(query) {
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
/** 查询额度流水列表 */
|
||||
export function listCreditFlow(query) {
|
||||
return request({
|
||||
url: '/customersupplier/customermoney/credit/flow/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
/** 调整客户额度(信用额度/临时额度),调整过程留额度流水 */
|
||||
export function adjustCredit(data) {
|
||||
return request({
|
||||
url: '/customersupplier/customermoney/credit/adjust',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
@@ -108,7 +108,11 @@
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="临时额度" prop="tempEd">
|
||||
<el-input v-model="form.tempEd" placeholder="请输入临时额度" />
|
||||
<el-input v-model="form.tempEd" :disabled="!isAdd" placeholder="请输入临时额度">
|
||||
<template #append v-if="!isAdd">
|
||||
<span style="color: #e6a23c">请在客户余额-额度管理中调整</span>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
@@ -130,7 +134,11 @@
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="信用额度" prop="creditEd">
|
||||
<el-input v-model="form.creditEd" placeholder="请输入信用额度" />
|
||||
<el-input v-model="form.creditEd" :disabled="!isAdd" placeholder="请输入信用额度">
|
||||
<template #append v-if="!isAdd">
|
||||
<span style="color: #e6a23c">请在客户余额-额度管理中调整</span>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
@@ -630,7 +638,7 @@ function buildEmptyForm() {
|
||||
foreignCurrencyBalance: null,
|
||||
weightThreshold: null,
|
||||
throwRatio: null,
|
||||
creditEd: null,
|
||||
creditEd: 1000,
|
||||
productIds: null,
|
||||
attachmentList: [],
|
||||
companyList: [],
|
||||
|
||||
+453
-4
@@ -11,6 +11,12 @@
|
||||
<div class="head-code">客户编码:{{ customer.customerCode || route.query.customerCode || '-' }}</div>
|
||||
<div class="head-right">
|
||||
<el-tag type="success" size="small">本位币账户 CNY</el-tag>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="Money"
|
||||
@click="openAdjustDialog()"
|
||||
v-hasPermi="['customer:credit:adjust']"
|
||||
>额度调整</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
@@ -31,14 +37,23 @@
|
||||
</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-label">
|
||||
总额度
|
||||
<el-button link type="primary" size="small" @click="openAdjustDialog('CREDIT')"
|
||||
v-hasPermi="['customer:credit:adjust']">调整</el-button>
|
||||
</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-label">
|
||||
额外额度
|
||||
<el-button link type="primary" size="small" @click="openAdjustDialog('TEMP')"
|
||||
v-hasPermi="['customer:credit:adjust']">调整</el-button>
|
||||
</div>
|
||||
<div class="balance-value">{{ formatMoney(currentAccount.extraCredit) }}</div>
|
||||
<div class="balance-sub">{{ tempCreditPeriodText }}</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="12" :sm="8" :md="4">
|
||||
@@ -225,6 +240,100 @@
|
||||
@pagination="getFlowList"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- ====== 额度流水 ====== -->
|
||||
<el-tab-pane label="额度流水" name="creditFlow">
|
||||
<el-form :model="creditFlowQuery" :inline="true" class="search-form">
|
||||
<el-form-item label="额度类型" prop="creditType">
|
||||
<el-select v-model="creditFlowQuery.creditType" placeholder="全部" clearable style="width: 130px;">
|
||||
<el-option v-for="opt in creditTypeOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="变动类型" prop="changeType">
|
||||
<el-select v-model="creditFlowQuery.changeType" placeholder="全部" clearable style="width: 130px;">
|
||||
<el-option v-for="opt in creditChangeTypeOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="日期">
|
||||
<el-date-picker
|
||||
v-model="creditFlowDateRange"
|
||||
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="getCreditFlowList">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetCreditFlowQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table :data="creditFlowList" border v-loading="creditFlowLoading">
|
||||
<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="creditType" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.creditType === 'TEMP' ? 'warning' : ''">
|
||||
{{ formatOption(creditTypeOptions, scope.row.creditType) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="变动类型" align="center" prop="changeType" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="creditChangeTag(scope.row.changeType)">
|
||||
{{ formatOption(creditChangeTypeOptions, scope.row.changeType) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="调整方式" align="center" prop="adjustMode" width="90">
|
||||
<template #default="scope">{{ formatOption(adjustModeOptions, scope.row.adjustMode) }}</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-if="scope.row.direction === -1" type="danger">减少</el-tag>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="变动前额度" align="center" prop="beforeAmount" width="110">
|
||||
<template #default="scope">{{ formatMoney(scope.row.beforeAmount) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="变动额度" align="center" width="110">
|
||||
<template #default="scope">
|
||||
<span :class="scope.row.direction === 1 ? 'money-in' : 'money-out'">
|
||||
{{ scope.row.direction === 1 ? '+' : '-' }}{{ formatMoney(scope.row.changeAmount) }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="变动后额度" align="center" prop="afterAmount" width="110">
|
||||
<template #default="scope">{{ formatMoney(scope.row.afterAmount) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="已用额度" align="center" prop="usedCredit" width="100">
|
||||
<template #default="scope">{{ formatMoney(scope.row.usedCredit) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="生效日期" align="center" prop="effectiveDate" width="110">
|
||||
<template #default="scope">{{ scope.row.effectiveDate || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="失效日期" align="center" prop="expireDate" width="110">
|
||||
<template #default="scope">{{ scope.row.expireDate || '长期' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="调整原因" align="center" prop="reason" show-overflow-tooltip min-width="160" />
|
||||
<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="creditFlowTotal > 0"
|
||||
:total="creditFlowTotal"
|
||||
v-model:page="creditFlowQuery.pageNum"
|
||||
v-model:limit="creditFlowQuery.pageSize"
|
||||
@pagination="getCreditFlowList"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-card>
|
||||
|
||||
@@ -402,6 +511,114 @@
|
||||
<el-button @click="viewDialog.visible = false">关 闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- ====== 额度调整弹窗 ====== -->
|
||||
<el-dialog
|
||||
:title="creditDialog.form.creditType === 'TEMP' ? '临时额度调整' : '信用额度调整'"
|
||||
v-model="creditDialog.visible"
|
||||
width="600px"
|
||||
append-to-body
|
||||
@closed="resetCreditForm"
|
||||
>
|
||||
<el-alert type="info" :closable="false" class="credit-tip">
|
||||
<div>当前{{ creditDialog.form.creditType === 'TEMP' ? '临时额度' : '信用额度' }}:
|
||||
<b>{{ formatMoney(creditBefore) }}</b>,已用额度:<b>{{ formatMoney(currentAccount.usedCredit) }}</b></div>
|
||||
</el-alert>
|
||||
<el-form ref="creditFormRef" :model="creditDialog.form" :rules="creditRules" label-width="100px">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="额度类型" prop="creditType">
|
||||
<el-radio-group v-model="creditDialog.form.creditType" @change="onCreditTypeChange">
|
||||
<el-radio value="CREDIT">信用额度(长期)</el-radio>
|
||||
<el-radio value="TEMP">临时额度</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="调整方式" prop="adjustMode">
|
||||
<el-radio-group v-model="creditDialog.form.adjustMode">
|
||||
<el-radio value="SET">设置为</el-radio>
|
||||
<el-radio value="ADD">增加</el-radio>
|
||||
<el-radio value="SUB">减少</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="调整金额" prop="changeAmount">
|
||||
<el-input-number
|
||||
v-model="creditDialog.form.changeAmount"
|
||||
:precision="2"
|
||||
:step="1000"
|
||||
:min="0.01"
|
||||
controls-position="right"
|
||||
style="width: 100%;"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<template v-if="creditDialog.form.creditType === 'TEMP'">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="生效日期" prop="effectiveDate">
|
||||
<el-date-picker
|
||||
v-model="creditDialog.form.effectiveDate"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="默认今天"
|
||||
:disabled-date="afterToday"
|
||||
style="width: 100%;"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="失效日期" prop="expireDate">
|
||||
<el-date-picker
|
||||
v-model="creditDialog.form.expireDate"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="不填为长期有效"
|
||||
style="width: 100%;"
|
||||
/>
|
||||
<div class="rate-tip">失效日当天仍有效,次日访问账户时自动清零并记到期流水</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</template>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="调整原因" prop="reason">
|
||||
<el-input
|
||||
v-model="creditDialog.form.reason"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
maxlength="255"
|
||||
show-word-limit
|
||||
placeholder="请必填,例如:客户旺季临时授信 / 年审调整长期额度"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<div class="credit-preview">
|
||||
<div>调整后{{ creditDialog.form.creditType === 'TEMP' ? '临时额度' : '信用额度' }}:
|
||||
<b :class="creditAfter < 0 ? 'money-out' : 'money-in'">{{ formatMoney(creditAfter) }}</b>
|
||||
</div>
|
||||
<div>调整后授信总额:<b>{{ formatMoney(totalCreditAfter) }}</b>
|
||||
(可用 = 授信总额 - 已用 {{ formatMoney(currentAccount.usedCredit) }})</div>
|
||||
<el-alert
|
||||
v-if="creditAfter < 0"
|
||||
type="error" :closable="false"
|
||||
title="调整后额度不能为负数" class="credit-alert"
|
||||
/>
|
||||
<el-alert
|
||||
v-else-if="totalCreditAfter < (Number(currentAccount.usedCredit) || 0)"
|
||||
type="error" :closable="false"
|
||||
title="授信总额不能小于已用额度" class="credit-alert"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<el-button type="primary" :loading="creditSubmitLoading" @click="submitAdjustCredit">确 定</el-button>
|
||||
<el-button @click="creditDialog.visible = false">取 消</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -409,11 +626,12 @@
|
||||
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 { listAccount, listReceipt, getReceipt, addReceipt, confirmReceipt, cancelReceipt, listFlow, listCreditFlow, adjustCredit } from "@/api/customersuppliermanagement/customermoney/customermoney.js"
|
||||
import { convertToBase } from "@/api/data/exchangeRate"
|
||||
import { getToken } from "@/utils/auth"
|
||||
|
||||
import { useRoute } from 'vue-router'
|
||||
import { nextTick } from 'vue'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
@@ -472,9 +690,28 @@ function receiptTypeTag(v) {
|
||||
function receiptStatusTag(v) {
|
||||
return { PENDING: 'warning', CONFIRMED: 'success', CANCELLED: 'danger' }[v] || 'info'
|
||||
}
|
||||
function bizTypeTag(v) {
|
||||
const bizTypeTag = function(v) {
|
||||
return { RECEIPT: 'success', BILL_PAY: 'danger', REFUND: 'warning', ADJUST: 'info', FREEZE: 'info', UNFREEZE: '' }[v] || 'info'
|
||||
}
|
||||
|
||||
/* ================= 额度相关枚举 ================= */
|
||||
const creditTypeOptions = [
|
||||
{ value: 'CREDIT', label: '信用额度' },
|
||||
{ value: 'TEMP', label: '临时额度' }
|
||||
]
|
||||
const creditChangeTypeOptions = [
|
||||
{ value: 'INIT', label: '初始化' },
|
||||
{ value: 'ADJUST', label: '手工调整' },
|
||||
{ value: 'EXPIRE', label: '到期失效' }
|
||||
]
|
||||
const adjustModeOptions = [
|
||||
{ value: 'SET', label: '设置为' },
|
||||
{ value: 'ADD', label: '增加' },
|
||||
{ value: 'SUB', label: '减少' }
|
||||
]
|
||||
function creditChangeTag(v) {
|
||||
return { INIT: '', ADJUST: 'primary', EXPIRE: 'danger' }[v] || 'info'
|
||||
}
|
||||
function formatMoney(val) {
|
||||
const num = Number(val)
|
||||
return isNaN(num) ? '0.00' : num.toFixed(2)
|
||||
@@ -506,6 +743,152 @@ const availableCredit = computed(() => {
|
||||
return formatMoney(total + extra - used)
|
||||
})
|
||||
|
||||
/** 临时额度有效期文案 */
|
||||
const tempCreditPeriodText = computed(() => {
|
||||
const a = currentAccount.value
|
||||
const extra = Number(a.extraCredit) || 0
|
||||
if (extra <= 0) return ''
|
||||
if (!a.extraExpireDate) return '长期有效'
|
||||
const today = proxy.parseTime(new Date(), '{y}-{m}-{d}')
|
||||
if (a.extraExpireDate < today) return '已过期'
|
||||
return '有效期至 ' + a.extraExpireDate
|
||||
})
|
||||
|
||||
/* ============ 额度调整弹窗 ============ */
|
||||
const creditFormRef = ref(null)
|
||||
const creditSubmitLoading = ref(false)
|
||||
const creditDialog = reactive({
|
||||
visible: false,
|
||||
form: {}
|
||||
})
|
||||
const creditRules = {
|
||||
creditType: [{ required: true, message: '请选择额度类型', trigger: 'change' }],
|
||||
adjustMode: [{ required: true, message: '请选择调整方式', trigger: 'change' }],
|
||||
changeAmount: [{ required: true, message: '调整金额必须大于0', trigger: 'blur' }],
|
||||
reason: [{ required: true, message: '请填写调整原因', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
/** 禁止选择晚于今天的日期(不允许预约未来生效) */
|
||||
function afterToday(date) {
|
||||
const today = new Date()
|
||||
today.setHours(23, 59, 59, 999)
|
||||
return date.getTime() > today.getTime()
|
||||
}
|
||||
|
||||
function defaultCreditForm(creditType) {
|
||||
return {
|
||||
customerId: route.query.customerId || null,
|
||||
creditType: creditType || 'CREDIT',
|
||||
adjustMode: 'ADD',
|
||||
changeAmount: null,
|
||||
reason: '',
|
||||
effectiveDate: proxy.parseTime(new Date(), '{y}-{m}-{d}'),
|
||||
expireDate: ''
|
||||
}
|
||||
}
|
||||
|
||||
function openAdjustDialog(creditType) {
|
||||
const customerId = Number(route.query.customerId)
|
||||
if (!customerId) {
|
||||
proxy.$modal.msgError('缺少客户ID,请从客户列表进入客户余额页')
|
||||
return
|
||||
}
|
||||
resetCreditForm()
|
||||
creditDialog.form = defaultCreditForm(creditType)
|
||||
creditDialog.visible = true
|
||||
}
|
||||
|
||||
function resetCreditForm() {
|
||||
creditDialog.form = defaultCreditForm()
|
||||
proxy.resetForm('creditFormRef')
|
||||
}
|
||||
|
||||
/** 切换额度类型:清掉校验红框,临时额度默认生效日为今天 */
|
||||
function onCreditTypeChange() {
|
||||
creditDialog.form.changeAmount = null
|
||||
if (creditDialog.form.creditType === 'TEMP' && !creditDialog.form.effectiveDate) {
|
||||
creditDialog.form.effectiveDate = proxy.parseTime(new Date(), '{y}-{m}-{d}')
|
||||
}
|
||||
nextTick(() => proxy.clearValidate('creditFormRef'))
|
||||
}
|
||||
|
||||
/** 当前被调整额度(调整前) */
|
||||
const creditBefore = computed(() => {
|
||||
const a = currentAccount.value
|
||||
return Number(creditDialog.form.creditType === 'TEMP' ? a.extraCredit : a.totalCredit) || 0
|
||||
})
|
||||
|
||||
/** 调整后被调额度 */
|
||||
const creditAfter = computed(() => {
|
||||
const input = Number(creditDialog.form.changeAmount) || 0
|
||||
const mode = creditDialog.form.adjustMode
|
||||
if (mode === 'SET') return input
|
||||
return mode === 'SUB' ? creditBefore.value - input : creditBefore.value + input
|
||||
})
|
||||
|
||||
/** 调整后授信总额(信用+临时) */
|
||||
const totalCreditAfter = computed(() => {
|
||||
const a = currentAccount.value
|
||||
const other = Number(creditDialog.form.creditType === 'TEMP' ? a.totalCredit : a.extraCredit) || 0
|
||||
return other + creditAfter.value
|
||||
})
|
||||
|
||||
function submitAdjustCredit() {
|
||||
creditFormRef.value.validate(valid => {
|
||||
if (!valid) return
|
||||
const f = creditDialog.form
|
||||
const amount = Number(f.changeAmount)
|
||||
if (!(amount > 0)) {
|
||||
proxy.$modal.msgError('调整金额必须大于0')
|
||||
return
|
||||
}
|
||||
if (f.creditType === 'TEMP') {
|
||||
if (f.expireDate && f.effectiveDate && f.expireDate <= f.effectiveDate) {
|
||||
proxy.$modal.msgError('失效日期必须晚于生效日期')
|
||||
return
|
||||
}
|
||||
}
|
||||
if (creditAfter.value < 0) {
|
||||
proxy.$modal.msgError('调整后额度不能为负数')
|
||||
return
|
||||
}
|
||||
const used = Number(currentAccount.value.usedCredit) || 0
|
||||
if (totalCreditAfter.value < used) {
|
||||
proxy.$modal.msgError('调整后授信总额不能小于已用额度 ' + formatMoney(used))
|
||||
return
|
||||
}
|
||||
if (creditAfter.value === creditBefore.value) {
|
||||
proxy.$modal.msgError('额度未发生变化,无需调整')
|
||||
return
|
||||
}
|
||||
const payload = {
|
||||
customerId: Number(f.customerId) || Number(route.query.customerId) || null,
|
||||
creditType: f.creditType,
|
||||
adjustMode: f.adjustMode,
|
||||
changeAmount: amount,
|
||||
reason: f.reason
|
||||
}
|
||||
if (!payload.customerId) {
|
||||
proxy.$modal.msgError('缺少客户ID,请从客户列表进入客户余额页')
|
||||
return
|
||||
}
|
||||
if (f.creditType === 'TEMP') {
|
||||
payload.effectiveDate = f.effectiveDate || null
|
||||
payload.expireDate = f.expireDate || null
|
||||
}
|
||||
creditSubmitLoading.value = true
|
||||
adjustCredit(payload).then(() => {
|
||||
proxy.$modal.msgSuccess('额度调整成功')
|
||||
creditDialog.visible = false
|
||||
loadCustomer()
|
||||
loadAccounts()
|
||||
getCreditFlowList()
|
||||
}).finally(() => {
|
||||
creditSubmitLoading.value = false
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function loadCustomer() {
|
||||
const customerId = route.query.customerId
|
||||
if (!customerId) return
|
||||
@@ -752,6 +1135,38 @@ function resetFlowQuery() {
|
||||
getFlowList()
|
||||
}
|
||||
|
||||
/* ================= 额度流水 ================= */
|
||||
const creditFlowList = ref([])
|
||||
const creditFlowTotal = ref(0)
|
||||
const creditFlowLoading = ref(false)
|
||||
const creditFlowDateRange = ref([])
|
||||
const creditFlowQuery = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
customerId: route.query.customerId,
|
||||
creditType: '',
|
||||
changeType: ''
|
||||
})
|
||||
|
||||
function getCreditFlowList() {
|
||||
creditFlowLoading.value = true
|
||||
const params = proxy.addDateRange({ ...creditFlowQuery }, creditFlowDateRange.value)
|
||||
listCreditFlow(params).then(res => {
|
||||
creditFlowList.value = res.rows || []
|
||||
creditFlowTotal.value = res.total || 0
|
||||
}).finally(() => {
|
||||
creditFlowLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function resetCreditFlowQuery() {
|
||||
creditFlowQuery.creditType = ''
|
||||
creditFlowQuery.changeType = ''
|
||||
creditFlowDateRange.value = []
|
||||
creditFlowQuery.pageNum = 1
|
||||
getCreditFlowList()
|
||||
}
|
||||
|
||||
/* ================= 附件上传 ================= */
|
||||
const uploadFileUrl = ref(baseUrl + "/common/upload")
|
||||
const uploadHeaders = ref({ Authorization: "Bearer " + getToken() })
|
||||
@@ -848,6 +1263,8 @@ function initPage() {
|
||||
viewDialog.attachments = []
|
||||
receiptDateRange.value = []
|
||||
flowDateRange.value = []
|
||||
creditFlowDateRange.value = []
|
||||
creditDialog.visible = false
|
||||
receiptQuery.customerId = customerId
|
||||
receiptQuery.receiptType = ''
|
||||
receiptQuery.status = ''
|
||||
@@ -856,15 +1273,22 @@ function initPage() {
|
||||
flowQuery.bizType = ''
|
||||
flowQuery.direction = ''
|
||||
flowQuery.pageNum = 1
|
||||
creditFlowQuery.customerId = customerId
|
||||
creditFlowQuery.creditType = ''
|
||||
creditFlowQuery.changeType = ''
|
||||
creditFlowQuery.pageNum = 1
|
||||
receiptList.value = []
|
||||
receiptTotal.value = 0
|
||||
flowList.value = []
|
||||
flowTotal.value = 0
|
||||
creditFlowList.value = []
|
||||
creditFlowTotal.value = 0
|
||||
if (!customerId) return
|
||||
loadCustomer()
|
||||
loadAccounts()
|
||||
getReceiptList()
|
||||
getFlowList()
|
||||
getCreditFlowList()
|
||||
}
|
||||
|
||||
onMounted(initPage)
|
||||
@@ -916,6 +1340,31 @@ watch(() => route.query.customerId, (newId, oldId) => {
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 13px;
|
||||
margin-bottom: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
}
|
||||
.balance-sub {
|
||||
margin-top: 6px;
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
min-height: 16px;
|
||||
}
|
||||
.credit-tip {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.credit-preview {
|
||||
margin-top: 4px;
|
||||
padding: 10px 14px;
|
||||
background: var(--el-fill-color-light);
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
.credit-alert {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.balance-value {
|
||||
font-size: 22px;
|
||||
|
||||
Reference in New Issue
Block a user