feat(线缆管理): 新增向导式接线创建功能
- 新增四步向导流程,简化接线创建过程 - 添加线缆标签、颜色、安装信息等新字段 - 实现端口可视化面板和冲突检测功能 - 新增耗材日志设备关联字段 - 添加耗材导入后台任务管理 - 更新线缆管理文档和使用指南
This commit is contained in:
@@ -459,7 +459,16 @@ router.post('/quick-inout', async (req, res) => {
|
||||
while (attempt < RETRY.MAX_RETRIES) {
|
||||
const transaction = await sequelize.transaction();
|
||||
try {
|
||||
const { consumableId, type, quantity, operator, reason, notes, snList } = req.body;
|
||||
const {
|
||||
consumableId,
|
||||
type,
|
||||
quantity,
|
||||
operator,
|
||||
reason,
|
||||
notes,
|
||||
snList,
|
||||
deviceId,
|
||||
} = req.body;
|
||||
|
||||
const consumable = await Consumable.findByPk(consumableId, { transaction });
|
||||
if (!consumable) {
|
||||
@@ -467,6 +476,39 @@ router.post('/quick-inout', async (req, res) => {
|
||||
return res.status(404).json({ error: '耗材不存在' });
|
||||
}
|
||||
|
||||
let deviceInfo = {};
|
||||
if (deviceId && type === 'out') {
|
||||
const Device = require('../models/Device');
|
||||
const Rack = require('../models/Rack');
|
||||
const Room = require('../models/Room');
|
||||
|
||||
const device = await Device.findByPk(deviceId, { transaction });
|
||||
if (device) {
|
||||
deviceInfo = {
|
||||
deviceId: device.deviceId,
|
||||
deviceName: device.name,
|
||||
rackId: device.rackId,
|
||||
rackName: null,
|
||||
roomId: null,
|
||||
roomName: null,
|
||||
};
|
||||
|
||||
if (device.rackId) {
|
||||
const rack = await Rack.findByPk(device.rackId, { transaction });
|
||||
if (rack) {
|
||||
deviceInfo.rackName = rack.name;
|
||||
if (rack.roomId) {
|
||||
const room = await Room.findByPk(rack.roomId, { transaction });
|
||||
if (room) {
|
||||
deviceInfo.roomId = room.roomId;
|
||||
deviceInfo.roomName = room.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const previousStock = parseFloat(consumable.currentStock);
|
||||
let newStock;
|
||||
const currentSnList = consumable.snList || [];
|
||||
@@ -554,6 +596,12 @@ router.post('/quick-inout', async (req, res) => {
|
||||
notes,
|
||||
isEditable: false,
|
||||
snList: operationSnList,
|
||||
deviceId: deviceInfo.deviceId || null,
|
||||
deviceName: deviceInfo.deviceName || null,
|
||||
rackId: deviceInfo.rackId || null,
|
||||
rackName: deviceInfo.rackName || null,
|
||||
roomId: deviceInfo.roomId || null,
|
||||
roomName: deviceInfo.roomName || null,
|
||||
consumableSnapshot: {
|
||||
category: consumable.category,
|
||||
unit: consumable.unit,
|
||||
@@ -571,6 +619,7 @@ router.post('/quick-inout', async (req, res) => {
|
||||
message: '操作成功',
|
||||
record,
|
||||
consumable: await Consumable.findByPk(consumableId),
|
||||
deviceInfo: Object.keys(deviceInfo).length > 0 ? deviceInfo : null,
|
||||
});
|
||||
return;
|
||||
} catch (error) {
|
||||
@@ -589,7 +638,18 @@ router.post('/inout', async (req, res) => {
|
||||
while (attempt < RETRY.MAX_RETRIES) {
|
||||
const transaction = await sequelize.transaction();
|
||||
try {
|
||||
const { consumableId, type, quantity, operator, reason, recipient, notes, snList } = req.body;
|
||||
const {
|
||||
consumableId,
|
||||
type,
|
||||
quantity,
|
||||
operator,
|
||||
reason,
|
||||
recipient,
|
||||
notes,
|
||||
snList,
|
||||
deviceId,
|
||||
deviceName,
|
||||
} = req.body;
|
||||
|
||||
const consumable = await Consumable.findByPk(consumableId, { transaction });
|
||||
if (!consumable) {
|
||||
@@ -597,6 +657,39 @@ router.post('/inout', async (req, res) => {
|
||||
return res.status(404).json({ error: '耗材不存在' });
|
||||
}
|
||||
|
||||
let deviceInfo = {};
|
||||
if (deviceId && type === 'out') {
|
||||
const Device = require('../models/Device');
|
||||
const Rack = require('../models/Rack');
|
||||
const Room = require('../models/Room');
|
||||
|
||||
const device = await Device.findByPk(deviceId, { transaction });
|
||||
if (device) {
|
||||
deviceInfo = {
|
||||
deviceId: device.deviceId,
|
||||
deviceName: device.name,
|
||||
rackId: device.rackId,
|
||||
rackName: null,
|
||||
roomId: null,
|
||||
roomName: null,
|
||||
};
|
||||
|
||||
if (device.rackId) {
|
||||
const rack = await Rack.findByPk(device.rackId, { transaction });
|
||||
if (rack) {
|
||||
deviceInfo.rackName = rack.name;
|
||||
if (rack.roomId) {
|
||||
const room = await Room.findByPk(rack.roomId, { transaction });
|
||||
if (room) {
|
||||
deviceInfo.roomId = room.roomId;
|
||||
deviceInfo.roomName = room.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const previousStock = parseFloat(consumable.currentStock);
|
||||
let newStock;
|
||||
const currentSnList = consumable.snList || [];
|
||||
@@ -682,6 +775,12 @@ router.post('/inout', async (req, res) => {
|
||||
notes,
|
||||
isEditable: false,
|
||||
snList: operationSnList,
|
||||
deviceId: deviceInfo.deviceId || null,
|
||||
deviceName: deviceInfo.deviceName || null,
|
||||
rackId: deviceInfo.rackId || null,
|
||||
rackName: deviceInfo.rackName || null,
|
||||
roomId: deviceInfo.roomId || null,
|
||||
roomName: deviceInfo.roomName || null,
|
||||
consumableSnapshot: {
|
||||
category: consumable.category,
|
||||
unit: consumable.unit,
|
||||
@@ -699,6 +798,7 @@ router.post('/inout', async (req, res) => {
|
||||
message: '操作成功',
|
||||
record,
|
||||
consumable: await Consumable.findByPk(consumableId),
|
||||
deviceInfo: Object.keys(deviceInfo).length > 0 ? deviceInfo : null,
|
||||
});
|
||||
return;
|
||||
} catch (error) {
|
||||
@@ -1314,4 +1414,97 @@ router.get('/logs/:id/history', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/devices/search', async (req, res) => {
|
||||
try {
|
||||
const { keyword, limit = 20 } = req.query;
|
||||
|
||||
if (!keyword) {
|
||||
return res.json({ devices: [] });
|
||||
}
|
||||
|
||||
const Device = require('../models/Device');
|
||||
const Rack = require('../models/Rack');
|
||||
const Room = require('../models/Room');
|
||||
|
||||
const escapedKeyword = keyword.replace(/'/g, "''");
|
||||
|
||||
const devices = await Device.findAll({
|
||||
where: {
|
||||
[Op.or]: [
|
||||
{ deviceId: { [Op.like]: `%${escapedKeyword}%` } },
|
||||
{ name: { [Op.like]: `%${escapedKeyword}%` } },
|
||||
{ serialNumber: { [Op.like]: `%${escapedKeyword}%` } },
|
||||
],
|
||||
},
|
||||
include: [
|
||||
{
|
||||
model: Rack,
|
||||
as: 'rack',
|
||||
include: [{ model: Room, as: 'room' }],
|
||||
},
|
||||
],
|
||||
limit: parseInt(limit),
|
||||
order: [['name', 'ASC']],
|
||||
});
|
||||
|
||||
const result = devices.map(device => ({
|
||||
deviceId: device.deviceId,
|
||||
name: device.name,
|
||||
type: device.type,
|
||||
model: device.model,
|
||||
serialNumber: device.serialNumber,
|
||||
status: device.status,
|
||||
location: device.rack
|
||||
? {
|
||||
rackId: device.rack.rackId,
|
||||
rackName: device.rack.name,
|
||||
roomId: device.rack.room ? device.rack.room.roomId : null,
|
||||
roomName: device.rack.room ? device.rack.room.name : null,
|
||||
}
|
||||
: null,
|
||||
}));
|
||||
|
||||
res.json({ devices: result });
|
||||
} catch (error) {
|
||||
console.error('搜索设备失败:', error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/devices/by-sn/:sn', async (req, res) => {
|
||||
try {
|
||||
const { sn } = req.params;
|
||||
const Device = require('../models/Device');
|
||||
|
||||
const device = await Device.findOne({
|
||||
where: {
|
||||
[Op.or]: [
|
||||
{ deviceId: { [Op.like]: `%${sn}%` } },
|
||||
{ serialNumber: { [Op.like]: `%${sn}%` } },
|
||||
{ name: { [Op.like]: `%${sn}%` } },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
if (!device) {
|
||||
return res.json({ found: false, device: null });
|
||||
}
|
||||
|
||||
res.json({
|
||||
found: true,
|
||||
device: {
|
||||
deviceId: device.deviceId,
|
||||
name: device.name,
|
||||
type: device.type,
|
||||
model: device.model,
|
||||
serialNumber: device.serialNumber,
|
||||
status: device.status,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('查询设备失败:', error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
||||
Reference in New Issue
Block a user