工单告警完善

This commit is contained in:
mmc
2026-07-20 13:56:10 +08:00
parent 916b6ff58a
commit bd47f417ad
11 changed files with 137 additions and 47 deletions

20
env.js
View File

@@ -9,17 +9,17 @@ const currentEnv = import.meta.env?.MODE || process.env.NODE_ENV;
// 环境配置映射
const envConfig = {
[ENV.DEV]: {
baseURL: 'http://192.168.2.59:8081',//http://192.168.2.56:8081
//baseURL: 'http://1.95.137.212:59015',//http://192.168.2.56:8081
WS_URL: 'ws://192.168.2.59:9002/ws/',
mqtt_url: "ws://1.95.137.212:8083/mqtt",
geoserver_url: "http://192.168.2.59:7000/geoserver"
//baseURL: 'http://1.95.137.212:8081',//http://192.168.2.56:8081
////baseURL: 'https://serviceri.satabot.com',
//WS_URL: 'ws://1.95.137.212:9002/ws/',
//baseURL: 'http://192.168.2.59:8081',//http://192.168.2.56:8081
////baseURL: 'http://1.95.137.212:59015',//http://192.168.2.56:8081
//WS_URL: 'ws://192.168.2.59:9002/ws/',
//mqtt_url: "ws://1.95.137.212:8083/mqtt",
//geoserver_url: "http://1.95.137.212:59018/geoserver"
//geoserver_url: "http://192.168.2.59:7000/geoserver"
baseURL: 'http://1.95.137.212:8081',//http://192.168.2.56:8081
//baseURL: 'https://serviceri.satabot.com',
WS_URL: 'ws://1.95.137.212:9002/ws/',
mqtt_url: "ws://1.95.137.212:8083/mqtt",
geoserver_url: "http://1.95.137.212:59018/geoserver"
//baseURL: 'http://1.95.137.212:59015',//http://192.168.2.56:8081
////baseURL: 'https://serviceri.satabot.com',

View File

@@ -39,7 +39,7 @@ export function updateAlarm(data) {
export function handleAlarm(data) {
return request({
url: '/iot/alarm/handle',
method: 'put',
method: 'post',
data
});
}

View File

@@ -51,7 +51,8 @@ const DraggableBox: React.FC<Props> = ({
</div>
{/* 传入的视频内容 */}
<div>{children}</div>
<div style={{ height: 'calc(100% - 50px)', width: '100%' }}>
{children}</div>
</div>
</Draggable>
);

View File

@@ -915,7 +915,12 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine,
}
viewer.scene.requestRender();
}
} else {
tracePointRef.current.setMode(TPMode.LOCATION);
isNavigationModeRef.current = false;
lastFinishPointRef.current = null;
}
}, [drawRealTime]);
// 处理完成点

View File

@@ -18,6 +18,7 @@ import {
Col,
App,
Typography,
Slider,
} from 'antd';
const { Text } = Typography;
@@ -767,6 +768,19 @@ export default function DeviceManagementPage() {
<Form.Item label="运行速度" name="runSpeed" rules={[{ required: true, message: '请输入运行速度' }]}>
<InputNumber placeholder="请输入运行速度" style={{ width: '100%' }} />
</Form.Item>
<Form.Item label="左轮增益系数" name="leftWheelGain" rules={[{ required: true, message: '请设置左轮增益系数' }]}>
<div style={{ display: 'flex', gap: 12, alignItems: 'center' }}>
<InputNumber min={0.1} max={1} step={0.01} style={{ width: 100 }} placeholder="0.1~1" />
</div>
</Form.Item>
<Form.Item label="右轮增益系数" name="rightWheelGain" rules={[{ required: true, message: '请设置右轮增益系数' }]}>
<div style={{ display: 'flex', gap: 12, alignItems: 'center' }}>
{/*<Slider min={0} max={1} step={0.01} style={{ flex: 1 }} />*/}
<InputNumber min={0.1} max={1} step={0.01} style={{ width: 100 }} placeholder="0.1~1" />
</div>
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">保存参数</Button>
</Form.Item>
@@ -921,7 +935,8 @@ export default function DeviceManagementPage() {
}}
/>
</>
)}
</div>
)
}
</div >
);
}

View File

