import React, { useState, useEffect, useMemo } from 'react'; import { Table, Input, Button, Form, Select, Space, Tag, Modal, Card, Typography, Switch, InputNumber, Row, Col, App, } from 'antd'; import { SearchOutlined, ReloadOutlined, PlusOutlined, EditOutlined, DeleteOutlined, EyeOutlined, ExclamationCircleOutlined, } from '@ant-design/icons'; import { updateTriggerRule, selectRuleBySiteId, deleteRule, } from '@/api/rule'; import utils, { CompareEnumOptions } from '@/lib/utils'; import { useSelector } from 'react-redux'; import { RootState } from '@/src/store'; import { useTranslation } from 'react-i18next'; const { TextArea } = Input; const { Text } = Typography; const { Option } = Select; const AntdCard = Card as any; // 规则类型 const RuleTypeOptions = [ { value: 'LOW_BATTERY_RETURN', labelKey: 'ruleTypeLowBatteryReturn' }, ]; // 适用范围 const DeviceScopeOptions = [ { value: 'ALL', labelKey: 'deviceScopeAll' }, { value: 'GROUP', labelKey: 'deviceScopeGroup' }, { value: 'SOME', labelKey: 'deviceScopeSome' }, ]; // 触发字段 const TriggerKeyOptions = [ { value: 'battery', labelKey: 'triggerKeyBattery' }, { value: 'temperature', labelKey: 'triggerKeyTemperature' }, { value: 'offline_duration', labelKey: 'triggerKeyOfflineDuration' }, { value: 'voltage', labelKey: 'triggerKeyVoltage' }, ]; // 执行动作 const ActionTypeOptions = [ { value: 'AUTO_TASK', labelKey: 'actionTypeAutoTask' }, { value: 'SEND_COMMAND', labelKey: 'actionTypeSendCommand' }, { value: 'ALARM', labelKey: 'actionTypeAlarm' }, { value: 'WORK_ORDER', labelKey: 'actionTypeWorkOrder' }, ]; const rangeOperators = ['BETWEEN', 'NOT_BETWEEN']; export default function RuleConfigManagement() { const { t } = useTranslation(); 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 [ruleList, setRuleList] = useState([]); const [filteredList, setFilteredList] = useState([]); const [modalVisible, setModalVisible] = useState(false); const [modalType, setModalType] = useState<'add' | 'edit' | 'view'>('add'); const [currentRecord, setCurrentRecord] = useState(null); const [submitting, setSubmitting] = useState(false); // 获取规则列表 const fetchRuleList = async () => { if (!stationId) return; setLoading(true); try { const res = await selectRuleBySiteId({ siteId: stationId }); const list = res?.data || res?.rows || []; setRuleList(list); setFilteredList(list); } catch (err) { utils.message.error(t('systemSetting.operationFailed')); } finally { setLoading(false); } }; useEffect(() => { fetchRuleList(); }, [stationId]); // 搜索过滤 const handleSearch = () => { searchForm.validateFields().then(values => { const next = ruleList.filter(item => { const matchName = !values.ruleName || (item.ruleName || '').toLowerCase().includes(values.ruleName.toLowerCase()); const matchType = !values.ruleType || item.ruleType === values.ruleType; const matchEnabled = values.enabled === undefined || values.enabled === '' || Number(item.enabled) === Number(values.enabled); return matchName && matchType && matchEnabled; }); setFilteredList(next); }); }; const handleReset = () => { searchForm.resetFields(); setFilteredList(ruleList); }; // 打开弹窗 const openModal = (type: 'add' | 'edit' | 'view', record?: any) => { setModalType(type); setCurrentRecord(record || null); setModalVisible(true); form.resetFields(); if ((type === 'edit' || type === 'view') && record) { const conditions = record.conditions || {}; const actionConfig = record.actionConfig || {}; const isRange = rangeOperators.includes(conditions.operator); let min = ''; let max = ''; let value = conditions.value; if (isRange && typeof conditions.value === 'string' && conditions.value.includes(',')) { const [a, b] = conditions.value.split(','); min = a; max = b; value = ''; } let argsJson = ''; try { argsJson = actionConfig.args ? JSON.stringify(actionConfig.args, null, 2) : ''; } catch (e) { argsJson = ''; } form.setFieldsValue({ ruleName: record.ruleName, ruleType: record.ruleType, deviceScope: record.deviceScope, key: conditions.key, operator: conditions.operator, value, min, max, actionType: actionConfig.actionType, argsJson, enabled: record.enabled === true || record.enabled === 1, ForceExecute: record.ForceExecute === true || record.ForceExecute === 1, }); } else { form.setFieldsValue({ ruleType: 'LOW_BATTERY_RETURN', deviceScope: 'ALL', key: 'battery', operator: 'LT', actionType: 'AUTO_TASK', enabled: true, ForceExecute: false, }); } }; // 保存规则 const handleSave = async (values: any) => { let argsObj: any = {}; if (values.argsJson) { try { argsObj = JSON.parse(values.argsJson); } catch (e) { utils.message.error(t('systemSetting.invalidJson')); return; } } const isRange = rangeOperators.includes(values.operator); const value = isRange ? `${values.min},${values.max}` : values.value; const submitData: any = { id: modalType === 'edit' ? currentRecord?.id : undefined, ruleName: values.ruleName, ruleType: values.ruleType, deviceScope: values.deviceScope, conditions: { key: values.key, operator: values.operator, value, }, actionConfig: { actionType: values.actionType, args: argsObj, }, enabled: values.enabled ? 1 : 0, ForceExecute: values.ForceExecute ? 1 : 0, orgId: currentStation?.orgId || '', siteId: stationId || '', }; setSubmitting(true); try { const res = await updateTriggerRule(submitData); if (res?.code === 200) { utils.message.success(modalType === 'edit' ? t('common.editSuccess') : t('common.addSuccess')); setModalVisible(false); fetchRuleList(); } else { utils.message.error(res?.msg || t('systemSetting.operationFailed')); } } catch (err) { utils.message.error(t('systemSetting.operationFailed')); } finally { setSubmitting(false); } }; // 删除规则 const handleDelete = (record: any) => { modal.confirm({ title: t('common.confirmDelete'), content: t('systemSetting.confirmDeleteRule', { name: record.ruleName }), icon: , okText: t('common.ok'), cancelText: t('common.cancel'), onOk: async () => { try { const res = await deleteRule({ ids: [record.id] }); if (res?.code === 200) { utils.message.success(t('common.deleteSuccess')); fetchRuleList(); } else { utils.message.error(res?.msg || t('common.deleteFail')); } } catch (err) { utils.message.error(t('common.deleteFail')); } }, }); }; const operatorWatch = Form.useWatch('operator', form); const isRangeOperator = useMemo(() => rangeOperators.includes(operatorWatch), [operatorWatch]); const getLabel = (options: any[], value: string, ns: string = 'systemSetting') => { const item = options.find(o => o.value === value); return item ? t(`${ns}.${item.labelKey}`) : value; }; const renderConditions = (record: any) => { const c = record.conditions || {}; const opItem = CompareEnumOptions.find(o => o.value === c.operator); const opLabel = opItem ? t(`constants.${opItem.label}`) : c.operator; return {c.key} {opLabel} {c.value}; }; const columns = [ { title: t('systemSetting.ruleName'), dataIndex: 'ruleName', key: 'ruleName', width: 180, fixed: 'left' as const, }, { title: t('systemSetting.ruleType'), dataIndex: 'ruleType', key: 'ruleType', width: 140, render: (text: string) => getLabel(RuleTypeOptions, text), }, { title: t('systemSetting.deviceScope'), dataIndex: 'deviceScope', key: 'deviceScope', width: 120, render: (text: string) => getLabel(DeviceScopeOptions, text), }, { title: t('systemSetting.triggerKey'), key: 'conditions', width: 220, render: (_: any, record: any) => renderConditions(record), }, { title: t('systemSetting.actionType'), dataIndex: ['actionConfig', 'actionType'], key: 'actionType', width: 140, render: (text: string) => getLabel(ActionTypeOptions, text), }, { title: t('systemSetting.enabledLabel'), dataIndex: 'enabled', key: 'enabled', width: 100, render: (val: any) => {val === 1 || val === true ? t('common.enable') : t('common.disable')}, }, { title: t('systemSetting.forceExecute'), dataIndex: 'ForceExecute', key: 'ForceExecute', width: 100, render: (val: any) => {val === 1 || val === true ? t('common.yes') : t('common.no')}, }, { title: t('common.actions'), key: 'action', width: 220, fixed: 'right' as const, render: (_: any, record: any) => ( ), }, ]; const disabledForm = modalType === 'view'; return (
t('common.total', { total }) }} /> setModalVisible(false)} footer={ disabledForm ? ( ) : ( ) } >
{isRangeOperator ? ( ) : ( )}