生产管理系统前端 - 更新瓦力提交的产品原型到参考目录
This commit is contained in:
239
src/lib/changeTracker.ts
Normal file
239
src/lib/changeTracker.ts
Normal file
@@ -0,0 +1,239 @@
|
||||
// 变更追踪工具
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user