Files
web-Iot/tmpp7zw7soq.tsx
2026-08-12 11:29:44 +08:00

543 lines
16 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState, useRef, useEffect } from 'react';
import {
Table,
Input,
Button,
Form,
Select,
Space,
Tag,
Modal,
Card,
Typography,
InputNumber,
Row,
Col,
App,
Divider,
} from 'antd';
import {
SearchOutlined,
ReloadOutlined,
PlusOutlined,
EditOutlined,
DeleteOutlined,
EyeOutlined,
ExclamationCircleOutlined,
} from '@ant-design/icons';
import {
getErrorListApi,
addErrorApi,
updateErrorApi,
deleteErrorApi,
getErrorDetailApi,
getErrorListAllApi
} from '@/api/systemSetting';
import utils from '@/lib/utils';
import { errorLevelOptions, errorSourceOptions, CompareEnumOptions } from '@/lib/utils';
import { useSelector } from 'react-redux';
import { RootState } from '@/src/store';
import { useTranslation } from 'react-i18next';
const { TextArea } = Input;
const { Text, Title } = Typography;
const { Option } = Select;
const AntdCard = Card as any;
export default function ErrorManagement() {
const { t } = useTranslation();
const { userInfo } = useSelector((state: RootState) => state.user);
const { stationId, currentStation } = useSelector((state: RootState) => state.station);
const { modal } = App.useApp();
const [form] = Form.useForm();
const [searchForm] = Form.useForm();
const [loading, setLoading] = useState(false);
const [errorList, setErrorList] = useState([]);
const [total, setTotal] = useState(0);
const [params, setParams] = useState({
errorCode: '',
errorName: '',
errorSource: '',
errorLevel: '',
orgId: currentStation?.orgId || '',
siteId: stationId || '',
});
const [modalVisible, setModalVisible] = useState(false);
const [modalType, setModalType] = useState<'add' | 'edit' | 'view'>('add');
const [currentRecord, setCurrentRecord] = useState<any>(null);
// 监听对比类型,判断是否是区间
const compareTypeWatch = Form.useWatch('compareType', form);
// 获取错误列表
const fetchErrorList = async () => {
setLoading(true);
try {
const requestParams = {
...params,
siteId: stationId || '',
};
const realParams = Object.fromEntries(
Object.entries(requestParams).filter(([_, val]) => val !== '')
);
const res = await getErrorListAllApi(realParams);
if (res.code === 200) {
setErrorList(res.rows || []);
setTotal(res.total || res.rows?.length || 0);
}
} catch (err) {
utils.message.error(t('systemSetting.getErrorListFailed'));
} finally {
setLoading(false);
}
};
useEffect(() => {
setParams(prev => ({
...prev,
siteId: stationId || ''
}));
}, [stationId]);
useEffect(() => {
fetchErrorList();
}, [params]);
// 打开弹窗
// 打开弹窗
const openModal = (type: 'add' | 'edit' | 'view', record?: any) => {
setModalType(type);
setCurrentRecord(record);
setModalVisible(true);
form.resetFields();
if (type === 'edit' || type === 'view') {
const fillData = { ...record };
// 区间规则拆分 compareValues 回填 min/max
const targetEnum = CompareEnumOptions.find(e => e.value === record.compareType);
if (targetEnum?.range && record.compareValues) {
const valArr = record.compareValues.split(',');
fillData.rangeMin = valArr[0];
fillData.rangeMax = valArr[1];
}
form.setFieldsValue(fillData);
}
};
// 保存错误(字段映射适配后端实体)
const handleSave = async (values: any) => {
console.log('提交表单数据', values);
//return;
// 组装后端需要的实体字段
const submitData = {
id: modalType == 'edit' ? currentRecord.id : undefined,
// 原有基础字段
errorCode: values.errorCode,
errorName: values.errorName,
errorSource: values.errorSource,
errorLevel: values.errorLevel,
errorDescription: values.errorDescription,
suggestion: values.suggestion,
// 新增校验规则字段
field: values.field,
compareType: values.compareType,
orgId: currentStation?.orgId || '',
siteId: stationId || '',
// 区间/普通值统一拼接逗号字符串传给 compareValues
compareValues: values.rangeMin && values.rangeMax
? `${values.rangeMin},${values.rangeMax}`
: values.compareValues || '',
};
console.log('提交表单数据', submitData);
try {
// 真实接口
const res = await addErrorApi(submitData);
if (res.code === 200) {
utils.message.success(modalType == 'edit' ? t('common.editSuccess') : t('common.addSuccess'));
setModalVisible(false);
fetchErrorList();
} else {
utils.message.error(res.msg || t('systemSetting.operationFailed'));
}
} catch (err) {
utils.message.error(t('systemSetting.operationFailed'));
}
};
// 删除错误
const handleDelete = (record: any) => {
modal.confirm({
title: t('common.confirmDelete'),
content: t('systemSetting.confirmDeleteError', { name: record.errorName }),
icon: <ExclamationCircleOutlined />,
okText: t('common.ok'),
cancelText: t('common.cancel'),
onOk: async () => {
try {
// 真实接口
const data = {
ids: [record.id]
}
console.log('删除请求参数', data);
const res = await deleteErrorApi(data.ids);
if (res.code == 200) {
utils.message.success(t('common.deleteSuccess'));
fetchErrorList();
} else {
utils.message.error(res.msg || t('common.deleteFail'));
}
} catch (err) {
utils.message.error(t('common.deleteFail'));
}
},
});
};
// 搜索
const handleSearch = () => {
searchForm.validateFields().then(values => {
setParams({
...params,
...values,
});
});
};
// 重置
const handleReset = () => {
searchForm.resetFields();
setParams({
errorCode: '',
errorName: '',
errorSource: '',
errorLevel: '',
});
};
// 标签颜色方法(原有不变)
const getTypeColor = (type: string) => {
const colorMap: Record<string, string> = {
ROBOT: 'blue',
DEVICE: 'green',
SYSTEM: 'purple',
COMMUNICATION: 'orange',
SENSOR: 'cyan',
OTHER: 'default',
};
return colorMap[type] || 'default';
};
const getSeverityColor = (severity: string) => {
const colorMap: Record<string, string> = {
MINOR: 'default',
NORMAL: 'blue',
SERIOUS: 'orange',
CRITICAL: 'red',
};
return colorMap[severity] || 'default';
};
const getStatusColor = (status: string) => {
const colorMap: Record<string, string> = {
PENDING: 'orange',
PROCESSING: 'blue',
RESOLVED: 'green',
IGNORED: 'default',
};
return colorMap[status] || 'default';
};
// 表格列(完全不变)
const columns = [
{
title: t('systemSetting.errorCode'),
dataIndex: 'errorCode',
key: 'errorCode',
width: 150,
fixed: 'left' as const,
},
{
title: t('systemSetting.errorName'),
dataIndex: 'errorName',
key: 'errorName',
width: 200,
},
{
title: t('systemSetting.errorSourceLabel'),
dataIndex: 'errorSource',
key: 'errorSource',
width: 120,
render: (text: string) => {
const typeObj = errorSourceOptions.find(t => t.value === text);
return <Tag color={getTypeColor(text)}>{typeObj?.label || text}</Tag>;
},
},
{
title: t('systemSetting.errorLevelLabel'),
dataIndex: 'errorLevel',
key: 'errorLevel',
width: 100,
render: (text: string) => {
const severityObj = errorLevelOptions.find(t => t.value === text);
return <Tag color={getSeverityColor(text)}>{severityObj?.label || text}</Tag>;
},
},
{
title: t('systemSetting.checkField'),
dataIndex: 'field',
key: 'field',
width: 130,
},
{
title: t('systemSetting.compareRule'),
dataIndex: 'compareType',
key: 'compareType',
width: 140,
render: (val) => {
const item = CompareEnumOptions.find(o => o.value === val);
return item?.label || val;
},
},
{
title: t('systemSetting.compareValueCol'),
dataIndex: 'compareValues',
key: 'compareValues',
width: 140,
},
{
title: t('common.description'),
dataIndex: 'errorDescription',
key: 'errorDescription',
ellipsis: true,
},
{
title: t('aiAnalysis.suggestion'),
dataIndex: 'suggestion',
key: 'suggestion',
width: 180,
},
{
title: t('roles.actions'),
key: 'action',
width: 300,
fixed: 'right' as const,
render: (_: any, record: any) => (
<Space>
<Button
type="link"
size="small"
icon={<EyeOutlined />}
onClick={() => openModal('view', record)}
>{t('common.view')}</Button>
<Button
type="link"
size="small"
icon={<EditOutlined />}
onClick={() => openModal('edit', record)}
>{t('common.edit')}</Button>
<Button
type="link"
size="small"
danger
icon={<DeleteOutlined />}
onClick={() => handleDelete(record)}
>{t('common.delete')}</Button>
</Space>
),
},
];
return (
<div style={{ padding: '0px 0px', background: '#f5f7fa', minHeight: 'calc(100vh - 120px)' }}>
<AntdCard style={{ borderRadius: 8 }}>
{/* 搜索区域完全不变 */}
<Form form={searchForm} layout="inline" style={{ marginBottom: 0 }}>
<Form.Item name="errorCode" label={t("systemSetting.errorCode")}>
<Input placeholder={t("systemSetting.pleaseInputErrorCode")} style={{ width: 150 }} allowClear />
</Form.Item>
<Form.Item name="errorName" label={t("systemSetting.errorName")}>
<Input placeholder={t("systemSetting.pleaseInputErrorName")} style={{ width: 150 }} allowClear />
</Form.Item>
<Form.Item name="errorSource" label={t("systemSetting.errorSourceLabel")}>
<Select placeholder={t("systemSetting.pleaseSelectSource")} style={{ width: 120 }} allowClear >
{errorSourceOptions.map(option => (
<Option key={option.value} value={option.value}>{option.label}</Option>
))}
</Select>
</Form.Item>
<Form.Item name="errorLevel" label={t("systemSetting.errorLevelLabel")}>
<Select placeholder={t("systemSetting.pleaseSelectLevel")} style={{ width: 120 }} allowClear >
{errorLevelOptions.map(option => (
<Option key={option.value} value={option.value}>{option.label}</Option>
))}
</Select>
</Form.Item>
<Form.Item>
<Space>
<Button type="primary" icon={<SearchOutlined />} onClick={handleSearch}>{t('common.search2')}</Button>
<Button icon={<ReloadOutlined />} onClick={handleReset}>{t('common.reset')}</Button>
<Button type="primary" icon={<PlusOutlined />} onClick={() => openModal('add')}>{t('common.addNew')}</Button>
</Space>
</Form.Item>
</Form>
<Table
rowKey="id"
columns={columns}
dataSource={errorList}
loading={loading}
scroll={{ x: 1600, y: 1200 }}
pagination={{
current: params.pageNum,
pageSize: params.pageSize,
total: total,
showSizeChanger: true,
showQuickJumper: true,
showTotal: (total) => `${t('common.total')} ${total} ${t('common.items')}`,
onChange: (page, pageSize) => {
setParams({ ...params, pageNum: page, pageSize });
},
}}
/>
</AntdCard>
{/* 新增/编辑/查看弹窗(核心优化区域) */}
<Modal
title={
modalType === 'add'
? t('systemSetting.addErrorRule')
: modalType === 'edit'
? t('systemSetting.editErrorRule')
: t('systemSetting.viewErrorRuleDetail')
}
open={modalVisible}
onCancel={() => {
setModalVisible(false);
form.resetFields();
}}
width={780}
footer={
modalType === 'view'
? [
<Button key="close" onClick={() => setModalVisible(false)}>{t('common.close')}</Button>
]
: [
<div style={{ margin: '8px 0 24px 0' }}></div>,
<Button key="cancel" onClick={() => {
setModalVisible(false);
form.resetFields();
}}>{t('common.cancel')}</Button>,
<Button key="submit" type="primary" onClick={() => form.submit()}>{t('common.save')}</Button>
]
}
>
<Divider style={{ margin: '8px 0 16px 0' }} />
<Form
form={form}
layout="vertical"
onFinish={handleSave}
disabled={modalType === 'view'}
>
{/* 第一行:基础编码名称 */}
<Row gutter={16}>
<Col span={12}>
<Form.Item label={t("systemSetting.errorCode")} name="errorCode" rules={[{ required: true, message: t("systemSetting.pleaseInputErrorCode") }]}>
<Input placeholder={t("systemSetting.errorCodePlaceholder")} />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label={t("systemSetting.errorName")} name="errorName" rules={[{ required: true, message: t("systemSetting.pleaseInputErrorName") }]}>
<Input placeholder={t("systemSetting.pleaseInputErrorName")} />
</Form.Item>
</Col>
</Row>
{/* 第二行:来源、等级、校验字段 */}
<Row gutter={16}>
<Col span={8}>
<Form.Item label={t("systemSetting.errorSourceLabel")} name="errorSource" rules={[{ required: true, message: t("systemSetting.pleaseSelectErrorSource") }]}>
<Select placeholder={t('common.pleaseSelect')}>
{errorSourceOptions.map(option => (
<Option key={option.value} value={option.value}>{option.label}</Option>
))}
</Select>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item label={t("systemSetting.errorLevelLabel")} name="errorLevel" rules={[{ required: true, message: t("systemSetting.pleaseSelectErrorLevel") }]}>
<Select placeholder={t('common.pleaseSelect')}>
{errorLevelOptions.map(option => (
<Option key={option.value} value={option.value}>{option.label}</Option>
))}
</Select>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item label={t("systemSetting.checkField")} name="field" rules={[{ required: true, message: t("systemSetting.pleaseInputCheckField") }]}>
<Input placeholder={t("systemSetting.checkFieldPlaceholder")} />
</Form.Item>
</Col>
</Row>
{/* 第三行:对比类型 + 对比值(自动切换单输入/区间双输入) */}
<Row gutter={16}>
<Col span={8}>
<Form.Item label={t("systemSetting.compareRule")} name="compareType" rules={[{ required: true, message: t("systemSetting.pleaseSelectCompareRule") }]}>
<Select placeholder={t("systemSetting.pleaseSelectCompareRule")}>
{CompareEnumOptions.map(item => (
<Option key={item.value} value={item.value}>{item.label} ({item.symbol})</Option>
))}
</Select>
</Form.Item>
</Col>
{/* 区间类型:BETWEEN / NOT_BETWEEN 显示两个输入框 */}
{CompareEnumOptions.find(o => o.value === compareTypeWatch)?.range ? (
<>
<Col span={8}>
<Form.Item label={t("systemSetting.rangeMin")} name="rangeMin" rules={[{ required: true, message: t("systemSetting.pleaseInputMinValue") }]}>
<InputNumber style={{ width: '100%' }} placeholder={t("systemSetting.minValuePlaceholder")} />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item label={t("systemSetting.rangeMax")} name="rangeMax" rules={[{ required: true, message: t("systemSetting.pleaseInputMaxValue") }]}>
<InputNumber style={{ width: '100%' }} placeholder={t("systemSetting.maxValuePlaceholder")} />
</Form.Item>
</Col>
</>
) : (
<Col span={16}>
<Form.Item label={t("systemSetting.compareValueLabel")} name="compareValues" rules={[{ required: true, message: t("systemSetting.pleaseInputCompareValue") }]}>
<Input placeholder={t("systemSetting.compareValuePlaceholder")} />
</Form.Item>
</Col>
)}
</Row>
{/* 错误描述 */}
<Form.Item label={t("systemSetting.errorDesc")} name="errorDescription">
<TextArea rows={3} placeholder={t("systemSetting.errorDescPlaceholder")} showCount maxLength={500} />
</Form.Item>
{/* 处理建议 */}
<Form.Item label={t('aiAnalysis.suggestion')} name="suggestion">
<TextArea rows={3} placeholder={t("systemSetting.suggestionPlaceholder")} showCount maxLength={500} />
</Form.Item>
</Form>
</Modal>
</div>
);
}