Files
yunrui_asset/frontend/src/components/device/DeviceFormModal.jsx
T

362 lines
11 KiB
React
Raw Normal View History

import React, { useState, useEffect } from 'react';
import { Modal, Form, Input, Select, InputNumber, DatePicker, Switch, Row, Col, Button, Space } from 'antd';
import { PlusOutlined, EditOutlined, DatabaseOutlined } from '@ant-design/icons';
import dayjs from 'dayjs';
import { designTokens } from '../../config/theme';
import { getFormInitialValues, prepareDeviceFormData } from '../../utils/deviceUtils.jsx';
const { Option } = Select;
const modalHeaderStyle = {
display: 'flex',
alignItems: 'center',
gap: '8px',
fontSize: '18px',
fontWeight: 600,
};
const inputStyle = {
borderRadius: '8px',
transition: 'all 0.3s ease',
};
const DeviceFormModal = ({
visible,
editingDevice,
deviceFields,
racks,
rooms,
onCancel,
onSubmit,
}) => {
const [form] = Form.useForm();
const [selectedRoomId, setSelectedRoomId] = useState(null);
useEffect(() => {
if (visible) {
if (editingDevice) {
const initialValues = getFormInitialValues(editingDevice, racks);
if (initialValues.purchaseDate) {
initialValues.purchaseDate = dayjs(initialValues.purchaseDate);
}
if (initialValues.warrantyExpiry) {
initialValues.warrantyExpiry = dayjs(initialValues.warrantyExpiry);
}
form.setFieldsValue(initialValues);
if (editingDevice.rackId) {
const rack = racks.find((r) => r.rackId === editingDevice.rackId);
if (rack) {
setSelectedRoomId(rack.roomId);
}
}
} else {
form.resetFields();
setSelectedRoomId(null);
}
}
}, [visible, editingDevice, racks, form]);
const handleSubmit = (values) => {
const deviceData = prepareDeviceFormData(values, !!editingDevice);
onSubmit(deviceData);
};
const handleRoomChange = (value) => {
setSelectedRoomId(value);
form.setFieldValue('rackId', undefined);
};
const renderFieldControl = (field) => {
switch (field.fieldType) {
case 'number':
return (
<InputNumber
placeholder={`请输入${field.displayName}`}
min={0}
style={{ width: '100%', ...inputStyle }}
className="form-input-enhanced"
/>
);
case 'boolean':
return <Switch />;
case 'date':
return (
<DatePicker
style={{ width: '100%', ...inputStyle }}
placeholder={`请选择${field.displayName}`}
className="form-input-enhanced"
/>
);
case 'textarea':
return (
<Input.TextArea
placeholder={`请输入${field.displayName}`}
rows={3}
style={inputStyle}
className="form-input-enhanced"
/>
);
case 'select':
return (
<Select
placeholder={`请选择${field.displayName}`}
style={inputStyle}
className="form-input-enhanced"
>
{Array.isArray(field.options) &&
field.options.map((option) => (
<Option key={option.value} value={option.value}>
{option.label}
</Option>
))}
</Select>
);
default:
return (
<Input
placeholder={`请输入${field.displayName}`}
style={inputStyle}
className="form-input-enhanced"
/>
);
}
};
const filteredFields = deviceFields.filter(
(field) => field.fieldName !== 'deviceId' && field.fieldName !== 'rackId'
);
const formItems = [];
filteredFields.forEach((field) => {
if (field.fieldName === 'serialNumber') {
formItems.push(
<React.Fragment key={field.fieldName}>
<Col span={12} key={`${field.fieldName}-col`}>
<Form.Item
name={field.fieldName}
label={
<span>
{field.displayName}
{field.required && (
<span style={{ color: '#ff4d4f', marginLeft: '4px' }}>*</span>
)}
</span>
}
rules={
field.required ? [{ required: true, message: `请输入${field.displayName}` }] : []
}
>
{renderFieldControl(field)}
</Form.Item>
</Col>
<Col span={24} key="room-rack-section">
<div
style={{
background: 'linear-gradient(135deg, #f0f5ff 0%, #e6f7ff 100%)',
borderRadius: '12px',
padding: '20px',
marginBottom: '16px',
border: '2px solid #d6e4ff',
boxShadow: '0 2px 8px rgba(24, 144, 255, 0.1)',
}}
>
<div
style={{
fontSize: '14px',
fontWeight: '600',
color: '#1890ff',
marginBottom: '16px',
display: 'flex',
alignItems: 'center',
}}
>
<DatabaseOutlined style={{ marginRight: '8px' }} />
设备位置选择
</div>
<Row gutter={16}>
<Col span={12}>
<Form.Item
name="roomId"
label={
<span>
机房
<span style={{ color: '#ff4d4f', marginLeft: '4px' }}>*</span>
</span>
}
rules={[{ required: true, message: '请选择机房' }]}
style={{ marginBottom: '0' }}
>
<Select
placeholder="请选择机房"
style={{ borderRadius: '8px' }}
showSearch
optionFilterProp="children"
onChange={handleRoomChange}
>
{rooms.map((room) => (
<Option key={room.roomId} value={room.roomId}>
{room.name}
</Option>
))}
</Select>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="rackId"
label={
<span>
机柜
<span style={{ color: '#ff4d4f', marginLeft: '4px' }}>*</span>
</span>
}
rules={[{ required: true, message: '请选择机柜' }]}
style={{ marginBottom: '0' }}
>
<Select
placeholder={selectedRoomId ? '请选择机柜' : '请先选择机房'}
style={{ borderRadius: '8px' }}
disabled={!selectedRoomId}
showSearch
optionFilterProp="children"
>
{(selectedRoomId ? racks.filter((rack) => rack.roomId === selectedRoomId) : []).map(
(rack) => (
<Option key={rack.rackId} value={rack.rackId}>
{rack.name} ({rack.rackId})
</Option>
)
)}
</Select>
</Form.Item>
</Col>
</Row>
</div>
</Col>
</React.Fragment>
);
} else if (field.fieldType === 'textarea') {
formItems.push(
<Col span={24} key={field.fieldName}>
<Form.Item
name={field.fieldName}
label={
<span>
{field.displayName}
{field.required && (
<span style={{ color: '#ff4d4f', marginLeft: '4px' }}>*</span>
)}
</span>
}
rules={
field.required ? [{ required: true, message: `请输入${field.displayName}` }] : []
}
>
{renderFieldControl(field)}
</Form.Item>
</Col>
);
} else {
formItems.push(
<Col span={12} key={field.fieldName}>
<Form.Item
name={field.fieldName}
label={
<span>
{field.displayName}
{field.required && (
<span style={{ color: '#ff4d4f', marginLeft: '4px' }}>*</span>
)}
</span>
}
rules={
field.required ? [{ required: true, message: `请输入${field.displayName}` }] : []
}
>
{renderFieldControl(field)}
</Form.Item>
</Col>
);
}
});
return (
<Modal
title={
<div style={{ ...modalHeaderStyle, paddingRight: '32px' }}>
{editingDevice ? (
<EditOutlined style={{ color: '#667eea' }} />
) : (
<PlusOutlined style={{ color: '#667eea' }} />
)}
{editingDevice ? '编辑设备' : '添加设备'}
</div>
}
open={visible}
onCancel={onCancel}
footer={null}
width={700}
style={{ borderRadius: '16px' }}
styles={{
header: {
borderBottom: '1px solid #f0f0f0',
padding: '16px 24px',
position: 'relative',
},
body: { padding: '24px' },
}}
className="device-modal"
>
<Form form={form} layout="vertical" onFinish={handleSubmit}>
<Row gutter={16}>{formItems}</Row>
<div
style={{
display: 'flex',
justifyContent: 'flex-end',
gap: '12px',
marginTop: '32px',
paddingTop: '24px',
borderTop: '1px solid #f0f0f0',
}}
>
<Button
onClick={onCancel}
style={{
height: '40px',
borderRadius: '8px',
padding: '0 24px',
fontWeight: '500',
transition: 'all 0.3s ease',
}}
>
取消
</Button>
<Button
type="primary"
htmlType="submit"
style={{
height: '40px',
borderRadius: '8px',
background: designTokens.colors.primary.gradient,
border: 'none',
color: '#ffffff',
boxShadow: designTokens.shadows.small,
fontWeight: '500',
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
padding: '0 32px',
transition: 'all 0.3s ease',
}}
>
确定
</Button>
</div>
</Form>
</Modal>
);
};
export default React.memo(DeviceFormModal);