新增提单费用和费用日志
This commit is contained in:
+39
@@ -1,18 +1,26 @@
|
||||
package com.bms.web.controller.price;
|
||||
|
||||
import java.util.ArrayList;
|
||||
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.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import com.bms.common.annotation.Log;
|
||||
import com.bms.common.core.controller.BaseController;
|
||||
import com.bms.common.core.domain.AjaxResult;
|
||||
import com.bms.common.core.page.TableDataInfo;
|
||||
import com.bms.common.enums.BusinessType;
|
||||
import com.bms.common.utils.StringUtils;
|
||||
import com.bms.price.airbill.domain.SrvBillAirFeeLog;
|
||||
import com.bms.price.airbill.domain.SrvBillAirFeeSave;
|
||||
import com.bms.price.domain.vo.SrvBillAirQuery;
|
||||
import com.bms.price.domain.vo.SrvBillAirVo;
|
||||
import com.bms.price.service.ISrvBillAirFeeService;
|
||||
import com.bms.price.service.ISrvBillAirService;
|
||||
|
||||
/**
|
||||
@@ -25,6 +33,9 @@ public class SrvBillAirController extends BaseController
|
||||
@Autowired
|
||||
private ISrvBillAirService srvBillAirService;
|
||||
|
||||
@Autowired
|
||||
private ISrvBillAirFeeService srvBillAirFeeService;
|
||||
|
||||
/**
|
||||
* 提单列表
|
||||
*/
|
||||
@@ -55,4 +66,32 @@ public class SrvBillAirController extends BaseController
|
||||
}
|
||||
return success(vo);
|
||||
}
|
||||
|
||||
/** 一票提单下未删除的费用 */
|
||||
@PreAuthorize("@ss.hasPermi('price:airBill:fee')")
|
||||
@GetMapping("/fee/list")
|
||||
public AjaxResult feeList(@RequestParam("billCode") String billCode)
|
||||
{
|
||||
return success(srvBillAirFeeService.listFees(billCode));
|
||||
}
|
||||
|
||||
/** 提单费用的新增、修改、删除日志,按页返回 */
|
||||
@PreAuthorize("@ss.hasPermi('price:airBill:fee')")
|
||||
@GetMapping("/fee/log")
|
||||
public TableDataInfo feeLog(@RequestParam("billCode") String billCode)
|
||||
{
|
||||
List<SrvBillAirFeeLog> list = srvBillAirFeeService.listLogs(billCode);
|
||||
TableDataInfo table = getDataTable(list);
|
||||
table.setRows(new ArrayList<>(list));
|
||||
return table;
|
||||
}
|
||||
|
||||
/** 只保存本次新增、修改、删除的费用,并写入提单日志 */
|
||||
@PreAuthorize("@ss.hasPermi('price:airBill:fee')")
|
||||
@Log(title = "提单费用", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/fee/save")
|
||||
public AjaxResult saveFee(@RequestBody SrvBillAirFeeSave command)
|
||||
{
|
||||
return success(srvBillAirFeeService.save(command));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.bms.price.airbill.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 空运提单费用明细 srv_bill_air_fee。
|
||||
*/
|
||||
@Data
|
||||
public class SrvBillAirFee
|
||||
{
|
||||
private Long id;
|
||||
|
||||
private String billCode;
|
||||
|
||||
/** 字典 bms_fee_type */
|
||||
private String feeType;
|
||||
|
||||
private BigDecimal price;
|
||||
|
||||
/** 字典 bms_currency */
|
||||
private String currency;
|
||||
|
||||
private String remark;
|
||||
|
||||
private String createBy;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date createTime;
|
||||
|
||||
private String updateBy;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date updateTime;
|
||||
|
||||
/** 0未删除 1已删除 */
|
||||
private String delFlag;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.bms.price.airbill.domain;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 空运提单费用操作日志。
|
||||
*/
|
||||
@Data
|
||||
public class SrvBillAirFeeLog
|
||||
{
|
||||
public static final String INSERT = "1";
|
||||
|
||||
public static final String UPDATE = "2";
|
||||
|
||||
public static final String DELETE = "3";
|
||||
|
||||
private Long logId;
|
||||
|
||||
private String billCode;
|
||||
|
||||
private Long feeId;
|
||||
|
||||
private String operationType;
|
||||
|
||||
private String summary;
|
||||
|
||||
private String changeContent;
|
||||
|
||||
private String createBy;
|
||||
|
||||
private String createByName;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date createTime;
|
||||
|
||||
private List<FieldChange> changes;
|
||||
|
||||
@Data
|
||||
public static class FieldChange
|
||||
{
|
||||
private String field;
|
||||
|
||||
private String fieldLabel;
|
||||
|
||||
private String oldValue;
|
||||
|
||||
private String newValue;
|
||||
|
||||
public FieldChange()
|
||||
{
|
||||
}
|
||||
|
||||
public FieldChange(String field, String fieldLabel, String oldValue, String newValue)
|
||||
{
|
||||
this.field = field;
|
||||
this.fieldLabel = fieldLabel;
|
||||
this.oldValue = oldValue;
|
||||
this.newValue = newValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.bms.price.airbill.domain;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 保存提单费用时只带有变动的行。未出现的费用保持原样。
|
||||
*/
|
||||
@Data
|
||||
public class SrvBillAirFeeSave
|
||||
{
|
||||
private String billCode;
|
||||
|
||||
private List<SrvBillAirFee> inserts = new ArrayList<>();
|
||||
|
||||
private List<SrvBillAirFee> updates = new ArrayList<>();
|
||||
|
||||
private List<Long> deletes = new ArrayList<>();
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.bms.price.mapper;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import com.bms.price.airbill.domain.SrvBillAirFee;
|
||||
import com.bms.price.airbill.domain.SrvBillAirFeeLog;
|
||||
|
||||
/**
|
||||
* 空运提单费用及操作日志。
|
||||
*/
|
||||
public interface SrvBillAirFeeMapper
|
||||
{
|
||||
List<SrvBillAirFee> selectByBillCode(String billCode);
|
||||
|
||||
/** 只取这一票提单上仍有效的费用,供修改和删除前核对。 */
|
||||
SrvBillAirFee selectLive(@Param("id") Long id, @Param("billCode") String billCode);
|
||||
|
||||
int insertFee(SrvBillAirFee fee);
|
||||
|
||||
int updateFee(SrvBillAirFee fee);
|
||||
|
||||
int deleteFee(@Param("id") Long id, @Param("billCode") String billCode, @Param("updateBy") String updateBy,
|
||||
@Param("updateTime") java.util.Date updateTime);
|
||||
|
||||
int insertLog(SrvBillAirFeeLog log);
|
||||
|
||||
List<SrvBillAirFeeLog> selectLogs(String billCode);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.bms.price.service;
|
||||
|
||||
import java.util.List;
|
||||
import com.bms.price.airbill.domain.SrvBillAirFee;
|
||||
import com.bms.price.airbill.domain.SrvBillAirFeeLog;
|
||||
import com.bms.price.airbill.domain.SrvBillAirFeeSave;
|
||||
|
||||
/**
|
||||
* 空运提单费用维护。
|
||||
*/
|
||||
public interface ISrvBillAirFeeService
|
||||
{
|
||||
List<SrvBillAirFee> listFees(String billCode);
|
||||
|
||||
List<SrvBillAirFeeLog> listLogs(String billCode);
|
||||
|
||||
/** 只写入有变动的行,并按行记录修改前后。返回给页面的提示。 */
|
||||
String save(SrvBillAirFeeSave command);
|
||||
}
|
||||
+382
@@ -0,0 +1,382 @@
|
||||
package com.bms.price.service.impl;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.bms.common.core.domain.entity.SysDictData;
|
||||
import com.bms.common.core.domain.entity.SysUser;
|
||||
import com.bms.common.exception.ServiceException;
|
||||
import com.bms.common.utils.DateUtils;
|
||||
import com.bms.common.utils.PageUtils;
|
||||
import com.bms.common.utils.SecurityUtils;
|
||||
import com.bms.price.airbill.domain.SrvBillAirFee;
|
||||
import com.bms.price.airbill.domain.SrvBillAirFeeLog;
|
||||
import com.bms.price.airbill.domain.SrvBillAirFeeSave;
|
||||
import com.bms.price.mapper.SrvBillAirFeeMapper;
|
||||
import com.bms.price.mapper.SrvBillAirMapper;
|
||||
import com.bms.price.service.ISrvBillAirFeeService;
|
||||
import com.bms.system.service.ISysDictTypeService;
|
||||
|
||||
/**
|
||||
* 提单费用只处理本次新增、修改、删除的行。没有变化的费用不更新。
|
||||
*/
|
||||
@Service
|
||||
public class SrvBillAirFeeServiceImpl implements ISrvBillAirFeeService
|
||||
{
|
||||
private static final String FEE_DICT = "bms_fee_type";
|
||||
|
||||
private static final String CURRENCY_DICT = "bms_currency";
|
||||
|
||||
@Autowired
|
||||
private SrvBillAirFeeMapper feeMapper;
|
||||
|
||||
@Autowired
|
||||
private SrvBillAirMapper billMapper;
|
||||
|
||||
@Autowired
|
||||
private ISysDictTypeService dictTypeService;
|
||||
|
||||
@Override
|
||||
public List<SrvBillAirFee> listFees(String billCode)
|
||||
{
|
||||
requireBill(billCode);
|
||||
return feeMapper.selectByBillCode(billCode.trim());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SrvBillAirFeeLog> listLogs(String billCode)
|
||||
{
|
||||
requireBill(billCode);
|
||||
PageUtils.startPage();
|
||||
List<SrvBillAirFeeLog> logs = feeMapper.selectLogs(billCode.trim());
|
||||
for (SrvBillAirFeeLog log : logs)
|
||||
{
|
||||
if (log.getChangeContent() != null && !log.getChangeContent().isBlank())
|
||||
{
|
||||
log.setChanges(JSON.parseArray(log.getChangeContent(), SrvBillAirFeeLog.FieldChange.class));
|
||||
}
|
||||
}
|
||||
return logs;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public String save(SrvBillAirFeeSave command)
|
||||
{
|
||||
if (command == null)
|
||||
{
|
||||
throw new ServiceException("请提交要保存的费用");
|
||||
}
|
||||
String billCode = requireBill(command.getBillCode());
|
||||
List<SrvBillAirFee> inserts = command.getInserts() == null ? List.of() : command.getInserts();
|
||||
List<SrvBillAirFee> updates = command.getUpdates() == null ? List.of() : command.getUpdates();
|
||||
List<Long> deletes = command.getDeletes() == null ? List.of() : command.getDeletes();
|
||||
if (inserts.isEmpty() && updates.isEmpty() && deletes.isEmpty())
|
||||
{
|
||||
throw new ServiceException("没有需要保存的变更");
|
||||
}
|
||||
Map<String, String> feeLabels = dictLabels(FEE_DICT, "费用类型");
|
||||
Map<String, String> currencyLabels = dictLabels(CURRENCY_DICT, "币种");
|
||||
Operator operator = operator();
|
||||
Date now = DateUtils.getNowDate();
|
||||
Set<Long> seen = new HashSet<>();
|
||||
int inserted = 0;
|
||||
int updated = 0;
|
||||
int removed = 0;
|
||||
for (Long id : deletes)
|
||||
{
|
||||
if (id == null)
|
||||
{
|
||||
throw new ServiceException("要删除的费用缺少编号");
|
||||
}
|
||||
if (!seen.add(id))
|
||||
{
|
||||
throw new ServiceException("同一条费用不能重复提交");
|
||||
}
|
||||
SrvBillAirFee current = liveFee(id, billCode);
|
||||
if (current == null)
|
||||
{
|
||||
throw new ServiceException("费用已删除或不属于该提单");
|
||||
}
|
||||
int rows = feeMapper.deleteFee(current.getId(), billCode, operator.account, now);
|
||||
if (rows > 0)
|
||||
{
|
||||
writeLog(billCode, id, SrvBillAirFeeLog.DELETE, current, null, feeLabels, currencyLabels, operator, now);
|
||||
removed++;
|
||||
}
|
||||
}
|
||||
for (SrvBillAirFee incoming : updates)
|
||||
{
|
||||
if (incoming == null || incoming.getId() == null)
|
||||
{
|
||||
throw new ServiceException("要修改的费用缺少编号");
|
||||
}
|
||||
if (!seen.add(incoming.getId()))
|
||||
{
|
||||
throw new ServiceException("同一条费用不能重复提交");
|
||||
}
|
||||
SrvBillAirFee current = liveFee(incoming.getId(), billCode);
|
||||
if (current == null)
|
||||
{
|
||||
throw new ServiceException("费用已删除或不属于该提单");
|
||||
}
|
||||
SrvBillAirFee next = normalize(incoming, feeLabels, currencyLabels);
|
||||
List<SrvBillAirFeeLog.FieldChange> changes = diff(current, next, feeLabels, currencyLabels);
|
||||
if (changes.isEmpty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
next.setId(current.getId());
|
||||
next.setBillCode(billCode);
|
||||
next.setUpdateBy(operator.account);
|
||||
next.setUpdateTime(now);
|
||||
if (feeMapper.updateFee(next) > 0)
|
||||
{
|
||||
writeLog(billCode, current.getId(), SrvBillAirFeeLog.UPDATE, current, next, feeLabels, currencyLabels, operator, now);
|
||||
updated++;
|
||||
}
|
||||
}
|
||||
for (SrvBillAirFee incoming : inserts)
|
||||
{
|
||||
if (incoming == null || blank(incoming.getFeeType()) && incoming.getPrice() == null && blank(incoming.getCurrency()) && blank(incoming.getRemark()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
SrvBillAirFee next = normalize(incoming, feeLabels, currencyLabels);
|
||||
next.setId(null);
|
||||
next.setBillCode(billCode);
|
||||
next.setCreateBy(operator.account);
|
||||
next.setCreateTime(now);
|
||||
next.setUpdateBy(operator.account);
|
||||
next.setUpdateTime(now);
|
||||
feeMapper.insertFee(next);
|
||||
writeLog(billCode, next.getId(), SrvBillAirFeeLog.INSERT, null, next, feeLabels, currencyLabels, operator, now);
|
||||
inserted++;
|
||||
}
|
||||
if (inserted + updated + removed == 0)
|
||||
{
|
||||
throw new ServiceException("没有需要保存的变更");
|
||||
}
|
||||
return "已保存:新增 " + inserted + " 条,修改 " + updated + " 条,删除 " + removed + " 条";
|
||||
}
|
||||
|
||||
private SrvBillAirFee liveFee(Long id, String billCode)
|
||||
{
|
||||
return feeMapper.selectLive(id, billCode);
|
||||
}
|
||||
|
||||
private SrvBillAirFee normalize(SrvBillAirFee raw, Map<String, String> feeLabels, Map<String, String> currencyLabels)
|
||||
{
|
||||
String feeType = trim(raw.getFeeType());
|
||||
String currency = trim(raw.getCurrency());
|
||||
String remark = trim(raw.getRemark());
|
||||
if (feeType.isEmpty())
|
||||
{
|
||||
throw new ServiceException("请选择费用项");
|
||||
}
|
||||
if (feeType.length() > 32 || !feeLabels.containsKey(feeType))
|
||||
{
|
||||
throw new ServiceException("费用项不在费用类型字典中");
|
||||
}
|
||||
if (raw.getPrice() == null)
|
||||
{
|
||||
throw new ServiceException("请填写价格");
|
||||
}
|
||||
if (raw.getPrice().signum() < 0)
|
||||
{
|
||||
throw new ServiceException("价格不能小于0");
|
||||
}
|
||||
if (raw.getPrice().precision() - raw.getPrice().scale() > 16)
|
||||
{
|
||||
throw new ServiceException("价格整数部分不能超过16位");
|
||||
}
|
||||
if (currency.isEmpty())
|
||||
{
|
||||
throw new ServiceException("请选择收费币种");
|
||||
}
|
||||
if (currency.length() > 16 || !currencyLabels.containsKey(currency))
|
||||
{
|
||||
throw new ServiceException("收费币种不在币种字典中");
|
||||
}
|
||||
if (remark.length() > 500)
|
||||
{
|
||||
throw new ServiceException("备注不能超过500字");
|
||||
}
|
||||
SrvBillAirFee fee = new SrvBillAirFee();
|
||||
fee.setFeeType(feeType);
|
||||
fee.setPrice(raw.getPrice());
|
||||
fee.setCurrency(currency);
|
||||
fee.setRemark(remark);
|
||||
return fee;
|
||||
}
|
||||
|
||||
private void writeLog(String billCode, Long feeId, String operation, SrvBillAirFee before, SrvBillAirFee after,
|
||||
Map<String, String> feeLabels, Map<String, String> currencyLabels, Operator operator, Date now)
|
||||
{
|
||||
List<SrvBillAirFeeLog.FieldChange> changes = diff(before, after, feeLabels, currencyLabels);
|
||||
SrvBillAirFeeLog log = new SrvBillAirFeeLog();
|
||||
log.setBillCode(billCode);
|
||||
log.setFeeId(feeId);
|
||||
log.setOperationType(operation);
|
||||
log.setSummary(cut(summary(operation, changes), 1000));
|
||||
log.setChangeContent(JSON.toJSONString(changes));
|
||||
log.setCreateBy(operator.account);
|
||||
log.setCreateByName(operator.name);
|
||||
log.setCreateTime(now);
|
||||
feeMapper.insertLog(log);
|
||||
}
|
||||
|
||||
private static List<SrvBillAirFeeLog.FieldChange> diff(SrvBillAirFee before, SrvBillAirFee after,
|
||||
Map<String, String> feeLabels, Map<String, String> currencyLabels)
|
||||
{
|
||||
List<SrvBillAirFeeLog.FieldChange> changes = new ArrayList<>();
|
||||
addChange(changes, "feeType", "费用类型", label(feeLabels, value(before, SrvBillAirFee::getFeeType)),
|
||||
label(feeLabels, value(after, SrvBillAirFee::getFeeType)));
|
||||
addChange(changes, "price", "价格", priceText(before), priceText(after));
|
||||
addChange(changes, "currency", "收费币种", label(currencyLabels, value(before, SrvBillAirFee::getCurrency)),
|
||||
label(currencyLabels, value(after, SrvBillAirFee::getCurrency)));
|
||||
addChange(changes, "remark", "备注", text(before == null ? null : before.getRemark()), text(after == null ? null : after.getRemark()));
|
||||
return changes;
|
||||
}
|
||||
|
||||
private static void addChange(List<SrvBillAirFeeLog.FieldChange> changes, String field, String fieldLabel, String oldValue, String newValue)
|
||||
{
|
||||
String left = oldValue == null ? "" : oldValue;
|
||||
String right = newValue == null ? "" : newValue;
|
||||
if (left.equals(right))
|
||||
{
|
||||
return;
|
||||
}
|
||||
changes.add(new SrvBillAirFeeLog.FieldChange(field, fieldLabel, left, right));
|
||||
}
|
||||
|
||||
private static String summary(String operation, List<SrvBillAirFeeLog.FieldChange> changes)
|
||||
{
|
||||
String action = SrvBillAirFeeLog.INSERT.equals(operation) ? "新增费用"
|
||||
: SrvBillAirFeeLog.DELETE.equals(operation) ? "删除费用" : "修改费用";
|
||||
StringBuilder text = new StringBuilder(action);
|
||||
for (SrvBillAirFeeLog.FieldChange change : changes)
|
||||
{
|
||||
text.append(";").append(change.getFieldLabel()).append(":")
|
||||
.append(show(change.getOldValue())).append(" → ").append(show(change.getNewValue()));
|
||||
}
|
||||
return text.toString();
|
||||
}
|
||||
|
||||
private Map<String, String> dictLabels(String dictType, String name)
|
||||
{
|
||||
List<SysDictData> rows = dictTypeService.selectDictDataByType(dictType);
|
||||
Map<String, String> labels = new LinkedHashMap<>();
|
||||
if (rows != null)
|
||||
{
|
||||
for (SysDictData row : rows)
|
||||
{
|
||||
if (row != null && row.getDictValue() != null && !row.getDictValue().isBlank())
|
||||
{
|
||||
labels.put(row.getDictValue().trim(), row.getDictLabel() == null ? row.getDictValue().trim() : row.getDictLabel().trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (labels.isEmpty())
|
||||
{
|
||||
throw new ServiceException(name + "字典还没有数据");
|
||||
}
|
||||
return labels;
|
||||
}
|
||||
|
||||
private String requireBill(String billCode)
|
||||
{
|
||||
if (billCode == null || billCode.isBlank())
|
||||
{
|
||||
throw new ServiceException("提单号不能为空");
|
||||
}
|
||||
String code = billCode.trim();
|
||||
if (billMapper.selectSrvBillAirByBillCode(code) == null)
|
||||
{
|
||||
throw new ServiceException("提单不存在");
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
private static Operator operator()
|
||||
{
|
||||
SysUser user = SecurityUtils.getLoginUser().getUser();
|
||||
String account = user.getUserName() == null ? "" : user.getUserName();
|
||||
String name = user.getNickName() == null || user.getNickName().isBlank() ? account : user.getNickName();
|
||||
return new Operator(account, name);
|
||||
}
|
||||
|
||||
private static String value(SrvBillAirFee fee, java.util.function.Function<SrvBillAirFee, String> getter)
|
||||
{
|
||||
return fee == null ? "" : text(getter.apply(fee));
|
||||
}
|
||||
|
||||
private static String priceText(SrvBillAirFee fee)
|
||||
{
|
||||
if (fee == null || fee.getPrice() == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
return fee.getPrice().stripTrailingZeros().toPlainString();
|
||||
}
|
||||
|
||||
private static String label(Map<String, String> labels, String value)
|
||||
{
|
||||
if (value == null || value.isBlank())
|
||||
{
|
||||
return "";
|
||||
}
|
||||
return labels.getOrDefault(value, value);
|
||||
}
|
||||
|
||||
private static String show(String value)
|
||||
{
|
||||
return value == null || value.isBlank() ? "空" : value;
|
||||
}
|
||||
|
||||
private static String text(String value)
|
||||
{
|
||||
return value == null ? "" : value.trim();
|
||||
}
|
||||
|
||||
private static String trim(String value)
|
||||
{
|
||||
return value == null ? "" : value.trim();
|
||||
}
|
||||
|
||||
private static boolean blank(String value)
|
||||
{
|
||||
return value == null || value.trim().isEmpty();
|
||||
}
|
||||
|
||||
private static String cut(String value, int max)
|
||||
{
|
||||
if (value == null || value.length() <= max)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
return value.substring(0, max);
|
||||
}
|
||||
|
||||
private static final class Operator
|
||||
{
|
||||
private final String account;
|
||||
|
||||
private final String name;
|
||||
|
||||
private Operator(String account, String name)
|
||||
{
|
||||
this.account = account;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?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.bms.price.mapper.SrvBillAirFeeMapper">
|
||||
|
||||
<select id="selectByBillCode" resultType="com.bms.price.airbill.domain.SrvBillAirFee">
|
||||
select id, bill_code as billCode, fee_type as feeType, price, currency, remark,
|
||||
create_by as createBy, create_time as createTime, update_by as updateBy,
|
||||
update_time as updateTime, del_flag as delFlag
|
||||
from srv_bill_air_fee
|
||||
where bill_code = #{billCode}
|
||||
and del_flag = '0'
|
||||
order by id
|
||||
</select>
|
||||
|
||||
<select id="selectLive" resultType="com.bms.price.airbill.domain.SrvBillAirFee">
|
||||
select id, bill_code as billCode, fee_type as feeType, price, currency, remark,
|
||||
create_by as createBy, create_time as createTime, update_by as updateBy,
|
||||
update_time as updateTime, del_flag as delFlag
|
||||
from srv_bill_air_fee
|
||||
where id = #{id}
|
||||
and bill_code = #{billCode}
|
||||
and del_flag = '0'
|
||||
</select>
|
||||
|
||||
<insert id="insertFee" useGeneratedKeys="true" keyProperty="id">
|
||||
insert into srv_bill_air_fee (
|
||||
bill_code, fee_type, price, currency, remark,
|
||||
create_by, create_time, update_by, update_time, del_flag
|
||||
) values (
|
||||
#{billCode}, #{feeType}, #{price}, #{currency}, #{remark},
|
||||
#{createBy}, #{createTime}, #{updateBy}, #{updateTime}, '0'
|
||||
)
|
||||
</insert>
|
||||
|
||||
<update id="updateFee">
|
||||
update srv_bill_air_fee
|
||||
set fee_type = #{feeType},
|
||||
price = #{price},
|
||||
currency = #{currency},
|
||||
remark = #{remark},
|
||||
update_by = #{updateBy},
|
||||
update_time = #{updateTime}
|
||||
where id = #{id}
|
||||
and bill_code = #{billCode}
|
||||
and del_flag = '0'
|
||||
</update>
|
||||
|
||||
<update id="deleteFee">
|
||||
update srv_bill_air_fee
|
||||
set del_flag = '1',
|
||||
update_by = #{updateBy},
|
||||
update_time = #{updateTime}
|
||||
where id = #{id}
|
||||
and bill_code = #{billCode}
|
||||
and del_flag = '0'
|
||||
</update>
|
||||
|
||||
<insert id="insertLog" useGeneratedKeys="true" keyProperty="logId">
|
||||
insert into srv_bill_air_fee_log (
|
||||
bill_code, fee_id, operation_type, summary, change_content,
|
||||
create_by, create_by_name, create_time
|
||||
) values (
|
||||
#{billCode}, #{feeId}, #{operationType}, #{summary}, #{changeContent},
|
||||
#{createBy}, #{createByName}, #{createTime}
|
||||
)
|
||||
</insert>
|
||||
|
||||
<select id="selectLogs" resultType="com.bms.price.airbill.domain.SrvBillAirFeeLog">
|
||||
select log_id as logId, bill_code as billCode, fee_id as feeId, operation_type as operationType,
|
||||
summary, change_content as changeContent, create_by as createBy,
|
||||
create_by_name as createByName, create_time as createTime
|
||||
from srv_bill_air_fee_log
|
||||
where bill_code = #{billCode}
|
||||
order by log_id desc
|
||||
</select>
|
||||
</mapper>
|
||||
@@ -14,6 +14,27 @@ INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component,
|
||||
SELECT 2522, N'详情', 2520, 2, '', NULL, 1, 0, 'F', '0', '0', 'price:airBill:query', '#', 'admin', sysdate(), N'提单详情'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2522 OR perms = 'price:airBill:query');
|
||||
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark)
|
||||
SELECT 2529, N'费用', 2520, 3, '', NULL, 1, 0, 'F', '0', '0', 'price:airBill:fee', '#', 'admin', sysdate(), N'维护空运提单费用'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2529 OR perms = 'price:airBill:fee');
|
||||
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT 1, m.menu_id FROM (SELECT 2520 AS menu_id UNION SELECT 2521 UNION SELECT 2522) m
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_role_menu rm WHERE rm.role_id = 1 AND rm.menu_id = m.menu_id);
|
||||
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT 1, fee.menu_id
|
||||
FROM sys_menu fee
|
||||
WHERE fee.perms = 'price:airBill:fee'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM sys_role_menu rm WHERE rm.role_id = 1 AND rm.menu_id = fee.menu_id
|
||||
);
|
||||
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT DISTINCT rm.role_id, fee.menu_id
|
||||
FROM sys_role_menu rm
|
||||
JOIN sys_menu fee ON fee.perms = 'price:airBill:fee'
|
||||
WHERE rm.menu_id IN (2520, 2522)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM sys_role_menu x WHERE x.role_id = rm.role_id AND x.menu_id = fee.menu_id
|
||||
);
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
-- 应付账单四段合计。total_amount 为账单合计,等于 A、B、C、D 四段之和。可重复执行。
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
SET @sql = (
|
||||
SELECT IF(
|
||||
EXISTS (SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pay_bill' AND COLUMN_NAME = 'amount_a'),
|
||||
'SELECT 1',
|
||||
'ALTER TABLE pay_bill ADD COLUMN amount_a decimal(18,4) NOT NULL DEFAULT 0.0000 COMMENT ''A段合计'' AFTER order_count'
|
||||
)
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @sql = (
|
||||
SELECT IF(
|
||||
EXISTS (SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pay_bill' AND COLUMN_NAME = 'amount_b'),
|
||||
'SELECT 1',
|
||||
'ALTER TABLE pay_bill ADD COLUMN amount_b decimal(18,4) NOT NULL DEFAULT 0.0000 COMMENT ''B段合计'' AFTER amount_a'
|
||||
)
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @sql = (
|
||||
SELECT IF(
|
||||
EXISTS (SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pay_bill' AND COLUMN_NAME = 'amount_c'),
|
||||
'SELECT 1',
|
||||
'ALTER TABLE pay_bill ADD COLUMN amount_c decimal(18,4) NOT NULL DEFAULT 0.0000 COMMENT ''C段合计'' AFTER amount_b'
|
||||
)
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @sql = (
|
||||
SELECT IF(
|
||||
EXISTS (SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pay_bill' AND COLUMN_NAME = 'amount_d'),
|
||||
'SELECT 1',
|
||||
'ALTER TABLE pay_bill ADD COLUMN amount_d decimal(18,4) NOT NULL DEFAULT 0.0000 COMMENT ''D段合计'' AFTER amount_c'
|
||||
)
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
ALTER TABLE pay_bill
|
||||
MODIFY COLUMN total_amount decimal(18,4) NOT NULL DEFAULT 0.0000 COMMENT '账单合计';
|
||||
@@ -17,3 +17,30 @@ export function getAirBill(billCode) {
|
||||
params: { billCode }
|
||||
})
|
||||
}
|
||||
|
||||
/** 一票提单的费用明细 */
|
||||
export function listAirBillFee(billCode) {
|
||||
return request({
|
||||
url: '/price/airBill/fee/list',
|
||||
method: 'get',
|
||||
params: { billCode }
|
||||
})
|
||||
}
|
||||
|
||||
/** 提单费用操作日志,分页参数 pageNum、pageSize */
|
||||
export function listAirBillFeeLog(query) {
|
||||
return request({
|
||||
url: '/price/airBill/fee/log',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
/** 只提交新增、修改、删除的费用 */
|
||||
export function saveAirBillFee(data) {
|
||||
return request({
|
||||
url: '/price/airBill/fee/save',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
@@ -49,18 +49,22 @@
|
||||
</template>
|
||||
<template #col-action="{ row }">
|
||||
<el-button link type="primary" v-hasPermi="['price:airBill:query']" @click="openDetail(row)">详情</el-button>
|
||||
<el-button link type="primary" v-hasPermi="['price:airBill:fee']" @click="openFee(row)">费用</el-button>
|
||||
</template>
|
||||
</basic-table>
|
||||
|
||||
<air-bill-detail v-model="open" :title="title" :loading="loading" :detail="detail" />
|
||||
<air-bill-fee-dialog ref="feeRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="AirBill">
|
||||
import { listAirBill, getAirBill } from '@/api/price/airBill'
|
||||
import AirBillDetail from './components/AirBillDetail.vue'
|
||||
import AirBillFeeDialog from './components/AirBillFeeDialog.vue'
|
||||
|
||||
const tableRef = ref()
|
||||
const feeRef = ref()
|
||||
const tableHeight = ref(480)
|
||||
let tableResizeObserver = null
|
||||
|
||||
@@ -102,7 +106,7 @@ const columns = [
|
||||
{ label: '到达时间', prop: 'arriveTime', width: 170, parseTime: true },
|
||||
{ label: '到仓时间', prop: 'arriveStockTime', width: 170, parseTime: true },
|
||||
{ label: '清关时间', prop: 'clearanceTime', width: 170, parseTime: true },
|
||||
{ label: '操作', prop: 'action', align: 'center', width: 90, fixed: 'right', slot: 'action', showInToolbar: false, showOverflowTooltip: false }
|
||||
{ label: '操作', prop: 'action', align: 'center', width: 130, fixed: 'right', slot: 'action', showInToolbar: false, showOverflowTooltip: false }
|
||||
]
|
||||
|
||||
const searchColumns = [
|
||||
@@ -163,6 +167,10 @@ function queryList(params) {
|
||||
return listAirBill(normalizeQuery(params))
|
||||
}
|
||||
|
||||
function openFee(row) {
|
||||
feeRef.value?.open(row)
|
||||
}
|
||||
|
||||
function openDetail(row) {
|
||||
if (!row?.billCode) return
|
||||
open.value = true
|
||||
|
||||
@@ -0,0 +1,341 @@
|
||||
<template>
|
||||
<el-dialog v-model="visible" :title="title" width="980px" append-to-body destroy-on-close @closed="reset">
|
||||
<div v-loading="loading">
|
||||
<div class="fee-toolbar">
|
||||
<el-button type="primary" plain icon="Plus" @click="addRow">新增</el-button>
|
||||
<span class="fee-hint">只保存本次新增、修改和删除的费用,未改动的行不会重写。</span>
|
||||
</div>
|
||||
<el-table :data="rows" border size="small" max-height="320" row-key="rowKey" empty-text="暂无费用,点击新增">
|
||||
<el-table-column label="费用项" min-width="160">
|
||||
<template #default="{ row }">
|
||||
<el-select v-model="row.feeType" filterable clearable placeholder="请选择" style="width: 100%">
|
||||
<el-option v-for="item in bms_fee_type" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="价格" width="150">
|
||||
<template #default="{ row }">
|
||||
<el-input-number v-model="row.price" :min="0" :precision="4" :controls="false" :value-on-clear="null" placeholder="价格" style="width: 100%" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="收费币种" width="140">
|
||||
<template #default="{ row }">
|
||||
<el-select v-model="row.currency" filterable clearable placeholder="请选择" style="width: 100%">
|
||||
<el-option v-for="item in bms_currency" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" min-width="180">
|
||||
<template #default="{ row }">
|
||||
<el-input v-model="row.remark" maxlength="500" clearable placeholder="备注" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="80" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="danger" @click="removeRow(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div class="log-title">操作日志</div>
|
||||
<div v-loading="logLoading">
|
||||
<el-table :data="logs" border size="small" max-height="320" empty-text="暂无操作记录">
|
||||
<el-table-column label="操作时间" width="168">
|
||||
<template #default="{ row }">{{ logTime(row) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作类型" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag size="small" :type="logType(row.operationType)">{{ logAction(row.operationType) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="更改前" min-width="200">
|
||||
<template #default="{ row }">
|
||||
<div v-if="blankSide(row, 'old')" class="log-empty">空</div>
|
||||
<div v-else class="log-change">
|
||||
<div v-for="change in row.changes" :key="change.field">{{ change.fieldLabel || change.field }}:{{ showValue(change.oldValue) }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="更改后" min-width="200">
|
||||
<template #default="{ row }">
|
||||
<div v-if="blankSide(row, 'new')" class="log-empty">空</div>
|
||||
<div v-else class="log-change">
|
||||
<div v-for="change in row.changes" :key="change.field">{{ change.fieldLabel || change.field }}:{{ showValue(change.newValue) }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作人" width="110" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ row.createByName || row.createBy || '-' }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-pagination
|
||||
v-model:current-page="logPageNum"
|
||||
v-model:page-size="logPageSize"
|
||||
class="log-pager"
|
||||
:total="logTotal"
|
||||
:page-sizes="[10, 20, 50]"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
background
|
||||
small
|
||||
@current-change="loadLogs"
|
||||
@size-change="changeLogSize"
|
||||
/>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="visible = false">取消</el-button>
|
||||
<el-button type="primary" :disabled="!dirty" :loading="saving" @click="save">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { listAirBillFee, listAirBillFeeLog, saveAirBillFee } from '@/api/price/airBill'
|
||||
import { parseTime } from '@/utils/bms'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const { bms_fee_type, bms_currency } = useDict('bms_fee_type', 'bms_currency')
|
||||
|
||||
const visible = ref(false)
|
||||
const loading = ref(false)
|
||||
const logLoading = ref(false)
|
||||
const saving = ref(false)
|
||||
const billCode = ref('')
|
||||
const rows = ref([])
|
||||
const deletedIds = ref([])
|
||||
const logs = ref([])
|
||||
const logPageNum = ref(1)
|
||||
const logPageSize = ref(10)
|
||||
const logTotal = ref(0)
|
||||
let rowSeed = 0
|
||||
let logSeq = 0
|
||||
|
||||
const title = computed(() => billCode.value ? `提单费用 - ${billCode.value}` : '提单费用')
|
||||
const dirty = computed(() => deletedIds.value.length > 0 || rows.value.some(row => rowChanged(row)))
|
||||
|
||||
function open(row) {
|
||||
if (!row?.billCode) return
|
||||
billCode.value = row.billCode
|
||||
visible.value = true
|
||||
load()
|
||||
}
|
||||
|
||||
function reset() {
|
||||
if (visible.value) return
|
||||
billCode.value = ''
|
||||
rows.value = []
|
||||
deletedIds.value = []
|
||||
logs.value = []
|
||||
logPageNum.value = 1
|
||||
logPageSize.value = 10
|
||||
logTotal.value = 0
|
||||
logSeq += 1
|
||||
saving.value = false
|
||||
loading.value = false
|
||||
logLoading.value = false
|
||||
}
|
||||
|
||||
function load() {
|
||||
logPageNum.value = 1
|
||||
loadFees()
|
||||
loadLogs()
|
||||
}
|
||||
|
||||
function loadFees() {
|
||||
const code = billCode.value
|
||||
loading.value = true
|
||||
listAirBillFee(code).then(feeRes => {
|
||||
if (code !== billCode.value) return
|
||||
deletedIds.value = []
|
||||
rows.value = (feeRes.data || []).map(item => ({
|
||||
rowKey: `fee-${item.id}`,
|
||||
id: item.id,
|
||||
feeType: item.feeType == null ? '' : String(item.feeType),
|
||||
price: item.price === null || item.price === undefined ? null : Number(item.price),
|
||||
currency: item.currency || '',
|
||||
remark: item.remark || '',
|
||||
origin: snapshot(item)
|
||||
}))
|
||||
}).finally(() => {
|
||||
if (code === billCode.value) loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function unwrapLogs(res) {
|
||||
const rows = res?.rows
|
||||
if (Array.isArray(rows)) {
|
||||
const total = Number(res.total)
|
||||
return { list: rows, total: Number.isFinite(total) ? total : rows.length }
|
||||
}
|
||||
if (rows && Array.isArray(rows.list)) {
|
||||
const total = Number(rows.total ?? res.total)
|
||||
return { list: rows.list, total: Number.isFinite(total) ? total : rows.list.length }
|
||||
}
|
||||
if (Array.isArray(res?.data)) {
|
||||
return { list: res.data, total: res.data.length }
|
||||
}
|
||||
return { list: [], total: 0 }
|
||||
}
|
||||
|
||||
function loadLogs() {
|
||||
const code = billCode.value
|
||||
if (!code) return
|
||||
const seq = ++logSeq
|
||||
logLoading.value = true
|
||||
listAirBillFeeLog({
|
||||
billCode: code,
|
||||
pageNum: logPageNum.value,
|
||||
pageSize: logPageSize.value
|
||||
}).then(logRes => {
|
||||
if (seq !== logSeq || code !== billCode.value) return
|
||||
const page = unwrapLogs(logRes)
|
||||
logs.value = page.list
|
||||
logTotal.value = page.total
|
||||
}).finally(() => {
|
||||
if (seq === logSeq && code === billCode.value) logLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function changeLogSize() {
|
||||
logPageNum.value = 1
|
||||
loadLogs()
|
||||
}
|
||||
|
||||
function addRow() {
|
||||
rowSeed += 1
|
||||
rows.value.push({
|
||||
rowKey: `new-${rowSeed}`,
|
||||
id: null,
|
||||
feeType: '',
|
||||
price: null,
|
||||
currency: '',
|
||||
remark: '',
|
||||
origin: null
|
||||
})
|
||||
}
|
||||
|
||||
function removeRow(row) {
|
||||
if (row.id != null && !deletedIds.value.some(id => String(id) === String(row.id))) {
|
||||
deletedIds.value.push(row.id)
|
||||
}
|
||||
rows.value = rows.value.filter(item => item.rowKey !== row.rowKey)
|
||||
}
|
||||
|
||||
function snapshot(item) {
|
||||
return {
|
||||
feeType: item.feeType || '',
|
||||
price: normPrice(item.price),
|
||||
currency: item.currency || '',
|
||||
remark: (item.remark || '').trim()
|
||||
}
|
||||
}
|
||||
|
||||
function currentOf(row) {
|
||||
return {
|
||||
feeType: row.feeType || '',
|
||||
price: normPrice(row.price),
|
||||
currency: row.currency || '',
|
||||
remark: (row.remark || '').trim()
|
||||
}
|
||||
}
|
||||
|
||||
function normPrice(value) {
|
||||
if (value === null || value === undefined || value === '') return ''
|
||||
const number = Number(value)
|
||||
return Number.isNaN(number) ? String(value).trim() : String(number)
|
||||
}
|
||||
|
||||
function rowChanged(row) {
|
||||
if (!row.origin) {
|
||||
const now = currentOf(row)
|
||||
return !!(now.feeType || now.price || now.currency || now.remark)
|
||||
}
|
||||
const now = currentOf(row)
|
||||
return now.feeType !== row.origin.feeType || now.price !== row.origin.price
|
||||
|| now.currency !== row.origin.currency || now.remark !== row.origin.remark
|
||||
}
|
||||
|
||||
function save() {
|
||||
const inserts = []
|
||||
const updates = []
|
||||
rows.value.forEach(row => {
|
||||
if (!rowChanged(row)) return
|
||||
const payload = {
|
||||
feeType: row.feeType || '',
|
||||
price: row.price,
|
||||
currency: row.currency || '',
|
||||
remark: (row.remark || '').trim()
|
||||
}
|
||||
if (row.id) updates.push({ id: row.id, ...payload })
|
||||
else inserts.push(payload)
|
||||
})
|
||||
if (!inserts.length && !updates.length && !deletedIds.value.length) {
|
||||
proxy.$modal.msgError('没有需要保存的变更')
|
||||
return
|
||||
}
|
||||
saving.value = true
|
||||
saveAirBillFee({ billCode: billCode.value, inserts, updates, deletes: deletedIds.value }).then(res => {
|
||||
proxy.$modal.msgSuccess(res.msg || '保存成功')
|
||||
load()
|
||||
}).catch(() => {
|
||||
load()
|
||||
}).finally(() => {
|
||||
saving.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function logAction(type) {
|
||||
return { 1: '新增', 2: '修改', 3: '删除' }[String(type)] || '维护'
|
||||
}
|
||||
|
||||
function logType(type) {
|
||||
if (String(type) === '1') return 'success'
|
||||
if (String(type) === '3') return 'danger'
|
||||
return 'warning'
|
||||
}
|
||||
|
||||
function logTime(item) {
|
||||
return parseTime(item.createTime) || ''
|
||||
}
|
||||
|
||||
function showValue(value) {
|
||||
return value === null || value === undefined || value === '' ? '空' : value
|
||||
}
|
||||
|
||||
function blankSide(row, side) {
|
||||
const changes = row.changes || []
|
||||
if (!changes.length) return true
|
||||
const key = side === 'old' ? 'oldValue' : 'newValue'
|
||||
return changes.every(change => showValue(change[key]) === '空')
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.fee-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.fee-hint {
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 12px;
|
||||
}
|
||||
.log-title {
|
||||
margin: 16px 0 8px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.log-change {
|
||||
line-height: 1.7;
|
||||
white-space: normal;
|
||||
}
|
||||
.log-empty {
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
.log-pager {
|
||||
margin-top: 10px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user