整合 F1 DagCanvas + F2 DagNodePanel + F3 DagPropertyPanel + F8 dag-converter
为完整的规则链 v2 编辑体验。
新增:
- apps/web-antd/src/views/iot/rule/chain/api/rule-chain.ts
- CRUD + enable/disable/deploy/debug/copy API 封装
- apps/web-antd/src/views/iot/rule/chain/stores/rule-chain.ts
- Pinia store: meta + nodes + edges + dirty guard + save/deploy 区分新建/更新
- apps/web-antd/src/views/iot/rule/chain/list.vue
- 4 维筛选(name/subsystem/type/status)+ 分页 20/50/100
- 启用/禁用乐观 UI + 调试切换 Modal 确认(评审 B8)
- WARNING 状态红 Badge + Tooltip 物模型变更原因(评审 E4)
- 双入口 Banner Alert 链到 v1 scene 页(决策 5)
- hasAccessByCodes iot:rule:{update,delete} 权限
- apps/web-antd/src/views/iot/rule/chain/edit.vue
- 三栏布局:DagNodePanel(220) + DagCanvas + DagPropertyPanel(320)
- drop 处理: handleDragOver preventDefault / screenToFlowCoordinate /
newTempId 新节点临时 ID
- nodeClick → selectedNodeStore.setCurrent; paneClick → clear
- onBeforeRouteLeave dirty 拦截 + Modal 确认
- readonly 模式: status=ENABLED 默认只读,可"解锁编辑"
- 发布: save(若 dirty) → deploy API(Modal 前置确认)
- 创建后 router.replace 含真实 ID
- apps/web-antd/src/views/iot/rule/chain/__tests__/rule-chain-store.spec.ts (10 用例)
修改:
- apps/web-antd/src/router/routes/modules/iot.ts
追加 /iot/rule/chain (顶级) + /iot/rule/chain/edit/:id? (hideInMenu detail)
- locales/langs/{zh-CN,en-US}/page.json 补 iot.ruleChain.* 约 80 键
Known Pitfalls 落地:
- drop 兼容: handleDragOver 调 e.preventDefault + dropEffect='copy'(Firefox)
- 临时 ID: newTempId 生成 new_<uuid>,isTempId 识别 INSERT(F8 约定)
- dirty guard: onBeforeRouteLeave 拦截离开
- readonly: status=ENABLED 已发布默认只读,可解锁重新编辑
- 画布双击 nodeDblclick 已透传(DagPropertyPanel 可选聚焦)
决策 5 双入口落实:
- 现有 /iot/rule/scene/* (v1 表单) 完全保留
- 新增 /iot/rule/chain/* (v2 DAG)
- list.vue 顶部 Alert 给用户两入口引导
质检:
- pnpm test:unit src/views/iot/rule/chain → 58/58 通过(累计 F1-F8 测试)
- pnpm lint F7 files → 0 errors
- pnpm check:type chain → 0 errors
后端 B2 API 契约:
- GET /iot/rule-chain/page / get?id=
- POST /iot/rule-chain/create PUT update DELETE delete?id=
- PUT enable / disable / deploy / debug
- POST copy?id=
- GET /iot/subsystem/simple-list (F9 提供,F7 筛选器复用)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
392 lines
13 KiB
JSON
392 lines
13 KiB
JSON
{
|
|
"auth": {
|
|
"login": "Login",
|
|
"register": "Register",
|
|
"codeLogin": "Code Login",
|
|
"qrcodeLogin": "Qr Code Login",
|
|
"forgetPassword": "Forget Password",
|
|
"profile": "Profile"
|
|
},
|
|
"dashboard": {
|
|
"title": "Dashboard",
|
|
"analytics": "Analytics",
|
|
"workspace": "Workspace"
|
|
},
|
|
"action": {
|
|
"action": "Action",
|
|
"add": "Add",
|
|
"edit": "Edit",
|
|
"delete": "Delete",
|
|
"save": "Save",
|
|
"import": "Import",
|
|
"export": "Export",
|
|
"submit": "Submit",
|
|
"cancel": "Cancel",
|
|
"confirm": "Confirm",
|
|
"reset": "Reset",
|
|
"search": "Search",
|
|
"more": "More"
|
|
},
|
|
"tenant": {
|
|
"placeholder": "Please select tenant",
|
|
"success": "Switch tenant success"
|
|
},
|
|
"mp": {
|
|
"upload": {
|
|
"invalidFormat": "Invalid {0} format!",
|
|
"maxSize": "{0} size cannot exceed {1}M!",
|
|
"image": "Image",
|
|
"video": "Video",
|
|
"voice": "Voice"
|
|
}
|
|
},
|
|
"iot": {
|
|
"device": {
|
|
"filter": {
|
|
"subsystem": {
|
|
"placeholder": "All Subsystems",
|
|
"unassigned": "Unassigned",
|
|
"bind": "Bind Subsystem",
|
|
"bindTitle": "Bind Subsystem",
|
|
"batchBind": "Batch Bind Subsystem",
|
|
"batchBindTitle": "Batch Bind Subsystem",
|
|
"batchBindSuccess": "{0} device(s) bound successfully",
|
|
"bindSuccess": "Bound successfully",
|
|
"selectRequired": "Please select a subsystem first",
|
|
"selectDeviceTip": "Please select devices first",
|
|
"binding": "Binding...",
|
|
"batchBindHint": "{0} device(s) selected, max 100 per batch"
|
|
}
|
|
}
|
|
},
|
|
"subsystem": {
|
|
"title": "Subsystem",
|
|
"listTitle": "Subsystem List",
|
|
"name": "Name",
|
|
"namePlaceholder": "Enter subsystem name",
|
|
"nameRequired": "Name is required",
|
|
"nameTooLong": "Name must not exceed 128 characters",
|
|
"code": "Code",
|
|
"codePlaceholder": "e.g. security, clean",
|
|
"codeRequired": "Code is required",
|
|
"codeTooLong": "Code must not exceed 64 characters",
|
|
"codeFormat": "Code must start with a lowercase letter and contain only lowercase letters, digits, or underscores",
|
|
"codeHelp": "Format: starts with a letter, allows digits and underscores, e.g. clean_01",
|
|
"status": "Status",
|
|
"statusEnabled": "Enabled",
|
|
"statusDisabled": "Disabled",
|
|
"statusPlaceholder": "Select status",
|
|
"icon": "Icon",
|
|
"iconPlaceholder": "Icon URL or iconify name",
|
|
"description": "Description",
|
|
"descriptionPlaceholder": "Enter description",
|
|
"deviceCount": "Devices",
|
|
"searchPlaceholder": "Search name / code",
|
|
"viewDevices": "View Devices",
|
|
"deleteDisabledTip": "Cannot delete: subsystem has {0} device(s)",
|
|
"deviceListTitle": "Subsystem Devices",
|
|
"deviceName": "Device Name",
|
|
"serialNumber": "Serial Number",
|
|
"deviceState": "Online State",
|
|
"deviceOnline": "Online",
|
|
"deviceOffline": "Offline",
|
|
"deviceSearchPlaceholder": "Search device name",
|
|
"statTotal": "Total Devices",
|
|
"statOnline": "Online",
|
|
"statAlarm": "Alarming",
|
|
"statsNotice": "Stats are provided by Redis cache and may have a short delay (⚠️ A6)",
|
|
"batchBind": "Batch Bind Devices",
|
|
"batchBindHint": "Max 100 devices per batch",
|
|
"bindSuccess": "{0} device(s) bound successfully",
|
|
"selectDeviceTip": "Please select devices first",
|
|
"unbind": "Remove",
|
|
"unbindConfirm": "Remove device \"{0}\" from this subsystem?",
|
|
"unbinding": "Removing device...",
|
|
"unbindSuccess": "Device removed",
|
|
"transferCandidate": "Candidate Devices",
|
|
"transferSelected": "Selected Devices"
|
|
},
|
|
"dag": {
|
|
"toolbar": {
|
|
"zoomIn": "Zoom In",
|
|
"zoomOut": "Zoom Out",
|
|
"fitView": "Fit View",
|
|
"undo": "Undo (Ctrl+Z)",
|
|
"redo": "Redo (Ctrl+Y)",
|
|
"save": "Save"
|
|
},
|
|
"canvas": {
|
|
"readonly": "Read-only mode",
|
|
"empty": "Drag nodes to canvas to start"
|
|
},
|
|
"panel": {
|
|
"title": "Node Types",
|
|
"searchPlaceholder": "Search nodes...",
|
|
"loading": "Loading...",
|
|
"loadError": "Load failed, please refresh",
|
|
"noMatch": "No matching nodes found",
|
|
"category": {
|
|
"trigger": "Trigger",
|
|
"condition": "Condition",
|
|
"action": "Action"
|
|
},
|
|
"empty": "Click a node on canvas to configure",
|
|
"schemaUnknown": "Unknown node type or metadata not loaded",
|
|
"titleEmpty": "Property Panel",
|
|
"titleFallback": "Node Configuration",
|
|
"nodeLabel": "Node Name",
|
|
"nodeLabelPlaceholder": "Enter node name"
|
|
},
|
|
"field": {
|
|
"device": "Device",
|
|
"devicePlaceholder": "Select device",
|
|
"propertyIdentifier": "Property",
|
|
"propertyPlaceholder": "Enter property identifier",
|
|
"dataType": "Data Type",
|
|
"deviceEvent": "Event",
|
|
"cron": "Cron Expression",
|
|
"timezone": "Timezone",
|
|
"expression": "Expression",
|
|
"value": "Value",
|
|
"valuePlaceholder": "Enter value",
|
|
"alarmName": "Alarm Name",
|
|
"alarmMessage": "Alarm Message",
|
|
"alarmMessagePlaceholder": "Supports ${data.x} / ${meta.x} / ${alarm.x} / ${trigger.x}",
|
|
"severity": "Severity",
|
|
"notifyChannel": "Notification Channel",
|
|
"notifyRecipients": "Recipients",
|
|
"notifyRecipientsPlaceholder": "Multiple recipients separated by commas",
|
|
"notifyTemplate": "Message Template"
|
|
},
|
|
"option": {
|
|
"online": "Online",
|
|
"offline": "Offline"
|
|
},
|
|
"validate": {
|
|
"deviceRequired": "Please select a device",
|
|
"propertyRequired": "Please enter property identifier",
|
|
"cronRequired": "Please enter a Cron expression",
|
|
"expressionRequired": "Please enter an expression",
|
|
"valueRequired": "Please enter a value",
|
|
"alarmNameRequired": "Please enter an alarm name"
|
|
},
|
|
"node": {
|
|
"device_state": {
|
|
"name": "Device State",
|
|
"desc": "Triggered when device goes online/offline"
|
|
},
|
|
"device_property": {
|
|
"name": "Device Property Report",
|
|
"desc": "Triggered when device reports property data"
|
|
},
|
|
"device_event": {
|
|
"name": "Device Event",
|
|
"desc": "Triggered when device reports an event"
|
|
},
|
|
"device_service": {
|
|
"name": "Device Service Call",
|
|
"desc": "Triggered when device service is invoked"
|
|
},
|
|
"timer": {
|
|
"name": "Timer",
|
|
"desc": "Triggered by Cron expression on schedule"
|
|
},
|
|
"expression": {
|
|
"name": "Expression",
|
|
"desc": "Aviator expression evaluation condition"
|
|
},
|
|
"time_range": {
|
|
"name": "Time Range",
|
|
"desc": "Check if current time is within a specified range"
|
|
},
|
|
"condition_device_state": {
|
|
"name": "Device State Check",
|
|
"desc": "Check current device online status"
|
|
},
|
|
"property_set": {
|
|
"name": "Set Property",
|
|
"desc": "Send property set command to device"
|
|
},
|
|
"service_invoke": {
|
|
"name": "Invoke Service",
|
|
"desc": "Invoke device service"
|
|
},
|
|
"alarm_trigger": {
|
|
"name": "Trigger Alarm",
|
|
"desc": "Trigger a device alarm"
|
|
},
|
|
"alarm_clear": {
|
|
"name": "Clear Alarm",
|
|
"desc": "Clear a device alarm"
|
|
},
|
|
"notify": {
|
|
"name": "Send Notification",
|
|
"desc": "Send notification (SMS/Email/Webhook)"
|
|
}
|
|
}
|
|
},
|
|
"alarm": {
|
|
"state": {
|
|
"archived": "Archived",
|
|
"activeUnacked": "Active·Unacked",
|
|
"activeAcked": "Active·Acked",
|
|
"clearedUnacked": "Cleared·Unacked",
|
|
"clearedAcked": "Cleared·Acked",
|
|
"unknown": "Unknown"
|
|
},
|
|
"severity": {
|
|
"critical": "Critical",
|
|
"major": "Major",
|
|
"minor": "Minor",
|
|
"warning": "Warning",
|
|
"info": "Info",
|
|
"unknown": "Unknown"
|
|
},
|
|
"list": {
|
|
"title": "Alarm Records"
|
|
},
|
|
"field": {
|
|
"alarmName": "Alarm Name",
|
|
"severity": "Severity",
|
|
"state": "State",
|
|
"deviceName": "Device",
|
|
"productName": "Product",
|
|
"triggerCount": "Trigger Count",
|
|
"startTs": "First Triggered",
|
|
"endTs": "Last Triggered",
|
|
"clearTs": "Cleared At",
|
|
"ackTs": "Acked At"
|
|
},
|
|
"filter": {
|
|
"all": "All",
|
|
"ackState": "Ack State",
|
|
"clearState": "Clear State",
|
|
"archived": "Archive",
|
|
"unacked": "Unacked",
|
|
"acked": "Acked",
|
|
"active": "Active",
|
|
"cleared": "Cleared",
|
|
"excludeArchived": "Exclude Archived",
|
|
"allRecords": "All Records",
|
|
"onlyArchived": "Archived Only"
|
|
},
|
|
"op": {
|
|
"ack": "Ack",
|
|
"unack": "Unack",
|
|
"clear": "Clear",
|
|
"archive": "Archive",
|
|
"noOps": "No Operations",
|
|
"confirmTitle": "Confirm",
|
|
"ackConfirm": "Acknowledge this alarm?",
|
|
"unackConfirm": "Unacknowledge this alarm?",
|
|
"clearConfirm": "Clear this alarm?",
|
|
"archiveConfirm": "Archive this alarm? No further operations after archiving.",
|
|
"batchAck": "Batch Ack",
|
|
"batchClear": "Batch Clear",
|
|
"batchArchive": "Batch Archive",
|
|
"batchAckConfirm": "Acknowledge {count} selected alarm(s)?",
|
|
"batchClearConfirm": "Clear {count} selected alarm(s)?",
|
|
"batchArchiveConfirm": "Archive {count} selected alarm(s)? No further operations after archiving.",
|
|
"selectFirst": "Please select records first",
|
|
"ackSuccess": "Acknowledged",
|
|
"unackSuccess": "Unacknowledged",
|
|
"clearSuccess": "Cleared",
|
|
"archiveSuccess": "Archived"
|
|
},
|
|
"detail": {
|
|
"title": "Alarm Detail",
|
|
"basicInfo": "Basic Info",
|
|
"propagationPath": "Propagation Path",
|
|
"details": "Alarm Details",
|
|
"remark": "Process Remark",
|
|
"remarkPlaceholder": "Enter remark",
|
|
"remarkSaved": "Remark saved",
|
|
"history": "History",
|
|
"noHistory": "No history records"
|
|
}
|
|
},
|
|
"ruleChain": {
|
|
"list": {
|
|
"title": "Rule Chain List"
|
|
},
|
|
"edit": {
|
|
"title": "Edit Rule Chain",
|
|
"namePlaceholder": "Enter rule chain name",
|
|
"nameRequired": "Rule chain name is required",
|
|
"loadFailed": "Failed to load rule chain, please retry",
|
|
"saveFailed": "Save failed, please retry",
|
|
"unsavedTitle": "Unsaved Changes",
|
|
"unsavedContent": "Leaving this page will discard unsaved changes. Continue?",
|
|
"unsavedLeave": "Leave",
|
|
"unsavedBadge": "Unsaved",
|
|
"unlockTip": "This rule chain is enabled. Click to unlock for editing.",
|
|
"dropHint": "Drag nodes to canvas to start building"
|
|
},
|
|
"v1Banner": {
|
|
"message": "Scene Rules (v1) Migrated",
|
|
"description": "v2 Rule Chain DAG is the new orchestration entry. v1 Scene Rules page remains available during the gradual rollout.",
|
|
"link": "Go to v1 Scene Rules"
|
|
},
|
|
"field": {
|
|
"name": "Name",
|
|
"type": "Type",
|
|
"subsystem": "Subsystem",
|
|
"bindScope": "Bind Scope",
|
|
"product": "Product",
|
|
"device": "Device",
|
|
"priority": "Priority",
|
|
"status": "Status",
|
|
"debugMode": "Debug Mode",
|
|
"version": "Version",
|
|
"updateTime": "Updated At",
|
|
"actions": "Actions"
|
|
},
|
|
"filter": {
|
|
"namePlaceholder": "Search name",
|
|
"subsystemPlaceholder": "All Subsystems",
|
|
"typePlaceholder": "All Types",
|
|
"statusPlaceholder": "All Status"
|
|
},
|
|
"status": {
|
|
"enabled": "Enabled",
|
|
"disabled": "Disabled",
|
|
"warning": "Warning",
|
|
"warningHint": "Model changes caused some rules to fail. Please check the rule chain configuration."
|
|
},
|
|
"type": {
|
|
"scene": "Scene Linkage",
|
|
"data": "Data Forwarding",
|
|
"custom": "Custom"
|
|
},
|
|
"debug": {
|
|
"confirmTitle": "Enable Debug Mode",
|
|
"confirmContent": "Debug mode records input/output for each node and may produce large logs affecting performance. Confirm?",
|
|
"toggleSuccess": "Debug mode toggled",
|
|
"toggleFailed": "Toggle failed, please retry"
|
|
},
|
|
"action": {
|
|
"create": "New Rule Chain",
|
|
"enable": "Enable",
|
|
"disable": "Disable",
|
|
"enableSuccess": "Enabled successfully",
|
|
"disableSuccess": "Disabled successfully",
|
|
"statusChangeFailed": "Operation failed, please retry",
|
|
"deleteConfirm": "Delete this rule chain? This action cannot be undone.",
|
|
"deleteSuccess": "Deleted successfully",
|
|
"deleteFailed": "Delete failed, please retry",
|
|
"copy": "Copy",
|
|
"copySuccess": "Copied successfully",
|
|
"copyFailed": "Copy failed, please retry",
|
|
"deploy": "Deploy",
|
|
"deployConfirmTitle": "Confirm Deploy",
|
|
"deployConfirmContent": "After deploying, the rule chain will take effect immediately at runtime. Please confirm the configuration is correct.",
|
|
"deploySuccess": "Deployed successfully",
|
|
"deployFailed": "Deploy failed, please retry",
|
|
"saveSuccess": "Saved successfully",
|
|
"unlock": "Unlock for Editing",
|
|
"back": "Back to List"
|
|
}
|
|
}
|
|
}
|
|
}
|