React 配置小组件
- DeviceConfig/index.tsx
import React, { useState, useEffect } from 'react';
import { Form, Input, Select, Radio, Button } from 'antd';
import { PlusOutlined, DeleteOutlined } from '@ant-design/icons';
import './index.css';
interface DeviceOption {
ua: string;
name: string;
}
interface Device {
device_type: string;
version_type: string;
version_start: string;
version_end: string;
version_list: string;
version_exc: string;
}
interface DeviceConfigProps {
deviceTypeOptions: DeviceOption[];
deviceList: Device[];
disabledDeviceOptions?: boolean;
maxCount?: number;
onDeviceTypeListDataChange?: (data: Device[]) => void;
}
const DeviceConfig: React.FC<DeviceConfigProps> = ({
deviceTypeOptions = [],
deviceList = [],
disabledDeviceOptions = false,
maxCount = 1,
onDeviceTypeListDataChange,
}) => {
const [deviceTypeListData, setDeviceTypeListData] = useState<Device[]>(deviceList);
// 添加 useEffect,当 deviceList 变化时更新 deviceTypeListData
useEffect(() => {
setDeviceTypeListData(deviceList);
}, [deviceList]);
useEffect(() => {
if (onDeviceTypeListDataChange) {
onDeviceTypeListDataChange(deviceTypeListData);
}
}, [deviceTypeListData, onDeviceTypeListDataChange]);
const deviceStruct: Device = {
device_type: '',
version_type: '0',
version_start: '',
version_end: '',
version_list: '',
version_exc: '',
};
const haveAbleChoice = deviceTypeOptions.some(
option => !deviceTypeListData.some(device => device.device_type === option.ua)
);
const getAvailableDeviceOptions = (index: number) => {
const selectedTypes = deviceTypeListData
.map((device, idx) => (idx !== index ? device.device_type : null))
.filter(type => type !== null);
return deviceTypeOptions.filter(option => !selectedTypes.includes(option.ua));
};
const addDevice = () => {
const selectedTypes = deviceTypeListData.map(device => device.device_type);
const firstAvailableType = deviceTypeOptions.find(
option => !selectedTypes.includes(option.ua)
);
if (firstAvailableType) {
const deviceData = { ...deviceStruct, device_type: firstAvailableType.ua };
setDeviceTypeListData([...deviceTypeListData, deviceData]);
}
};
const deleteDevice = (index: number) => {
const newData = [...deviceTypeListData];
newData.splice(index, 1);
setDeviceTypeListData(newData);
};
const handleChange = (index: number, field: string, value: any) => {
const newData = [...deviceTypeListData];
(newData[index] as any)[field] = value;
setDeviceTypeListData(newData);
};
const getVersionPlaceholder = (deviceType: string) =>
'输入示例:' + (deviceType === 'pc_exe_template' ? '5123400' : '5.12.34');
const getMultiVersionPlaceholder = (deviceType: string) =>
'输入示例:' + (deviceType === 'pc_exe_template' ? '5123400,5123411' : '5.12.34,5.12.35');
return (
<div>
{deviceTypeListData.map((device, index) => (
<div key={index} className="device-item">
<div className="group-row">
<Form layout="vertical" style={{ width: '100%' }}>
<Form.Item label="下发终端" required>
<Select
value={device.device_type}
placeholder="请选择"
allowClear
style={{ width: 200 }}
onChange={value => handleChange(index, 'device_type', value)}
>
{getAvailableDeviceOptions(index).map(item => (
<Select.Option key={item.ua} value={item.ua}>
{item.name}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item label="指定版本" required>
<Radio.Group
value={device.version_type}
onChange={e => handleChange(index, 'version_type', e.target.value)}
>
<Radio value="0">全部</Radio>
<Radio value="1">范围</Radio>
<Radio value="2">包含</Radio>
</Radio.Group>
</Form.Item>
{device.version_type === '1' && (
<div style={{ display: 'flex', flexDirection: 'row' }}>
<Form.Item label="开始版本">
<Input
value={device.version_start}
placeholder={getVersionPlaceholder(device.device_type)}
onChange={e => handleChange(index, 'version_start', e.target.value)}
/>
</Form.Item>
<Form.Item label="结束版本" style={{ marginLeft: 0 }}>
<Input
value={device.version_end}
placeholder={getVersionPlaceholder(device.device_type)}
onChange={e => handleChange(index, 'version_end', e.target.value)}
/>
</Form.Item>
</div>
)}
{device.version_type === '2' && (
<Form.Item label="指定版本号">
<Input.TextArea
value={device.version_list}
placeholder={getMultiVersionPlaceholder(device.device_type)}
onChange={e => handleChange(index, 'version_list', e.target.value)}
/>
</Form.Item>
)}
<Form.Item label="剔除版本号">
<Input.TextArea
value={device.version_exc}
placeholder={getMultiVersionPlaceholder(device.device_type)}
onChange={e => handleChange(index, 'version_exc', e.target.value)}
/>
<span className="extra-info">若输入多个版本号,请使用英文逗号分隔</span>
</Form.Item>
</Form>
<Button
icon={<DeleteOutlined />}
type="text"
style={{ marginLeft: 10 }}
onClick={() => deleteDevice(index)}
/>
</div>
</div>
))}
{haveAbleChoice && (
<div className="device-btn" onClick={addDevice}>
<PlusOutlined style={{ fontWeight: 'bold', marginRight: 5 }} /> 增加终端
</div>
)}
</div>
);
};
export default DeviceConfig;
- DeviceConfig/index.css
/* DeviceConfig.css */
.device-btn {
width: 138px;
height: 32px;
display: flex;
justify-content: center;
align-items: center;
border: 1px dashed #d9d9d9;
background: #ffffff;
padding: 4px 15px;
}
.device-btn:hover {
cursor: pointer;
color: #409EFF;
border: 1px dashed #409EFF;
}
.device-btn:active {
background: #f2f0f7;
}
.device-item {
margin: 0 0 24px;
background-color: #f4f5ffda;
padding: 16px;
border-radius: 8px;
}
.group-row {
display: flex;
flex-direction: row;
align-items: flex-end;
}
.extra-info {
color: rgba(0, 0, 0, 0.45);
font-size: 14px;
line-height: 1.5715;
transition: color 0.3s cubic-bezier(.215, .61, .355, 1);
}
index.tsx
import React, { useState } from 'react';
import { useModel } from 'umi';
import { PageContainer } from '@ant-design/pro-layout';
import { Button, Modal, Form } from 'antd';
import DeviceConfig from './DeviceConfig'; // 请根据实际路径调整
const IndexPage: React.FC = () => {
const { initialState } = useModel('@@initialState');
const appConfigOption = initialState?.appConfigOption;
const [visible, setVisible] = useState(false);
const [modalType, setModalType] = useState<'modify' | 'open'>('open');
const [deviceList, setDeviceList] = useState([
{ device_type: 'pc_exe_template', version_type: '0', version_exc: '5123400' },
]);
const [form] = Form.useForm();
const deviceTypeOptions = [
{ ua: 'pc_exe_template', name: 'PC EXE' },
{ ua: 'mobile_app', name: '移动应用' },
// 添加更多设备类型选项
];
const checkDeviceConfig = (rule: any, value: any) => {
return new Promise<void>((resolve, reject) => {
if (!value || value.length === 0) {
reject('请至少配置一条下发的终端');
} else {
for (let item of value) {
if (
(item.version_type === '1' &&
(!item.version_start || item.version_start.length === 0) &&
(!item.version_end || item.version_end.length === 0)) ||
(item.version_type === '2' && (!item.version_list || item.version_list.length === 0))
) {
reject('请完善终端版本配置');
return;
}
let regex = /^\d+\.\d+\.\d+$/;
if (item.device_type === 'pc_exe_template') {
regex = /^\d{7}$/;
}
if (item.version_type === '0') {
if (
item.version_exc &&
item.version_exc.length !== 0 &&
!item.version_exc
.split(',')
.every((segment: string) => segment.length === 0 || regex.test(segment))
) {
reject('剔除版本号输入不合法');
return;
}
} else if (item.version_type === '1') {
if (item.version_start && item.version_start.length !== 0 && !regex.test(item.version_start)) {
reject('开始版本输入不合法');
return;
}
if (item.version_end && item.version_end.length !== 0 && !regex.test(item.version_end)) {
reject('结束版本输入不合法');
return;
}
if (
item.version_exc &&
item.version_exc.length !== 0 &&
!item.version_exc
.split(',')
.every((segment: string) => segment.length === 0 || regex.test(segment))
) {
reject('剔除版本号输入不合法');
return;
}
} else {
if (
item.version_list &&
item.version_list.length !== 0 &&
!item.version_list
.split(',')
.every((segment: string) => segment.length === 0 || regex.test(segment))
) {
reject('指定版本号输入不合法');
return;
}
if (
item.version_exc &&
item.version_exc.length !== 0 &&
!item.version_exc
.split(',')
.every((segment: string) => segment.length === 0 || regex.test(segment))
) {
reject('剔除版本号输入不合法');
return;
}
}
}
resolve();
}
});
};
const showModal = (type: 'modify' | 'open') => {
setModalType(type);
form.resetFields();
if (type === 'modify') {
form.setFieldsValue({ deviceList: deviceList });
} else {
form.setFieldsValue({
deviceList: [{ device_type: 'pc_exe_template', version_type: '0', version_exc: '' }],
});
}
setVisible(true);
};
const handleOk = () => {
form
.validateFields()
.then((values) => {
if (modalType === 'modify') {
setDeviceList(values.deviceList); // 更新 deviceList
}
setVisible(false);
})
.catch((errorInfo) => {
console.log('校验失败:', errorInfo);
});
};
const handleCancel = () => {
setVisible(false);
};
return (
<PageContainer title={false}>
<Button type="primary" onClick={() => showModal('modify')}>
修改
</Button>
<Button type="default" onClick={() => showModal('open')} style={{ marginLeft: 8 }}>
打开
</Button>
<Modal
title="终端设置"
visible={visible}
onOk={handleOk}
onCancel={handleCancel}
destroyOnClose
width={800}
>
<Form form={form}>
<Form.Item
label="终端设置"
name="deviceList"
rules={[{ validator: checkDeviceConfig }]}
>
<DeviceConfig
deviceTypeOptions={deviceTypeOptions}
deviceList={form.getFieldValue('deviceList')}
onDeviceTypeListDataChange={(data) => {
form.setFieldsValue({ deviceList: data });
}}
/>
</Form.Item>
</Form>
</Modal>
</PageContainer>
);
};
export default IndexPage;
原文地址:https://blog.csdn.net/weixin_41987016/article/details/145183374
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!