feat(工单): 支持手动输入设备信息或从列表选择
添加设备来源选项,允许用户在创建工单时选择从设备列表选择或手动输入设备信息 后端接口适配处理两种设备来源方式 添加terser依赖用于前端代码压缩优化
This commit is contained in:
@@ -269,6 +269,8 @@ router.post('/', async (req, res) => {
|
|||||||
try {
|
try {
|
||||||
const {
|
const {
|
||||||
deviceId,
|
deviceId,
|
||||||
|
deviceName,
|
||||||
|
serialNumber,
|
||||||
faultCategory,
|
faultCategory,
|
||||||
faultSubCategory,
|
faultSubCategory,
|
||||||
priority,
|
priority,
|
||||||
@@ -279,23 +281,44 @@ router.post('/', async (req, res) => {
|
|||||||
tags
|
tags
|
||||||
} = req.body;
|
} = req.body;
|
||||||
|
|
||||||
// 获取设备信息
|
let device = null;
|
||||||
const device = await Device.findByPk(deviceId, {
|
let ticketDeviceName = '';
|
||||||
|
let ticketDeviceModel = '';
|
||||||
|
let ticketSerialNumber = '';
|
||||||
|
let ticketLocation = '';
|
||||||
|
|
||||||
|
// 判断是选择设备还是手动输入
|
||||||
|
if (deviceId) {
|
||||||
|
// 从设备列表选择
|
||||||
|
device = await Device.findByPk(deviceId, {
|
||||||
include: [{ model: require('../models/Rack') }]
|
include: [{ model: require('../models/Rack') }]
|
||||||
});
|
});
|
||||||
if (!device) {
|
if (!device) {
|
||||||
return res.status(404).json({ error: '设备不存在' });
|
return res.status(404).json({ error: '设备不存在' });
|
||||||
}
|
}
|
||||||
|
ticketDeviceName = device.name;
|
||||||
|
ticketDeviceModel = device.model;
|
||||||
|
ticketSerialNumber = device.serialNumber;
|
||||||
|
ticketLocation = device.Rack ? `${device.Rack.name}` : '未知位置';
|
||||||
|
} else if (deviceName && serialNumber) {
|
||||||
|
// 手动输入设备信息
|
||||||
|
ticketDeviceName = deviceName;
|
||||||
|
ticketSerialNumber = serialNumber;
|
||||||
|
ticketDeviceModel = '';
|
||||||
|
ticketLocation = '未知位置';
|
||||||
|
} else {
|
||||||
|
return res.status(400).json({ error: '请选择设备或手动输入设备信息' });
|
||||||
|
}
|
||||||
|
|
||||||
const ticketId = `TKT${Date.now().toString(36).toUpperCase()}${Math.random().toString(36).substr(2, 4).toUpperCase()}`;
|
const ticketId = `TKT${Date.now().toString(36).toUpperCase()}${Math.random().toString(36).substr(2, 4).toUpperCase()}`;
|
||||||
|
|
||||||
const ticket = await Ticket.create({
|
const ticket = await Ticket.create({
|
||||||
ticketId,
|
ticketId,
|
||||||
title: title || `${faultCategory} - ${device.name}`,
|
title: title || `${faultCategory} - ${ticketDeviceName}`,
|
||||||
deviceId,
|
deviceId: deviceId || null,
|
||||||
deviceName: device.name,
|
deviceName: ticketDeviceName,
|
||||||
deviceModel: device.model,
|
deviceModel: ticketDeviceModel,
|
||||||
serialNumber: device.serialNumber,
|
serialNumber: ticketSerialNumber,
|
||||||
faultCategory,
|
faultCategory,
|
||||||
faultSubCategory,
|
faultSubCategory,
|
||||||
priority: priority || 'medium',
|
priority: priority || 'medium',
|
||||||
@@ -303,7 +326,7 @@ router.post('/', async (req, res) => {
|
|||||||
expectedCompletionDate: expectedCompletionDate ? new Date(expectedCompletionDate) : null,
|
expectedCompletionDate: expectedCompletionDate ? new Date(expectedCompletionDate) : null,
|
||||||
reporterId: req.body.reporterId || 'USER001',
|
reporterId: req.body.reporterId || 'USER001',
|
||||||
reporterName: req.body.reporterName || '系统用户',
|
reporterName: req.body.reporterName || '系统用户',
|
||||||
location: device.Rack ? `${device.Rack.name}` : '未知位置',
|
location: ticketLocation,
|
||||||
attachments: attachments || [],
|
attachments: attachments || [],
|
||||||
tags: tags || [],
|
tags: tags || [],
|
||||||
status: 'pending'
|
status: 'pending'
|
||||||
|
|||||||
Generated
+80
@@ -24,6 +24,7 @@
|
|||||||
"@testing-library/user-event": "^14.6.1",
|
"@testing-library/user-event": "^14.6.1",
|
||||||
"@vitejs/plugin-react": "^4.0.3",
|
"@vitejs/plugin-react": "^4.0.3",
|
||||||
"jsdom": "^27.3.0",
|
"jsdom": "^27.3.0",
|
||||||
|
"terser": "^5.44.1",
|
||||||
"vite": "^4.4.9",
|
"vite": "^4.4.9",
|
||||||
"vitest": "^4.0.16"
|
"vitest": "^4.0.16"
|
||||||
}
|
}
|
||||||
@@ -1126,6 +1127,17 @@
|
|||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@jridgewell/source-map": {
|
||||||
|
"version": "0.3.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz",
|
||||||
|
"integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/gen-mapping": "^0.3.5",
|
||||||
|
"@jridgewell/trace-mapping": "^0.3.25"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@jridgewell/sourcemap-codec": {
|
"node_modules/@jridgewell/sourcemap-codec": {
|
||||||
"version": "1.5.5",
|
"version": "1.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
||||||
@@ -1908,6 +1920,19 @@
|
|||||||
"url": "https://opencollective.com/vitest"
|
"url": "https://opencollective.com/vitest"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/acorn": {
|
||||||
|
"version": "8.15.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||||
|
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"acorn": "bin/acorn"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/adler-32": {
|
"node_modules/adler-32": {
|
||||||
"version": "1.3.1",
|
"version": "1.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz",
|
||||||
@@ -2127,6 +2152,13 @@
|
|||||||
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/buffer-from": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/call-bind-apply-helpers": {
|
"node_modules/call-bind-apply-helpers": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||||
@@ -2220,6 +2252,13 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/commander": {
|
||||||
|
"version": "2.20.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||||
|
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/compute-scroll-into-view": {
|
"node_modules/compute-scroll-into-view": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.1.tgz",
|
||||||
@@ -3890,6 +3929,16 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/source-map": {
|
||||||
|
"version": "0.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||||
|
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/source-map-js": {
|
"node_modules/source-map-js": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||||
@@ -3900,6 +3949,17 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/source-map-support": {
|
||||||
|
"version": "0.5.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
|
||||||
|
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"buffer-from": "^1.0.0",
|
||||||
|
"source-map": "^0.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ssf": {
|
"node_modules/ssf": {
|
||||||
"version": "0.11.2",
|
"version": "0.11.2",
|
||||||
"resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz",
|
"resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz",
|
||||||
@@ -3958,6 +4018,26 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/terser": {
|
||||||
|
"version": "5.44.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/terser/-/terser-5.44.1.tgz",
|
||||||
|
"integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/source-map": "^0.3.3",
|
||||||
|
"acorn": "^8.15.0",
|
||||||
|
"commander": "^2.20.0",
|
||||||
|
"source-map-support": "~0.5.20"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"terser": "bin/terser"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/three": {
|
"node_modules/three": {
|
||||||
"version": "0.160.1",
|
"version": "0.160.1",
|
||||||
"resolved": "https://registry.npmjs.org/three/-/three-0.160.1.tgz",
|
"resolved": "https://registry.npmjs.org/three/-/three-0.160.1.tgz",
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
"@testing-library/user-event": "^14.6.1",
|
"@testing-library/user-event": "^14.6.1",
|
||||||
"@vitejs/plugin-react": "^4.0.3",
|
"@vitejs/plugin-react": "^4.0.3",
|
||||||
"jsdom": "^27.3.0",
|
"jsdom": "^27.3.0",
|
||||||
|
"terser": "^5.44.1",
|
||||||
"vite": "^4.4.9",
|
"vite": "^4.4.9",
|
||||||
"vitest": "^4.0.16"
|
"vitest": "^4.0.16"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ function TicketManagement() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const [searchFilters, setSearchFilters] = useState({});
|
const [searchFilters, setSearchFilters] = useState({});
|
||||||
|
const [deviceSource, setDeviceSource] = useState('select');
|
||||||
|
|
||||||
const fetchTickets = useCallback(async (page = 1, pageSize = 10, filters = {}) => {
|
const fetchTickets = useCallback(async (page = 1, pageSize = 10, filters = {}) => {
|
||||||
try {
|
try {
|
||||||
@@ -148,9 +149,18 @@ function TicketManagement() {
|
|||||||
if (ticketData.completionDate) {
|
if (ticketData.completionDate) {
|
||||||
ticketData.completionDate = dayjs(ticketData.completionDate);
|
ticketData.completionDate = dayjs(ticketData.completionDate);
|
||||||
}
|
}
|
||||||
|
if (ticket.deviceId) {
|
||||||
|
setDeviceSource('select');
|
||||||
|
ticketData.deviceId = ticket.deviceId;
|
||||||
|
} else {
|
||||||
|
setDeviceSource('manual');
|
||||||
|
ticketData.deviceName = ticket.deviceName;
|
||||||
|
ticketData.serialNumber = ticket.serialNumber;
|
||||||
|
}
|
||||||
form.setFieldsValue(ticketData);
|
form.setFieldsValue(ticketData);
|
||||||
} else {
|
} else {
|
||||||
form.resetFields();
|
form.resetFields();
|
||||||
|
setDeviceSource('select');
|
||||||
}
|
}
|
||||||
setModalVisible(true);
|
setModalVisible(true);
|
||||||
}, []);
|
}, []);
|
||||||
@@ -168,6 +178,12 @@ function TicketManagement() {
|
|||||||
completionDate: values.completionDate ? values.completionDate.format('YYYY-MM-DD HH:mm:ss') : null
|
completionDate: values.completionDate ? values.completionDate.format('YYYY-MM-DD HH:mm:ss') : null
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (deviceSource === 'manual') {
|
||||||
|
ticketData.deviceId = null;
|
||||||
|
ticketData.deviceName = values.deviceName;
|
||||||
|
ticketData.serialNumber = values.serialNumber;
|
||||||
|
}
|
||||||
|
|
||||||
if (editingTicket) {
|
if (editingTicket) {
|
||||||
await axios.put(`/api/tickets/${editingTicket.ticketId}`, ticketData);
|
await axios.put(`/api/tickets/${editingTicket.ticketId}`, ticketData);
|
||||||
message.success('工单更新成功');
|
message.success('工单更新成功');
|
||||||
@@ -182,11 +198,12 @@ function TicketManagement() {
|
|||||||
setModalVisible(false);
|
setModalVisible(false);
|
||||||
fetchTickets();
|
fetchTickets();
|
||||||
setEditingTicket(null);
|
setEditingTicket(null);
|
||||||
|
setDeviceSource('select');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message.error(editingTicket ? '工单更新失败' : '工单创建失败');
|
message.error(editingTicket ? '工单更新失败' : '工单创建失败');
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
}, [editingTicket, fetchTickets]);
|
}, [editingTicket, fetchTickets, deviceSource]);
|
||||||
|
|
||||||
const handleDelete = useCallback(async (ticketId) => {
|
const handleDelete = useCallback(async (ticketId) => {
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
@@ -473,6 +490,14 @@ function TicketManagement() {
|
|||||||
<Input placeholder="请输入工单标题" />
|
<Input placeholder="请输入工单标题" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item label="设备来源">
|
||||||
|
<Select value={deviceSource} onChange={setDeviceSource} style={{ width: 200 }}>
|
||||||
|
<Option value="select">从设备列表选择</Option>
|
||||||
|
<Option value="manual">手动输入设备信息</Option>
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
{deviceSource === 'select' ? (
|
||||||
<Form.Item name="deviceId" label="关联设备" rules={[{ required: true }]}>
|
<Form.Item name="deviceId" label="关联设备" rules={[{ required: true }]}>
|
||||||
<Select placeholder="选择设备" showSearch optionFilterProp="children">
|
<Select placeholder="选择设备" showSearch optionFilterProp="children">
|
||||||
{devices.map(device => (
|
{devices.map(device => (
|
||||||
@@ -482,6 +507,16 @@ function TicketManagement() {
|
|||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Form.Item name="deviceName" label="设备名称" rules={[{ required: true }]}>
|
||||||
|
<Input placeholder="请输入设备名称" />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="serialNumber" label="设备序列号" rules={[{ required: true }]}>
|
||||||
|
<Input placeholder="请输入设备序列号" />
|
||||||
|
</Form.Item>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
<Form.Item name="faultCategory" label="故障分类" rules={[{ required: true }]}>
|
<Form.Item name="faultCategory" label="故障分类" rules={[{ required: true }]}>
|
||||||
<Select placeholder="选择故障分类">
|
<Select placeholder="选择故障分类">
|
||||||
|
|||||||
Reference in New Issue
Block a user