错误管理增删改查

This commit is contained in:
mmc
2026-07-08 16:43:25 +08:00
parent f8fef3e40b
commit 1c229f32b0
3 changed files with 246 additions and 344 deletions

View File

@@ -331,16 +331,6 @@ export function getErrorListApi(params) {
params
});
}
// 查询错误列表(不分页)
export function getErrorListAllApi(params) {
return request({
url: '/system/error/list',
method: 'get',
params
});
}
// 查询错误详情
export function getErrorDetailApi(id) {
return request({
@@ -349,28 +339,35 @@ export function getErrorDetailApi(id) {
});
}
// 新增错误
export function addErrorApi(data) {
// 查询错误列表(不分页)
export function getErrorListAllApi(data) {
return request({
url: '/system/error',
url: '/iot/errorIdentification/list',
method: 'post',
data
});
}
// 修改错误
export function updateErrorApi(data) {
// 新增修改错误
export function addErrorApi(data) {
return request({
url: '/system/error',
method: 'put',
url: '/iot/errorIdentification/save',
method: 'post',
data
});
}
// 删除错误
export function deleteErrorApi(ids) {
export function deleteErrorApi(data) {
return request({
url: `/system/error/${ids}`,
method: 'delete'
url: `/iot/errorIdentification/delete`,
method: 'post',
data
});
}

View File

@@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useRef, useEffect } from 'react';
import {
Table,
Input,
@@ -31,94 +31,15 @@ import {
updateErrorApi,
deleteErrorApi,
getErrorDetailApi,
getErrorListAllApi
} from '@/api/systemSetting';
import utils from '@/lib/utils';
import { errorLevelOptions, errorSourceOptions, CompareEnumOptions } from '@/lib/utils';
const { TextArea } = Input;
const { Text, Title } = Typography;
const { Option } = Select;
// 错误类型选项
const errorTypeOptions = [
{ label: '机器人故障', value: 'ROBOT' },
{ label: '设备故障', value: 'DEVICE' },
{ label: '系统异常', value: 'SYSTEM' },
{ label: '通信故障', value: 'COMMUNICATION' },
{ label: '传感器故障', value: 'SENSOR' },
{ label: '其他', value: 'OTHER' },
];
// 严重程度选项
const severityOptions = [
{ label: '轻微', value: 'MINOR' },
{ label: '一般', value: 'NORMAL' },
{ label: '严重', value: 'SERIOUS' },
{ label: '紧急', value: 'CRITICAL' },
];
// 错误状态选项
const statusOptions = [
{ label: '未处理', value: 'PENDING' },
{ label: '处理中', value: 'PROCESSING' },
{ label: '已解决', value: 'RESOLVED' },
{ label: '已忽略', value: 'IGNORED' },
];
// 模拟错误数据
const mockErrorList = [
{
id: 1,
errorCode: 'ERR-ROBOT-001',
errorName: '机器人运动异常',
errorType: 'ROBOT',
severity: 'SERIOUS',
status: 'PROCESSING',
deviceCode: 'ROBOT-001',
description: '机器人在运动过程中出现卡顿现象,无法正常完成导航任务',
solution: '检查电机驱动模块,重新校准导航传感器',
createTime: '2025-05-20 14:30:00',
updateTime: '2025-05-21 09:15:00',
},
{
id: 2,
errorCode: 'ERR-CAM-002',
errorName: '摄像头连接失败',
errorType: 'DEVICE',
severity: 'NORMAL',
status: 'PENDING',
deviceCode: 'CAM-002',
description: '摄像头无法正常连接到系统,视频流中断',
solution: '检查摄像头电源和网络连接,重启摄像头设备',
createTime: '2025-05-21 10:20:00',
updateTime: '2025-05-21 10:20:00',
},
{
id: 3,
errorCode: 'ERR-SYS-001',
errorName: '数据库连接超时',
errorType: 'SYSTEM',
severity: 'CRITICAL',
status: 'RESOLVED',
deviceCode: 'SERVER-01',
description: '系统与数据库的连接出现超时,导致部分功能无法正常使用',
solution: '优化数据库连接池配置,增加连接数量',
createTime: '2025-05-19 08:45:00',
updateTime: '2025-05-19 10:30:00',
},
{
id: 4,
errorCode: 'ERR-SENSOR-003',
errorName: '激光雷达数据异常',
errorType: 'SENSOR',
severity: 'SERIOUS',
status: 'PENDING',
deviceCode: 'LIDAR-001',
description: '激光雷达返回的数据出现异常波动,影响建图精度',
solution: '清洁激光雷达镜头,检查传感器安装位置',
createTime: '2025-05-21 15:00:00',
updateTime: '2025-05-21 15:00:00',
},
];
const AntdCard = Card as any;
@@ -128,55 +49,33 @@ export default function ErrorManagement() {
const [searchForm] = Form.useForm();
const [loading, setLoading] = useState(false);
const [errorList, setErrorList] = useState(mockErrorList);
const [total, setTotal] = useState(mockErrorList.length);
const [errorList, setErrorList] = useState([]);
const [total, setTotal] = useState(0);
const [params, setParams] = useState({
pageNum: 1,
pageSize: 10,
errorCode: '',
errorName: '',
errorType: '',
status: '',
errorSource: '',
errorLevel: '',
});
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 {
// 真实 API 调用
// const res = await getErrorListApi(params);
// if (res.code === 200) {
// setErrorList(res.rows || []);
// setTotal(res.total || res.rows?.length || 0);
// }
// 模拟数据
await new Promise(resolve => setTimeout(resolve, 500));
let filteredList = [...mockErrorList];
if (params.errorCode) {
filteredList = filteredList.filter(item =>
item.errorCode.toLowerCase().includes(params.errorCode.toLowerCase())
);
const realParams = Object.fromEntries(
Object.entries(params).filter(([_, val]) => val !== '')
);
const res = await getErrorListAllApi(realParams);
if (res.code === 200) {
setErrorList(res.rows || []);
setTotal(res.total || res.rows?.length || 0);
}
if (params.errorName) {
filteredList = filteredList.filter(item =>
item.errorName.toLowerCase().includes(params.errorName.toLowerCase())
);
}
if (params.errorType) {
filteredList = filteredList.filter(item => item.errorType === params.errorType);
}
if (params.status) {
filteredList = filteredList.filter(item => item.status === params.status);
}
setErrorList(filteredList);
setTotal(filteredList.length);
} catch (err) {
utils.message.error('获取错误列表失败');
} finally {
@@ -188,62 +87,63 @@ export default function ErrorManagement() {
fetchErrorList();
}, [params]);
// 打开弹窗
// 打开弹窗
const openModal = (type: 'add' | 'edit' | 'view', record?: any) => {
setModalType(type);
setCurrentRecord(record);
setModalVisible(true);
form.resetFields();
if (type === 'edit' || type === 'view') {
form.setFieldsValue(record);
} else {
form.resetFields();
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,
// 区间/普通值统一拼接逗号字符串传给 compareValues
compareValues: values.rangeMin && values.rangeMax
? `${values.rangeMin},${values.rangeMax}`
: values.compareValues || '',
};
console.log('提交表单数据', submitData);
try {
if (modalType === 'add') {
// 真实 API 调用
// const res = await addErrorApi(values);
// if (res.code === 200) {
// utils.message.success('新增成功');
// setModalVisible(false);
// fetchErrorList();
// }
// 模拟新增
await new Promise(resolve => setTimeout(resolve, 500));
const newError = {
...values,
id: Date.now(),
createTime: new Date().toLocaleString(),
updateTime: new Date().toLocaleString(),
};
setErrorList([newError, ...errorList]);
setTotal(total + 1);
utils.message.success('新增成功');
setModalVisible(false);
} else if (modalType === 'edit') {
// 真实 API 调用
// const res = await updateErrorApi({ ...values, id: currentRecord.id });
// if (res.code === 200) {
// utils.message.success('更新成功');
// setModalVisible(false);
// fetchErrorList();
// }
// 模拟更新
await new Promise(resolve => setTimeout(resolve, 500));
setErrorList(errorList.map(item =>
item.id === currentRecord.id
? { ...item, ...values, updateTime: new Date().toLocaleString() }
: item
));
utils.message.success('更新成功');
// 真实接口
const res = await addErrorApi(submitData);
if (res.code === 200) {
utils.message.success(modalType == 'edit' ? '编辑成功' : '添加成功');
setModalVisible(false);
fetchErrorList();
} else {
utils.message.error(res.msg || '操作失败');
}
} catch (err) {
utils.message.error('操作失败');
}
@@ -259,18 +159,20 @@ export default function ErrorManagement() {
cancelText: '取消',
onOk: async () => {
try {
// 真实 API 调用
// const res = await deleteErrorApi(record.id);
// if (res.code === 200) {
// utils.message.success('删除成功');
// fetchErrorList();
// }
// 真实接口
const data = {
ids: [record.id]
}
console.log('删除请求参数', data);
const res = await deleteErrorApi(data.ids);
if (res.code == 200) {
utils.message.success('删除成功');
fetchErrorList();
} else {
utils.message.error(res.msg || '删除失败');
}
// 模拟删除
await new Promise(resolve => setTimeout(resolve, 500));
setErrorList(errorList.filter(item => item.id !== record.id));
setTotal(total - 1);
utils.message.success('删除成功');
} catch (err) {
utils.message.error('删除失败');
}
@@ -283,7 +185,6 @@ export default function ErrorManagement() {
searchForm.validateFields().then(values => {
setParams({
...params,
pageNum: 1,
...values,
});
});
@@ -293,16 +194,15 @@ export default function ErrorManagement() {
const handleReset = () => {
searchForm.resetFields();
setParams({
pageNum: 1,
pageSize: 10,
errorCode: '',
errorName: '',
errorType: '',
status: '',
errorSource: '',
errorLevel: '',
});
};
// 获取类型标签颜色
// 标签颜色方法(原有不变)
const getTypeColor = (type: string) => {
const colorMap: Record<string, string> = {
ROBOT: 'blue',
@@ -314,8 +214,6 @@ export default function ErrorManagement() {
};
return colorMap[type] || 'default';
};
// 获取严重程度标签颜色
const getSeverityColor = (severity: string) => {
const colorMap: Record<string, string> = {
MINOR: 'default',
@@ -325,8 +223,6 @@ export default function ErrorManagement() {
};
return colorMap[severity] || 'default';
};
// 获取状态标签颜色
const getStatusColor = (status: string) => {
const colorMap: Record<string, string> = {
PENDING: 'orange',
@@ -337,7 +233,7 @@ export default function ErrorManagement() {
return colorMap[status] || 'default';
};
// 表格列定义
// 表格列(完全不变)
const columns = [
{
title: '错误编码',
@@ -353,51 +249,59 @@ export default function ErrorManagement() {
width: 200,
},
{
title: '类型',
dataIndex: 'errorType',
key: 'errorType',
title: '错误来源',
dataIndex: 'errorSource',
key: 'errorSource',
width: 120,
render: (text: string) => {
const typeObj = errorTypeOptions.find(t => t.value === text);
const typeObj = errorSourceOptions.find(t => t.value === text);
return <Tag color={getTypeColor(text)}>{typeObj?.label || text}</Tag>;
},
},
{
title: '严重程度',
dataIndex: 'severity',
key: 'severity',
title: '错误等级',
dataIndex: 'errorLevel',
key: 'errorLevel',
width: 100,
render: (text: string) => {
const severityObj = severityOptions.find(t => t.value === text);
const severityObj = errorLevelOptions.find(t => t.value === text);
return <Tag color={getSeverityColor(text)}>{severityObj?.label || text}</Tag>;
},
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
width: 100,
render: (text: string) => {
const statusObj = statusOptions.find(t => t.value === text);
return <Tag color={getStatusColor(text)}>{statusObj?.label || text}</Tag>;
},
title: '校验字段',
dataIndex: 'field',
key: 'field',
width: 130,
},
{
title: '关联设备',
dataIndex: 'deviceCode',
key: 'deviceCode',
width: 150,
title: '对比规则',
dataIndex: 'compareType',
key: 'compareType',
width: 140,
render: (val) => {
const item = CompareEnumOptions.find(o => o.value === val);
return item?.label || val;
},
},
{
title: '对比数值',
dataIndex: 'compareValues',
key: 'compareValues',
width: 140,
},
{
title: '描述',
dataIndex: 'description',
key: 'description',
dataIndex: 'errorDescription',
key: 'errorDescription',
ellipsis: true,
},
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
title: '建议',
dataIndex: 'suggestion',
key: 'suggestion',
width: 180,
},
{
@@ -438,76 +342,45 @@ export default function ErrorManagement() {
];
return (
<div style={{ padding: '8px 0px', paddingTop: 8, background: '#f5f7fa', minHeight: 'calc(100vh - 120px)' }}>
<AntdCard title="错误管理" style={{ borderRadius: 8 }}>
{/* 搜索区域完全不变 */}
<Form form={searchForm} layout="inline" style={{ marginBottom: 0 }}>
<Form.Item name="errorCode" label="错误编码">
<Input
placeholder="请输入错误编码"
style={{ width: 150 }}
allowClear
size="small"
/>
<Input placeholder="请输入错误编码" style={{ width: 150 }} allowClear size="small" />
</Form.Item>
<Form.Item name="errorName" label="错误名称">
<Input
placeholder="请输入错误名称"
style={{ width: 150 }}
allowClear
size="small"
/>
<Input placeholder="请输入错误名称" style={{ width: 150 }} allowClear size="small" />
</Form.Item>
<Form.Item name="errorType" label="类型">
<Select
placeholder="请选择类型"
style={{ width: 120 }}
allowClear
size="small"
>
{errorTypeOptions.map(option => (
<Option key={option.value} value={option.value}>
{option.label}
</Option>
<Form.Item name="errorSource" label="错误来源">
<Select placeholder="请选择来源" style={{ width: 120 }} allowClear size="small">
{errorSourceOptions.map(option => (
<Option key={option.value} value={option.value}>{option.label}</Option>
))}
</Select>
</Form.Item>
<Form.Item name="status" label="状态">
<Select
placeholder="请选择状态"
style={{ width: 120 }}
allowClear
size="small"
>
{statusOptions.map(option => (
<Option key={option.value} value={option.value}>
{option.label}
</Option>
<Form.Item name="errorLevel" label="错误等级">
<Select placeholder="请选择等级" style={{ width: 120 }} allowClear size="small">
{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} size="small">
查询
</Button>
<Button icon={<ReloadOutlined />} onClick={handleReset} size="small">
重置
</Button>
<Button type="primary" icon={<PlusOutlined />} onClick={() => openModal('add')} size="small">
新增
</Button>
<Button type="primary" icon={<SearchOutlined />} onClick={handleSearch} size="small">查询</Button>
<Button icon={<ReloadOutlined />} onClick={handleReset} size="small">重置</Button>
<Button type="primary" icon={<PlusOutlined />} onClick={() => openModal('add')} size="small">新增</Button>
</Space>
</Form.Item>
</Form>
<Table
rowKey="id"
columns={columns}
dataSource={errorList}
loading={loading}
scroll={{ x: 1400, y: 1200 }}
scroll={{ x: 1600, y: 1200 }}
pagination={{
current: params.pageNum,
pageSize: params.pageSize,
@@ -520,34 +393,36 @@ export default function ErrorManagement() {
},
}}
/>
</AntdCard>
{/* 新增/编辑/查看弹窗 */}
{/* 新增/编辑/查看弹窗(核心优化区域) */}
<Modal
title={
modalType === 'add'
? '新增错误'
? '新增错误规则'
: modalType === 'edit'
? '编辑错误'
: '查看错误详情'
? '编辑错误规则'
: '查看错误规则详情'
}
open={modalVisible}
onCancel={() => {
setModalVisible(false);
form.resetFields();
}}
onOk={() => modalType !== 'view' && form.submit()}
width={700}
width={780}
footer={
modalType === 'view'
? [
<Button key="close" onClick={() => setModalVisible(false)}>
关闭
</Button>,
<Button key="close" onClick={() => setModalVisible(false)}>关闭</Button>
]
: [
<div style={{ margin: '8px 0 24px 0' }}></div>,
<Button key="cancel" onClick={() => {
setModalVisible(false);
form.resetFields();
}}>取消</Button>,
<Button key="submit" type="primary" onClick={() => form.submit()}>保存</Button>
]
: null
}
>
<Divider style={{ margin: '8px 0 16px 0' }} />
@@ -557,94 +432,93 @@ export default function ErrorManagement() {
onFinish={handleSave}
disabled={modalType === 'view'}
>
{/* 第一行:基础编码名称 */}
<Row gutter={16}>
<Col span={12}>
<Form.Item
label="错误编码"
name="errorCode"
rules={[{ required: true, message: '请输入错误编码' }]}
>
<Form.Item label="错误编码" name="errorCode" rules={[{ required: true, message: '请输入错误编码' }]}>
<Input placeholder="例如:ERR-ROBOT-001" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
label="错误名称"
name="errorName"
rules={[{ required: true, message: '请输入错误名称' }]}
>
<Form.Item label="错误名称" name="errorName" rules={[{ required: true, message: '请输入错误名称' }]}>
<Input placeholder="请输入错误名称" />
</Form.Item>
</Col>
</Row>
{/* 第二行:来源、等级、校验字段 */}
<Row gutter={16}>
<Col span={8}>
<Form.Item
label="错误类型"
name="errorType"
rules={[{ required: true, message: '请选择错误类型' }]}
>
<Select placeholder="请选择错误类型">
{errorTypeOptions.map(option => (
<Option key={option.value} value={option.value}>
{option.label}
</Option>
<Form.Item label="错误来源" name="errorSource" rules={[{ required: true, message: '请选择错误来源' }]}>
<Select placeholder="请选择">
{errorSourceOptions.map(option => (
<Option key={option.value} value={option.value}>{option.label}</Option>
))}
</Select>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
label="严重程度"
name="severity"
rules={[{ required: true, message: '请选择严重程度' }]}
>
<Select placeholder="请选择严重程度">
{severityOptions.map(option => (
<Option key={option.value} value={option.value}>
{option.label}
</Option>
<Form.Item label="错误等级" name="errorLevel" rules={[{ required: true, message: '请选择错误等级' }]}>
<Select placeholder="请选择">
{errorLevelOptions.map(option => (
<Option key={option.value} value={option.value}>{option.label}</Option>
))}
</Select>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
label="状态"
name="status"
rules={[{ required: true, message: '请选择状态' }]}
>
<Select placeholder="请选择状态">
{statusOptions.map(option => (
<Option key={option.value} value={option.value}>
{option.label}
</Option>
))}
</Select>
<Form.Item label="校验字段" name="field" rules={[{ required: true, message: '请填写校验字段名' }]}>
<Input placeholder="如:battery、speed、temperature" />
</Form.Item>
</Col>
</Row>
<Form.Item label="关联设备" name="deviceCode">
<Input placeholder="请输入关联设备编码" />
{/* 第三行:对比类型 + 对比值(自动切换单输入/区间双输入) */}
<Row gutter={16}>
<Col span={8}>
<Form.Item label="对比规则" name="compareType" rules={[{ required: true, message: '请选择对比规则' }]}>
<Select placeholder="请选择对比规则">
{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="区间最小值" name="rangeMin" rules={[{ required: true, message: '请填写最小值' }]}>
<InputNumber style={{ width: '100%' }} placeholder="最小值" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item label="区间最大值" name="rangeMax" rules={[{ required: true, message: '请填写最大值' }]}>
<InputNumber style={{ width: '100%' }} placeholder="最大值" />
</Form.Item>
</Col>
</>
) : (
<Col span={16}>
<Form.Item label="对比值" name="compareValues" rules={[{ required: true, message: '请填写对比值' }]}>
<Input placeholder="数值/文本,多个用逗号分隔" />
</Form.Item>
</Col>
)}
</Row>
{/* 错误描述 */}
<Form.Item label="错误描述 " name="errorDescription">
<TextArea rows={3} placeholder="详细描述触发该错误的场景" showCount maxLength={500} />
</Form.Item>
<Form.Item label="错误描述" name="description">
<TextArea
placeholder="请输入错误描述"
rows={4}
showCount
maxLength={500}
/>
</Form.Item>
<Form.Item label="建议处理方案" name="solution">
<TextArea
placeholder="请输入建议处理方案"
rows={4}
showCount
maxLength={500}
/>
{/* 处理建议 */}
<Form.Item label="处理建议" name="suggestion">
<TextArea rows={3} placeholder="出现该错误后的处理方案" showCount maxLength={500} />
</Form.Item>
</Form>
</Modal>
</div>
);
}

View File

@@ -234,3 +234,34 @@ export function dataUrlToFile(dataUrl, fileName) {
}
return new File([u8arr], fileName, { type: mime });
}
// 错误来源
export const errorSourceOptions = [
{ label: '无人机', value: 'UAV' },
{ label: '割草机', value: 'MOWER' },
{ label: '其他', value: 'OTHERS' },
];
// 错误等级
export const errorLevelOptions = [
{ label: '信息', value: 'INFO' },
{ label: '警告', value: 'WARNING' },
{ label: '错误', value: 'ERROR' },
];
export const CompareEnumOptions = [
{ value: 'EQ', label: '等于', symbol: '=' },
{ value: 'NEQ', label: '不等于', symbol: '!=' },
{ value: 'GT', label: '大于', symbol: '>' },
{ value: 'LT', label: '小于', symbol: '<' },
{ value: 'GTE', label: '大于等于', symbol: '>=' },
{ value: 'LTE', label: '小于等于', symbol: '<=' },
{ value: 'BETWEEN', label: '在之间', symbol: 'between', range: true },
{ value: 'NOT_BETWEEN', label: '不在之间', symbol: 'notBetween', range: true },
{ value: 'CONTAIN', label: '包含', symbol: 'contain' },
{ value: 'NOT_CONTAIN', label: '不包含', symbol: 'notContain' },
];