客户管理记录修改日志

This commit is contained in:
luojiaquan
2026-09-30 16:00:06 +08:00
parent 998aac74c8
commit 0145b24193
10 changed files with 652 additions and 6 deletions
@@ -15,6 +15,7 @@ import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
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;
@@ -73,6 +74,16 @@ public class BmsCustomerController extends BaseController
return success(bmsCustomerService.selectBmsCustomerByCustomerId(customerId));
}
/**
* 查询客户业务操作日志(新建/修改/删除及字段级变更明细)
*/
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:query')")
@GetMapping("/log/list")
public AjaxResult logList(@RequestParam("customerId") Long customerId)
{
return success(bmsCustomerService.selectCustomerLogList(customerId));
}
/**
* 新增客户信息
*/
@@ -0,0 +1,80 @@
package com.bms.customer.customerinfo.domain;
import java.util.List;
import lombok.Data;
import com.bms.common.core.domain.BaseEntity;
/**
* 客户业务操作日志对象 ctr_customer_log
*
* @author bms
* @date 2026-09-30
*/
@Data
public class BmsCustomerLog extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 操作类型:新建 */
public static final String OP_INSERT = "1";
/** 操作类型:修改 */
public static final String OP_UPDATE = "2";
/** 操作类型:删除 */
public static final String OP_DELETE = "3";
/** 日志ID */
private Long logId;
/** 客户ID */
private Long customerId;
/** 客户编码(操作时快照) */
private String customerCode;
/** 客户名称(操作时快照) */
private String customerName;
/** 操作类型(1新建 2修改 3删除) */
private String operationType;
/** 变更明细JSON(落库内容):[{field,fieldLabel,oldValue,newValue}] */
private String changeContent;
/** 操作人姓名 */
private String createByName;
/** 变更明细(由 changeContent 解析,供前端直接展示) */
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,30 @@
package com.bms.customer.customerinfo.mapper;
import java.util.List;
import com.bms.customer.customerinfo.domain.BmsCustomerLog;
/**
* 客户业务操作日志Mapper接口
*
* @author bms
* @date 2026-09-30
*/
public interface BmsCustomerLogMapper
{
/**
* 新增客户操作日志
*
* @param customerLog 日志
* @return 结果
*/
public int insertBmsCustomerLog(BmsCustomerLog customerLog);
/**
* 按客户查询全部业务日志(时间倒序)
*
* @param customerId 客户ID
* @return 日志列表
*/
public List<BmsCustomerLog> selectBmsCustomerLogList(Long customerId);
}
@@ -0,0 +1,35 @@
package com.bms.customer.customerinfo.service;
import java.util.List;
import com.bms.customer.customerinfo.domain.BmsCustomer;
import com.bms.customer.customerinfo.domain.BmsCustomerLog;
/**
* 客户业务操作日志Service接口
*
* @author bms
* @date 2026-09-30
*/
public interface IBmsCustomerLogService
{
/**
* 记录新建客户日志(含建档时的字段初始值)
*/
void recordInsert(BmsCustomer customer);
/**
* 记录修改客户日志(字段级 修改前→修改后,无实际变更时不记录)
*/
void recordUpdate(BmsCustomer oldCustomer, BmsCustomer newCustomer);
/**
* 记录删除客户日志
*/
void recordDelete(BmsCustomer customer);
/**
* 查询某客户的全部业务日志(时间倒序,含已解析的变更明细)
*/
List<BmsCustomerLog> selectByCustomerId(Long customerId);
}
@@ -5,6 +5,7 @@ import com.bms.customer.customerinfo.domain.BmsCustomer;
import com.bms.customer.customerinfo.domain.BmsCustomerAttachment;
import com.bms.customer.customerinfo.domain.BmsCustomerCompany;
import com.bms.customer.customerinfo.domain.BmsCustomerContact;
import com.bms.customer.customerinfo.domain.BmsCustomerLog;
/**
* 客户主Service接口
@@ -22,6 +23,14 @@ public interface IBmsCustomerService
*/
public BmsCustomer selectBmsCustomerByCustomerId(Long customerId);
/**
* 查询某客户的全部业务操作日志(时间倒序)
*
* @param customerId 客户ID
* @return 业务日志列表
*/
public List<BmsCustomerLog> selectCustomerLogList(Long customerId);
/**
* 查询客户主列表
*
@@ -0,0 +1,263 @@
package com.bms.customer.customerinfo.service.impl;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import com.alibaba.fastjson2.JSON;
import com.bms.common.core.domain.entity.SysUser;
import com.bms.common.utils.DateUtils;
import com.bms.common.utils.SecurityUtils;
import com.bms.customer.customerinfo.domain.BmsCustomer;
import com.bms.customer.customerinfo.domain.BmsCustomerLog;
import com.bms.customer.customerinfo.mapper.BmsCustomerLogMapper;
import com.bms.customer.customerinfo.service.IBmsCustomerLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* 客户业务操作日志Service实现
*
* @author bms
* @date 2026-09-30
*/
@Service
public class BmsCustomerLogServiceImpl implements IBmsCustomerLogService
{
@Autowired
private BmsCustomerLogMapper bmsCustomerLogMapper;
/** 业务类型字典 */
private static final Map<String, String> CUSTOMER_TYPE_MAP = Map.of(
"0", "直客", "1", "同行", "2", "集运");
/** 启用状态字典 */
private static final Map<String, String> ENABLE_MAP = Map.of("0", "正常", "1", "停用");
/** 结算方式字典 */
private static final Map<String, String> SETTLEMENT_TYPE_MAP = Map.of(
"0", "月结", "1", "周结", "2", "日结");
/**
* 需要记录日志的主表字段(顺序即展示顺序)。
* requireNonBlank 为 true 的字段对应 update SQL 的 &lt;if test="x!=null and x!=''"&gt;,
* 其余动态字段仅在新值非 null 时落库;业务员/结算员/客服为无条件写入。
*/
private static final List<FieldDef> FIELD_DEFS = List.of(
new FieldDef("customerCode", "客户编码", BmsCustomer::getCustomerCode, true, Function.identity()),
new FieldDef("customerName", "客户名称", BmsCustomer::getCustomerName, true, Function.identity()),
new FieldDef("customerCodeDw", "对外代码", BmsCustomer::getCustomerCodeDw, false, Function.identity()),
new FieldDef("creditCode", "统一社会信用代码", BmsCustomer::getCreditCode, false, Function.identity()),
new FieldDef("customerType", "业务类型", BmsCustomer::getCustomerType, false, CUSTOMER_TYPE_MAP::get),
new FieldDef("isEnable", "启用状态", BmsCustomer::getIsEnable, false, ENABLE_MAP::get),
new FieldDef("salesUserId", "业务员", c -> userDisplay(c.getSalesUserId(), c.getSalesUser()), false, Function.identity()),
new FieldDef("ed", "资金额度(元)", BmsCustomer::getEd, false, BmsCustomerLogServiceImpl::numText),
new FieldDef("zq", "回款账期(天)", BmsCustomer::getZq, false, BmsCustomerLogServiceImpl::numText),
new FieldDef("settlementUserId", "结算员", c -> userDisplay(c.getSettlementUserId(), c.getSettlementUser()), false, Function.identity()),
new FieldDef("serviceUserId", "客服", c -> userDisplay(c.getServiceUserId(), c.getServiceUser()), false, Function.identity()),
new FieldDef("settlementType", "结算方式", BmsCustomer::getSettlementType, false, SETTLEMENT_TYPE_MAP::get),
new FieldDef("accountBalance", "账户余额(元)", BmsCustomer::getAccountBalance, false, BmsCustomerLogServiceImpl::numText),
new FieldDef("foreignCurrencyBalance", "外币余额(元)", BmsCustomer::getForeignCurrencyBalance, false, BmsCustomerLogServiceImpl::numText),
new FieldDef("weightThreshold", "重量阈值(kg)", BmsCustomer::getWeightThreshold, false, BmsCustomerLogServiceImpl::numText),
new FieldDef("throwRatio", "抛比(%)", BmsCustomer::getThrowRatio, false, BmsCustomerLogServiceImpl::numText),
new FieldDef("productCodes", "绑定产品", BmsCustomer::getProductCodes, false, Function.identity()),
new FieldDef("remark", "备注", BmsCustomer::getRemark, false, Function.identity()));
@Override
public void recordInsert(BmsCustomer customer)
{
if (customer == null || customer.getCustomerId() == null)
{
return;
}
List<BmsCustomerLog.FieldChange> changes = new ArrayList<>();
for (FieldDef def : FIELD_DEFS)
{
String value = def.display(customer);
if (value != null && !value.isEmpty())
{
changes.add(new BmsCustomerLog.FieldChange(def.field, def.label, "", value));
}
}
saveLog(customer, BmsCustomerLog.OP_INSERT, changes);
}
@Override
public void recordUpdate(BmsCustomer oldCustomer, BmsCustomer newCustomer)
{
if (oldCustomer == null || newCustomer == null || newCustomer.getCustomerId() == null)
{
return;
}
List<BmsCustomerLog.FieldChange> changes = new ArrayList<>();
for (FieldDef def : FIELD_DEFS)
{
// 与 update SQL 的动态条件保持一致:未提交(null/空串)的字段本次不落库,不参与比对
Object raw = def.rawGetter.apply(newCustomer);
if (raw == null)
{
continue;
}
if (def.requireNonBlank && raw.toString().trim().isEmpty())
{
continue;
}
String oldValue = def.display(oldCustomer);
String newValue = def.display(newCustomer);
if (!sameValue(def.rawGetter.apply(oldCustomer), raw))
{
changes.add(new BmsCustomerLog.FieldChange(def.field, def.label,
oldValue == null ? "" : oldValue, newValue == null ? "" : newValue));
}
}
if (changes.isEmpty())
{
return;
}
// 日志快照以修改后的客户编码/名称为准
saveLog(newCustomer, BmsCustomerLog.OP_UPDATE, changes);
}
@Override
public void recordDelete(BmsCustomer customer)
{
if (customer == null || customer.getCustomerId() == null)
{
return;
}
saveLog(customer, BmsCustomerLog.OP_DELETE, null);
}
@Override
public List<BmsCustomerLog> selectByCustomerId(Long customerId)
{
List<BmsCustomerLog> list = bmsCustomerLogMapper.selectBmsCustomerLogList(customerId);
for (BmsCustomerLog log : list)
{
if (log.getChangeContent() != null && !log.getChangeContent().isEmpty())
{
log.setChanges(JSON.parseArray(log.getChangeContent(), BmsCustomerLog.FieldChange.class));
}
}
return list;
}
private void saveLog(BmsCustomer customer, String operationType, List<BmsCustomerLog.FieldChange> changes)
{
BmsCustomerLog log = new BmsCustomerLog();
log.setCustomerId(customer.getCustomerId());
log.setCustomerCode(customer.getCustomerCode());
log.setCustomerName(customer.getCustomerName());
log.setOperationType(operationType);
if (changes != null)
{
log.setChangeContent(JSON.toJSONString(changes));
}
try
{
SysUser user = SecurityUtils.getLoginUser().getUser();
log.setCreateBy(user.getUserName());
log.setCreateByName(user.getNickName());
}
catch (Exception ignored)
{
// 无登录上下文(如系统任务)时不阻断业务
}
log.setCreateTime(DateUtils.getNowDate());
bmsCustomerLogMapper.insertBmsCustomerLog(log);
}
/** 人员字段展示:姓名(工号),缺姓名时只显示工号 */
private static String userDisplay(String code, String name)
{
String c = trimToNull(code);
String n = trimToNull(name);
if (c == null && n == null)
{
return "";
}
if (c == null)
{
return n;
}
if (n == null || n.equals(c))
{
return c;
}
return n + "(" + c + ")";
}
private static String numText(Object num)
{
return num == null ? "" : num.toString();
}
private static String trimToNull(String s)
{
if (s == null)
{
return null;
}
String t = s.trim();
return t.isEmpty() ? null : t;
}
/** 数值按大小比较(规避 BigDecimal 精度差异),其余按字符串比较 */
private static boolean sameValue(Object a, Object b)
{
if (a instanceof BigDecimal && b instanceof BigDecimal)
{
return ((BigDecimal) a).compareTo((BigDecimal) b) == 0;
}
return Objects.equals(a == null ? null : a.toString(), b == null ? null : b.toString());
}
/**
* 字段定义
*/
private static final class FieldDef
{
private final String field;
private final String label;
/** 取原始值(用于变更判断) */
private final Function<BmsCustomer, Object> rawGetter;
/** 动态写入时是否要求非空串(对应 update SQL 的 != '' 判断) */
private final boolean requireNonBlank;
/** 原始值 -> 字典翻译(查不到字典时回退原值) */
private final Function<String, String> dictTranslator;
private FieldDef(String field, String label, Function<BmsCustomer, ?> rawGetter,
boolean requireNonBlank, Function<String, String> dictTranslator)
{
this.field = field;
this.label = label;
this.rawGetter = rawGetter::apply;
this.requireNonBlank = requireNonBlank;
this.dictTranslator = dictTranslator;
}
/** 取日志展示值:人员/数字类直接是文本;字典类翻译失败回退原值 */
private String display(BmsCustomer c)
{
Object raw = rawGetter.apply(c);
if (raw == null)
{
return "";
}
String text = raw.toString();
if (text.isEmpty())
{
return "";
}
if (dictTranslator == null)
{
return text;
}
String translated = dictTranslator.apply(text);
return translated != null ? translated : text;
}
}
}
@@ -1,5 +1,6 @@
package com.bms.customer.customerinfo.service.impl;
import java.util.ArrayList;
import java.util.List;
import com.bms.common.annotation.DataScope;
@@ -14,6 +15,8 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.bms.customer.customerinfo.mapper.BmsCustomerMapper;
import com.bms.customer.customerinfo.domain.BmsCustomer;
import com.bms.customer.customerinfo.domain.BmsCustomerLog;
import com.bms.customer.customerinfo.service.IBmsCustomerLogService;
import com.bms.customer.customerinfo.service.IBmsCustomerService;
import com.bms.customer.customermoney.service.ICustomerMoneyService;
@@ -32,6 +35,9 @@ public class BmsCustomerServiceImpl implements IBmsCustomerService
@Autowired
private ICustomerMoneyService customerMoneyService;
@Autowired
private IBmsCustomerLogService customerLogService;
/**
* 查询客户主
*
@@ -58,9 +64,18 @@ public class BmsCustomerServiceImpl implements IBmsCustomerService
return bmsCustomer;
}
/**
* 查询某客户的全部业务操作日志(时间倒序)
*/
@Override
public List<BmsCustomerLog> selectCustomerLogList(Long customerId)
{
return customerLogService.selectByCustomerId(customerId);
}
/**
* 查询客户主列表
*
*
* @param bmsCustomer 客户主
* @return 客户主
*/
@@ -235,6 +250,8 @@ public class BmsCustomerServiceImpl implements IBmsCustomerService
saveBmsCustomerContacts(bmsCustomer.getCustomerId(), bmsCustomer.getContactList());
// 新建客户即初始化默认CNY账户:余额0、总额度1000 CNY,写INIT流水
customerMoneyService.initDefaultCnyAccount(bmsCustomer.getCustomerId());
// 业务日志:新建(含建档初始值)
customerLogService.recordInsert(bmsCustomer);
}
return rows;
}
@@ -252,6 +269,8 @@ public class BmsCustomerServiceImpl implements IBmsCustomerService
SysUser user = SecurityUtils.getLoginUser().getUser();
bmsCustomer.setUpdateTime(DateUtils.getNowDate());
bmsCustomer.setUpdateBy(user.getUserName());
// 修改前先取旧值快照,用于业务日志记录字段级变更
BmsCustomer oldCustomer = bmsCustomerMapper.selectBmsCustomerByCustomerId(bmsCustomer.getCustomerId());
// 仅重建客户档案模块的附件,收款水单等其他业务模块的附件保留不动
bmsCustomerMapper.deleteBmsCustomerProfileAttachmentByCustomerId(bmsCustomer.getCustomerId());
if (bmsCustomer.getAttachmentList() != null)
@@ -266,7 +285,13 @@ public class BmsCustomerServiceImpl implements IBmsCustomerService
saveBmsCustomerCompanies(bmsCustomer.getCustomerId(), bmsCustomer.getCompanyList());
// 联系人采用增量 upsert
saveBmsCustomerContacts(bmsCustomer.getCustomerId(), bmsCustomer.getContactList());
return bmsCustomerMapper.updateBmsCustomer(bmsCustomer);
int rows = bmsCustomerMapper.updateBmsCustomer(bmsCustomer);
// 业务日志:修改(字段级 修改前→修改后;无实际变更时不记录)
if (rows > 0)
{
customerLogService.recordUpdate(oldCustomer, bmsCustomer);
}
return rows;
}
/**
@@ -279,13 +304,31 @@ public class BmsCustomerServiceImpl implements IBmsCustomerService
@Override
public int deleteBmsCustomerByCustomerIds(Long[] customerIds)
{
// 删除前取客户快照,用于业务日志
List<BmsCustomer> snapshotList = new ArrayList<>();
for (Long customerId : customerIds)
{
BmsCustomer snapshot = bmsCustomerMapper.selectBmsCustomerByCustomerId(customerId);
if (snapshot != null)
{
snapshotList.add(snapshot);
}
}
for (Long customerId : customerIds)
{
bmsCustomerMapper.deleteBmsCustomerAttachmentByCustomerId(customerId);
bmsCustomerMapper.deleteBmsCustomerCompanyByCustomerId(customerId);
bmsCustomerMapper.deleteBmsCustomerContactByCustomerId(customerId);
}
return bmsCustomerMapper.deleteBmsCustomerByCustomerIds(customerIds);
int rows = bmsCustomerMapper.deleteBmsCustomerByCustomerIds(customerIds);
if (rows > 0)
{
for (BmsCustomer snapshot : snapshotList)
{
customerLogService.recordDelete(snapshot);
}
}
return rows;
}
/**
@@ -298,10 +341,16 @@ public class BmsCustomerServiceImpl implements IBmsCustomerService
@Override
public int deleteBmsCustomerByCustomerId(Long customerId)
{
BmsCustomer snapshot = bmsCustomerMapper.selectBmsCustomerByCustomerId(customerId);
bmsCustomerMapper.deleteBmsCustomerAttachmentByCustomerId(customerId);
bmsCustomerMapper.deleteBmsCustomerCompanyByCustomerId(customerId);
bmsCustomerMapper.deleteBmsCustomerContactByCustomerId(customerId);
return bmsCustomerMapper.deleteBmsCustomerByCustomerId(customerId);
int rows = bmsCustomerMapper.deleteBmsCustomerByCustomerId(customerId);
if (rows > 0 && snapshot != null)
{
customerLogService.recordDelete(snapshot);
}
return rows;
}
/**
@@ -0,0 +1,54 @@
<?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.customer.customerinfo.mapper.BmsCustomerLogMapper">
<resultMap type="com.bms.customer.customerinfo.domain.BmsCustomerLog" id="BmsCustomerLogResult">
<result property="logId" column="log_id" />
<result property="customerId" column="customer_id" />
<result property="customerCode" column="customer_code" />
<result property="customerName" column="customer_name" />
<result property="operationType" column="operation_type" />
<result property="changeContent" column="change_content" />
<result property="createBy" column="create_by" />
<result property="createByName" column="create_by_name" />
<result property="createTime" column="create_time" />
</resultMap>
<sql id="selectBmsCustomerLogVo">
select log_id, customer_id, customer_code, customer_name, operation_type, change_content,
create_by, create_by_name, create_time
from ctr_customer_log
</sql>
<insert id="insertBmsCustomerLog" parameterType="com.bms.customer.customerinfo.domain.BmsCustomerLog"
useGeneratedKeys="true" keyProperty="logId">
insert into ctr_customer_log
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="customerId != null">customer_id,</if>
<if test="customerCode != null">customer_code,</if>
<if test="customerName != null">customer_name,</if>
<if test="operationType != null">operation_type,</if>
<if test="changeContent != null">change_content,</if>
<if test="createBy != null">create_by,</if>
<if test="createByName != null">create_by_name,</if>
<if test="createTime != null">create_time,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="customerId != null">#{customerId},</if>
<if test="customerCode != null">#{customerCode},</if>
<if test="customerName != null">#{customerName},</if>
<if test="operationType != null">#{operationType},</if>
<if test="changeContent != null">#{changeContent},</if>
<if test="createBy != null">#{createBy},</if>
<if test="createByName != null">#{createByName},</if>
<if test="createTime != null">#{createTime},</if>
</trim>
</insert>
<select id="selectBmsCustomerLogList" parameterType="Long" resultMap="BmsCustomerLogResult">
<include refid="selectBmsCustomerLogVo"/>
where customer_id = #{customerId}
order by create_time desc, log_id desc
</select>
</mapper>
@@ -17,6 +17,15 @@ export function getCustomer(customerId) {
})
}
// 查询客户业务操作日志
export function listCustomerLog(customerId) {
return request({
url: '/customersupplier/customerinfo/log/list',
method: 'get',
params: { customerId }
})
}
// 新增客户主
export function addCustomer(data) {
return request({
@@ -85,6 +85,16 @@
>客户余额</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="info"
plain
icon="Document"
:disabled="single"
@click="handleViewLog()"
v-hasPermi="['customersupplier:customerinfo:query']"
>日志</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
@@ -167,11 +177,12 @@
</template>
</el-table-column>
<el-table-column v-if="isColVisible('remark')" label="备注说明" align="center" prop="remark" :show-overflow-tooltip="true" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="240" fixed="right">
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="290" fixed="right">
<template #default="scope">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['customersupplier:customerinfo:edit']">修改</el-button>
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['customersupplier:customerinfo:remove']">删除</el-button>
<el-button link type="primary" icon="Wallet" @click="handleCustomerMoney(scope.row)">客户余额</el-button>
<el-button link type="primary" icon="Document" @click="handleViewLog(scope.row)" v-hasPermi="['customersupplier:customerinfo:query']">日志</el-button>
</template>
</el-table-column>
@@ -184,11 +195,62 @@
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
<!-- 客户业务操作日志弹窗 -->
<el-dialog v-model="logOpen" :title="`业务日志 - ${logCustomer.customerName || ''}`" width="820px" append-to-body>
<div style="margin-bottom: 10px; color: #909399; font-size: 13px;">
客户编码:{{ logCustomer.customerCode || '-' }}
<span style="margin-left: 16px;">共 {{ logList.length }} 条日志(按操作时间倒序)</span>
</div>
<el-table v-loading="logLoading" :data="logList" border size="small" row-key="logId" :empty-text="'暂无业务日志'">
<el-table-column type="expand">
<template #default="scope">
<div style="padding: 8px 16px; background: #fafafa;">
<div v-if="!scope.row.changes || !scope.row.changes.length" style="color:#909399;">无字段明细</div>
<el-table v-else :data="scope.row.changes" border size="small">
<el-table-column label="字段" prop="fieldLabel" width="180" align="center" />
<el-table-column label="修改前" align="center">
<template #default="cell">
<span :style="{ color: cell.row.oldValue === '' ? '#909399' : '' }">{{ cell.row.oldValue === '' ? '(空)' : cell.row.oldValue }}</span>
</template>
</el-table-column>
<el-table-column label="修改后" align="center">
<template #default="cell">
<span :style="{ color: cell.row.newValue === '' ? '#909399' : '' }">{{ cell.row.newValue === '' ? '(空)' : cell.row.newValue }}</span>
</template>
</el-table-column>
</el-table>
</div>
</template>
</el-table-column>
<el-table-column label="操作类型" align="center" width="90">
<template #default="scope">
<el-tag :type="opTagType(scope.row.operationType)" size="small">{{ opLabel(scope.row.operationType) }}</el-tag>
</template>
</el-table-column>
<el-table-column label="操作人" align="center" width="150">
<template #default="scope">{{ scope.row.createByName || scope.row.createBy || '-' }}</template>
</el-table-column>
<el-table-column label="操作时间" align="center" width="170">
<template #default="scope">{{ fmtTime(scope.row.createTime) }}</template>
</el-table-column>
<el-table-column label="变更字段" align="center">
<template #default="scope">
<span v-if="scope.row.operationType === '1'">新建客户,录入 {{ scope.row.changes?.length || 0 }} 项信息</span>
<span v-else-if="scope.row.operationType === '2'">{{ changeSummary(scope.row.changes) }}</span>
<span v-else style="color:#f56c6c;">客户被删除</span>
</template>
</el-table-column>
</el-table>
<template #footer>
<el-button @click="logOpen = false">关 闭</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup name="Customer">
import { listCustomer, delCustomer } from "@/api/customersuppliermanagement/customerinfo/customer.js"
import { listCustomer, delCustomer, listCustomerLog } from "@/api/customersuppliermanagement/customerinfo/customer.js"
import { useProducts, parseProductCodes } from '@/components/ProductSelect/useProducts'
import { useColumnSettings } from '@/utils/columnSettings'
@@ -359,5 +421,49 @@ function handleExport() {
}, `customer_${new Date().getTime()}.xlsx`)
}
/** ========== 客户业务操作日志 ========== */
const logOpen = ref(false)
const logLoading = ref(false)
const logList = ref([])
const logCustomer = ref({})
const OP_META = {
'1': { label: '新建', tag: 'success' },
'2': { label: '修改', tag: 'warning' },
'3': { label: '删除', tag: 'danger' }
}
function opLabel(type) {
return OP_META[type]?.label || '未知'
}
function opTagType(type) {
return OP_META[type]?.tag || 'info'
}
function fmtTime(t) {
return t ? proxy.parseTime(t, '{y}-{m}-{d} {h}:{i}:{s}') : '-'
}
/** 修改日志的变更字段摘要 */
function changeSummary(changes) {
if (!changes || !changes.length) return '-'
return changes.map(c => c.fieldLabel).join('、')
}
/** 打开业务日志弹窗(操作列传 row;工具栏按钮取勾选行) */
function handleViewLog(row) {
const target = (row && row.customerId) ? row : selectedRows.value[0]
if (!target || !target.customerId) {
proxy.$modal.msgWarning("请先勾选一个客户")
return
}
logCustomer.value = { customerId: target.customerId, customerName: target.customerName, customerCode: target.customerCode }
logList.value = []
logOpen.value = true
logLoading.value = true
listCustomerLog(target.customerId).then(res => {
logList.value = res.data || []
}).finally(() => {
logLoading.value = false
})
}
getList()
</script>