Files
smart-crop-ui/src/lib/changeTracker.ts

240 lines
6.0 KiB
TypeScript

// 变更追踪工具
import { MachineryRecord, MachineryChangeHistory } from '../types/machinery';
// 字段名称到中文标签的映射
export const FIELD_LABELS: Record<string, string> = {
// 基本信息
name: '设备名称',
model: '型号规格',
category: '农机类型',
usage: '使用场景',
manufacturer: '生产厂家',
manufactureDate: '出厂日期',
purchaseDate: '购买日期',
// 技术参数
engineNumber: '发动机号',
chassisNumber: '车架号',
power: '额定功率',
weight: '整机重量',
workingWidth: '工作幅宽',
// 购机信息
purchasePrice: '购机价格',
supplier: '供应商',
invoiceNumber: '发票号码',
invoiceUrl: '购机发票',
// 保险信息
insuranceCompany: '保险公司',
insurancePolicyNumber: '保单号',
insuranceStartDate: '保险起始日期',
insuranceEndDate: '保险结束日期',
insuranceAmount: '保险金额',
// 使用信息
status: '设备状态',
currentLocation: '当前位置',
operator: '操作人员',
department: '所属部门',
// 保养信息
maintenanceCycle: '保养周期',
maintenanceCycleUnit: '保养周期单位',
// 其他信息
remarks: '备注',
tags: '标签',
};
// 需要排除的字段(不记录变更)
const EXCLUDED_FIELDS = [
'id',
'qrCode',
'createdAt',
'updatedAt',
'createdBy',
'updatedBy',
];
/**
* 格式化字段值用于显示
*/
export function formatFieldValue(fieldName: string, value: any): string {
if (value === null || value === undefined || value === '') {
return '(空)';
}
// 日期字段
if (fieldName.includes('Date') || fieldName.includes('Time')) {
try {
return new Date(value).toLocaleDateString('zh-CN');
} catch {
return String(value);
}
}
// 金额字段
if (fieldName.includes('Price') || fieldName.includes('Amount')) {
const num = parseFloat(value);
return isNaN(num) ? String(value) : `¥${num.toLocaleString()}`;
}
// 保养周期单位
if (fieldName === 'maintenanceCycleUnit') {
const unitMap: Record<string, string> = {
day: '天',
month: '月',
year: '年'
};
return unitMap[value] || String(value);
}
// 数组字段(如标签)
if (Array.isArray(value)) {
return value.length > 0 ? value.join(', ') : '(空)';
}
// 对象字段
if (typeof value === 'object') {
return JSON.stringify(value);
}
// 布尔值
if (typeof value === 'boolean') {
return value ? '是' : '否';
}
return String(value);
}
/**
* 比较两个值是否相同
*/
function isEqual(oldValue: any, newValue: any): boolean {
// 处理null/undefined/空字符串的情况
const isOldEmpty = oldValue === null || oldValue === undefined || oldValue === '';
const isNewEmpty = newValue === null || newValue === undefined || newValue === '';
if (isOldEmpty && isNewEmpty) return true;
if (isOldEmpty !== isNewEmpty) return false;
// 数组比较
if (Array.isArray(oldValue) && Array.isArray(newValue)) {
if (oldValue.length !== newValue.length) return false;
return oldValue.every((val, index) => val === newValue[index]);
}
// 对象比较
if (typeof oldValue === 'object' && typeof newValue === 'object') {
return JSON.stringify(oldValue) === JSON.stringify(newValue);
}
// 基本类型比较
return oldValue === newValue;
}
/**
* 追踪农机档案的变更
* @param oldRecord 旧记录
* @param newRecord 新记录
* @param operator 操作人
* @returns 变更历史记录数组
*/
export function trackMachineryChanges(
oldRecord: MachineryRecord | undefined,
newRecord: Partial<MachineryRecord>,
operator: string
): MachineryChangeHistory[] {
const changes: MachineryChangeHistory[] = [];
const timestamp = new Date().toISOString();
// 如果是新建,不记录变更
if (!oldRecord) {
return [];
}
// 遍历新记录的所有字段
Object.keys(newRecord).forEach((fieldName) => {
// 跳过排除的字段
if (EXCLUDED_FIELDS.includes(fieldName)) {
return;
}
const oldValue = (oldRecord as any)[fieldName];
const newValue = (newRecord as any)[fieldName];
// 检查值是否发生变化
if (!isEqual(oldValue, newValue)) {
const fieldLabel = FIELD_LABELS[fieldName] || fieldName;
changes.push({
id: `change-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
machineryId: oldRecord.id,
fieldName,
fieldLabel,
oldValue,
newValue,
operator,
operatedAt: timestamp,
});
}
});
return changes;
}
/**
* 获取字段的变更描述
*/
export function getChangeDescription(change: MachineryChangeHistory): string {
const oldValueStr = formatFieldValue(change.fieldName, change.oldValue);
const newValueStr = formatFieldValue(change.fieldName, change.newValue);
return `${change.fieldLabel} 从 "${oldValueStr}" 修改为 "${newValueStr}"`;
}
/**
* 按日期分组变更记录
*/
export function groupChangesByDate(
changes: MachineryChangeHistory[]
): Record<string, MachineryChangeHistory[]> {
const grouped: Record<string, MachineryChangeHistory[]> = {};
changes.forEach(change => {
const date = new Date(change.operatedAt).toLocaleDateString('zh-CN');
if (!grouped[date]) {
grouped[date] = [];
}
grouped[date].push(change);
});
return grouped;
}
/**
* 获取变更统计信息
*/
export function getChangeStats(changes: MachineryChangeHistory[]) {
const stats = {
total: changes.length,
byField: {} as Record<string, number>,
byOperator: {} as Record<string, number>,
recentChanges: changes
.sort((a, b) => new Date(b.operatedAt).getTime() - new Date(a.operatedAt).getTime())
.slice(0, 5),
};
changes.forEach(change => {
// 按字段统计
const field = change.fieldLabel;
stats.byField[field] = (stats.byField[field] || 0) + 1;
// 按操作人统计
stats.byOperator[change.operator] = (stats.byOperator[change.operator] || 0) + 1;
});
return stats;
}