@@ -11,19 +11,11 @@ import {
getAlarmList,
getAlarmDetail
} from '../../api/alarmApi.js';
import { alarmTypeMap } from '@/constants.js';
const { Option } = Select;
const { RangePicker } = DatePicker;
// ====================== 枚举配置 ======================
const alarmTypeMap = {
1: '设备故障',
2: '数据异常',
3: '通信中断',
4: '性能告警',
5: '安全告警',
6: '环境告警'
};
const alarmLevelConfig = {
1: { description: '严重', color: '#FF4D4F' },
@@ -120,6 +112,7 @@ const AlarmHistory = () => {
setQueryParams({
deviceId: values.deviceId || '',
alarmLevel: values.alarmLevel || '',
alarmType: values.alarmType || '',
handleStatus: values.handleStatus || '',
startTime,
endTime,
@@ -142,6 +135,12 @@ const AlarmHistory = () => {
},
{ title: '设备ID', dataIndex: 'deviceId', width: 160 },
{ title: '设备名称', dataIndex: 'deviceName', ellipsis: true },
{
title: '告警类型',
dataIndex: 'alarmType',
width: 130,
render: (t) => alarmTypeMap[t] || '-'
},
{
title: '告警级别',
width: 100,
@@ -201,6 +200,16 @@ const AlarmHistory = () => {
</Select>
</Form.Item>
<Form.Item name="alarmType" label="告警类型">
<Select placeholder="全部" style={{ width: 150 }} allowClear>
<Option value="UAV_FAULT">无人机故障</Option>
<Option value="MOWER_FAULT">割草机故障</Option>
<Option value="OTHER_DEVICE_FAULT">其他设备故障</Option>
<Option value="SERVER_FAILURE">服务器故障</Option>
<Option value="SYSTEM_ERROR">系统错误</Option>
</Select>
</Form.Item>
<Form.Item name="handleStatus" label="处理状态">
<Select placeholder="全部" style={{ width: 130 }} allowClear>
<Option value={1}>待处理</Option>
@@ -266,6 +275,7 @@ const AlarmHistory = () => {
<Descriptions.Item label="告警时间">
{currentAlarm?.alarmTime ? dayjs(currentAlarm.alarmTime).format('YYYY-MM-DD HH:mm:ss') : '-'}
</Descriptions.Item>
<Descriptions.Item label="告警类型">{alarmTypeMap[currentAlarm?.alarmType] || '-'}</Descriptions.Item>
<Descriptions.Item label="告警级别">
<Tag color={levelColor[currentAlarm?.alarmLevel]} style={{ border: 'none', color: '#fff' }}>
{levelText[currentAlarm?.alarmLevel]}

View File

@@ -129,6 +129,7 @@ export default function AlarmCenterPage() {
const [queryParams, setQueryParams] = useState({
deviceId: '',
alarmLevel: '',
alarmType: '',
handleStatus: '',
startTime: '',
endTime: '',
@@ -198,7 +199,7 @@ export default function AlarmCenterPage() {
const doHandle = async (values) => {
try {
await handleAlarm({
id: currentAlarm.id,
alarmId: currentAlarm.id,
handleStatus: values.handleStatus,
handleRemark: values.handleRemark || '已处理'
});
@@ -357,19 +358,20 @@ export default function AlarmCenterPage() {
const day = dayjs(item.alarmTime).format('MM-DD');
const level = String(item.alarmLevel);
if (!groupMap[day]) groupMap[day] = {};
if (!groupMap[day]) groupMap[day] = { ERROR: 0, WARNING: 0, INFO: 0 };
groupMap[day][level] = (groupMap[day][level] || 0) + 1;
});
// 规整成图表一维数组
// 直接返回 ERROR/WARNING/INFO 字段,匹配图表series key
return Object.entries(groupMap).map(([date, obj]) => ({
date,
severe: obj['ERROR'] || 0, // 严重
important: obj['WARNING'] || 0, // 重要
normal: obj['INFO'] || 0, // 一般
ERROR: obj.ERROR || 0,
WARNING: obj.WARNING || 0,
INFO: obj.INFO || 0,
}));
};
const pieData = [
{
name: '错误',
@@ -417,6 +419,19 @@ export default function AlarmCenterPage() {
}
};
// 联系负责人
const handleContactUser = () => {
// 自行实现:弹窗联系人列表、拨打电话、跳转聊天、弹窗信息等
console.log('联系负责人', currentAlarm);
};
// 派发工单
const handleDispatchOrder = () => {
// 自行实现:选择处理人、分配工单接口、弹窗派发表单
console.log('派发工单', currentAlarm);
};
// 生成工单
const handleaddOrder = async () => {
if (!currentAlarm) {
@@ -805,12 +820,15 @@ export default function AlarmCenterPage() {
{/* 告警详情抽屉 */}
<Drawer
title="告警详情"
//open={true}
open={drawerVisible}
onClose={() => setDrawerVisible(false)}
width={480}
destroyOnClose
>
<Spin spinning={detailLoading}>
{currentAlarm && (
<Form form={form} onFinish={doHandle} layout="vertical">
<>
@@ -867,11 +885,35 @@ export default function AlarmCenterPage() {
<div>
</div>
<Button type="primary" onClick={handleaddOrder}>生成工单</Button>
<div style={{ display: 'flex', justifyContent: 'flex-end', gap: 12 }}>
<Button type="default" onClick={handleContactUser}>联系负责人</Button>
<Button type="primary" onClick={handleaddOrder}>
生成工单
</Button>
</div>
</div>
</Card>
{/* 处理操作 */}
<Card size="small" title="处理操作" style={{ marginBottom: 16 }}>
<Form.Item label="处理状态" name="handleStatus" rules={[{ required: true, message: '请选择处理状态' }]}>
<Select>
<Option value={1}>待处理</Option>
<Option value={2}>处理中</Option>
<Option value={3}>已关闭</Option>
<Option value={4}>已忽略</Option>
</Select>
</Form.Item>
<Form.Item label="处理备注" name="handleRemark">
<Input.TextArea rows={2} placeholder="请输入处理备注" />
</Form.Item>
</Card>
{/* 底部按钮 */}
<Button type="primary" block htmlType="submit">确认处理</Button>
@@ -922,12 +964,11 @@ export default function AlarmCenterPage() {
rules={[{ required: true, message: '请选择告警类型' }]}
>
<Select>
<Option value={1}>设备故障</Option>
<Option value={2}>数据异常</Option>
<Option value={3}>通信中断</Option>
<Option value={4}>性能告警</Option>
<Option value={5}>安全告警</Option>
<Option value={6}>环境告警</Option>
<Option value="UAV_FAULT">无人机故障</Option>
<Option value="MOWER_FAULT">割草机故障</Option>
<Option value="OTHER_DEVICE_FAULT">其他设备故障</Option>
<Option value="SERVER_FAILURE">服务器故障</Option>
<Option value="SYSTEM_ERROR">系统错误</Option>
</Select>
</Form.Item>

View File

@@ -304,7 +304,7 @@ export default function RobotTaskPage() {
const data = {
lon: robotRealtimePosition?.longitude || 0,
lat: robotRealtimePosition?.latitude || 0,
//heading: robotRealtimePosition?.yaw || 0,
heading: robotRealtimePosition?.yaw || 0,
}
return data;
}, [robotRealtimePosition])
@@ -1772,7 +1772,7 @@ export default function RobotTaskPage() {
title={`机器人全景视频 - ${executingRobot.deviceAlias || executingRobot.serialNumber}`}
onClose={() => setShowVideo(false)}
>
<div >
<div style={{ height: '100%', width: '100%' }}>
<MowerAllView
serialNumber={executingRobot.serialNumber}
robotVideo={robotVideo}

View File

@@ -205,7 +205,7 @@ export function MowerAllView({ serialNumber, robotVideo, error, ready, statusDat
<span>俯仰角:</span>
<span>{statusData.pitch !== undefined ? `${Number(statusData.pitch).toFixed(1)}°` : '-'}</span>
<span>航向角状态:</span>
<span>{runningStatus.headingStatus !== undefined ? `${runningStatus.headingStatus == 1 ? '初始化' : '未初始化'}` : '-'}</span>
<span>{statusData.headingStatus !== undefined ? `${statusData.headingStatus == 1 ? '初始化' : '未初始化'}` : '-'}</span>
<span>定位:</span>
<span>
{statusData.qual == 0 ? '无效' :

View File

@@ -18,12 +18,11 @@ export const sourceTypeMap = {
};
export const alarmTypeMap = {
1: '设备故障',
2: '数据异常',
3: '通信中断',
4: '性能告警',
5: '安全告警',
6: '环境告警'
'UAV_FAULT': '无人机故障',
'MOWER_FAULT': '割草机故障',
'OTHER_DEVICE_FAULT': '其他设备故障',
'SERVER_FAILURE': '服务器故障',
'SYSTEM_ERROR': '系统错误'
};
export const alarmLevelConfig = {

View File

@@ -423,7 +423,8 @@ const WorkOrderPage = () => {
const submitComplete = async (values) => {
try {
const now = dayjs().format('YYYY-MM-DDTHH:mm:ss');
const params = {
// 1. 组装后端需要的 IOTWorkOrder JSON 对象
const workOrderObj = {
...currentDetail,
id: values.id,
orderNo: currentDetail?.orderNo,
@@ -433,7 +434,24 @@ const WorkOrderPage = () => {
handleResult: values.handleResult,
handleRemark: values.handleRemark,
};
await completeWorkOrder(params);
// 2. 新建 FormData,适配 @RequestPart 混合参数
const formData = new FormData();
// workOrder 字段:转JSON字符串
formData.append(
"workOrder",
new Blob([JSON.stringify(workOrderObj)], {
type: "application/json"
})
);
//formData.append('workOrder', JSON.stringify(workOrderObj));
// 4. 请求方法 completeWorkOrder 必须修改:传 formData,删除 headers 的 Content-Type
await completeWorkOrder(formData);
messageApi.success('工单已完成');
setCompleteModalVisible(false);
getWorkOrderList();
@@ -444,6 +462,7 @@ const WorkOrderPage = () => {
}
};
// 获取工单详情
const getWorkOrderDetail = async (id) => {
try {