feat(线缆管理): 新增向导式接线创建功能

- 新增四步向导流程,简化接线创建过程
- 添加线缆标签、颜色、安装信息等新字段
- 实现端口可视化面板和冲突检测功能
- 新增耗材日志设备关联字段
- 添加耗材导入后台任务管理
- 更新线缆管理文档和使用指南
This commit is contained in:
zhang1106
2026-03-31 14:34:34 +08:00
parent 5d972799f2
commit 06e3b56469
16 changed files with 3914 additions and 105 deletions
+174
View File
@@ -0,0 +1,174 @@
const { EventEmitter } = require('events');
const path = require('path');
const fs = require('fs');
class ImportJobManager extends EventEmitter {
constructor() {
super();
this.jobs = new Map();
this.JOB_STATUS = {
PENDING: 'pending',
PROCESSING: 'processing',
COMPLETED: 'completed',
FAILED: 'failed',
CANCELLED: 'cancelled',
};
}
createJob(jobId, type, totalItems) {
const job = {
jobId,
type,
status: this.JOB_STATUS.PENDING,
totalItems,
processedItems: 0,
successCount: 0,
failedCount: 0,
skippedCount: 0,
updatedCount: 0,
startTime: null,
endTime: null,
error: null,
canCancel: true,
result: null,
createdAt: new Date(),
};
this.jobs.set(jobId, job);
return job;
}
getJob(jobId) {
return this.jobs.get(jobId);
}
updateJob(jobId, updates) {
const job = this.jobs.get(jobId);
if (!job) return null;
Object.assign(job, updates);
this.emit('jobUpdate', job);
return job;
}
startJob(jobId) {
const job = this.jobs.get(jobId);
if (!job) return null;
job.status = this.JOB_STATUS.PROCESSING;
job.startTime = new Date();
this.emit('jobStart', job);
return job;
}
incrementProgress(jobId, success = 0, failed = 0, skipped = 0, updated = 0) {
const job = this.jobs.get(jobId);
if (!job) return null;
job.processedItems += success + failed + skipped + updated;
job.successCount += success;
job.failedCount += failed;
job.skippedCount += skipped;
job.updatedCount += updated;
this.emit('jobProgress', job);
return job;
}
completeJob(jobId, result = null) {
const job = this.jobs.get(jobId);
if (!job) return null;
job.status = this.JOB_STATUS.COMPLETED;
job.endTime = new Date();
job.canCancel = false;
job.result = result;
this.emit('jobComplete', job);
return job;
}
failJob(jobId, error) {
const job = this.jobs.get(jobId);
if (!job) return null;
job.status = this.JOB_STATUS.FAILED;
job.endTime = new Date();
job.canCancel = false;
job.error = error;
this.emit('jobFailed', job);
return job;
}
cancelJob(jobId) {
const job = this.jobs.get(jobId);
if (!job || !job.canCancel) return null;
job.status = this.JOB_STATUS.CANCELLED;
job.endTime = new Date();
job.canCancel = false;
this.emit('jobCancelled', job);
return job;
}
getJobProgress(jobId) {
const job = this.jobs.get(jobId);
if (!job) return null;
const progress = {
jobId: job.jobId,
status: job.status,
totalItems: job.totalItems,
processedItems: job.processedItems,
progressPercent:
job.totalItems > 0 ? Math.round((job.processedItems / job.totalItems) * 100) : 0,
successCount: job.successCount,
failedCount: job.failedCount,
skippedCount: job.skippedCount,
updatedCount: job.updatedCount,
canCancel: job.canCancel,
error: job.error,
startTime: job.startTime,
endTime: job.endTime,
elapsedTime: job.startTime
? Date.now() - new Date(job.startTime).getTime()
: null,
};
return progress;
}
listJobs() {
return Array.from(this.jobs.values()).map(job => ({
jobId: job.jobId,
type: job.type,
status: job.status,
totalItems: job.totalItems,
processedItems: job.processedItems,
progressPercent:
job.totalItems > 0 ? Math.round((job.processedItems / job.totalItems) * 100) : 0,
canCancel: job.canCancel,
startTime: job.startTime,
endTime: job.endTime,
createdAt: job.createdAt,
}));
}
cleanupOldJobs(maxAgeMs = 24 * 60 * 60 * 1000) {
const now = Date.now();
for (const [jobId, job] of this.jobs.entries()) {
const jobEndTime = job.endTime || job.createdAt;
if (now - new Date(jobEndTime).getTime() > maxAgeMs) {
this.jobs.delete(jobId);
}
}
}
}
const importJobManager = new ImportJobManager();
setInterval(() => {
importJobManager.cleanupOldJobs();
}, 60 * 60 * 1000);
module.exports = {
importJobManager,
ImportJobManager,
};