diff --git a/src/api/workOrder.ts b/src/api/workOrder.ts index 416a4f5..9a2fa0f 100644 --- a/src/api/workOrder.ts +++ b/src/api/workOrder.ts @@ -37,7 +37,7 @@ export function updateWorkOrder(data) { export function dispatchWorkOrder(data) { return request({ - url: `/iot/ioTworkOrder/dispat`, + url: `/iot/ioTworkOrder/dispatch`, method: 'post', data }); @@ -45,10 +45,11 @@ export function dispatchWorkOrder(data) { //开始执行工单 -export function startWorkOrder(id) { +export function startWorkOrder(data) { return request({ - url: `/iot/ioTworkOrder/start/${id}`, - method: 'put', + url: `/iot/ioTworkOrder/start`, + method: 'post', + data }); } diff --git a/src/components/SystemSetting/AlarmSettingsManagement.tsx b/src/components/SystemSetting/AlarmSettingsManagement.tsx index f9e7940..3784a8f 100644 --- a/src/components/SystemSetting/AlarmSettingsManagement.tsx +++ b/src/components/SystemSetting/AlarmSettingsManagement.tsx @@ -30,7 +30,7 @@ import { } from '../../api/systemSetting'; import { useSelector } from 'react-redux'; import { RootState } from '@/src/store'; -import utils, { workOrderTypeOptions, getWorkOrderTypeText, errorLevelOptions, LEVEL_TAG_COLOR } from '@/lib/utils'; +import utils, { errorSourceOptions, getErrorSourceText } from '@/lib/utils'; const { Option } = Select; @@ -121,6 +121,7 @@ const AlarmModelManagement: React.FC = () => { form.setFieldsValue({ errorCode: record.errorCode, modelId: record.modelId, + sourceType: record.sourceType, autoGenerateOrder: record.autoGenerateOrder }); setModalVisible(true); @@ -183,7 +184,8 @@ const AlarmModelManagement: React.FC = () => { // 后端要求字段 errorCode: values.errorCode, modelId: values.modelId, - autoGenerateOrder: values.autoGenerateOrder ?? false + autoGenerateOrder: values.autoGenerateOrder ?? false, + sourceType: values.sourceType ?? 1 }; const res = await saveAlarmOrderConfigApi(submitParams); if (res.code === 200 && res.data === true) { @@ -200,6 +202,12 @@ const AlarmModelManagement: React.FC = () => { // 表格列配置 const columns = [ { title: '错误编码', dataIndex: 'errorCode', key: 'errorCode' }, + { + title: '错误来源', + dataIndex: 'sourceType', + key: 'sourceType', + render: (val: string) => getErrorSourceText(val) + }, { title: '关联工单模型', dataIndex: 'modelId', @@ -284,6 +292,15 @@ const AlarmModelManagement: React.FC = () => { + + + {/* 是否自动生成工单 */} { map[key] = alarmLevelConfig[key].color; return map; @@ -42,6 +41,70 @@ const levelText = Object.keys(alarmLevelConfig).reduce((map, key) => { return map; }, {}); +// 级别颜色配置 +const levelColorMap = { + ERROR: '#FF4D4F', // 错误 红 + WARNING: '#FA8C16', // 警告 橙 + INFO: '#1890FF', // 信息 蓝 +}; + + +// 快速根据 value 获取 label +const getLevelLabel = (val) => { + const item = errorLevelOptions.find(opt => opt.value === val); + return item?.label || '未知'; +}; + +/** + * 通用告警级别标签组件 + * @param level 接口返回字符串 INFO / WARNING / ERROR + * @param size small表格 | large详情弹窗 + */ +const AlarmLevelTag = ({ level, size = 'small' }) => { + const color = levelColorMap[level] || '#86909C'; + const text = getLevelLabel(level); + + // 详情大标签 + if (size === 'large') { + return ( + + {text} + + ); + } + + // 表格小标签 + return ( + + {text} + + ); +}; + export default function AlarmCenterPage() { const [form] = Form.useForm(); const [loading, setLoading] = useState(false); @@ -55,6 +118,8 @@ export default function AlarmCenterPage() { const [messageApi, contextHolder] = message.useMessage(); const [todayData, setTodayData] = useState({}); const [yesterdayData, setYesterdayData] = useState({}); + const { stationId } = useSelector((state: RootState) => state.station); + const { userInfo } = useSelector((state: RootState) => state.user); const [editModalVisible, setEditModalVisible] = useState(false); const [editForm] = Form.useForm(); @@ -86,8 +151,8 @@ export default function AlarmCenterPage() { const fetchList = async () => { setLoading(true); try { - const res = await getAlarmList({ pageNum, pageSize, ...queryParams }); - if (res.code === 0) { + const res = await getAlarmList({ pageNum, pageSize, ...queryParams, siteId: stationId }); + if (res.code === 200) { setAlarmList(res.rows || []); setTotal(res.total || 0); } @@ -189,7 +254,7 @@ export default function AlarmCenterPage() { fetchList(); - }, [pageNum]); + }, [pageNum, stationId]); useEffect(() => { fetchTodayAndYesterdayStat(); fetchStat(); @@ -201,7 +266,6 @@ export default function AlarmCenterPage() { { title: '告警编号', dataIndex: 'alarmNo', - width: 180, render: (text) => {text} }, { @@ -218,7 +282,7 @@ export default function AlarmCenterPage() { { title: '设备名称', dataIndex: 'deviceName', - width: 160 + width: 280 }, { title: '告警类型', @@ -230,11 +294,8 @@ export default function AlarmCenterPage() { title: '告警级别', dataIndex: 'alarmLevel', width: 100, - render: (l) => ( - - {levelText[l]} - - ) + render: (l) => + }, { title: '告警时间', @@ -379,10 +440,9 @@ export default function AlarmCenterPage() { priorityLevel: currentAlarm.alarmLevel === 1 ? 1 : 2, // 严重=高,其他=中 deviceId: currentAlarm.deviceId || '', deviceName: currentAlarm.deviceName || '', - deviceType: '', // 没有就传空 - siteId: '', - siteName: '', - orgId: '', + deviceType: currentAlarm.deviceType, // 没有就传空 + siteId: stationId, + orgId: userInfo?.orgId || '', assigneeId: '', assigneeName: '', collaboratorIds: '', @@ -752,7 +812,7 @@ export default function AlarmCenterPage() { title="告警详情" open={drawerVisible} onClose={() => setDrawerVisible(false)} - width={420} + width={480} destroyOnClose > @@ -767,9 +827,7 @@ export default function AlarmCenterPage() {
{currentAlarm.alarmTitle} - - {levelText[currentAlarm.alarmLevel]} - +
告警编号:{currentAlarm.alarmNo}
@@ -795,7 +853,6 @@ export default function AlarmCenterPage() { return {handleStatusMap[status] || '-'}; })()} - {currentAlarm.handleUserName || '-'} {/* 告警描述 */} @@ -902,11 +959,8 @@ export default function AlarmCenterPage() { name="alarmLevel" rules={[{ required: true, message: '请选择告警级别' }]} > - +
diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 1a7159a..f91ae8a 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -237,12 +237,28 @@ export function dataUrlToFile(dataUrl, fileName) { // 错误来源 -export const errorSourceOptions = [ - { label: '无人机', value: 'UAV' }, - { label: '割草机', value: 'MOWER' }, - { label: '其他', value: 'OTHERS' }, +export const ERROR_SOURCE = { + UAV: 'UAV', + MOWER: 'MOWER', + OTHERS: 'OTHERS' +} as const; -]; +export type ErrorSourceType = typeof ERROR_SOURCE[keyof typeof ERROR_SOURCE]; + +export const errorSourceOptions: Array<{ + value: ErrorSourceType; + label: string; +}> = [ + { label: '无人机', value: ERROR_SOURCE.UAV }, + { label: '割草机', value: ERROR_SOURCE.MOWER }, + { label: '其他', value: ERROR_SOURCE.OTHERS }, + ]; + +export const getErrorSourceText = (source?: ErrorSourceType | string): string => { + if (!source) return ""; + const target = errorSourceOptions.find((item) => item.value === source); + return target?.label ?? String(source); +}; // 错误等级 export const errorLevelOptions = [ @@ -296,9 +312,9 @@ export const workOrderTypeOptions: Array<{ { value: WORK_ORDER_TYPE.UAV_ERROR, label: "无人机故障" }, { value: WORK_ORDER_TYPE.INSPECTION_TASK, label: "巡检" }, { value: WORK_ORDER_TYPE.COMPONENT_DEFECT, label: "光伏组件故障" }, - { value: WORK_ORDER_TYPE.CLEAN_TASK, label: "清洗任务" }, - { value: WORK_ORDER_TYPE.MAINTAIN_TASK, label: "维护任务" }, - { value: WORK_ORDER_TYPE.REPAIR_TASK, label: "维修任务" }, + { value: WORK_ORDER_TYPE.CLEAN_TASK, label: "清洗" }, + { value: WORK_ORDER_TYPE.MAINTAIN_TASK, label: "维护" }, + { value: WORK_ORDER_TYPE.REPAIR_TASK, label: "维修" }, { value: WORK_ORDER_TYPE.OTHER, label: "其他" }, ]; diff --git a/src/pages/WorkOrder.tsx b/src/pages/WorkOrder.tsx index 905005b..6185410 100644 --- a/src/pages/WorkOrder.tsx +++ b/src/pages/WorkOrder.tsx @@ -33,18 +33,19 @@ import { getWorkOrderApi, getWorkOrderDetailApi, updateWorkOrder, - dispatchWorkOrder, + startWorkOrder, completeWorkOrder, suspendWorkOrder, deleteWorkOrder } from '../api/workOrder'; +import { getSiteDevice } from '../api/device'; import { UniversalLineChart } from '../components/recharts'; import { orderTypeConfig, sourceTypeMap } from '../constants'; import { useSelector } from 'react-redux'; import { RootState } from '../store'; import { getUser } from '../api/user'; -import utils from '../lib/utils'; +import utils, { getWorkOrderTypeText, workOrderTypeOptions } from '../lib/utils'; const { Option } = Select; const { Search } = Input; @@ -78,6 +79,7 @@ const WorkOrderPage = () => { const [workOrderTotal, setWorkOrderTotal] = useState(0); const [currentDetail, setCurrentDetail] = useState(null); const [userList, setUserList] = useState([]); + const [deviceList, setDeviceList] = useState([]); // 从 redux 获取用户信息 const { userInfo } = useSelector((state: RootState) => state.user); @@ -108,6 +110,23 @@ const WorkOrderPage = () => { } }; + // 获取设备列表 + const fetchDeviceList = async () => { + try { + const res: any = await getSiteDevice({ + siteId: userInfo?.siteId, + pageNum: 1, + pageSize: 10000, + orgId: userInfo?.orgId + }); + if (res.code == 200) { + setDeviceList(res.rows || []); + } + } catch (error) { + console.error('获取设备列表失败', error); + } + }; + // 统计数据 const stats = [ { title: '今日新增工单', value: 18, subValue: '+3', subText: '较昨日', icon: , bg: '#e6f7ff' }, @@ -241,20 +260,24 @@ const WorkOrderPage = () => { setEditModalVisible(true); }; - // 打开派发工单弹窗 - const openDispatchModal = () => { - if (!selectedRowKey) return messageApi.warning('请先选择一条工单'); - if (!isAdmin) return messageApi.error('只有管理员才可以派发工单'); + // 打开执行工单弹窗 + const openExecuteModal = () => { + if (selectedRowKeys.length > 0) { + // 批量执行 + form.setFieldsValue({ + orderIds: selectedRowKeys + }); + } else if (selectedRowKey) { + // 单个执行 + form.setFieldsValue({ + orderIds: [selectedRowKey] + }); + } else { + return messageApi.warning('请先选择一条工单'); + } - fetchUserList(); - form.setFieldsValue({ - orderId: selectedRowKey, - assigneeId: currentDetail?.assigneeId ?? '', - assigneeName: currentDetail?.assigneeName || '', - planStartTime: currentDetail?.planStartTime ? dayjs(currentDetail.planStartTime) : null, - planEndTime: currentDetail?.planEndTime ? dayjs(currentDetail.planEndTime) : null, - deadlineTime: currentDetail?.deadlineTime ? dayjs(currentDetail.deadlineTime) : null, - }); + + fetchDeviceList(); setDispatchModalVisible(true); }; @@ -280,25 +303,21 @@ const WorkOrderPage = () => { } }; - // 提交派发工单 - const submitDispatch = async (values) => { + // 提交执行工单 + const submitExecute = async (values) => { try { + // 调用 startWorkOrder 接口,传递 ids 和 选择的设备 id const params = { - orderId: values.orderId, - assigneeId: values.assigneeId ?? '', - assigneeName: values.assigneeName, - collaboratorIds: '', - collaboratorNames: values.collaboratorNames?.join(',') || '', - planStartTime: values.planStartTime?.format('YYYY-MM-DDTHH:mm:ss') || '', - planEndTime: values.planEndTime?.format('YYYY-MM-DDTHH:mm:ss') || '', - deadlineTime: values.deadlineTime?.format('YYYY-MM-DDTHH:mm:ss') || '', + ids: values.orderIds, + deviceId: values.deviceId }; - await dispatchWorkOrder(params); - messageApi.success('工单派发成功'); + await startWorkOrder(params); + messageApi.success('工单执行成功'); setDispatchModalVisible(false); + setSelectedRowKeys([]); // 清空选中 getWorkOrderList(); } catch (error) { - messageApi.error('工单派发失败'); + messageApi.error('工单执行失败'); console.error(error); } }; @@ -517,20 +536,32 @@ const WorkOrderPage = () => { 工单列表 (共 {workOrderTotal} 条) {selectedRowKeys.length > 0 && ( - - + + - + + )} } bodyStyle={{ padding: 0 }} style={{ ...cardStyle, marginBottom: 8 }} > + { legend={{ position: 'right', layout: 'vertical' }} height={260} /> -
+
总数
{workOrderTotal}
@@ -623,14 +654,14 @@ const WorkOrderPage = () => { {currentDetail?.orderTitle || '暂无标题'} {currentDetail?.priorityLevel && ( - {priorityConfig[currentDetail.priorityLevel].label}优先级 + {currentDetail.priorityLevel} )}
来源:{sourceTypeMap[currentDetail?.sourceType] || '--'} - 工单类型:{orderTypeConfig[currentDetail?.orderType] || '--'} + 工单类型:{getWorkOrderTypeText(currentDetail?.orderType)}负责人:{currentDetail?.assigneeName || '--'}协同人员:{currentDetail?.collaboratorNames || '无'}计划时间: @@ -672,7 +703,7 @@ const WorkOrderPage = () => { }}> {item.active ? : item.step} -
{item.label}
+
{item?.label}
))} @@ -683,12 +714,11 @@ const WorkOrderPage = () => {
@@ -781,9 +811,8 @@ const WorkOrderPage = () => { - + @@ -800,30 +829,49 @@ const WorkOrderPage = () => { - {/* 派发工单弹窗 */} - setDispatchModalVisible(false)} footer={null} width={550}> -
- - - + {/* 执行工单弹窗 */} + setDispatchModalVisible(false)} footer={null} width={550}> + + + +
+ {selectedRowKeys.length > 0 ? ( + workOrderList + .filter(item => selectedRowKeys.includes(item.id)) + .map(item => ( +
+ • {item.orderTitle} ({item.orderNo}) +
+ )) + ) : ( +
• {currentDetail?.orderTitle} ({currentDetail?.orderNo})
+ )} +
+
+ -