refactor(设备管理): 移除批量移动和批量下线功能
前端移除批量移动和批量下线相关UI组件和逻辑 后端优化批量移动和批量状态变更接口实现
This commit is contained in:
+194
-54
@@ -564,6 +564,162 @@ router.post('/import', async (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 批量上线设备
|
||||||
|
router.put('/batch-online', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { deviceIds } = req.body;
|
||||||
|
|
||||||
|
if (!deviceIds || !Array.isArray(deviceIds) || deviceIds.length === 0) {
|
||||||
|
return res.status(400).json({ error: '请提供有效的设备ID列表' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新设备状态为运行中
|
||||||
|
const [affectedCount] = await Device.update(
|
||||||
|
{ status: 'running' },
|
||||||
|
{ where: { deviceId: { [Op.in]: deviceIds } } }
|
||||||
|
);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
message: `批量上线成功,已更新 ${affectedCount} 个设备`,
|
||||||
|
affectedCount
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 批量下线设备
|
||||||
|
router.put('/batch-offline', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { deviceIds } = req.body;
|
||||||
|
|
||||||
|
if (!deviceIds || !Array.isArray(deviceIds) || deviceIds.length === 0) {
|
||||||
|
return res.status(400).json({ error: '请提供有效的设备ID列表' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新设备状态为离线
|
||||||
|
const [affectedCount] = await Device.update(
|
||||||
|
{ status: 'offline' },
|
||||||
|
{ where: { deviceId: { [Op.in]: deviceIds } } }
|
||||||
|
);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
message: `批量下线成功,已更新 ${affectedCount} 个设备`,
|
||||||
|
affectedCount
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 批量变更设备状态
|
||||||
|
router.put('/batch-status', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { deviceIds, status } = req.body;
|
||||||
|
|
||||||
|
console.log('批量状态变更请求:', { deviceIds, status });
|
||||||
|
|
||||||
|
if (!deviceIds || !Array.isArray(deviceIds) || deviceIds.length === 0) {
|
||||||
|
return res.status(400).json({ error: '请提供有效的设备ID列表' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查数据库中是否存在这些设备
|
||||||
|
const existingDevices = await Device.findAll({
|
||||||
|
where: { deviceId: { [Op.in]: deviceIds } },
|
||||||
|
attributes: ['deviceId']
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('数据库中找到的设备:', existingDevices.map(d => d.deviceId));
|
||||||
|
console.log('请求的设备ID:', deviceIds);
|
||||||
|
|
||||||
|
// 检查是否有不存在的设备
|
||||||
|
const existingIds = existingDevices.map(d => d.deviceId);
|
||||||
|
const missingIds = deviceIds.filter(id => !existingIds.includes(id));
|
||||||
|
|
||||||
|
if (missingIds.length > 0) {
|
||||||
|
console.log('不存在的设备ID:', missingIds);
|
||||||
|
return res.status(404).json({ error: `设备不存在: ${missingIds.join(', ')}` });
|
||||||
|
}
|
||||||
|
|
||||||
|
const validStatus = ['running', 'maintenance', 'offline', 'fault'];
|
||||||
|
if (!validStatus.includes(status)) {
|
||||||
|
return res.status(400).json({
|
||||||
|
error: `状态值无效,有效值为:${validStatus.join('、')}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 状态映射
|
||||||
|
const statusText = {
|
||||||
|
running: '运行中',
|
||||||
|
maintenance: '维护中',
|
||||||
|
offline: '离线',
|
||||||
|
fault: '故障'
|
||||||
|
};
|
||||||
|
|
||||||
|
// 更新设备状态
|
||||||
|
const [affectedCount] = await Device.update(
|
||||||
|
{ status },
|
||||||
|
{ where: { deviceId: { [Op.in]: deviceIds } } }
|
||||||
|
);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
message: `批量状态变更成功,已将 ${affectedCount} 个设备状态变更为"${statusText[status]}"`,
|
||||||
|
affectedCount,
|
||||||
|
newStatus: status
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 批量移动设备
|
||||||
|
router.put('/batch-move', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { deviceIds, targetRackId, startPosition } = req.body;
|
||||||
|
|
||||||
|
if (!deviceIds || !Array.isArray(deviceIds) || deviceIds.length === 0) {
|
||||||
|
return res.status(400).json({ error: '请提供有效的设备ID列表' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!targetRackId) {
|
||||||
|
return res.status(400).json({ error: '请提供目标机柜ID' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证目标机柜是否存在
|
||||||
|
const targetRack = await Rack.findByPk(targetRackId);
|
||||||
|
if (!targetRack) {
|
||||||
|
return res.status(404).json({ error: '目标机柜不存在' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量更新设备位置
|
||||||
|
let movedCount = 0;
|
||||||
|
for (let i = 0; i < deviceIds.length; i++) {
|
||||||
|
const deviceId = deviceIds[i];
|
||||||
|
const position = startPosition ? startPosition + i : undefined;
|
||||||
|
|
||||||
|
const updateData = { rackId: targetRackId };
|
||||||
|
if (position) {
|
||||||
|
updateData.position = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [updated] = await Device.update(updateData, {
|
||||||
|
where: { deviceId }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (updated) {
|
||||||
|
movedCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
message: `批量移动成功,已将 ${movedCount} 个设备移动到机柜 ${targetRackId}`,
|
||||||
|
movedCount
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// 获取单个设备
|
// 获取单个设备
|
||||||
router.get('/:deviceId', async (req, res) => {
|
router.get('/:deviceId', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
@@ -773,10 +929,30 @@ router.put('/batch-status', async (req, res) => {
|
|||||||
try {
|
try {
|
||||||
const { deviceIds, status } = req.body;
|
const { deviceIds, status } = req.body;
|
||||||
|
|
||||||
|
console.log('批量状态变更请求:', { deviceIds, status });
|
||||||
|
|
||||||
if (!deviceIds || !Array.isArray(deviceIds) || deviceIds.length === 0) {
|
if (!deviceIds || !Array.isArray(deviceIds) || deviceIds.length === 0) {
|
||||||
return res.status(400).json({ error: '请提供有效的设备ID列表' });
|
return res.status(400).json({ error: '请提供有效的设备ID列表' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查数据库中是否存在这些设备
|
||||||
|
const existingDevices = await Device.findAll({
|
||||||
|
where: { deviceId: { [Op.in]: deviceIds } },
|
||||||
|
attributes: ['deviceId']
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('数据库中找到的设备:', existingDevices.map(d => d.deviceId));
|
||||||
|
console.log('请求的设备ID:', deviceIds);
|
||||||
|
|
||||||
|
// 检查是否有不存在的设备
|
||||||
|
const existingIds = existingDevices.map(d => d.deviceId);
|
||||||
|
const missingIds = deviceIds.filter(id => !existingIds.includes(id));
|
||||||
|
|
||||||
|
if (missingIds.length > 0) {
|
||||||
|
console.log('不存在的设备ID:', missingIds);
|
||||||
|
return res.status(404).json({ error: `设备不存在: ${missingIds.join(', ')}` });
|
||||||
|
}
|
||||||
|
|
||||||
const validStatus = ['running', 'maintenance', 'offline', 'fault'];
|
const validStatus = ['running', 'maintenance', 'offline', 'fault'];
|
||||||
if (!validStatus.includes(status)) {
|
if (!validStatus.includes(status)) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
@@ -841,73 +1017,37 @@ router.put('/batch-move', async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const movedDevices = [];
|
const movedDevices = [];
|
||||||
let currentPosition = parseInt(startPosition);
|
let currentPosition = startPosition;
|
||||||
|
|
||||||
// 按原位置排序设备
|
|
||||||
devices.sort((a, b) => a.position - b.position);
|
|
||||||
|
|
||||||
for (const device of devices) {
|
for (const device of devices) {
|
||||||
// 计算新位置
|
const oldRackId = device.rackId;
|
||||||
const newPosition = currentPosition;
|
const oldPosition = device.position;
|
||||||
|
const deviceHeight = device.height || 1;
|
||||||
|
|
||||||
// 验证位置是否在机柜范围内
|
const [updated] = await Device.update(
|
||||||
if (newPosition < 1 || newPosition > targetRack.height) {
|
{
|
||||||
return res.status(400).json({
|
|
||||||
error: `设备 ${device.name} 的位置 ${newPosition} 超出机柜高度范围(1-${targetRack.height})`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查目标位置是否被其他设备占用(排除自身)
|
|
||||||
const existingDevice = await Device.findOne({
|
|
||||||
where: {
|
|
||||||
rackId: targetRackId,
|
rackId: targetRackId,
|
||||||
position: newPosition,
|
position: currentPosition
|
||||||
deviceId: { [Op.ne]: device.deviceId }
|
},
|
||||||
}
|
{ where: { deviceId: device.deviceId } }
|
||||||
});
|
);
|
||||||
|
|
||||||
if (existingDevice) {
|
|
||||||
return res.status(400).json({
|
|
||||||
error: `机柜 ${targetRack.name} 的位置 ${newPosition} 已被设备 ${existingDevice.name} 占用`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 计算功率变化
|
|
||||||
const oldPower = device.powerConsumption;
|
|
||||||
|
|
||||||
// 更新设备位置
|
|
||||||
await device.update({
|
|
||||||
rackId: targetRackId,
|
|
||||||
position: newPosition
|
|
||||||
});
|
|
||||||
|
|
||||||
// 更新原机柜和新机柜的功率
|
|
||||||
const oldRack = await Rack.findByPk(device.rackId);
|
|
||||||
if (oldRack) {
|
|
||||||
await oldRack.update({
|
|
||||||
currentPower: Math.max(0, oldRack.currentPower - oldPower)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await targetRack.update({
|
|
||||||
currentPower: targetRack.currentPower + oldPower
|
|
||||||
});
|
|
||||||
|
|
||||||
|
if (updated) {
|
||||||
movedDevices.push({
|
movedDevices.push({
|
||||||
deviceId: device.deviceId,
|
deviceId: device.deviceId,
|
||||||
name: device.name,
|
name: device.name,
|
||||||
oldRackId: device.rackId,
|
oldRackId,
|
||||||
|
oldPosition,
|
||||||
newRackId: targetRackId,
|
newRackId: targetRackId,
|
||||||
oldPosition: device.position,
|
newPosition: currentPosition
|
||||||
newPosition: newPosition
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 下一个设备的起始位置 = 当前设备位置 + 当前设备高度
|
currentPosition += deviceHeight;
|
||||||
currentPosition += device.height;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
message: `批量移动成功,已移动 ${movedDevices.length} 个设备`,
|
message: `批量移动成功,已将 ${movedDevices.length} 个设备移动到机柜 ${targetRackId}`,
|
||||||
movedDevices
|
movedDevices
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -915,7 +1055,7 @@ router.put('/batch-move', async (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 增强导出设备数据(支持自定义字段选择和格式选择)
|
// 增强导出设备数据(支持自定义字段)
|
||||||
router.get('/enhanced-export', async (req, res) => {
|
router.get('/enhanced-export', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { deviceIds, format = 'csv', fields, fieldLabels } = req.query;
|
const { deviceIds, format = 'csv', fields, fieldLabels } = req.query;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react';
|
import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react';
|
||||||
import { Table, Button, Modal, Form, Input, Select, DatePicker, message, Card, Space, InputNumber, Switch, Upload, Progress, Checkbox, Spin, Dropdown, Tooltip } from 'antd';
|
import { Table, Button, Modal, Form, Input, Select, DatePicker, message, Card, Space, InputNumber, Switch, Upload, Progress, Checkbox, Spin, Dropdown, Tooltip } from 'antd';
|
||||||
import { PlusOutlined, EditOutlined, DeleteOutlined, SearchOutlined, UploadOutlined, DownloadOutlined, SettingOutlined, UndoOutlined, CloudServerOutlined, SwapOutlined, SafetyOutlined, DatabaseOutlined, AppstoreOutlined, MoreOutlined, ReloadOutlined, ExportOutlined, DragOutlined, FileExcelOutlined } from '@ant-design/icons';
|
import { PlusOutlined, EditOutlined, DeleteOutlined, SearchOutlined, UploadOutlined, DownloadOutlined, SettingOutlined, UndoOutlined, CloudServerOutlined, SafetyOutlined, DatabaseOutlined, AppstoreOutlined, MoreOutlined, ReloadOutlined, ExportOutlined, FileExcelOutlined, SwapOutlined } from '@ant-design/icons';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
@@ -210,11 +210,6 @@ function DeviceManagement() {
|
|||||||
const [batchStatusLoading, setBatchStatusLoading] = useState(false);
|
const [batchStatusLoading, setBatchStatusLoading] = useState(false);
|
||||||
const [batchStatusForm] = Form.useForm();
|
const [batchStatusForm] = Form.useForm();
|
||||||
|
|
||||||
// 批量移动模态框
|
|
||||||
const [batchMoveModalVisible, setBatchMoveModalVisible] = useState(false);
|
|
||||||
const [batchMoveLoading, setBatchMoveLoading] = useState(false);
|
|
||||||
const [batchMoveForm] = Form.useForm();
|
|
||||||
|
|
||||||
// 导出选项模态框
|
// 导出选项模态框
|
||||||
const [exportModalVisible, setExportModalVisible] = useState(false);
|
const [exportModalVisible, setExportModalVisible] = useState(false);
|
||||||
const [exportFormat, setExportFormat] = useState('csv');
|
const [exportFormat, setExportFormat] = useState('csv');
|
||||||
@@ -614,31 +609,6 @@ function DeviceManagement() {
|
|||||||
fetchDevices(newPagination.current, newPagination.pageSize);
|
fetchDevices(newPagination.current, newPagination.pageSize);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 批量下线设备
|
|
||||||
const handleBatchOffline = async () => {
|
|
||||||
Modal.confirm({
|
|
||||||
title: '批量下线确认',
|
|
||||||
content: `确定要将选中的 ${selectedDevices.length} 个设备下线吗?`,
|
|
||||||
okText: '确认下线',
|
|
||||||
okType: 'primary',
|
|
||||||
cancelText: '取消',
|
|
||||||
onOk: async () => {
|
|
||||||
try {
|
|
||||||
const response = await axios.put('/api/devices/batch-offline', {
|
|
||||||
deviceIds: selectedDevices
|
|
||||||
});
|
|
||||||
message.success(response.data.message || '批量下线成功');
|
|
||||||
setSelectedDevices([]);
|
|
||||||
setSelectAll(false);
|
|
||||||
fetchDevices();
|
|
||||||
} catch (error) {
|
|
||||||
message.error('批量下线失败');
|
|
||||||
console.error('批量下线设备失败:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// 批量删除设备
|
// 批量删除设备
|
||||||
const handleBatchDelete = async () => {
|
const handleBatchDelete = async () => {
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
@@ -728,45 +698,6 @@ function DeviceManagement() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 打开批量移动模态框
|
|
||||||
const showBatchMoveModal = () => {
|
|
||||||
if (selectedDevices.length === 0) {
|
|
||||||
message.warning('请先选择要移动的设备');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
batchMoveForm.resetFields();
|
|
||||||
setBatchMoveModalVisible(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 执行批量移动
|
|
||||||
const handleBatchMove = async () => {
|
|
||||||
try {
|
|
||||||
const values = await batchMoveForm.validateFields();
|
|
||||||
setBatchMoveLoading(true);
|
|
||||||
|
|
||||||
const response = await axios.put('/api/devices/batch-move', {
|
|
||||||
deviceIds: selectedDevices,
|
|
||||||
targetRackId: values.targetRackId,
|
|
||||||
startPosition: values.startPosition
|
|
||||||
});
|
|
||||||
|
|
||||||
message.success(response.data.message || '批量移动成功');
|
|
||||||
setBatchMoveModalVisible(false);
|
|
||||||
setSelectedDevices([]);
|
|
||||||
setSelectAll(false);
|
|
||||||
fetchDevices();
|
|
||||||
fetchRacks();
|
|
||||||
} catch (error) {
|
|
||||||
if (error.errorFields) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
message.error('批量移动失败');
|
|
||||||
console.error('批量移动失败:', error);
|
|
||||||
} finally {
|
|
||||||
setBatchMoveLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 打开导出选项模态框
|
// 打开导出选项模态框
|
||||||
const showExportModal = () => {
|
const showExportModal = () => {
|
||||||
if (selectedDevices.length === 0) {
|
if (selectedDevices.length === 0) {
|
||||||
@@ -1449,18 +1380,6 @@ function DeviceManagement() {
|
|||||||
>
|
>
|
||||||
状态变更 ({selectedDevices.length})
|
状态变更 ({selectedDevices.length})
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
|
||||||
style={{
|
|
||||||
...secondaryButtonStyle,
|
|
||||||
color: selectedDevices.length > 0 ? '#722ed1' : undefined,
|
|
||||||
borderColor: selectedDevices.length > 0 ? '#722ed1' : undefined
|
|
||||||
}}
|
|
||||||
icon={<DragOutlined />}
|
|
||||||
disabled={selectedDevices.length === 0}
|
|
||||||
onClick={showBatchMoveModal}
|
|
||||||
>
|
|
||||||
批量移动 ({selectedDevices.length})
|
|
||||||
</Button>
|
|
||||||
<Button
|
<Button
|
||||||
style={{
|
style={{
|
||||||
...secondaryButtonStyle,
|
...secondaryButtonStyle,
|
||||||
@@ -1474,19 +1393,6 @@ function DeviceManagement() {
|
|||||||
>
|
>
|
||||||
批量删除 ({selectedDevices.length})
|
批量删除 ({selectedDevices.length})
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
|
||||||
style={{
|
|
||||||
...secondaryButtonStyle,
|
|
||||||
color: selectedDevices.length > 0 ? '#ff4d4f' : undefined,
|
|
||||||
borderColor: selectedDevices.length > 0 ? '#ff4d4f' : undefined
|
|
||||||
}}
|
|
||||||
danger
|
|
||||||
icon={<SwapOutlined />}
|
|
||||||
disabled={selectedDevices.length === 0}
|
|
||||||
onClick={handleBatchOffline}
|
|
||||||
>
|
|
||||||
批量下线 ({selectedDevices.length})
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -2105,53 +2011,6 @@ function DeviceManagement() {
|
|||||||
</Form>
|
</Form>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
<Modal
|
|
||||||
title={
|
|
||||||
<div style={modalHeaderStyle}>
|
|
||||||
<DragOutlined style={{ color: '#722ed1' }} />
|
|
||||||
批量移动设备
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
open={batchMoveModalVisible}
|
|
||||||
onCancel={() => setBatchMoveModalVisible(false)}
|
|
||||||
footer={[
|
|
||||||
<Button key="cancel" onClick={() => setBatchMoveModalVisible(false)} style={secondaryButtonStyle}>
|
|
||||||
取消
|
|
||||||
</Button>,
|
|
||||||
<Button key="submit" type="primary" loading={batchMoveLoading} onClick={handleBatchMove} style={primaryButtonStyle}>
|
|
||||||
确定
|
|
||||||
</Button>
|
|
||||||
]}
|
|
||||||
destroyOnHidden
|
|
||||||
styles={{ header: { borderBottom: '1px solid #f0f0f0', padding: '16px 24px' }, body: { padding: '24px' } }}
|
|
||||||
>
|
|
||||||
<Form form={batchMoveForm} layout="vertical">
|
|
||||||
<Form.Item
|
|
||||||
name="targetRackId"
|
|
||||||
label="目标机柜"
|
|
||||||
rules={[{ required: true, message: '请选择目标机柜' }]}
|
|
||||||
>
|
|
||||||
<Select placeholder="请选择目标机柜" style={{ width: '100%' }}>
|
|
||||||
{racks.map(rack => (
|
|
||||||
<Option key={rack.rackId} value={rack.rackId}>
|
|
||||||
{rack.name} {rack.Room ? `(${rack.Room.name})` : ''}
|
|
||||||
</Option>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name="startPosition"
|
|
||||||
label="起始U位"
|
|
||||||
rules={[{ required: true, message: '请输入起始U位' }]}
|
|
||||||
>
|
|
||||||
<InputNumber min={1} placeholder="输入起始U位" style={{ width: '100%' }} />
|
|
||||||
</Form.Item>
|
|
||||||
<div style={{ color: '#666', fontSize: '13px' }}>
|
|
||||||
已选择 <span style={{ color: '#1890ff', fontWeight: 600 }}>{selectedDevices.length}</span> 个设备
|
|
||||||
</div>
|
|
||||||
</Form>
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
title={
|
title={
|
||||||
<div style={modalHeaderStyle}>
|
<div style={modalHeaderStyle}>
|
||||||
|
|||||||
Reference in New Issue
Block a user