From 62b03bba03b25d6b80f0694ba8752989ba11c1c3 Mon Sep 17 00:00:00 2001 From: mmc <1556375442@qq.com> Date: Fri, 14 Aug 2026 09:02:52 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=AD=E8=8B=B1=E6=96=87=E5=88=87=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .workbuddy/gen_doc.py | 4 +- .workbuddy/memory/2026-08-11.md | 4 +- docs/系统架构设计文档.html | 1275 +++-- i18n_progress_20260812.md | 25 + index.html | 2 +- metadata.json | 2 +- src/components/DictManager.jsx | 29 +- src/components/LanguageSwitcher.tsx | 6 +- src/components/Map.jsx | 8 +- .../SystemSetting/AlarmSettingsManagement.tsx | 54 +- .../SystemSetting/AllDeviceManagement.tsx | 47 +- .../SystemSetting/BasicSettings.tsx | 106 +- .../SystemSetting/CameraManagement.tsx | 231 +- .../SystemSetting/DeviceAccessPage.tsx | 282 +- .../SystemSetting/DeviceManagement.tsx | 8 +- .../SystemSetting/DroneManagement.tsx | 82 +- src/components/SystemSetting/FlvPlayer.tsx | 35 +- .../SystemSetting/LogManagement.tsx | 44 +- src/components/SystemSetting/MenuList.tsx | 20 +- src/components/SystemSetting/OnlineUser.tsx | 103 +- src/components/SystemSetting/Organization.tsx | 36 +- .../SystemSetting/SerialNumGenerate.jsx | 82 +- .../SystemSetting/SmartDeviceManagement.tsx | 124 +- .../SystemSetting/StationManage.tsx | 80 +- .../SystemSetting/SystemMaintain.tsx | 6 +- src/components/SystemSetting/UserPage.tsx | 62 +- .../SystemSetting/VideoManagement.tsx | 30 +- .../WorkOrderSettingsManagement.tsx | 60 +- src/components/VideoPlayer.tsx | 2 +- src/components/alerts/AlarmHistory.tsx | 86 +- src/components/alerts/AlarmOverview.tsx | 182 +- src/components/alerts/AlarmRealtime.tsx | 33 +- src/components/alerts/AlarmRules.tsx | 43 +- src/components/alerts/AlarmStatistics.tsx | 16 +- src/components/alerts/AlarmSubscription.tsx | 60 +- src/components/devices/DeviceControl.tsx | 265 +- src/components/devices/DeviceOverviewPage.tsx | 187 +- src/components/devices/DeviceParamModal.tsx | 100 +- src/components/devices/DeviceStatusPage.tsx | 182 +- src/components/devices/DroneVideoPlayer.tsx | 67 +- src/components/devices/RobotTaskPage.tsx | 42 +- src/components/devices/WayLinePage.tsx | 358 +- src/components/devices/deviceController.js | 24 +- src/locales/en/index.ts | 2688 +++++++++- src/locales/zh/index.ts | 2707 +++++++++- src/pages/AIAnalysis.tsx | 216 +- src/pages/Agri.jsx | 124 +- src/pages/Alerts.tsx | 8 +- src/pages/CleanOptimization.tsx | 269 +- src/pages/Devices.tsx | 4 +- src/pages/Downloads.tsx | 20 +- src/pages/Home.tsx | 72 +- src/pages/Login.tsx | 10 +- src/pages/Profile.tsx | 52 +- src/pages/RealTimeMonitor.tsx | 56 +- src/pages/ReportCenter.tsx | 288 +- src/pages/Roles.tsx | 36 +- src/pages/SystemSetting.tsx | 2 +- src/pages/Users.tsx | 18 +- src/pages/Video.tsx | 30 +- src/pages/VideoMonitorPage.tsx | 91 +- src/pages/WorkOrder.tsx | 170 +- src/router/index.tsx | 65 +- src/types/index.ts | 2 +- tmp_additions.json | 4692 +++++++++++++++++ tmp_analyze2.cjs | 80 + tmp_analyze2_output.txt | Bin 0 -> 386560 bytes tmp_cjk.json | 2544 +++++++++ tmp_cjk_output.txt | Bin 0 -> 224766 bytes tmp_compare.cjs | 45 + tmp_dbg.cjs | 34 + tmp_dbg3.cjs | 15 + tmp_finalcheck.cjs | 23 + tmp_findcjk.cjs | 51 + tmp_fixkeys.cjs | 25 + tmp_gen.cjs | 563 ++ tmp_inject.cjs | 64 + tmp_inject2.cjs | 75 + tmp_inspect.cjs | 9 + tmp_missing.json | 2202 ++++++++ tmp_newkeys.json | 118 + tmp_orb.cjs | 28 + tmp_pagefiles.txt | 33 + tmp_perfile.cjs | 43 + tmp_replace.cjs | 148 + tmp_scan_cjk.cjs | 45 + tmp_scanfalse.cjs | 16 + tmp_test.cjs | 27 + tmp_tokens.cjs | 12 + tmp_verify.cjs | 8 + tmp_verify2.cjs | 44 + tmp_verify3.cjs | 49 + tmp_verify_i18n.cjs | 74 + tsc_baseline.txt | Bin 0 -> 189792 bytes 94 files changed, 19723 insertions(+), 2766 deletions(-) create mode 100644 i18n_progress_20260812.md create mode 100644 tmp_additions.json create mode 100644 tmp_analyze2.cjs create mode 100644 tmp_analyze2_output.txt create mode 100644 tmp_cjk.json create mode 100644 tmp_cjk_output.txt create mode 100644 tmp_compare.cjs create mode 100644 tmp_dbg.cjs create mode 100644 tmp_dbg3.cjs create mode 100644 tmp_finalcheck.cjs create mode 100644 tmp_findcjk.cjs create mode 100644 tmp_fixkeys.cjs create mode 100644 tmp_gen.cjs create mode 100644 tmp_inject.cjs create mode 100644 tmp_inject2.cjs create mode 100644 tmp_inspect.cjs create mode 100644 tmp_missing.json create mode 100644 tmp_newkeys.json create mode 100644 tmp_orb.cjs create mode 100644 tmp_pagefiles.txt create mode 100644 tmp_perfile.cjs create mode 100644 tmp_replace.cjs create mode 100644 tmp_scan_cjk.cjs create mode 100644 tmp_scanfalse.cjs create mode 100644 tmp_test.cjs create mode 100644 tmp_tokens.cjs create mode 100644 tmp_verify.cjs create mode 100644 tmp_verify2.cjs create mode 100644 tmp_verify3.cjs create mode 100644 tmp_verify_i18n.cjs create mode 100644 tsc_baseline.txt diff --git a/.workbuddy/gen_doc.py b/.workbuddy/gen_doc.py index 2c49390..d82ee30 100644 --- a/.workbuddy/gen_doc.py +++ b/.workbuddy/gen_doc.py @@ -14,7 +14,7 @@ from docx.oxml import OxmlElement SCREENSHOT_DIR = r"D:\code\机器人智慧服务系统\.workbuddy\screenshots" OUTPUT_PATH = r"D:\code\机器人智慧服务系统\用户手册-机器人智慧服务系统.docx" -TITLE = "光伏电站智能监控与运维系统" +TITLE = "光伏机器人环境感知服务平台" SUBTITLE = "用户操作手册" VERSION = "V1.0" DATE = "2026年8月" @@ -128,7 +128,7 @@ def build_doc(): # ==================== 1. 系统概述 ==================== make_heading(doc, "一、系统概述", 1) - make_body(doc, "机器人智慧服务平台(正式名称:光伏电站智能监控与运维系统)是一个面向光伏电站场景的综合性智能管理平台。系统将巡检机器人、无人机、视频监控、告警处理与运维工单整合到统一 Web 控制台中,为运维人员提供设备管理、实时监控、视频回传、状态预警、AI 诊断分析、清洗优化、报表中心与视频下载等一站式能力。") + make_body(doc, "机器人智慧服务平台(正式名称:光伏机器人环境感知服务平台)是一个面向光伏电站场景的综合性智能管理平台。系统将巡检机器人、无人机、视频监控、告警处理与运维工单整合到统一 Web 控制台中,为运维人员提供设备管理、实时监控、视频回传、状态预警、AI 诊断分析、清洗优化、报表中心与视频下载等一站式能力。") make_heading(doc, "1.1 系统架构", 2) make_body(doc, "系统采用前后端分离的 B/S 架构,前端基于 React 19 + TypeScript + Vite 6 构建为单页应用(SPA),通过四条独立通信通道对接后端的 API、MQTT、WebSocket 与 GeoServer 服务:") diff --git a/.workbuddy/memory/2026-08-11.md b/.workbuddy/memory/2026-08-11.md index 7f78c47..5551429 100644 --- a/.workbuddy/memory/2026-08-11.md +++ b/.workbuddy/memory/2026-08-11.md @@ -1,7 +1,7 @@ # 2026-08-11 工作日志 ## 用户手册生成 -- 根据"机器人智慧服务系统"(光伏电站智能监控与运维系统)项目,生成了完整的用户操作手册 Word 文档 +- 根据"机器人智慧服务系统"(光伏机器人环境感知服务平台)项目,生成了完整的用户操作手册 Word 文档 - 文档路径:`用户手册-机器人智慧服务系统.docx` ### 截图获取方案 @@ -32,5 +32,5 @@ ## 软著采集表填写 - 根据项目信息,完整填写了软著采集表,保存至 `C:/Users/15563/Desktop/软著/采集表(已填写).doc` -- 填入内容涵盖 20 个字段:软件名称(光伏电站智能监控与运维系统)、简称(光伏智能运维系统)、版本V1.0、分类(应用软件)、开发/发表日期、硬件环境、编程语言、源程序量(约53700行)、开发目的、面向领域(光伏新能源/工业物联网)、主要功能(701字描述10大模块)、主要特点、软件类型(物联网软件)等 +- 填入内容涵盖 20 个字段:软件名称(光伏机器人环境感知服务平台)、简称(光伏智能运维系统)、版本V1.0、分类(应用软件)、开发/发表日期、硬件环境、编程语言、源程序量(约53700行)、开发目的、面向领域(光伏新能源/工业物联网)、主要功能(701字描述10大模块)、主要特点、软件类型(物联网软件)等 - 使用 editor_sdk 的 doc_modify_table_region 工具逐行填写表格单元格 diff --git a/docs/系统架构设计文档.html b/docs/系统架构设计文档.html index aa9e13b..482555e 100644 --- a/docs/系统架构设计文档.html +++ b/docs/系统架构设计文档.html @@ -1,359 +1,919 @@ + - - -光伏电站智能监控与运维系统 · 架构设计文档 - + + + 光伏机器人环境感知服务平台 · 架构设计文档 + + -
+
-
-
架构设计文档React 19Vite 6
-

光伏电站智能监控与运维系统

-
机器人智慧服务平台 · 前端系统架构与设计说明
-
- 项目代号:机器人智慧服务系统 - 文档版本:v1.0 - 生成日期:2026-08-10 -
-
+
+
架构设计文档React 19Vite + 6
+

光伏机器人环境感知服务平台

+
机器人智慧服务平台 · 前端系统架构与设计说明
+
+ 项目代号:机器人智慧服务系统 + 文档版本:v1.0 + 生成日期:2026-08-10 +
+
- + -
-

1项目概述

-

本项目(metadata 名:光伏电站智能监控与运维系统)是一个面向光伏电站场景的机器人智能管理平台。它将巡检机器人 / 无人机、视频监控、告警处理与运维工单整合到单一 Web 控制台,提供设备管理、实时监控、视频回传、状态预警、AI 诊断分析、清洗优化、报表中心与视频下载等一站式能力。

+
+

1项目概述

+

本项目(metadata 名:光伏机器人环境感知服务平台)是一个面向光伏电站场景的机器人智能管理平台。它将巡检机器人 / + 无人机、视频监控、告警处理与运维工单整合到单一 Web 控制台,提供设备管理、实时监控、视频回传、状态预警、AI 诊断分析、清洗优化、报表中心与视频下载等一站式能力。

-
-
4
通信协议通道
-
17
业务页面
-
全懒加载
路由按需加载
-
RBAC
三级角色权限
-
+
+
+
4
+
通信协议通道
+
+
+
17
+
业务页面
+
+
+
全懒加载
+
路由按需加载
+
+
+
RBAC
+
三级角色权限
+
+
-

系统定位是前端 SPA,通过四条独立通信通道对接后端的 API、MQTT、WebSocket 与 GeoServer 服务,实现“低延迟控制 + 高吞吐遥测 + 实时视频”的多协议协同。

-
+

系统定位是前端 SPA,通过四条独立通信通道对接后端的 API、MQTT、WebSocket 与 GeoServer 服务,实现“低延迟控制 + + 高吞吐遥测 + 实时视频”的多协议协同。

+
-
-

2技术栈选型

- - - - - - - - - - - - - - - -
层级技术用途
框架React 19 + TypeScriptUI 渲染与类型安全
构建Vite 6 + @vitejs/plugin-react极速 HMR 与打包,含 CSS 代码分割
UI 组件Ant Design 5 + TailwindCSS 4企业级组件库 + 原子化样式
状态管理Redux Toolkit + react-reduxuser / station 全局状态切片
路由React Router DOM 7全路由懒加载 + ProtectedRoute 鉴权
3D / GISCesium + vite-plugin-cesium三维地图、设备轨迹与场站可视化
图表ECharts 6 / Recharts / @ant-design/charts告警统计、运行数据可视化
HTTPAxios统一请求实例 + 拦截器
实时通信MQTT / WebSocket / WebRTC / Socket.io遥测 / 控制 / 视频 / 实时推送
音视频@volcengine/rtc · agora-rtc-sdk-ng · JSWebrtc多路无人机/机器人视频回传
其它js-cookie · qrcode · lodash · motion · react-draggable会话/二维码/工具/动画/拖拽
-
+
+

2技术栈选型

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
层级技术用途
框架React 19 + TypeScriptUI 渲染与类型安全
构建Vite 6 + @vitejs/plugin-react极速 HMR 与打包,含 CSS 代码分割
UI 组件Ant Design 5 + TailwindCSS 4企业级组件库 + 原子化样式
状态管理Redux Toolkit + react-reduxuser / station 全局状态切片
路由React Router DOM 7全路由懒加载 + ProtectedRoute 鉴权
3D / GISCesium + vite-plugin-cesium三维地图、设备轨迹与场站可视化
图表ECharts 6 / Recharts / @ant-design/charts告警统计、运行数据可视化
HTTPAxios统一请求实例 + 拦截器
实时通信MQTT / WebSocket / WebRTC / Socket.io遥测 / 控制 / 视频 / 实时推送
音视频@volcengine/rtc · agora-rtc-sdk-ng · JSWebrtc多路无人机/机器人视频回传
其它js-cookie · qrcode · lodash · motion · react-draggable会话/二维码/工具/动画/拖拽
+
-
-

3系统分层架构

-

系统自上而下分为四层。应用层内部进一步拆为路由、页面、组件、状态四个子层;通信层以四条并行通道桥接前后端。

+
+

3系统分层架构

+

系统自上而下分为四层。应用层内部进一步拆为路由、页面、组件、状态四个子层;通信层以四条并行通道桥接前后端。

-
- - 光伏电站智能监控与运维系统 · 分层架构 - - 用户浏览器 / Web Client - - - - React 19 + TypeScript · Vite 6 (SPA 前端应用) - - 路由层 - Router7 · 懒加载 · 鉴权 - - 页面层 - Home·设备·视频·告警·AI - - 组件层 - Layout·devices·alerts - - 状态管理 - Redux (user·station) - - - 实时通信层(四通道并行) - - HTTP · Axios - API 请求 · 401 拦截 - - MQTT - 设备遥测 useMqtt - - WebSocket - 设备控制 wsManager - - WebRTC - 视频流 JSWebrtc - - - 后端服务层(1.95.137.212) - - API server - :8081 REST 业务 - - MQTT broker - :8083 消息 - - WS server - :9002 设备控制 - - GeoServer - :59018 GIS 地图 - -
图 1 · 系统四层分层架构
-
-
+
+ + 光伏机器人环境感知服务平台 · 分层架构 + + 用户浏览器 / Web Client + + + + + + + + React 19 + TypeScript · Vite 6 (SPA 前端应用) + + 路由层 + Router7 · 懒加载 · 鉴权 + + 页面层 + Home·设备·视频·告警·AI + + 组件层 + Layout·devices·alerts + + 状态管理 + Redux (user·station) + + + 实时通信层(四通道并行) + + HTTP · Axios + API 请求 · 401 拦截 + + MQTT + 设备遥测 useMqtt + + WebSocket + 设备控制 wsManager + + WebRTC + 视频流 JSWebrtc + + + 后端服务层(1.95.137.212) + + API server + :8081 REST 业务 + + MQTT broker + :8083 消息 + + WS server + :9002 设备控制 + + GeoServer + :59018 GIS 地图 + +
图 1 · 系统四层分层架构
+
+
-
-

4核心模块详解

+
+

4核心模块详解

-

4.1 路由层 src/router

-

采用 createBrowserRouter + 全量 lazy() 懒加载,首屏只加载必要代码。路由通过 routeMeta 声明每个路径的 角色白名单(admin / operator / viewer)与 requiresAuth,ProtectedRoute 在进入受保护路由前校验登录态与权限,未登录跳转 /login。

+

4.1 路由层 src/router

+

采用 createBrowserRouter + 全量 lazy() 懒加载,首屏只加载必要代码。路由通过 routeMeta + 声明每个路径的 角色白名单(admin / operator / viewer)与 + requiresAuth,ProtectedRoute 在进入受保护路由前校验登录态与权限,未登录跳转 /login。

-

4.2 页面层 src/pages

- - - - - - - - - - - - - - - -
页面职责
Home / overview总览首页,聚合设备状态、告警、作业统计概览
Devices 相关设备总览 / 状态 / 航线任务 / 机器人任务
Video / VideoMonitorPage在线视频与视频监控,多路 WebRTC 拉流
RealTimeMonitor实时监控,Cesium 地图 + 设备轨迹
Alerts告警中心:实时 / 历史 / 统计 / 规则 / 订阅
AIAnalysisAI 诊断分析(热成像分析)
CleanOptimization清洗优化
WorkOrder工单任务管理
ReportCenter / Downloads报表中心 / 视频下载
SystemSetting(嵌套子路由)系统设置:基础设置、用户、菜单、组织、场站、设备接入、系统维护等
Login / Profile / Users / Roles登录 / 个人中心 / 用户管理 / 角色管理
+

4.2 页面层 src/pages

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
页面职责
Home / overview总览首页,聚合设备状态、告警、作业统计概览
Devices 相关设备总览 / 状态 / 航线任务 / 机器人任务
Video / VideoMonitorPage在线视频与视频监控,多路 WebRTC 拉流
RealTimeMonitor实时监控,Cesium 地图 + 设备轨迹
Alerts告警中心:实时 / 历史 / 统计 / 规则 / 订阅
AIAnalysisAI 诊断分析(热成像分析)
CleanOptimization清洗优化
WorkOrder工单任务管理
ReportCenter / Downloads报表中心 / 视频下载
SystemSetting(嵌套子路由)系统设置:基础设置、用户、菜单、组织、场站、设备接入、系统维护等
Login / Profile / Users / Roles登录 / 个人中心 / 用户管理 / 角色管理
-

4.3 组件层 src/components

- - - - - - - - - - - - - -
目录/组件职责
Layout.tsx应用外壳:侧边栏菜单(动态构建菜单树)、顶栏、场站切换器、全屏控制
ProtectedRoute路由级鉴权守卫
devices/设备控制核心:DeviceControl、DeviceController(控制类)、DroneVideoPlayer、WayLinePage、RobotTaskPage、XboxController 手柄
alerts/告警六件套:实时 / 历史 / 概览 / 统计 / 规则 / 订阅
SystemSetting/系统设置全部子页面
Map.jsxCesium 三维地图封装
VideoPlayer / VideoCanvas / AgoraRtc / VolcRtcPlayer / useWebRTCStream多策略视频播放组件
WebSocketManager.tsWebSocket 单例管理器(见 5.3)
Weather/天气动效组件(晴/云/雨)
+

4.3 组件层 src/components

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
目录/组件职责
Layout.tsx应用外壳:侧边栏菜单(动态构建菜单树)、顶栏、场站切换器、全屏控制
ProtectedRoute路由级鉴权守卫
devices/设备控制核心:DeviceControl、DeviceController(控制类)、DroneVideoPlayer、WayLinePage、RobotTaskPage、XboxController + 手柄
alerts/告警六件套:实时 / 历史 / 概览 / 统计 / 规则 / 订阅
SystemSetting/系统设置全部子页面
Map.jsxCesium 三维地图封装
VideoPlayer / VideoCanvas / AgoraRtc / VolcRtcPlayer / useWebRTCStream多策略视频播放组件
WebSocketManager.tsWebSocket 单例管理器(见 5.3)
Weather/天气动效组件(晴/云/雨)
-

4.4 状态管理 src/store

-
-

Redux Toolkit,仅两个切片,均持久化到 localStorage,刷新自动恢复:

-
    -
  • userSlice:token、userInfo、isAuthenticated;含 userLogin 异步 thunk(调登录 API → 存 token / cookie / userInfo)与 logout、setUserInfo 同步 action。
  • -
  • stationSlice:currentStation、stationId;场站切换后所有页面据此过滤数据。
  • -
-
+

4.4 状态管理 src/store

+
+

Redux Toolkit,仅两个切片,均持久化到 localStorage,刷新自动恢复:

+
    +
  • userSlice:token、userInfo、isAuthenticated;含 userLogin 异步 thunk(调登录 API + → 存 token / cookie / userInfo)与 logout、setUserInfo 同步 action。
  • +
  • stationSlice:currentStation、stationId;场站切换后所有页面据此过滤数据。
  • +
+
-

4.5 API 服务层 src/api

-

request.ts 创建全局唯一 Axios 实例:baseURL 来自 env.js,超时 100s,withCredentials。请求拦截器注入 Authorization: Bearer <token>;响应拦截器对 401(业务码或 HTTP 状态)触发带全局锁的统一登出(isLoggingOut 保证并发 401 只执行一次),清存储 → dispatch logout → 跳转 /login。

-

业务 API 按域拆分:api.ts(设备任务/作业/热成像)、device.ts、aiAnalysis.ts、alarmApi.js、workOrder.ts、reportDownloader.ts、systemSetting.ts,以及 login / mune / organization / role / stationManage / user 子模块。

-
+

4.5 API 服务层 src/api

+

request.ts 创建全局唯一 Axios 实例:baseURL 来自 env.js,超时 + 100s,withCredentials。请求拦截器注入 Authorization: Bearer <token>;响应拦截器对 + 401(业务码或 HTTP 状态)触发带全局锁的统一登出(isLoggingOut 保证并发 401 只执行一次),清存储 → dispatch + logout → 跳转 /login。

+

业务 API + 按域拆分:api.ts(设备任务/作业/热成像)、device.ts、aiAnalysis.ts、alarmApi.js、workOrder.ts、reportDownloader.ts、systemSetting.ts,以及 + login / mune / organization / role / stationManage / user 子模块。

+
-
-

5实时通信层设计

-

这是系统区别于普通 CRUD 后台的核心。四条通道各司其职、并行工作:

+
+

5实时通信层设计

+

这是系统区别于普通 CRUD 后台的核心。四条通道各司其职、并行工作:

- - - - - - - - -
通道协议方向用途前端实现
业务接口HTTP/REST请求-响应用户/设备/工单/报表等增删改查Axios 实例 + 拦截器
设备遥测MQTT over WS发布-订阅(上行)机器人实时位置、状态、传感器数据广播useMqtt Hook(自动重连 3s)
设备控制WebSocket双向下发手柄/触控指令、获取控制权、保活WebSocketManager 单例 + DeviceController
视频回传WebRTC单向拉流无人机/机器人摄像头实时画面JSWebrtc.Player / Agora / Volcengine RTC
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
通道协议方向用途前端实现
业务接口HTTP/REST请求-响应用户/设备/工单/报表等增删改查Axios 实例 + 拦截器
设备遥测MQTT over WS发布-订阅(上行)机器人实时位置、状态、传感器数据广播useMqtt Hook(自动重连 3s)
设备控制WebSocket双向下发手柄/触控指令、获取控制权、保活WebSocketManager 单例 + DeviceController
视频回传WebRTC单向拉流无人机/机器人摄像头实时画面JSWebrtc.Player / Agora / Volcengine RTC
-
-

WebSocketManager 单例设计要点

-
    -
  • 单例:全局唯一连接,按设备序列号 serialNumber 绑定,切换设备先关旧连新。
  • -
  • AUTH 握手:连接 onopen 后立即发送 {type:'AUTH', token, userName, deviceId} 认证包。
  • -
  • 自动重连:非主动关闭时,onclose 后 5s 自动重连;切后台关连接、切前台自动恢复。
  • -
  • 观察者模式:onMessage / onStatusChange 注册回调,返回取消订阅函数。
  • -
-
+
+

WebSocketManager 单例设计要点

+
    +
  • 单例:全局唯一连接,按设备序列号 serialNumber 绑定,切换设备先关旧连新。
  • +
  • AUTH 握手:连接 onopen 后立即发送 {type:'AUTH', token, userName, deviceId} + 认证包。
  • +
  • 自动重连:非主动关闭时,onclose 后 5s 自动重连;切后台关连接、切前台自动恢复。
  • +
  • 观察者模式:onMessage / onStatusChange 注册回调,返回取消订阅函数。
  • +
+
-
-

DeviceController 控制类

-

封装设备控制全流程:申请控制权(getControlRight)→ 握手连接 → sendGamepad 经 lodash throttle(50ms) 节流下发手柄数据 → 通过回调上抛设备信息 / 控制状态 / WS 状态变化。手柄输入 50ms 一发,兼顾流畅与带宽。

-
-
+
+

DeviceController 控制类

+

封装设备控制全流程:申请控制权(getControlRight)→ 握手连接 → sendGamepad 经 lodash + throttle(50ms) 节流下发手柄数据 → 通过回调上抛设备信息 / 控制状态 / WS 状态变化。手柄输入 50ms 一发,兼顾流畅与带宽。

+
+
-
-

6数据流设计

+
+

6数据流设计

-

6.1 认证与会话流

-
-
Login 页
→
-
userLogin thunk
→
-
/login API
→
-
存 token/cookie/userInfo
→
-
ProtectedRoute 放行
-
-

登录成功后 token 写入 localStorage 与 cookie(含跨域 sharetoken),Layout 据用户 ID 拉取菜单列表(动态构建侧边栏)与场站列表(默认选第一个)。后续所有请求由拦截器自动带 token;401 时统一登出回登录页。

+

6.1 认证与会话流

+
+
Login 页
+
→
+
userLogin thunk
+
→
+
/login API
+
→
+
存 token/cookie/userInfo
+
→
+
ProtectedRoute 放行
+
+

登录成功后 token 写入 localStorage 与 cookie(含跨域 sharetoken),Layout 据用户 ID + 拉取菜单列表(动态构建侧边栏)与场站列表(默认选第一个)。后续所有请求由拦截器自动带 token;401 时统一登出回登录页。

-

6.2 实时设备控制环(核心链路)

-
- - - 实时设备控制与遥测数据流 - - 操作员 - 手柄 / 触控指令 - - - DeviceController - 设备控制类 · 50ms 节流 - - - WebSocketManager - 单例 · AUTH 握手 · 自动重连 - - - WS server :9002 - 设备控制服务 - - - 机器人 / 无人机 - 执行动作 · 采集状态 - - MQTT - 遥测上行 - → 监控页 - -
图 2 · 控制指令下行(蓝色实线 WebSocket)与遥测上行(橙色虚线 MQTT)走两条独立通道
-
-

关键设计:控制下行用 WebSocket(低延迟、可靠、双向),状态上行用 MQTT(发布订阅、一对多广播)。两条通道互不阻塞——即便视频/遥测流量大,也不会影响控制指令的实时性。手柄输入经 50ms 节流,避免高频刷爆带宽。

+

6.2 实时设备控制环(核心链路)

+
+ + + + + + + 实时设备控制与遥测数据流 + + 操作员 + 手柄 / 触控指令 + + + DeviceController + 设备控制类 · 50ms 节流 + + + WebSocketManager + 单例 · AUTH 握手 · 自动重连 + + + WS server :9002 + 设备控制服务 + + + 机器人 / 无人机 + 执行动作 · 采集状态 + + MQTT + 遥测上行 + → 监控页 + +
图 2 · 控制指令下行(蓝色实线 WebSocket)与遥测上行(橙色虚线 MQTT)走两条独立通道
+
+

关键设计:控制下行用 WebSocket(低延迟、可靠、双向),状态上行用 + MQTT(发布订阅、一对多广播)。两条通道互不阻塞——即便视频/遥测流量大,也不会影响控制指令的实时性。手柄输入经 50ms 节流,避免高频刷爆带宽。

-

6.3 实时监控遥测流

-
-
机器人设备
→
-
MQTT broker :8083
→
-
useMqtt 订阅
→
-
监控页/Cesium 地图
-
-

设备按 topic 发布状态(位置、电量、传感器等),前端 useMqtt 订阅后驱动 Cesium 地图更新设备图标位置与实时监控面板。

+

6.3 实时监控遥测流

+
+
机器人设备
+
→
+
MQTT broker :8083
+
→
+
useMqtt 订阅
+
→
+
监控页/Cesium 地图
+
+

设备按 topic 发布状态(位置、电量、传感器等),前端 useMqtt 订阅后驱动 Cesium 地图更新设备图标位置与实时监控面板。

-

6.4 视频回传流

-
-
机器人摄像头
→
-
WebRTC/流媒体服务
→
-
JSWebrtc.Player
→
-
<video> 元素
-
-

useWebRTCPlayer(deviceId, url) 以 deviceId+url 为唯一键创建独立 video 元素并拉流,组件卸载时统一销毁 player,防止内存泄漏。同时支持 Agora RTC 与火山引擎 RTC 两套备选方案。

+

6.4 视频回传流

+
+
机器人摄像头
+
→
+
WebRTC/流媒体服务
+
→
+
JSWebrtc.Player
+
→
+
<video> 元素
+
+

useWebRTCPlayer(deviceId, url) 以 deviceId+url 为唯一键创建独立 video 元素并拉流,组件卸载时统一销毁 + player,防止内存泄漏。同时支持 Agora RTC 与火山引擎 RTC 两套备选方案。

-

6.5 HTTP 业务数据流

-
-
页面/组件
→
-
api/*.ts
→
-
request.ts (拦截器)
→
-
API server :8081
-
-

所有 REST 请求经统一 Axios 实例:请求拦截器注入 Bearer token,响应拦截器解包 response.data 并统一处理 401 登出。

-
+

6.5 HTTP 业务数据流

+
+
页面/组件
+
→
+
api/*.ts
+
→
+
request.ts (拦截器)
+
→
+
API server :8081
+
+

所有 REST 请求经统一 Axios 实例:请求拦截器注入 Bearer token,响应拦截器解包 response.data 并统一处理 401 登出。

+
-
-

7目录结构

-
机器人智慧服务系统/
+		
+

7目录结构

+
机器人智慧服务系统/
 ├── env.js                  # 环境配置(baseURL / WS_URL / mqtt_url / geoserver_url)
 ├── vite.config.ts          # 构建配置(别名 @、Cesium 插件、CSS 分割)
 ├── index.html              # 入口 HTML
-├── metadata.json           # 项目元信息(真实名称:光伏电站智能监控与运维系统)
+├── metadata.json           # 项目元信息(真实名称:光伏机器人环境感知服务平台)
 └── src/
     ├── api/                # API 服务层
     │   ├── request.ts      #   Axios 实例 + 拦截器(401 统一登出)
@@ -376,46 +936,49 @@
     ├── lib/TracePoint/     # 工具库(轨迹点)
     ├── assets/ / types/    # 静态资源 / 类型定义
     └── App / main 入口
-
+
-
-

8关键设计决策与特点

+
+

8关键设计决策与特点

-
-

多协议并行,职责分离

-

控制(WS)、遥测(MQTT)、视频(WebRTC)、业务(HTTP)各走独立通道。这是物联网实时控制系统的成熟模式:避免单通道拥塞互相拖累,也便于后端按特性独立伸缩。

-
+
+

多协议并行,职责分离

+

控制(WS)、遥测(MQTT)、视频(WebRTC)、业务(HTTP)各走独立通道。这是物联网实时控制系统的成熟模式:避免单通道拥塞互相拖累,也便于后端按特性独立伸缩。

+
-
-

全路由懒加载 + RBAC 元数据声明

-

所有页面 lazy() 按需加载,首屏轻量;权限不写死在组件里,而是在 routeMeta 集中声明角色白名单,ProtectedRoute 统一校验,新增页面只改路由表即可获得权限控制。

-
+
+

全路由懒加载 + RBAC 元数据声明

+

所有页面 lazy() 按需加载,首屏轻量;权限不写死在组件里,而是在 routeMeta + 集中声明角色白名单,ProtectedRoute 统一校验,新增页面只改路由表即可获得权限控制。

+
-
-

状态最小化 + 本地持久化

-

Redux 仅保留 user / station 两个跨页全局状态,其余页面数据就地管理;两者都持久化到 localStorage,刷新即恢复,体验接近原生应用。

-
+
+

状态最小化 + 本地持久化

+

Redux 仅保留 user / station 两个跨页全局状态,其余页面数据就地管理;两者都持久化到 localStorage,刷新即恢复,体验接近原生应用。

+
-
-

连接健壮性

-

WebSocket 单例 + AUTH 握手 + 5s 自动重连 + 页面可见性监听(切后台关、切前台重连);MQTT 3s 自动重连;Axios 401 全局锁防并发重复登出。多重保障弱网/切屏场景下的连接稳定。

-
+
+

连接健壮性

+

WebSocket 单例 + AUTH 握手 + 5s 自动重连 + 页面可见性监听(切后台关、切前台重连);MQTT 3s 自动重连;Axios 401 + 全局锁防并发重复登出。多重保障弱网/切屏场景下的连接稳定。

+
-
-待优化项(建议): -
    -
  • useMqtt.ts 中 MQTT 凭据硬编码在前端(username/password),建议改为后端动态下发 token。
  • -
  • useWebRTCStream.tsx 中视频 token 硬编码,应从登录态/接口动态获取。
  • -
  • vite.config.ts 的 manualChunks 分包策略被注释掉,Cesium 等大体积依赖建议启用分包以优化加载。
  • -
  • Redux store 仅两个切片,部分跨组件共享的设备/告警实时数据可考虑纳入统一管理或用 SWR/缓存层。
  • -
-
-
+
+ 待优化项(建议): +
    +
  • useMqtt.ts 中 MQTT 凭据硬编码在前端(username/password),建议改为后端动态下发 token。
  • +
  • useWebRTCStream.tsx 中视频 token 硬编码,应从登录态/接口动态获取。
  • +
  • vite.config.ts 的 manualChunks 分包策略被注释掉,Cesium 等大体积依赖建议启用分包以优化加载。
  • +
  • Redux store 仅两个切片,部分跨组件共享的设备/告警实时数据可考虑纳入统一管理或用 SWR/缓存层。
  • +
+
+
-
- 光伏电站智能监控与运维系统 · 架构设计文档 · 由 WorkBuddy 基于源码静态分析生成 -
+
+ 光伏机器人环境感知服务平台 · 架构设计文档 · 由 WorkBuddy 基于源码静态分析生成 +
-
+
+ diff --git a/i18n_progress_20260812.md b/i18n_progress_20260812.md new file mode 100644 index 0000000..e6114cb --- /dev/null +++ b/i18n_progress_20260812.md @@ -0,0 +1,25 @@ +# 中英文切换功能完善 — 进度记录 (2026-08-12) + +## 目标 +完善 `D:\code\机器人智慧服务系统` 的中英文(i18next)切换:补充 en/zh 词条,并消除页面上硬编码的中文。 + +## 已完成(已验证) +1. **补充 en 缺失的 3 个词条**(对比 zh=828 / en=825 后定位): + - `common.completed` → 'Completed' + - `home.underMaintenance` → 'Under Maintenance' + - `roles.abnormal` → 'Abnormal' + - 验证:en 现 828 词条,3 个 key 均存在。 +2. **router/index.tsx 菜单硬编码中文 → t()**: + - `通知策略页面` → `` 用 `t('router.noticePolicy')` + - `数据与安全页面` → `` 用 `t('router.dataSafety')` + - `模块开发中`(catch-all) → `` 用 `t('router.moduleDeveloping')` + - 因为 `routes` 是模块级静态对象,不能用 hook,故包装成使用 `useTranslation` 的小组件(可响应语言切换)。 + +## 关键发现(影响整体方案) +- **~1070 个被代码引用的 t() key 在 zh 与 en 中都不存在**(例如 `devices.tlStartCleaning`、`timelineStatusStart`、`alarmNo`、`mockDeviceSensor`、`paramLeftForwardGain` 等)。这些界面当前**直接显示原始 key 字符串**(中英文模式下都如此)。 +- 中文源文本已不在代码中(替换成 t() 时未回填 locale),因此这 ~1070 个 key 的「中文」需要重新拟定。 +- 页面中另有**硬编码中文**:69 个文件、约 1403 行(已统计到按文件分布)。这些中文仍在代码里,可安全提取并补到 locale。 + +## 待办 / 待决定 +- 大量工作需明确推进方式(见对话中的问题)。 +- 临时脚本待清理:tmp_compare.cjs、tmp_findcjk.cjs、tmp_analyze2.cjs、tmp_dbg.cjs、tmp_test.cjs、tmp_verify.cjs、tmp_perfile.cjs。 diff --git a/index.html b/index.html index 9959425..49446e4 100644 --- a/index.html +++ b/index.html @@ -5,7 +5,7 @@ - 光伏电站智能监控与运维系统 + 光伏机器人环境感知服务平台 diff --git a/metadata.json b/metadata.json index 1f4e605..443ac4f 100644 --- a/metadata.json +++ b/metadata.json @@ -1,5 +1,5 @@ { - "name": "光伏电站智能监控与运维系统", + "name": "光伏机器人环境感知服务平台", "description": "提供设备管理、视频监控、状态预警、警报处理及视频下载功能的一站式机器人智能管理平台。", "requestFramePermissions": [], "majorCapabilities": [] diff --git a/src/components/DictManager.jsx b/src/components/DictManager.jsx index 114040a..90fe73c 100644 --- a/src/components/DictManager.jsx +++ b/src/components/DictManager.jsx @@ -4,14 +4,15 @@ import { } from 'antd'; import { EditOutlined, DeleteOutlined } from '@ant-design/icons'; import { useTranslation } from 'react-i18next'; +import i18n from '../i18n'; const DictManager = ({ title, data, fields = [ - { name: 'storeValue', label: '存储值', required: true }, - { name: 'displayName', label: t('common.name'), required: true }, - { name: 'remark', label: t('roles.remark'), required: false } + { name: 'storeValue', label: i18n.t('common.storeValue'), required: true }, + { name: 'displayName', label: i18n.t('common.name'), required: true }, + { name: 'remark', label: i18n.t('roles.remark'), required: false } ], onAdd, onUpdate, @@ -19,6 +20,7 @@ const DictManager = ({ refresh, loading, }) => { + const { t } = useTranslation(); const [visible, setVisible] = useState(false); const [form] = Form.useForm(); const [editing, setEditing] = useState(null); @@ -26,7 +28,6 @@ const DictManager = ({ // 打开新增 const handleAddNew = () => { - const { t } = useTranslation(); setEditing(null); form.resetFields(); setVisible(true); @@ -57,10 +58,10 @@ const DictManager = ({ setEditing(null); refresh?.(); } else { - messageApi.error(res?.msg || '操作失败'); + messageApi.error(res?.msg || t('common.operationFailed')); } } catch (err) { - messageApi.error('提交失败'); + messageApi.error(t('common.submitFail')); } }; @@ -77,11 +78,11 @@ const DictManager = ({ render: (_, record) => ( { @@ -98,9 +99,9 @@ const DictManager = ({ } }} > - + ), @@ -111,7 +112,7 @@ const DictManager = ({ {contextHolder} diff --git a/src/components/LanguageSwitcher.tsx b/src/components/LanguageSwitcher.tsx index dfd5fb3..293ee4f 100644 --- a/src/components/LanguageSwitcher.tsx +++ b/src/components/LanguageSwitcher.tsx @@ -5,16 +5,14 @@ import { useTranslation } from 'react-i18next'; import type { MenuProps } from 'antd'; export default function LanguageSwitcher() { - const { i18n } = useTranslation(); + const { i18n ,t} = useTranslation(); const currentLng = i18n.language?.startsWith('en') ? 'en' : 'zh'; const items: MenuProps['items'] = [ { key: 'zh', label: ( - - 中文 - + {t('systemSetting.chinese')} ), }, { diff --git a/src/components/Map.jsx b/src/components/Map.jsx index c01bb2e..0b26bcd 100644 --- a/src/components/Map.jsx +++ b/src/components/Map.jsx @@ -346,7 +346,7 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, function showRobotTooltip(tooltip, robot, position) { const { t } = useTranslation(); const statusMap = { - 1: { text: "工作中", color: "#10b981" }, + 1: { text: t('common.working'), color: "#10b981" }, 2: { text: t('devices.idle'), color: "#f59e0b" }, 3: { text: t('devices.offline'), color: "#f87171" } }; @@ -365,9 +365,9 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, tooltip.innerHTML = `
🤖 ${robot.deviceAlias || robot.serialNumber || robot.name}
- ${deviceValuesHTML ? `
设备数据:${deviceValuesHTML}
` : ''} -
经度:${Number(robot.lastRunningStatus?.longitude).toFixed(6)}
-
纬度:${Number(robot.lastRunningStatus?.latitude).toFixed(6)}
+ ${deviceValuesHTML ? `
${t('devices.deviceData')}:${deviceValuesHTML}
` : ''} +
${t('systemSetting.longitude')}:${Number(robot.lastRunningStatus?.longitude).toFixed(6)}
+
${t('systemSetting.latitude')}:${Number(robot.lastRunningStatus?.latitude).toFixed(6)}
`; } diff --git a/src/components/SystemSetting/AlarmSettingsManagement.tsx b/src/components/SystemSetting/AlarmSettingsManagement.tsx index ab1b1fa..df2df93 100644 --- a/src/components/SystemSetting/AlarmSettingsManagement.tsx +++ b/src/components/SystemSetting/AlarmSettingsManagement.tsx @@ -66,7 +66,7 @@ const AlarmModelManagement: React.FC = () => { setSelectedRowKeys([]); } } catch (e) { - utils.message.error('获取告警模型列表失败'); + utils.message.error(t('systemSetting.alertListFixed2FailedFixed2')); setList([]); } finally { setLoading(false); @@ -134,8 +134,8 @@ const AlarmModelManagement: React.FC = () => { // 单条删除 const handleDelete = (id: string | number) => { modal.confirm({ - title: '确认删除', - content: '您确定要删除这条告警模型吗?', + title: t('systemSetting.fixed2ConfirmFixed2DeleteFixed2'), + content: t('systemSetting.okDeleteAlert'), okText: t('common.ok'), cancelText: t('common.cancel'), onOk: async () => { @@ -155,12 +155,12 @@ const AlarmModelManagement: React.FC = () => { // 批量删除 const handleBatchDelete = () => { if (!selectedRowKeys.length) { - message.warning('请先勾选要删除的数据'); + message.warning(t('systemSetting.delete')); return; } modal.confirm({ - title: '批量删除确认', - content: `您确定要删除选中的 ${selectedRowKeys.length} 条告警模型吗?删除后不可恢复!`, + title: t('systemSetting.fixed2BatchDeleteFixed2ConfirmFixed2'), + content: t('systemSetting.confirmBatchDeleteAlarm', { count: selectedRowKeys.length }), okText: t('common.ok'), cancelText: t('common.cancel'), danger: true, @@ -168,11 +168,11 @@ const AlarmModelManagement: React.FC = () => { try { const res = await deleteAlarmOrderConfigApi(selectedRowKeys); if (res.code === 200) { - message.success('批量删除成功'); + message.success(t('workOrder.batchDeleteSuccess')); fetchList(); } } catch (e) { - message.error('批量删除失败'); + message.error(t('workOrder.batchDeleteFail')); } } }); @@ -194,7 +194,7 @@ const AlarmModelManagement: React.FC = () => { }; const res = await saveAlarmOrderConfigApi(submitParams); if (res.code === 200 && res.data === true) { - message.success(editingItem ? '更新成功' : t('common.addSuccess')); + message.success(editingItem ? t('systemSetting.updateSuccess') : t('common.addSuccess')); setModalVisible(false); form.resetFields(); fetchList(); @@ -206,15 +206,15 @@ const AlarmModelManagement: React.FC = () => { // 表格列配置 const columns = [ - { title: '错误编码', dataIndex: 'errorCode', key: 'errorCode' }, + { title: t('systemSetting.errorCode'), dataIndex: 'errorCode', key: 'errorCode' }, { - title: '错误来源', + title: t('systemSetting.errorSourceLabel'), dataIndex: 'sourceType', key: 'sourceType', render: (val: string) => getErrorSourceText(val) }, { - title: '关联工单模型', + title: t('systemSetting.s34'), dataIndex: 'modelId', key: 'modelId', render: (val: number) => { @@ -223,10 +223,10 @@ const AlarmModelManagement: React.FC = () => { } }, { - title: '自动生成工单', + title: t('systemSetting.generateOrderFixed2'), dataIndex: 'autoGenerateOrder', key: 'autoGenerateOrder', - render: (val: boolean) => val ? '是' : '否' + render: (val: boolean) => val ? t('common.yes') : t('common.no') }, { title: t('roles.actions'), @@ -250,7 +250,7 @@ const AlarmModelManagement: React.FC = () => {
- +
{ setModalVisible(false)} footer={null} @@ -281,34 +281,34 @@ const AlarmModelManagement: React.FC = () => {
{/* 错误编码(关联错误管理) */} - {/* 工单模型ID(关联工单模型) */} - - {/* 是否自动生成工单 */} diff --git a/src/components/SystemSetting/AllDeviceManagement.tsx b/src/components/SystemSetting/AllDeviceManagement.tsx index 580f5cf..3ce2fe8 100644 --- a/src/components/SystemSetting/AllDeviceManagement.tsx +++ b/src/components/SystemSetting/AllDeviceManagement.tsx @@ -20,31 +20,30 @@ import { DeleteOutlined, } from '@ant-design/icons'; -const allDeviceList = [ - { id: 1, code: 'CAM-01', name: '摄像头 CAM-01', type: 'CAMERA', online: 'ONLINE', status: 'NORMAL' }, - { id: 2, code: 'CAM-02', name: '摄像头 CAM-02', type: 'CAMERA', online: 'OFFLINE', status: 'FAULT' }, - { id: 3, code: 'CAM-03', name: '摄像头 CAM-03', type: 'CAMERA', online: 'ONLINE', status: 'NORMAL' }, - { id: 4, code: 'CLEAN-01', name: '清洗机器人 CLEAN-01', type: 'CLEANING_DEVICE', online: 'ONLINE', status: 'IDLE' }, - { id: 5, code: 'CMB-A01', name: '汇流箱 CMB-A01', type: 'COMBINER_BOX', online: 'ONLINE', status: 'WARNING' }, - { id: 6, code: 'CMB-A02', name: '汇流箱 CMB-A02', type: 'COMBINER_BOX', online: 'ONLINE', status: 'NORMAL' }, - { id: 7, code: 'INV-A01', name: '逆变器 INV-A01', type: 'INVERTER', online: 'ONLINE', status: 'NORMAL' }, - { id: 8, code: 'INV-A02', name: '逆变器 INV-A02', type: 'INVERTER', online: 'ONLINE', status: 'WARNING' }, - { id: 9, code: 'INV-B01', name: '逆变器 INV-B01', type: 'INVERTER', online: 'ONLINE', status: 'NORMAL' }, - { id: 10, code: 'METER-01', name: '电表 METER-01', type: 'METER', online: 'ONLINE', status: 'NORMAL' }, - { id: 11, code: 'ROBOT-01', name: '运维机器人 ROBOT-01', type: 'ROBOT', online: 'ONLINE', status: 'IDLE' }, - { id: 12, code: 'DRONE-01', name: '无人机 DRONE-01', type: 'DRONE', online: 'OFFLINE', status: 'FAULT' }, - { id: 13, code: 'WEATHER-01', name: '气象站 WEATHER-01', type: 'WEATHER_STATION', online: 'ONLINE', status: 'NORMAL' }, -]; - export default function AllDeviceManagement() { const { t } = useTranslation(); const { modal } = App.useApp(); const [form] = Form.useForm(); const [loading, setLoading] = useState(false); + const allDeviceList = [ + { id: 1, code: 'CAM-01', name: `${t('devices.camera')} CAM-01`, type: 'CAMERA', online: 'ONLINE', status: 'NORMAL' }, + { id: 2, code: 'CAM-02', name: `${t('devices.camera')} CAM-02`, type: 'CAMERA', online: 'OFFLINE', status: 'FAULT' }, + { id: 3, code: 'CAM-03', name: `${t('devices.camera')} CAM-03`, type: 'CAMERA', online: 'ONLINE', status: 'NORMAL' }, + { id: 4, code: 'CLEAN-01', name: `${t('systemSetting.cleaningRobot')} CLEAN-01`, type: 'CLEANING_DEVICE', online: 'ONLINE', status: 'IDLE' }, + { id: 5, code: 'CMB-A01', name: `${t('home.combinerBox')} CMB-A01`, type: 'COMBINER_BOX', online: 'ONLINE', status: 'WARNING' }, + { id: 6, code: 'CMB-A02', name: `${t('home.combinerBox')} CMB-A02`, type: 'COMBINER_BOX', online: 'ONLINE', status: 'NORMAL' }, + { id: 7, code: 'INV-A01', name: `${t('home.inverter')} INV-A01`, type: 'INVERTER', online: 'ONLINE', status: 'NORMAL' }, + { id: 8, code: 'INV-A02', name: `${t('home.inverter')} INV-A02`, type: 'INVERTER', online: 'ONLINE', status: 'WARNING' }, + { id: 9, code: 'INV-B01', name: `${t('home.inverter')} INV-B01`, type: 'INVERTER', online: 'ONLINE', status: 'NORMAL' }, + { id: 10, code: 'METER-01', name: `${t('systemSetting.meter')} METER-01`, type: 'METER', online: 'ONLINE', status: 'NORMAL' }, + { id: 11, code: 'ROBOT-01', name: `${t('systemSetting.operationRobot')} ROBOT-01`, type: 'ROBOT', online: 'ONLINE', status: 'IDLE' }, + { id: 12, code: 'DRONE-01', name: `${t('devices.drone')} DRONE-01`, type: 'DRONE', online: 'OFFLINE', status: 'FAULT' }, + { id: 13, code: 'WEATHER-01', name: `${t('systemSetting.weatherStation')} WEATHER-01`, type: 'WEATHER_STATION', online: 'ONLINE', status: 'NORMAL' }, + ]; const [deviceList, setDeviceList] = useState(allDeviceList); const columns = [ - { title: '设备编码', dataIndex: 'code', key: 'code' }, + { title: t('systemSetting.fixed2DeviceFixed2Fixed22'), dataIndex: 'code', key: 'code' }, { title: t('devices.deviceName'), dataIndex: 'name', key: 'name' }, { title: t('common.type'), dataIndex: 'type', key: 'type' }, { @@ -54,7 +53,7 @@ export default function AllDeviceManagement() { ), }, { - title: '运行', dataIndex: 'status', key: 'status', + title: t('systemSetting.s24'), dataIndex: 'status', key: 'status', render: (text) => { const colorMap: Record = { NORMAL: 'green', @@ -101,15 +100,15 @@ export default function AllDeviceManagement() { const handleEdit = (record) => { modal.info({ - title: '编辑设备', - content: `编辑设备:${record.name}`, + title: t('systemSetting.fixed2EditFixed2DeviceFixed2'), + content: t('systemSetting.editDeviceInfo', { name: record.name }), }); }; const handleDelete = (record) => { modal.confirm({ - title: '确认删除', - content: `确定要删除设备 ${record.name} 吗?`, + title: t('systemSetting.fixed2ConfirmFixed2DeleteFixed2'), + content: t('systemSetting.confirmDeleteDevice', { name: record.name }), onOk: () => { setDeviceList(prev => prev.filter((item: any) => item.id !== record.id)); }, @@ -121,7 +120,7 @@ export default function AllDeviceManagement() {
- + @@ -144,7 +143,7 @@ export default function AllDeviceManagement() { pageSize: 10, showSizeChanger: true, showQuickJumper: true, - showTotal: (total) => `共 ${total} 条`, + showTotal: (total) => `${t('common.total')} ${total} ${t('common.items')}`, }} /> diff --git a/src/components/SystemSetting/BasicSettings.tsx b/src/components/SystemSetting/BasicSettings.tsx index c6cf4b9..f1f6438 100644 --- a/src/components/SystemSetting/BasicSettings.tsx +++ b/src/components/SystemSetting/BasicSettings.tsx @@ -72,13 +72,13 @@ const BasicSettings = () => { const fontLarge = 'clamp(18px, 1.1vw, 22px)'; const handleSave = () => { - message.success('设置已保存'); + message.success(t('systemSetting.fixed2SettingsSaveFixed2')); }; // 顶部统计卡片数据 const topCards = [ { - title: '当前版本', + title: t('systemSetting.fixed2Current'), value: 'v2.8.1', tag: '最新版', tagColor: 'blue', @@ -94,7 +94,7 @@ const BasicSettings = () => { bg: 'linear-gradient(135deg, #f6ffed 0%, #d9f7be 100%)' }, { - title: '告警通道', + title: t('systemSetting.fixed2AlertFixed2Fixed22'), value: '3 / 4', trend: t('roles.normal'), trendColor: '#52c41a', @@ -102,7 +102,7 @@ const BasicSettings = () => { bg: 'linear-gradient(135deg, #fff2f0 0%, #ffccc7 100%)' }, { - title: '数据备份状态', + title: t('systemSetting.statusFixed2'), value: t('roles.normal'), trend: '●', trendColor: '#52c41a', @@ -110,7 +110,7 @@ const BasicSettings = () => { bg: 'linear-gradient(135deg, #e6fffb 0%, #b5f5ec 100%)' }, { - title: '最近更新', + title: t('systemSetting.s282'), value: '2025-05-20', icon: , bg: 'linear-gradient(135deg, #f9f0ff 0%, #efdbff 100%)' @@ -120,7 +120,7 @@ const BasicSettings = () => { // 角色权限概览 const roleOverview = [ { - title: '超级管理员', + title: t('roles.superAdmin'), users: 2, perms: 28, tags: ['全部权限', '系统管理'], @@ -129,7 +129,7 @@ const BasicSettings = () => { icon: }, { - title: '运维工程师', + title: t('systemSetting.fixed2OM'), users: 5, perms: 18, tags: [t('router.devices'), '工单管理'], @@ -138,7 +138,7 @@ const BasicSettings = () => { icon: }, { - title: '巡检主管', + title: t('systemSetting.fixed2Inspection2'), users: 8, perms: 12, tags: ['巡检管理', '告警处理'], @@ -147,7 +147,7 @@ const BasicSettings = () => { icon: }, { - title: '数据分析师', + title: t('systemSetting.s322'), users: 3, perms: 15, tags: ['数据分析', '报表查看'], @@ -204,7 +204,7 @@ const BasicSettings = () => {
{/* 基础配置 */} 基础配置} + title={{t('systemSetting.s02')}} bordered={false} style={{ borderRadius: 10, marginBottom: 8, boxShadow: '0 2px 8px rgba(0,0,0,0.04)' }} bodyStyle={{ padding: '16px' }} @@ -217,7 +217,7 @@ const BasicSettings = () => { - + @@ -239,63 +239,63 @@ const BasicSettings = () => { - + - + - +
- 工单自动编号 + {t('systemSetting.s34')} - AI建议自动生成 + {t('systemSetting.fixed2Ai')} - 移动端同步 + {t('systemSetting.s52')}
- - + +
{/* 通知与告警策略 */} 通知与告警策略} + title={{t('systemSetting.alert2')}} bordered={false} style={{ borderRadius: 10, marginBottom: 8, boxShadow: '0 2px 8px rgba(0,0,0,0.04)' }} bodyStyle={{ padding: '16px' }} > {[ - { label: '短信', icon: , color: '#52c41a', status: '已启用' }, - { label: '邮件', icon: , color: '#1677ff', status: '已启用' }, + { label: t('alerts.sms'), icon: , color: '#52c41a', status: '已启用' }, + { label: t('alerts.email'), icon: , color: '#1677ff', status: '已启用' }, { label: 'Telegram', icon: , color: '#13c2c2', status: '已启用' }, - { label: '系统通知', icon: , color: '#fa8c16', status: '已启用' }, + { label: t('report.systemNotification'), icon: , color: '#fa8c16', status: '已启用' }, ].map((item, idx) => (
@@ -312,13 +312,13 @@ const BasicSettings = () => { ))} -
严重告警立即通知 - 发电下降超过 15% - 通信中断通知 - 每日汇总推送 + {t('systemSetting.fixed2CriticalAlert')} + {t('systemSetting.fixed2GenerationFixed2Descend15')} + {t('systemSetting.fixed2Communicating')} + {t('systemSetting.s122')} - 夜间免打扰 + {t('systemSetting.s132')} @@ -328,7 +328,7 @@ const BasicSettings = () => { {/* 用户与权限预览 */} 用户与权限预览} + title={{t('systemSetting.s142')}} bordered={false} style={{ borderRadius: 10, boxShadow: '0 2px 8px rgba(0,0,0,0.04)', marginBottom: 0 }} bodyStyle={{ padding: '12px' }} @@ -342,7 +342,7 @@ const BasicSettings = () => { pagination={{ size: 'small', total: 4, pageSize: 4, showTotal: (total) => `共 ${total} 条` }} columns={[ - { title: '用户名', dataIndex: 'name', key: 'name', fixed: 'left', width: 80 }, + { title: t('systemSetting.s36'), dataIndex: 'name', key: 'name', fixed: 'left', width: 80 }, { title: t('users.role'), dataIndex: 'role', @@ -379,9 +379,7 @@ const BasicSettings = () => { size="small" icon={} style={{ padding: 0, fontSize: 11 }} - > - 查看 - , fixed: 'right' + >{t('common.view')}, fixed: 'right' }, ]} dataSource={[ @@ -398,7 +396,7 @@ const BasicSettings = () => { {/* 角色权限概览 */} 角色权限概览} + title={{t('systemSetting.fixed2RolePermissionFixed2OverviewFixed2')}} bordered={false} style={{ borderRadius: 10, marginBottom: 8, boxShadow: '0 2px 8px rgba(0,0,0,0.04)' }} bodyStyle={{ padding: '10px' }} @@ -428,8 +426,8 @@ const BasicSettings = () => { {/* 设备接入状态 */} 设备接入状态} - extra={} + title={{t('systemSetting.fixed2DeviceAccessFixed2StatusFixed2')}} + extra={} bordered={false} style={{ borderRadius: 10, marginBottom: 8, boxShadow: '0 2px 8px rgba(0,0,0,0.04)' }} bodyStyle={{ padding: '12px' }} @@ -468,10 +466,10 @@ const BasicSettings = () => { {[ { x: 60, y: 40, icon: , label: t('home.inverter') }, { x: 140, y: 40, icon: , label: t('home.combinerBox') }, - { x: 160, y: 80, icon: , label: '气象站' }, + { x: 160, y: 80, icon: , label: t('video.s0') }, { x: 140, y: 120, icon: , label: t('devices.camera') }, { x: 60, y: 120, icon: , label: t('devices.robot') }, - { x: 40, y: 80, icon: , label: '接口' }, + { x: 40, y: 80, icon: , label: t('systemSetting.s38') }, ].map((node, i) => ( @@ -488,20 +486,20 @@ const BasicSettings = () => { {/* 数据与安全 */} systemSetting.dataSafety} + title={{t('systemSetting.dataSafety')}} bordered={false} style={{ borderRadius: 10, marginBottom: 8, boxShadow: '0 2px 8px rgba(0,0,0,0.04)' }} bodyStyle={{ padding: '12px' }} >
{[ - { label: '数据保留', value: '3 年', icon: }, - { label: '登录审计', value: '已开启', icon: , color: '#52c41a' }, - { label: '自动备份', value: '02:00', icon: }, - { label: 'API 密钥', value: t('roles.normal'), icon: , color: '#52c41a' }, - { label: '最近备份', value: '2025-05-24 02:00', icon: }, - { label: 'SSL 证书', value: '有效期至 2026-12-31', icon: }, - { label: '双因素认证', value: '已开启', icon: , color: '#52c41a' }, + { label: t('systemSetting.s39'), value: '3 年', icon: }, + { label: t('systemSetting.fixed2Login2'), value: '已开启', icon: , color: '#52c41a' }, + { label: t('systemSetting.s412'), value: '02:00', icon: }, + { label: t('systemSetting.api'), value: t('roles.normal'), icon: , color: '#52c41a' }, + { label: t('systemSetting.s43'), value: '2025-05-24 02:00', icon: }, + { label: t('systemSetting.ssl'), value: '有效期至 2026-12-31', icon: }, + { label: t('systemSetting.s45'), value: '已开启', icon: , color: '#52c41a' }, ].map((item, idx) => (
@@ -518,18 +516,18 @@ const BasicSettings = () => { {/* 系统维护 */} systemSetting.systemMaintenance} - extra={} + title={{t('systemSetting.systemMaintenance')}} + extra={} bordered={false} style={{ borderRadius: 10, boxShadow: '0 2px 8px rgba(0,0,0,0.04)' }} bodyStyle={{ padding: '12px' }} > {[ - { label: '清理缓存', icon: , color: '#1677ff' }, - { label: '重启服务', icon: , color: '#1677ff' }, - { label: '备份下载', icon: , color: '#1677ff' }, - { label: '日志导出', icon: , color: '#1677ff' }, + { label: t('systemSetting.s46'), icon: , color: '#1677ff' }, + { label: t('systemSetting.s47'), icon: , color: '#1677ff' }, + { label: t('systemSetting.downloadFixed2'), icon: , color: '#1677ff' }, + { label: t('systemSetting.fixed2LogDesc6Fixed2ExportFixed2'), icon: , color: '#1677ff' }, ].map((btn, idx) => (
+ @@ -202,7 +201,7 @@ export default function CameraManagement() { const handleView = (record) => { modal.info({ - title: `${record.name} - 视频预览`, + title: `${record.name} - ${t('systemSetting.videoPreview')}`, width: 720, content: (
- 视频预览占位 - {record.name} + {t('systemSetting.videoPreviewPlaceholder', { name: record.name })} IP: {record.ip}
@@ -233,8 +232,8 @@ export default function CameraManagement() { const handleDelete = (record) => { modal.confirm({ - title: '确认删除', - content: `确定要删除摄像头 ${record.name} 吗?`, + title: t('systemSetting.fixed2ConfirmFixed2DeleteFixed2'), + content: t('systemSetting.confirmDeleteCamera', { name: record.name }), onOk: () => { setCameraList(prev => prev.filter((item: any) => item.id !== record.id)); }, @@ -261,7 +260,7 @@ export default function CameraManagement() {
-
摄像头总数
+
{t('systemSetting.fixed2Camera2')}
{cameraList.length}
-
离线/故障
+
{t('systemSetting.fixed2OfflineErrorFixed2')}
{cameraList.filter((c: any) => c.online === 'OFFLINE' || c.status === 'FAULT').length}
@@ -335,10 +334,10 @@ export default function CameraManagement() {
- } /> + } /> - {t('roles.normal')} {t('constants.warning')} {t('devices.error')} @@ -351,7 +350,7 @@ export default function CameraManagement() { - +
@@ -366,13 +365,13 @@ export default function CameraManagement() { pageSize: 10, showSizeChanger: true, showQuickJumper: true, - showTotal: (total) => `共 ${total} 条`, + showTotal: (total) => `${t('common.total')} ${total} ${t('common.items')}`, }} scroll={{ x: 1400 }} /> { setEditModalVisible(false); @@ -384,43 +383,43 @@ export default function CameraManagement() {
- - + + - - + + - - + + - - + {t('systemSetting.fixed2Hd2')} + {t('systemSetting.fixed2Hd')} + {t('systemSetting.domeCamera')} + {t('systemSetting.fixed2Panorama')} - - + {t('systemSetting.hikvision')} + {t('systemSetting.dahua')} + {t('systemSetting.uniview')} {t('constants.others')} - - 720P 1080P 4K @@ -429,17 +428,17 @@ export default function CameraManagement() { - - } /> + + } /> - + - + diff --git a/src/components/SystemSetting/DeviceAccessPage.tsx b/src/components/SystemSetting/DeviceAccessPage.tsx index 08c3387..586072b 100644 --- a/src/components/SystemSetting/DeviceAccessPage.tsx +++ b/src/components/SystemSetting/DeviceAccessPage.tsx @@ -73,25 +73,25 @@ export default function DeviceAccessPage() { 10: { props: [ { id: 1, code: 'device_online', name: t('devices.onlineStatus'), type: 'bool', rw: 'r', isAlarm: false, unit: '', required: true, defaultValue: null }, - { id: 2, code: 'record_status', name: '录像状态', type: 'string', rw: 'r', isAlarm: false, unit: '', required: true, defaultValue: null }, + { id: 2, code: 'record_status', name: t('systemSetting.recordStatus'), type: 'string', rw: 'r', isAlarm: false, unit: '', required: true, defaultValue: null }, ], - events: [{ id: 1, code: 'alarm_perimeter', name: '周界入侵告警' }], + events: [{ id: 1, code: 'alarm_perimeter', name: t('systemSetting.perimeterIntrusionAlarm') }], actions: [{ actionId: 'take_snapshot', actionName: t('video.snapshot'), params: {} }] }, 11: { props: [ { id: 1, code: 'temp', name: t('home.temperature'), type: 'float', rw: 'r', isAlarm: true, unit: '℃', required: true, defaultValue: null }, - { id: 2, code: 'humi', name: '湿度', type: 'float', rw: 'r', isAlarm: true, unit: '%', required: true, defaultValue: null }, + { id: 2, code: 'humi', name: t('systemSetting.humidity'), type: 'float', rw: 'r', isAlarm: true, unit: '%', required: true, defaultValue: null }, ], - events: [{ id: 1, code: 'temp_over', name: '超温告警' }], + events: [{ id: 1, code: 'temp_over', name: t('systemSetting.overtempAlarm') }], actions: [] }, 12: { props: [ { id: 1, code: 'battery', name: t('devices.batteryLevel'), type: 'int', rw: 'r', isAlarm: true, unit: '%', required: true, defaultValue: null }, - { id: 2, code: 'voltage', name: '电压', type: 'float', rw: 'r', isAlarm: true, unit: 'V', required: true, defaultValue: null }, + { id: 2, code: 'voltage', name: t('systemSetting.voltage'), type: 'float', rw: 'r', isAlarm: true, unit: 'V', required: true, defaultValue: null }, ], - events: [{ id: 1, code: 'low_power', name: '低电量告警' }], + events: [{ id: 1, code: 'low_power', name: t('systemSetting.lowBatteryAlarm') }], actions: [] } }; @@ -161,7 +161,7 @@ export default function DeviceAccessPage() { messageApi.success(t('common.deleteSuccess')); fetchModelPageList(); // 【优化】删除后刷新 } else { - messageApi.error('删除失败:' + res.msg); + messageApi.error(`${t('common.deleteFail')}:${res.msg}`); } }; @@ -172,7 +172,7 @@ export default function DeviceAccessPage() { messageApi.success(t('common.deleteSuccess')); fetchProductPageList(); // 【优化】删除后刷新 } else { - messageApi.error('删除失败:' + res.msg); + messageApi.error(`${t('common.deleteFail')}:${res.msg}`); } }; @@ -304,10 +304,10 @@ export default function DeviceAccessPage() { } if (res.code === 200) { - messageApi.success(editData ? '编辑设备成功' : '新增设备成功'); + messageApi.success(editData ? t('systemSetting.deviceEditSuccess') : t('systemSetting.deviceAddSuccess')); success = true; } else { - messageApi.error(res.msg || '操作失败'); + messageApi.error(res.msg || t('systemSetting.operationFailed')); } } if (modalType === 'model') { @@ -355,7 +355,7 @@ export default function DeviceAccessPage() { const submitData = { ...params, thingsModels, id: editData?.id }; const res = editData ? await updateProduct(submitData) : await addProduct(submitData); if (res.code === 200) { - messageApi.success(editData ? '产品编辑成功' : '产品新增成功'); + messageApi.success(editData ? t('systemSetting.productEditSuccess') : t('systemSetting.productAddSuccess')); success = true; } else { messageApi.error(res.msg); @@ -445,11 +445,11 @@ export default function DeviceAccessPage() { const deviceColumns = [ { title: t('devices.deviceName'), dataIndex: 'deviceName' }, // 👈 改成 deviceName { title: 'SN', dataIndex: 'sn' }, - { title: '产品ID', dataIndex: 'productId' }, - { title: '模型ID', dataIndex: 'modelId' }, + { title: t('systemSetting.id2'), dataIndex: 'productId' }, + { title: t('systemSetting.id'), dataIndex: 'modelId' }, { title: 'IP', dataIndex: 'ip' }, - { title: '端口', dataIndex: 'port' }, - { title: '账号', dataIndex: 'username' }, + { title: t('systemSetting.s41'), dataIndex: 'port' }, + { title: t('systemSetting.account'), dataIndex: 'username' }, { title: t('roles.status'), render: (r) => @@ -460,27 +460,25 @@ export default function DeviceAccessPage() { title: t('roles.actions'), fixed: 'right', render: (r) => ( - + ) } ]; const modelColumns = [ - { title: '模型名称', dataIndex: 'name' }, - { title: '连接协议', dataIndex: 'connectType' }, + { title: t('systemSetting.nameFixed22'), dataIndex: 'name' }, + { title: t('systemSetting.s32'), dataIndex: 'connectType' }, { - title: '属性列表', width: 300, render: (record) => { + title: t('systemSetting.listFixed22'), width: 300, render: (record) => { const list = record.supportProperties || []; - if (list.length === 0) return 无属性; + if (list.length === 0) return {t('systemSetting.s110')}; return {list.map(item => {item.propertyName})}; } }, { - title: '指令列表', width: 300, render: (record) => { + title: t('systemSetting.listFixed2'), width: 300, render: (record) => { const list = record.supportActions || []; - if (list.length === 0) return 无指令; + if (list.length === 0) return {t('systemSetting.noneFixed2')}; return {list.map(item => {item.actionName})}; } }, @@ -488,7 +486,7 @@ export default function DeviceAccessPage() { title: t('roles.actions'), fixed: "right", render: r => ( - handleDeleteModel(r.id)} okText={t('common.ok')} cancelText={t('common.cancel')}> + handleDeleteModel(r.id)} okText={t('common.ok')} cancelText={t('common.cancel')}> @@ -504,12 +502,12 @@ export default function DeviceAccessPage() { {t('router.devices')}} key="device" /> - 产品管理} key="product" /> - 物模型管理} key="model" /> + {t('systemSetting.s34')}} key="product" /> + {t('systemSetting.s42')}} key="model" /> - {activeTab === 'device' && + {activeTab === 'device' &&
@@ -526,25 +524,25 @@ export default function DeviceAccessPage() { initialValues={{ pageNum: 1, pageSize: 10 }} style={{ display: 'flex', gap: '8px', flexWrap: 'wrap', alignItems: 'center' }} > - - + + - - + + - - + + - - {protocolList.map(p => ( ))} - - @@ -552,7 +550,7 @@ export default function DeviceAccessPage() { @@ -560,35 +558,33 @@ export default function DeviceAccessPage() { - +
{s === 1 ? t('common.enable') : '禁用'} + render: (s) => {s === 1 ? t('common.enable') : t('common.disable')} }, { title: t('roles.actions'), fixed: "right", width: 180, render: (r) => ( - + handleDeleteProduct(r.id)} okText={t('common.ok')} cancelText={t('common.cancel')} @@ -605,7 +601,7 @@ export default function DeviceAccessPage() { pageSize: pagination.pageSize, total: pagination.total, showSizeChanger: true, - showTotal: (total) => `共 ${total} 条` + showTotal: (total) => `${t('common.total')} ${total} ${t('common.items')}` }} onChange={handleTableChange} scroll={{ x: 'max-content' }} @@ -618,11 +614,11 @@ export default function DeviceAccessPage() {
- + - - {protocolList.map(p => ( ))} @@ -633,7 +629,7 @@ export default function DeviceAccessPage() { - +
@@ -643,33 +639,33 @@ export default function DeviceAccessPage() { )} setVisible(false)} onOk={handleSubmit} destroyOnClose - title={modalType === 'device' ? '设备配置' : modalType === 'product' ? '产品配置' : '物模型配置'}> + title={modalType === 'device' ? t('systemSetting.deviceConfig') : modalType === 'product' ? t('systemSetting.productConfig') : t('systemSetting.thingModelConfig')}>
{modalType === 'device' && ( <> - + - + + + - - + + - - + + - - + + )} {/* RTSP 配置 */} {deviceProtocol === 'RTSP' && ( - + )} @@ -733,48 +729,48 @@ export default function DeviceAccessPage() { {/* MQTT 配置 */} {deviceProtocol === 'MQTT' && ( <> - + - + - + - - + + - - + + )} {/* 告警阈值配置 */} - 告警阈值配置 + {t('systemSetting.fixed2AlertFixed2Threshold')} {getAlarmProps().length > 0 ? ( getAlarmProps().map(item => (
- + - +
)) ) : ( - + )} )} @@ -782,21 +778,21 @@ export default function DeviceAccessPage() { {modalType === 'product' && ( <>
- + - - + + - - + + - - @@ -805,15 +801,15 @@ export default function DeviceAccessPage() {
- 绑定物模型 - + {t('systemSetting.s112')} + - + @@ -879,7 +875,7 @@ export default function DeviceAccessPage() { )} {/* ==== 属性配置(你原来的) ==== */} - 物模型属性配置 + {t('systemSetting.s132')}
{propertyList?.map((item) => (
@@ -887,24 +883,22 @@ export default function DeviceAccessPage() { {item.propertyName}({item.propertyKey})
- - + + - - + +
))} {(!propertyList || propertyList.length === 0) && ( -
- 请选择物模型以显示属性 -
+
{t('systemSetting.fixed2PleaseSelect2')}
)} {/* ==== ✅ 行为(指令)配置 新增 ==== */} - 物模型行为(指令)配置 + {t('systemSetting.s152')}
{thinsModelList.find(m => m.id === selectedModelId)?.supportActions?.map((action) => (
@@ -915,11 +909,11 @@ export default function DeviceAccessPage() { {Object.entries(action.params || {}).map(([paramKey, paramCfg]: [string, any]) => (
- + m.id === selectedModelId)?.supportActions?.length) && ( -
- 该物模型暂无指令 -
+
{t('systemSetting.noneFixed2Fixed2')}
)}
@@ -954,10 +946,10 @@ export default function DeviceAccessPage() { <>
- +
- +
}, { title: t('roles.actions'), render: r => ( - deleteItem('props', r.id)}> + deleteItem('props', r.id)}> ) } ]} /> - 指令 + {t('systemSetting.s18')}
Object.keys(r.params || []).join(',') || '无' }, + { title: t('systemSetting.s77'), dataIndex: 'actionId' }, + { title: t('systemSetting.fixed2Fixed2NameFixed2'), dataIndex: 'actionName' }, + { title: t('systemSetting.s89'), render: r => Object.keys(r.params || []).join(',') || t('systemSetting.none') }, { title: t('roles.actions'), render: r => ( @@ -1019,7 +1011,7 @@ export default function DeviceAccessPage() { setActionModal(true); }}>{t('roles.edit')} setModelDetail({ ...modelDetail, actions: modelDetail.actions.filter(x => x.actionId !== r.actionId) })}> - + ) @@ -1031,11 +1023,11 @@ export default function DeviceAccessPage() { {/* 属性弹窗 */} - setItemModal(false)} onOk={saveItem} title="配置属性"> + setItemModal(false)} onOk={saveItem} title={t('systemSetting.s69')}> - - + + - - - + + + {/* 指令弹窗 */} - setActionModal(false)} + setActionModal(false)} onOk={() => { const params = paramList.reduce((obj, item) => { obj[item.paramKey] = { type: item.type, description: item.desc, default: item.defaultValue, ...(item.enum?.length && { enum: item.enum }) }; @@ -1066,32 +1058,32 @@ export default function DeviceAccessPage() { }} width={750}>
- setCurrentAction({ ...currentAction, actionId: e.target.value })} style={{ flex: 1 }} /> - setCurrentAction({ ...currentAction, actionName: e.target.value })} style={{ flex: 1 }} /> + setCurrentAction({ ...currentAction, actionId: e.target.value })} style={{ flex: 1 }} /> + setCurrentAction({ ...currentAction, actionName: e.target.value })} style={{ flex: 1 }} />
- 指令参数 + {t('systemSetting.fixed2Fixed2Fixed2')}
- setParamList([{ ...paramList[0], paramKey: e.target.value }])} /> + setParamList([{ ...paramList[0], paramKey: e.target.value }])} /> setParamList([{ ...paramList[0], desc: e.target.value }])} /> - setParamList([{ ...paramList[0], defaultValue: e.target.value }])} /> - setParamList([{ ...paramList[0], enum: e.target.value.split(',').map(s => s.trim()) }])} /> + setParamList([{ ...paramList[0], defaultValue: e.target.value }])} /> + setParamList([{ ...paramList[0], enum: e.target.value.split(',').map(s => s.trim()) }])} />
{/* 字段映射 */} - setMappingModal(false)} onOk={saveMapping} title="字段映射"> + setMappingModal(false)} onOk={saveMapping} title={t('systemSetting.s83')}>
diff --git a/src/components/SystemSetting/DeviceManagement.tsx b/src/components/SystemSetting/DeviceManagement.tsx index e00b4b0..cfdc49c 100644 --- a/src/components/SystemSetting/DeviceManagement.tsx +++ b/src/components/SystemSetting/DeviceManagement.tsx @@ -89,10 +89,10 @@ export default function DeviceManagementPage() { style={{ marginBottom: 0 }} items={[ { key: 'all', label: t('router.devices') }, - { key: 'drone', label: '无人机管理' }, - { key: 'smart', label: '智能装备' }, - { key: 'camera', label: '摄像头管理' }, - { key: 'serialGenerate', label: '机器人生成' }, + { key: 'drone', label: t('systemSetting.fixed2Uav') }, + { key: 'smart', label: t('systemSetting.equipmentFixed22') }, + { key: 'camera', label: t('systemSetting.fixed2Camera') }, + { key: 'serialGenerate', label: t('systemSetting.fixed2Robot2') }, ]} /> diff --git a/src/components/SystemSetting/DroneManagement.tsx b/src/components/SystemSetting/DroneManagement.tsx index f39e660..fb3d50b 100644 --- a/src/components/SystemSetting/DroneManagement.tsx +++ b/src/components/SystemSetting/DroneManagement.tsx @@ -88,7 +88,7 @@ export default function DroneManagement({ setDroneTotal(res.total || res.rows?.length || 0); } } catch (err) { - utils.message.error('获取无人机列表失败'); + utils.message.error(t('systemSetting.uavFixed2ListFixed2FailedFixed2')); } finally { setLoading(false); } @@ -143,7 +143,7 @@ export default function DroneManagement({ const openBindModal = async () => { if (!selectedDroneKeys.length) { - return utils.message.warning('请先在列表中选中需要绑定的无人机'); + return utils.message.warning(t('systemSetting.listUavFixed2')); } bindForm.resetFields(); setBindModalVisible(true); @@ -210,17 +210,17 @@ export default function DroneManagement({ }; const res = await bindDrone(payload); if (res.code === 200 && res.data == true) { - utils.message.success('绑定成功'); + utils.message.success(t('systemSetting.successFixed22')); setBindModalVisible(false); setSelectedDroneKeys([]); bindForm.resetFields(); fetchDroneList(); onAllDataNeedRefresh?.(); } else { - utils.message.error(res.msg || '绑定失败'); + utils.message.error(res.msg || t('systemSetting.bindFail')); } } catch (err) { - utils.message.error('操作异常'); + utils.message.error(t('systemSetting.fixed2ActionsFixed2AbnormalFixed2')); } }; @@ -229,27 +229,27 @@ export default function DroneManagement({ item !== null && item !== undefined && item !== '' && item.trim() !== '' ); if (validSns.length === 0) { - return utils.message.warning('未选择有效设备,无法解绑'); + return utils.message.warning(t('systemSetting.device2')); } - const typeText = { 1: '人员', 2: '场站', 3: '组织' }[type]; + const typeText = { 1: t('systemSetting.person'), 2: t('systemSetting.s24'), 3: t('systemSetting.s34') }[type]; modal.confirm({ - title: '确认解绑', - content: `确定要为选中的无人机清除${typeText}绑定吗?`, + title: t('systemSetting.fixed2Confirm'), + content: t('systemSetting.confirmClearBinding', { type: typeText }), okText: t('common.ok'), cancelText: t('common.cancel'), onOk: async () => { try { const res = await unbindDrone({ sns: validSns, type }); if (res.code === 200) { - utils.message.success('解绑成功'); + utils.message.success(t('systemSetting.successFixed2')); fetchDroneList(); setSelectedDroneKeys([]); onAllDataNeedRefresh?.(); } else { - utils.message.error(res.msg || '解绑失败'); + utils.message.error(res.msg || t('systemSetting.unbindFail')); } } catch (err) { - utils.message.error('操作异常'); + utils.message.error(t('systemSetting.fixed2ActionsFixed2AbnormalFixed2')); } } }); @@ -258,26 +258,26 @@ export default function DroneManagement({ const droneColumns = [ { title: t('devices.deviceName'), width: 150, dataIndex: 'drone_callsign', key: 'name' }, { title: t('systemSetting.snCode'), width: 180, dataIndex: 'device_sn', key: 'sn' }, - { title: '机场SN', width: 150, dataIndex: 'gateway_sn', key: 'gsn' }, + { title: t('systemSetting.sn'), width: 150, dataIndex: 'gateway_sn', key: 'gsn' }, { - title: '所属组织', width: 150, key: 'orgName', + title: t('systemSetting.belongingOrg'), width: 150, key: 'orgName', render: (_, record) => { const org = allOrgOptions.find(o => (o.orgId || o.id) == record.orgId); - return org ? (org.orgName || org.name) : 未绑定; + return org ? (org.orgName || org.name) : {t('systemSetting.s02')}; }, }, { - title: '所属场站', width: 150, key: 'siteName', + title: t('systemSetting.s9'), width: 150, key: 'siteName', render: (_, record) => { const site = allSiteOptions.find(s => (s.siteId || s.id) == record.siteId); - return site ? (site.siteName || site.name) : 未绑定; + return site ? (site.siteName || site.name) : {t('systemSetting.s02')}; }, }, { title: t('systemSetting.leader'), width: 150, key: 'userName', render: (_, record) => { const user = allUserOptions.find(u => u.userId == record.userId); - return user ? (user.nickName || user.userName) : 未绑定; + return user ? (user.nickName || user.userName) : {t('systemSetting.s02')}; }, }, { @@ -288,14 +288,14 @@ export default function DroneManagement({ title: t('roles.actions'), key: 'action', fixed: 'right' as const, width: 250, render: (_, record: any) => ( - - + + - - + + - - + + ), @@ -321,7 +321,7 @@ export default function DroneManagement({ <>
{t('devices.offline')} - - - - + + + +
@@ -356,7 +356,7 @@ export default function DroneManagement({ pageSize: droneParams.pageSize, total: droneTotal, showSizeChanger: true, - showTotal: t => `共 ${t} 条`, + showTotal: t => `${t('common.total')} ${t} ${t('common.items')}`, onChange: (page, pageSize) => { setDroneParams({ ...droneParams, pageNum: page, pageSize }); }, @@ -369,7 +369,7 @@ export default function DroneManagement({ title={
- 绑定无人机 + {t('systemSetting.uavFixed2')}
} open={bindModalVisible} @@ -385,19 +385,19 @@ export default function DroneManagement({ width={540} >
- 已选无人机({selectedDroneKeys.length}台)SN} name="sns" rules={[{ required: true, message: '请输入 SN' }]}> - + + - 所属组织 + {t('systemSetting.belongingOrg')} } - name="orgId" rules={[{ required: true, message: '请选择组织' }]} + name="orgId" rules={[{ required: true, message: t('systemSetting.pleaseSelectOrg') }]} > { @@ -436,9 +436,9 @@ export default function DroneManagement({ ))} - + } value={keyword} onChange={(e) => setKeyword(e.target.value)} /> @@ -149,7 +149,7 @@ const LogManagement = () => { columns={columns} dataSource={filtered} scroll={{ x: 'max-content' }} - pagination={{ size: 'small', pageSize: 10, showTotal: (t) => `共 ${t} 条` }} + pagination={{ size: 'small', pageSize: 10, showTotal: (total) => `${t('common.total')} ${total} ${t('common.items')}` }} />
diff --git a/src/components/SystemSetting/MenuList.tsx b/src/components/SystemSetting/MenuList.tsx index 7fbea8d..9597ba6 100644 --- a/src/components/SystemSetting/MenuList.tsx +++ b/src/components/SystemSetting/MenuList.tsx @@ -67,7 +67,7 @@ export default function MenuList() { }); } }).catch(() => { - messageApi.error('菜单加载失败'); + messageApi.error(t('systemSetting.fixed2MenuFixed2LoadFailedFixed2')); }); }; @@ -167,9 +167,7 @@ export default function MenuList() { render: (_, record) => ( {record.parentId === 0 && ( - + )} handleDelete(record.menuId)}> @@ -198,7 +196,7 @@ export default function MenuList() { setEditMenu(null); menuForm.resetFields(); setMenuModal(true); - }}>新增菜单 + }}>{t('systemSetting.fixed2AddNewFixed2MenuFixed2')}
- + {topMenus.map(m => ( ))} @@ -232,7 +230,7 @@ export default function MenuList() { - + @@ -241,15 +239,15 @@ export default function MenuList() { - - + + {/* 图标选择弹窗 */} {/* setIconModal(false)} footer={null} diff --git a/src/components/SystemSetting/OnlineUser.tsx b/src/components/SystemSetting/OnlineUser.tsx index cae70e9..2fe9545 100644 --- a/src/components/SystemSetting/OnlineUser.tsx +++ b/src/components/SystemSetting/OnlineUser.tsx @@ -9,34 +9,33 @@ import { useTranslation } from 'react-i18next'; const { Search } = Input; const { TabPane } = Tabs; -// 模拟在线用户数据 -const mockOnlineUsers = [ - { - id: 1, - sessionId: 'c85c6fe7-2e70-47...', - loginName: 'admin', - dept: '研发部门', - host: '127.0.0.1', - location: '内网IP', - os: 'Linux', - browser: 'Firefox 136.0', - loginTime: '2026-05-08 09:46:08' - }, - { - id: 2, - sessionId: '9f6c7d73-f684-4e...', - loginName: 'admin', - dept: '研发部门', - host: '127.0.0.1', - location: '内网IP', - os: 'Windows >=10', - browser: 'Edge 147', - loginTime: '2026-05-08 10:11:27' - } -]; - const OnlineUserPage = () => { const { t } = useTranslation(); + // 模拟在线用户数据 + const mockOnlineUsers = [ + { + id: 1, + sessionId: 'c85c6fe7-2e70-47...', + loginName: 'admin', + dept: t('systemSetting.deptRnd'), + host: '127.0.0.1', + location: t('systemSetting.intranetIp'), + os: 'Linux', + browser: 'Firefox 136.0', + loginTime: '2026-05-08 09:46:08' + }, + { + id: 2, + sessionId: '9f6c7d73-f684-4e...', + loginName: 'admin', + dept: t('systemSetting.deptRnd'), + host: '127.0.0.1', + location: t('systemSetting.intranetIp'), + os: 'Windows >=10', + browser: 'Edge 147', + loginTime: '2026-05-08 10:11:27' + } + ]; const [activeTab, setActiveTab] = useState('onlineUser'); // 默认选中在线用户 const [loading, setLoading] = useState(false); const [dataSource, setDataSource] = useState(mockOnlineUsers); @@ -51,7 +50,7 @@ const OnlineUserPage = () => { // 模拟接口请求 setTimeout(() => { setLoading(false); - message.success('搜索完成'); + message.success(t('systemSetting.fixed2SearchFixed2CompleteFixed2')); }, 500); }; @@ -59,31 +58,29 @@ const OnlineUserPage = () => { const handleReset = () => { setSearchParams({ loginAddress: '', userName: '' }); setDataSource(mockOnlineUsers); - message.info('已重置搜索条件'); + message.info(t('systemSetting.resetFixed2Search')); }; // 强退用户 const handleForceLogout = (record) => { - message.info(`已向会话 ${record.sessionId} 发送强退指令`); + message.info(t('systemSetting.forceLogoutSent', { sessionId: record.sessionId })); }; // 表格列配置 const columns = [ - { title: '序号', dataIndex: 'id', width: 60 }, - { title: '会话编号', dataIndex: 'sessionId', ellipsis: true }, - { title: '登录名称', dataIndex: 'loginName' }, - { title: '所属部门', dataIndex: 'dept' }, - { title: '主机', dataIndex: 'host' }, - { title: '登录地点', dataIndex: 'location' }, - { title: '操作系统', dataIndex: 'os' }, - { title: '浏览器', dataIndex: 'browser' }, - { title: '登录时间', dataIndex: 'loginTime' }, + { title: t('systemSetting.s122'), dataIndex: 'id', width: 60 }, + { title: t('systemSetting.s132'), dataIndex: 'sessionId', ellipsis: true }, + { title: t('systemSetting.fixed2LoginFixed2NameFixed2'), dataIndex: 'loginName' }, + { title: t('systemSetting.s152'), dataIndex: 'dept' }, + { title: t('systemSetting.s162'), dataIndex: 'host' }, + { title: t('systemSetting.fixed2Login'), dataIndex: 'location' }, + { title: t('systemSetting.fixed2Actions'), dataIndex: 'os' }, + { title: t('systemSetting.s192'), dataIndex: 'browser' }, + { title: t('systemSetting.fixed2LoginFixed2TimeFixed2'), dataIndex: 'loginTime' }, { title: t('roles.actions'), width: 100, render: (_, record) => ( - + ) } ]; @@ -91,7 +88,7 @@ const OnlineUserPage = () => { return (
- 系统监控 + {t('systemSetting.s110')}
{/* ====================== 顶部 Tabs ====================== */} { style={{ marginBottom: 0 }} > - + {/* ====================== Tab 1:在线用户 ====================== */} @@ -109,9 +106,9 @@ const OnlineUserPage = () => { {/* 搜索栏 */}
- 登录地址 + {t('systemSetting.fixed2LoginFixed2Fixed2')} setSearchParams({ ...searchParams, loginAddress: e.target.value })} style={{ width: 200 }} @@ -120,18 +117,14 @@ const OnlineUserPage = () => {
profile.username setSearchParams({ ...searchParams, userName: e.target.value })} style={{ width: 200 }} />
- - + +
{/* 在线用户表格 */} @@ -144,7 +137,7 @@ const OnlineUserPage = () => { total: 2, pageSize: 10, showSizeChanger: true, - showTotal: (total) => `共 ${total} 条`, + showTotal: (total) => `${t('common.total')} ${total} ${t('common.items')}`, showQuickJumper: true }} bordered={false} @@ -155,8 +148,8 @@ const OnlineUserPage = () => { {/* ====================== Tab 2:其他页面(你可以随便改内容) ====================== */} {activeTab === 'other' && ( -

其他监控内容

-

这里可以放服务状态、CPU监控、内存监控、接口监控等内容

+

{t('systemSetting.fixed2OthersContentFixed2')}

+

{t('systemSetting.statusCpuContentFixed2')}

)}
diff --git a/src/components/SystemSetting/Organization.tsx b/src/components/SystemSetting/Organization.tsx index 66ed0db..e8dfdbb 100644 --- a/src/components/SystemSetting/Organization.tsx +++ b/src/components/SystemSetting/Organization.tsx @@ -123,7 +123,7 @@ export default function Organization() { const columns = [ { title: t('systemSetting.orgName'), dataIndex: 'orgName' }, - //{ title: '组织编码', dataIndex: 'orgCode' }, + //{ title: t('systemSetting.fixed22'), dataIndex: 'orgCode' }, { title: t('systemSetting.leader'), dataIndex: 'manager' }, { title: t('systemSetting.contact'), dataIndex: 'phone' }, { title: t('users.email'), dataIndex: 'email' }, @@ -141,7 +141,7 @@ export default function Organization() { render: (_, r) => ( - handleDelete(r.id)}> + handleDelete(r.id)}> @@ -158,9 +158,7 @@ export default function Organization() {
- +
{/* 表格 + 统一分页 */} @@ -173,7 +171,7 @@ export default function Organization() { pageSize: pagination.pageSize, total: pagination.total, showSizeChanger: true, - showTotal: (total) => `共 ${total} 条` + showTotal: (total) => `${t('common.total')} ${total} ${t('common.items')}` }} onChange={handleTableChange} bordered @@ -182,7 +180,7 @@ export default function Organization() {
setModalVisible(false)} onOk={handleSave} @@ -191,37 +189,37 @@ export default function Organization() { >
- + - {/* - + {/* + */} - - + + - - + + - + - - + + - + diff --git a/src/components/SystemSetting/SerialNumGenerate.jsx b/src/components/SystemSetting/SerialNumGenerate.jsx index ff19807..4c82548 100644 --- a/src/components/SystemSetting/SerialNumGenerate.jsx +++ b/src/components/SystemSetting/SerialNumGenerate.jsx @@ -147,15 +147,15 @@ const SerialNumGenerate = () => { // 批量删除 const handleBatchDelete = async () => { if (selectedRowKeys.length === 0) { - message.warning('请选择要删除的数据'); + message.warning(t('systemSetting.fixed2PleaseSelectDelete')); return; } try { await delteCodesApi(selectedRowKeys.join(',')); - messageApi.success('批量删除成功'); + messageApi.success(t('workOrder.batchDeleteSuccess')); loadCodeList(); } catch (e) { - messageApi.error('批量删除失败'); + messageApi.error(t('workOrder.batchDeleteFail')); } }; @@ -232,7 +232,7 @@ const SerialNumGenerate = () => { // 下载 const downloadQRCode = () => { - if (!qrUrl) return messageApi.warning('请先生成'); + if (!qrUrl) return messageApi.warning(t('systemSetting.s26')); const link = document.createElement('a'); link.download = `${serialNumber || 'qrcode'}.png`; link.href = qrUrl; @@ -241,7 +241,7 @@ const SerialNumGenerate = () => { // 保存 const saveToDatabase = async () => { - if (!serialNumber) return messageApi.warning('请先生成'); + if (!serialNumber) return messageApi.warning(t('systemSetting.s26')); try { setLoading(true); const values = form.getFieldsValue(); @@ -268,7 +268,7 @@ const SerialNumGenerate = () => { setSerialNumber(''); setQrJson(''); } else { - messageApi.error('保存失败:' + res?.msg); + messageApi.error(`${t('common.saveFail')}:${res?.msg}`); } } catch (e) { messageApi.error(t('common.saveFail')); @@ -293,10 +293,10 @@ const SerialNumGenerate = () => { setCodeList([res.data]); } else { - messageApi.warning('未查询到数据'); + messageApi.warning(t('systemSetting.notFixed2Search')); } } catch (e) { - messageApi.error('查询失败'); + messageApi.error(t('systemSetting.fixed2SearchFixed2FailedFixed2')); } finally { setQueryLoading(false); } @@ -326,14 +326,14 @@ const SerialNumGenerate = () => { // 表格列 —— 已修改:全部显示名称,不显示编码 const columns = [ { - title: '序号', + title: t('systemSetting.s222'), key: 'idx', width: 70, render: (_, __, index) => (currentPage - 1) * pageSize + index + 1 }, - { title: '序列号', width: 380, dataIndex: 'serialNumber', key: 'sn', ellipsis: true }, + { title: t('systemSetting.s6'), width: 380, dataIndex: 'serialNumber', key: 'sn', ellipsis: true }, { - title: '型号', + title: t('systemSetting.s92'), key: 'model', render: (_, r) => { const qrInfo = parseQrJson(r.qrJson); @@ -342,7 +342,7 @@ const SerialNumGenerate = () => { } }, { - title: '配置', + title: t('systemSetting.s112'), key: 'config', render: (_, r) => { const qrInfo = parseQrJson(r.qrJson); @@ -360,17 +360,17 @@ const SerialNumGenerate = () => { } }, { - title: '国家', + title: t('systemSetting.s142'), key: 'country', render: (_, r) => parseQrJson(r.qrJson).countryFullName || '-' }, { - title: '省份', + title: t('systemSetting.s162'), key: 'province', render: (_, r) => parseQrJson(r.qrJson).provinceFullName || '-' }, { - title: '二维码', + title: t('systemSetting.s23'), key: 'img', width: 100, render: (_, r) => r.qrCodeBase64 && qrcode @@ -387,9 +387,7 @@ const SerialNumGenerate = () => { title={t('common.confirmDelete')} onConfirm={() => handleDelete(r.id)} > - + ) @@ -408,12 +406,12 @@ const SerialNumGenerate = () => { - - + + @@ -422,8 +420,8 @@ const SerialNumGenerate = () => { {/* 筛选栏 */}
- - + + @@ -450,7 +448,7 @@ const SerialNumGenerate = () => { {/* 新增弹窗 */} {
- - {models.map(m => )} - - {configs.map(c => )} - {types.map(t => )} - - {allCountries.map(c => )} - - {provinces.map(p => )} - + @@ -501,13 +499,13 @@ const SerialNumGenerate = () => {
{serialNumber && (
- 序列号:{serialNumber} + {t('systemSetting.s6')}:{serialNumber}
)} {qrUrl ? ( qrcode ) : ( -
请先生成二维码
+
{t('systemSetting.fixed2')}
)} @@ -519,20 +517,20 @@ const SerialNumGenerate = () => { {/* 查看二维码 */} - setViewModal(false)} footer={null} width={400}> + setViewModal(false)} footer={null} width={400}> {viewRecord && (
qrcode -
序列号:{viewRecord.serialNumber}
+
{t('systemSetting.s6')}:{viewRecord.serialNumber}
)}
{/* 数据管理 */} - setManageVisible(false)} footer={} destroyOnClose> - - - + setManageVisible(false)} footer={} destroyOnClose> + + +
); diff --git a/src/components/SystemSetting/SmartDeviceManagement.tsx b/src/components/SystemSetting/SmartDeviceManagement.tsx index dc5a696..89b6664 100644 --- a/src/components/SystemSetting/SmartDeviceManagement.tsx +++ b/src/components/SystemSetting/SmartDeviceManagement.tsx @@ -102,7 +102,7 @@ export default function SmartDeviceManagement({ setSmartTotal(res.total || res.rows.length); } } catch (err) { - utils.message.error('获取智能装备列表失败'); + utils.message.error(t('systemSetting.equipmentFixed2ListFixed2FailedFixed2')); } finally { setLoading(false); } @@ -172,7 +172,7 @@ export default function SmartDeviceManagement({ } return item; })); - utils.message.success('装备编辑成功'); + utils.message.success(t('systemSetting.fixed2EquipmentFixed2EditSuccessFixed2')); setEditSmartModalVisible(false); editSmartForm.resetFields(); setCurrentEditSmart(null); @@ -197,7 +197,7 @@ export default function SmartDeviceManagement({ setCurrentParamDevice(data); setParamModalVisible(true); } else { - utils.message.error('无权限'); + utils.message.error(t('devices.noPermission')); } } }); @@ -211,19 +211,19 @@ export default function SmartDeviceManagement({ }; const res = await getDeviceAuth(payload); if (res.code === 200 && res.data == true) { - utils.message.success('保存参数成功'); + utils.message.success(t('systemSetting.fixed2SaveSuccessFixed2')); setParamModalVisible1(false); } else { - utils.message.error(res.msg || '授权失败'); + utils.message.error(res.msg || t('systemSetting.authFail')); } } catch (err) { - utils.message.error('操作异常'); + utils.message.error(t('systemSetting.fixed2ActionsFixed2AbnormalFixed2')); } }; const openBindSmartModal = async () => { if (!selectedSmartKeys.length) { - return utils.message.warning('请先在列表中选中需要绑定的智能装备'); + return utils.message.warning(t('systemSetting.listEquipmentFixed2')); } bindSmartForm.resetFields(); setBindSmartModalVisible(true); @@ -271,43 +271,43 @@ export default function SmartDeviceManagement({ }; const res = await bindSmartDevice(payload); if (res.code === 200 && res.data == true) { - utils.message.success('绑定成功'); + utils.message.success(t('systemSetting.successFixed22')); setBindSmartModalVisible(false); bindSmartForm.resetFields(); fetchSmartList(); onAllDataNeedRefresh?.(); } else { - utils.message.error('绑定失败'); + utils.message.error(t('systemSetting.failedFixed22')); } } catch (err) { - utils.message.error('操作异常'); + utils.message.error(t('systemSetting.fixed2ActionsFixed2AbnormalFixed2')); } }; const handleUnbindSmart = (deviceIds, type) => { const validIds = (deviceIds || []).filter(id => id); if (validIds.length === 0) { - return utils.message.warning('未选择有效设备,无法解绑'); + return utils.message.warning(t('systemSetting.device2')); } - const typeText = { 1: '人员', 2: '场站', 3: '组织' }[type as number]; + const typeText = { 1: t('systemSetting.person'), 2: t('systemSetting.s24'), 3: t('systemSetting.s34') }[type as number]; modal.confirm({ - title: '确认解绑', - content: `确定要为选中的智能装备清除${typeText}绑定吗?`, + title: t('systemSetting.fixed2Confirm'), + content: t('systemSetting.confirmClearSmartBinding', { type: typeText }), okText: t('common.ok'), cancelText: t('common.cancel'), onOk: async () => { try { const res = await unbindSmartDevice({ deviceIds: validIds, type }); if (res.code === 200) { - utils.message.success('解绑成功'); + utils.message.success(t('systemSetting.successFixed2')); fetchSmartList(); setSelectedSmartKeys([]); onAllDataNeedRefresh?.(); } else { - utils.message.error(res.msg || '解绑失败'); + utils.message.error(res.msg || t('systemSetting.unbindFail')); } } catch (err) { - utils.message.error('操作异常'); + utils.message.error(t('systemSetting.fixed2ActionsFixed2AbnormalFixed2')); } } }); @@ -315,26 +315,26 @@ export default function SmartDeviceManagement({ const smartColumns = [ { title: t('devices.deviceName'), width: 150, dataIndex: 'deviceAlias', key: 'name' }, - { title: '设备码', width: 280, dataIndex: 'serialNumber', key: 'sn' }, + { title: t('systemSetting.fixed2Device'), width: 280, dataIndex: 'serialNumber', key: 'sn' }, { - title: '所属组织', width: 150, key: 'orgName', + title: t('systemSetting.belongingOrg'), width: 150, key: 'orgName', render: (_, record: any) => { const org = allOrgOptions.find(o => (o.orgId || o.id) == record.orgId); - return org ? (org.orgName || org.name) : 未绑定; + return org ? (org.orgName || org.name) : {t('systemSetting.s02')}; }, }, { - title: '所属场站', width: 150, key: 'siteName', + title: t('systemSetting.s9'), width: 150, key: 'siteName', render: (_, record: any) => { const site = allSiteOptions.find(s => (s.siteId || s.id) == record.siteId); - return site ? (site.siteName || site.name) : 未绑定; + return site ? (site.siteName || site.name) : {t('systemSetting.s02')}; }, }, { title: t('systemSetting.leader'), width: 150, key: 'userName', render: (_, record: any) => { const user = allUserOptions.find(u => u.userId == record.userId); - return user ? (user.nickName || user.userName) : 未绑定; + return user ? (user.nickName || user.userName) : {t('systemSetting.s02')}; }, }, { title: t('devices.onlineStatus'), width: 120, dataIndex: 'onlineStatus', key: 'online', render: s => {s === 'ONLINE' || s === 1 ? t('devices.online') : t('devices.offline')} }, @@ -342,17 +342,17 @@ export default function SmartDeviceManagement({ title: t('roles.actions'), width: 280, key: 'action', fixed: 'right' as const, render: (_, record: any) => ( - - + + - - + + - - + + - - {userInfo?.userId == 1 && } + + {userInfo?.userId == 1 && } ), }, @@ -378,17 +378,17 @@ export default function SmartDeviceManagement({
setSmartParams({ ...smartParams, serialNumber: e.target.value.trim() })} /> - - - - + + + +
@@ -404,7 +404,7 @@ export default function SmartDeviceManagement({ pageSize: smartParams.pageSize, total: smartTotal, showSizeChanger: true, - showTotal: (t) => `共 ${t} 条`, + showTotal: (t) => `${t('common.total')} ${t} ${t('common.items')}`, onChange: (page, pageSize) => { setSmartParams({ pageNum: page, pageSize }); }, @@ -417,7 +417,7 @@ export default function SmartDeviceManagement({ title={
- 绑定智能装备 + {t('systemSetting.equipmentFixed2')}
} open={bindSmartModalVisible} @@ -433,19 +433,19 @@ export default function SmartDeviceManagement({ width={540} > - 已选智能装备({selectedSmartKeys.length}台)ID} name="deviceIds" rules={[{ required: true, message: '请选择装备' }]}> - + + - 所属组织 + {t('systemSetting.belongingOrg')} } - name="orgId" rules={[{ required: true, message: '请选择组织' }]} + name="orgId" rules={[{ required: true, message: t('systemSetting.pleaseSelectOrg') }]} > { @@ -484,9 +484,9 @@ export default function SmartDeviceManagement({ ))} - + + + - - + + - - {t('devices.online')} {t('devices.offline')} @@ -539,7 +539,7 @@ export default function SmartDeviceManagement({ background: 'linear-gradient(180deg, #1890ff 0%, #096dd9 100%)', borderRadius: 2, }}> - 设备授权 + {t('systemSetting.fixed2DeviceFixed2Fixed2')} - {(currentParamDevice1 as any)?.deviceAlias || (currentParamDevice1 as any)?.serialNumber} @@ -569,27 +569,27 @@ export default function SmartDeviceManagement({ >
💡 - 设置设备操作授权,授权到期后设备将失去对应操作权限 + {t('systemSetting.fixed2SettingsFixed2DeviceFixed2ActionsDeviceActions')}
授权时间(min)} + label={{t('systemSetting.authDuration')}(min)} name="minute" rules={[ - { required: true, message: '请输入授权剩余时间' }, - { type: 'number', min: 0, message: '授权时间不能小于0' } + { required: true, message: t('systemSetting.fixed2PleaseEnterTimeFixed2') }, + { type: 'number', min: 0, message: t('systemSetting.timeLessThan0') } ]} >
- +
diff --git a/src/components/SystemSetting/StationManage.tsx b/src/components/SystemSetting/StationManage.tsx index 0e655a5..d000600 100644 --- a/src/components/SystemSetting/StationManage.tsx +++ b/src/components/SystemSetting/StationManage.tsx @@ -247,22 +247,22 @@ export default function StationManage() { const columns = [ { title: t('systemSetting.siteName'), dataIndex: 'siteName' }, { - title: '所属组织', + title: t('systemSetting.belongingOrg'), dataIndex: 'orgId', render: (orgId) => { const org = orgList.find(item => item.id == orgId); return org ? org.orgName : '—'; } }, - { title: '地址', dataIndex: 'address' }, + { title: t('systemSetting.s14'), dataIndex: 'address' }, { title: t('roles.actions'), width: 400, render: (_, record) => ( - - + + handleDelete(record.id)}> @@ -283,7 +283,7 @@ export default function StationManage() { {!userInfo?.orgId && (
+ + + + - 消息推送配置 + {t('systemSetting.s3')}
- - + + - - + + - - + + - - + + @@ -396,23 +394,23 @@ export default function StationManage() { - + - + @@ -430,7 +428,7 @@ export default function StationManage() { {/* 分区弹窗 */} setAreaVisible(false)} @@ -439,20 +437,20 @@ export default function StationManage() {
- + - + - - + + @@ -462,7 +460,7 @@ export default function StationManage() { - +
r.status === 1 ? t('common.enable') : t('roles.stopped') @@ -492,17 +490,15 @@ export default function StationManage() { {/* 地图绘制弹窗 */} setMapVisible(false)} footer={null} > -
- 地图区域绘制(可自行接入高德/百度地图) - - - +
{t('systemSetting.area')} + +
diff --git a/src/components/SystemSetting/SystemMaintain.tsx b/src/components/SystemSetting/SystemMaintain.tsx index 26ea66d..e611656 100644 --- a/src/components/SystemSetting/SystemMaintain.tsx +++ b/src/components/SystemSetting/SystemMaintain.tsx @@ -35,9 +35,9 @@ export const SystemMaintenancePage = () => {
- 错误管理} key="error" /> - 告警模型管理} key="alarm" /> - 工单模型管理} key="workOrder" /> + {t('systemSetting.fixed2Error')}} key="error" /> + {t('systemSetting.fixed2Alert')}} key="alarm" /> + {t('systemSetting.s2')}} key="workOrder" /> diff --git a/src/components/SystemSetting/UserPage.tsx b/src/components/SystemSetting/UserPage.tsx index d4c9ff6..6703ee1 100644 --- a/src/components/SystemSetting/UserPage.tsx +++ b/src/components/SystemSetting/UserPage.tsx @@ -215,17 +215,17 @@ export default function UserRolePage() { }; const handleBatchDeleteRoles = () => { - if (selectedRoleIds.length == 0) return messageApi.warning('请选择要删除的角色'); + if (selectedRoleIds.length == 0) return messageApi.warning(t('systemSetting.fixed2PleaseSelectDeleteRoleFixed2')); modal.confirm({ - title: '批量删除角色', + title: t('systemSetting.fixed2BatchDeleteFixed2RoleFixed2'), content: `确定要删除已选中的 ${selectedRoleIds.length} 个角色吗?`, - okText: '确定删除', + okText: t('systemSetting.fixed2OkFixed2DeleteFixed2'), cancelText: t('common.cancel'), okType: 'danger', onOk: () => { deleteRole(selectedRoleIds).then(res => { if (res.code === 200) { - messageApi.success('批量删除成功'); + messageApi.success(t('workOrder.batchDeleteSuccess')); setSelectedRoleIds([]); getRoleData(); } else messageApi.error(res.msg || t('common.deleteFail')); @@ -332,7 +332,7 @@ export default function UserRolePage() { cancelText: t('common.cancel'), onOk: () => { editUser({ userId: user.userId, password: '123456' }).then(res => { - res.code === 200 ? messageApi.success(t('users.passwordResetTo')) : messageApi.error('重置失败'); + res.code === 200 ? messageApi.success(t('users.passwordResetTo')) : messageApi.error(t('systemSetting.resetFail')); }); } }); @@ -348,16 +348,16 @@ export default function UserRolePage() { }; const handleBatchDeleteUsers = () => { - if (selectedUserIds.length === 0) return messageApi.warning('请选择要删除的用户'); + if (selectedUserIds.length === 0) return messageApi.warning(t('systemSetting.fixed2PleaseSelectDelete2')); modal.confirm({ - title: '批量删除用户', + title: t('systemSetting.fixed2BatchDelete'), content: `确定要删除已选中的 ${selectedUserIds.length} 个用户吗?`, - okText: '确定删除', + okText: t('systemSetting.fixed2OkFixed2DeleteFixed2'), cancelText: t('common.cancel'), okType: 'danger', onOk: () => { Promise.all(selectedUserIds.map(id => deleteUser(id))).then(() => { - messageApi.success('批量删除成功'); + messageApi.success(t('workOrder.batchDeleteSuccess')); setSelectedUserIds([]); getUserData(); }); @@ -401,7 +401,7 @@ export default function UserRolePage() { }}> {contextHolder}
- 用户与角色 + {t('systemSetting.userAndRole')}
@@ -427,7 +427,7 @@ export default function UserRolePage() { { title: t('users.phone'), dataIndex: 'phonenumber', ellipsis: true }, { title: t('users.email'), dataIndex: 'email', ellipsis: true }, { - title: '所属机构', + title: t('systemSetting.s222'), render: (_, r) => { const org = orgList.find(o => o.id == r.orgId); return org ? org.orgName : '—'; @@ -435,7 +435,7 @@ export default function UserRolePage() { ellipsis: true }, { - title: '所属场站', + title: t('systemSetting.s9'), render: (_, r) => { if (!r.siteId) return '—'; const site = allSiteList.find(s => s.id == r.siteId); @@ -457,10 +457,10 @@ export default function UserRolePage() { : {t('roles.stopped')} }, { - title: '是否有设备权限', dataIndex: 'deviceOperatePerm', width: 140, + title: t('systemSetting.devicePermFixed2'), dataIndex: 'deviceOperatePerm', width: 140, render: (_, s) => s.deviceOperatePerm == true - ? 有权限 - : 无权限 + ? {t('systemSetting.hasPerm')} + : {t('devices.noPermission')} }, { title: t('roles.actions'), @@ -469,7 +469,7 @@ export default function UserRolePage() { render: (_, r) => ( - handleDeleteUser(r.userId)} title="确定删除该用户吗?"> + handleDeleteUser(r.userId)} title={t('systemSetting.fixed2OkFixed2Delete2')}> @@ -534,7 +534,7 @@ export default function UserRolePage() { 权限:{r.roleKey} 等级:{r.roleLevel || '-'} - {/*{r.roleKey == "admin1" && 超级管理员}*/} + {/*{r.roleKey == "admin1" && {t('roles.superAdmin')}}*/} } /> @@ -592,7 +592,7 @@ export default function UserRolePage() {
- + @@ -625,11 +625,11 @@ export default function UserRolePage() { >
-
- + + - + @@ -655,7 +655,7 @@ export default function UserRolePage() { - 男 - 女 + {t('common.male')} + {t('common.female')} {t('constants.locationUnknown')} - + @@ -740,14 +740,14 @@ export default function UserRolePage() { diff --git a/src/components/SystemSetting/VideoManagement.tsx b/src/components/SystemSetting/VideoManagement.tsx index e85d182..932ac87 100644 --- a/src/components/SystemSetting/VideoManagement.tsx +++ b/src/components/SystemSetting/VideoManagement.tsx @@ -73,7 +73,7 @@ const VideoManagement = () => { } } catch (e) { setChannels([]); - message.error('无法连接视频服务'); + message.error(t('systemSetting.video')); } finally { setLoading(false); } @@ -136,7 +136,7 @@ const VideoManagement = () => { const data = await fetchList({ deviceId: channel, year, month, day: val }); setFiles(data.code === 200 ? (data.files || []) : []); if (data.code !== 200) message.warning(data.error || '读取文件失败'); - } catch { setFiles([]); message.error('读取文件失败'); } + } catch { setFiles([]); message.error(t('systemSetting.failedFixed2')); } finally { setLoading(false); } }; @@ -144,7 +144,7 @@ const VideoManagement = () => { const openPreview = (file: { fileName: string }) => { if (!channel || !year || !month || !day) { - message.warning('请先选择完整的时间路径'); + message.warning(t('systemSetting.time')); return; } setPreview({ @@ -155,7 +155,7 @@ const VideoManagement = () => { const columns = [ { title: t('video.fileName'), dataIndex: 'fileName', key: 'fileName', ellipsis: true }, - { title: '大小', dataIndex: 'sizeFormatted', key: 'sizeFormatted', width: 120 }, + { title: t('systemSetting.s11'), dataIndex: 'sizeFormatted', key: 'sizeFormatted', width: 120 }, { title: t('roles.actions'), key: 'action', width: 160, render: (_: any, rec: { fileName: string }) => ( @@ -190,9 +190,9 @@ const VideoManagement = () => { - 通道 + {t('systemSetting.s0')} ({ value: y, label: y }))} /> - 月 + {t('devices.month')} ({ value: d, label: d }))} /> @@ -231,11 +231,11 @@ const VideoManagement = () => { {loading ? (
) : !channel ? ( - + ) : !day ? ( - + ) : files.length === 0 ? ( - + ) : (
{ setSelectedRowKeys([]); } } catch (e) { - utils.message.error('获取工单模型列表失败'); + utils.message.error(t('systemSetting.listFixed2FailedFixed2')); setList([]); } finally { setLoading(false); @@ -211,8 +211,8 @@ const WorkOrderModelManagement: React.FC = () => { // 单条删除 const handleDelete = (id: string | number) => { modal.confirm({ - title: '确认删除', - content: '您确定要删除这条工单模型吗?', + title: t('systemSetting.fixed2ConfirmFixed2DeleteFixed2'), + content: t('systemSetting.okDelete'), okText: t('common.ok'), cancelText: t('common.cancel'), onOk: async () => { @@ -232,12 +232,12 @@ const WorkOrderModelManagement: React.FC = () => { // 批量删除 const handleBatchDelete = () => { if (!selectedRowKeys.length) { - message.warning('请先勾选要删除的数据'); + message.warning(t('systemSetting.delete')); return; } modal.confirm({ - title: '批量删除确认', - content: `您确定要删除选中的 ${selectedRowKeys.length} 条工单模型吗?删除后不可恢复!`, + title: t('systemSetting.fixed2BatchDeleteFixed2ConfirmFixed2'), + content: t('systemSetting.confirmBatchDeleteWorkOrder', { count: selectedRowKeys.length }), okText: t('common.ok'), cancelText: t('common.cancel'), danger: true, @@ -245,11 +245,11 @@ const WorkOrderModelManagement: React.FC = () => { try { const res = await deleteWorkOrderModelApi(selectedRowKeys); if (res.code === 200) { - message.success('批量删除成功'); + message.success(t('workOrder.batchDeleteSuccess')); fetchList(); } } catch (e) { - message.error('批量删除失败'); + message.error(t('workOrder.batchDeleteFail')); } } }); @@ -274,7 +274,7 @@ const WorkOrderModelManagement: React.FC = () => { res = await addWorkOrderModelApi(payload); } if (res.code === 200 && res.data == true) { - message.success(editingItem ? '更新成功' : t('common.addSuccess')); + message.success(editingItem ? t('systemSetting.updateSuccess') : t('common.addSuccess')); setModalVisible(false); form.resetFields(); fetchList(); @@ -298,7 +298,7 @@ const WorkOrderModelManagement: React.FC = () => { }; const columns = [ - { title: '工单名称', dataIndex: 'orderName', key: 'orderName' }, + { title: t('systemSetting.nameFixed2'), dataIndex: 'orderName', key: 'orderName' }, { title: t('workOrder.orderType'), dataIndex: 'orderType', @@ -306,17 +306,17 @@ const WorkOrderModelManagement: React.FC = () => { render: getWorkOrderTypeText }, { - title: '错误等级', + title: t('systemSetting.errorLevelLabel'), dataIndex: 'errorLevel', key: 'errorLevel', render: (val) => { const color = LEVEL_TAG_COLOR[val] || 'default'; - const text = { 1: '轻微', 2: t('constants.general'), 3: t('constants.critical') }[val] || val; + const text = { 1: t('systemSetting.minor'), 2: t('constants.general'), 3: t('constants.critical') }[val] || val; return {text}; } }, { - title: '设备', + title: t('devices.device'), dataIndex: 'deviceId', key: 'deviceId', render: (val) => { @@ -326,7 +326,7 @@ const WorkOrderModelManagement: React.FC = () => { } }, { - title: '路径/航线', + title: t('systemSetting.s19'), dataIndex: 'routeId', key: 'routeId', render: (val, record) => { @@ -339,10 +339,10 @@ const WorkOrderModelManagement: React.FC = () => { } }, { - title: '自动生成任务', + title: t('systemSetting.generateTaskFixed2'), dataIndex: 'autoGenerateTask', key: 'autoGenerateTask', - render: (val: boolean) => val ? '是' : '否' + render: (val: boolean) => val ? t('common.yes') : t('common.no') }, { title: t('roles.actions'), @@ -366,7 +366,7 @@ const WorkOrderModelManagement: React.FC = () => {
- +
{ setModalVisible(false)} footer={null} width={600} > - - + + + + { {/* 根据设备类型显示不同的路径选择 */} {selectedDeviceType === 'robot' && ( - - )} {selectedDeviceType === 'uav' && ( - - )} - {/* + {/* */} diff --git a/src/components/VideoPlayer.tsx b/src/components/VideoPlayer.tsx index 1a0c76e..34a5091 100644 --- a/src/components/VideoPlayer.tsx +++ b/src/components/VideoPlayer.tsx @@ -29,7 +29,7 @@ const VideoPlayer: React.FC = ({ const currentTitle = title || viewTitles[idx - 1]; if (!deviceId || error) { - return ; + return ; } if (!ready || !video) { diff --git a/src/components/alerts/AlarmHistory.tsx b/src/components/alerts/AlarmHistory.tsx index d3fbdea..432eb0e 100644 --- a/src/components/alerts/AlarmHistory.tsx +++ b/src/components/alerts/AlarmHistory.tsx @@ -28,7 +28,7 @@ const alarmLevelConfig = { const handleStatusMap = { 1: t('constants.pending'), - 2: '处理中', + 2: t('alerts.processing'), 3: t('constants.closed'), 4: t('constants.ignored') }; @@ -79,7 +79,7 @@ const AlarmHistory = () => { setTotal(res.total || 0); } } catch (err) { - message.error('加载失败'); + message.error(t('common.loadFailed')); } finally { setLoading(false); } @@ -94,10 +94,10 @@ const AlarmHistory = () => { setCurrentAlarm(res.data); setDrawerVisible(true); } else { - message.error('获取详情失败'); + message.error(t('common.loadDetailFailed')); } } catch (err) { - message.error('请求异常'); + message.error(t('common.requestError')); } finally { setDetailLoading(false); } @@ -129,14 +129,14 @@ const AlarmHistory = () => { // 表格列 const columns = [ - { title: '告警编号', dataIndex: 'alarmNo', width: 180 }, + { title: t('alerts.alarmNo'), dataIndex: 'alarmNo', width: 180 }, { title: t('alerts.alertTime'), dataIndex: 'alarmTime', width: 190, render: (t) => t ? dayjs(t).format('YYYY-MM-DD HH:mm:ss') : '-' }, - { title: '设备ID', dataIndex: 'deviceId', width: 160 }, + { title: t('common.deviceId'), dataIndex: 'deviceId', width: 160 }, { title: t('devices.deviceName'), dataIndex: 'deviceName', ellipsis: true }, { title: t('alerts.alertType'), @@ -153,8 +153,8 @@ const AlarmHistory = () => { ) }, - { title: '告警标题', dataIndex: 'alarmTitle', ellipsis: true, width: 180 }, - { title: '告警内容', dataIndex: 'alarmContent', ellipsis: true }, + { title: t('alerts.alarmTitle'), dataIndex: 'alarmTitle', ellipsis: true, width: 180 }, + { title: t('alerts.alarmContent'), dataIndex: 'alarmContent', ellipsis: true }, { title: t('alerts.handleStatus'), width: 110, @@ -174,7 +174,7 @@ const AlarmHistory = () => { fixed: 'right', render: (_, r) => ( ) } @@ -182,7 +182,7 @@ const AlarmHistory = () => { return (
- + { style={{ marginBottom: 16, flexWrap: 'wrap', gap: 16 }} > {/* 设备ID */} - - - + + + setQueryParams({ ...queryParams, deviceId: e.target.value })} style={{ width: 280 }} @@ -697,7 +697,7 @@ export default function AlarmCenterPage() { />
-
时间范围
+
{t('common.timeRange')}
- 查询 + {t('common.search2')} @@ -807,7 +807,7 @@ export default function AlarmCenterPage() { - + - + {/* 告警详情抽屉 */} - setDrawerVisible(false)} width={480} destroyOnClose @@ -872,15 +872,15 @@ export default function AlarmCenterPage() { {currentAlarm.alarmTitle} -
告警编号:{currentAlarm.alarmNo}
+
{t('alerts.alarmNo')}:{currentAlarm.alarmNo}
{/* 告警基础信息 */} - {currentAlarm.deviceName || '-'} - {alarmTypeMap[currentAlarm.alarmType] || '-'} - {dayjs(currentAlarm.alarmTime).format('YYYY-MM-DD HH:mm:ss')} +{currentAlarm.deviceName || '-'} + {alarmTypeMap[currentAlarm.alarmType] || '-'} + {dayjs(currentAlarm.alarmTime).format('YYYY-MM-DD HH:mm:ss')} {(() => { let color = 'default'; @@ -899,23 +899,23 @@ export default function AlarmCenterPage() { {/* 告警描述 */} - +
{currentAlarm.alarmContent || '-'}
{/* 根因分析 */} {/* 推荐处理措施 */} - +
{currentAlarm.handleSuggestion || '-'}
- +
@@ -923,33 +923,33 @@ export default function AlarmCenterPage() { {/* 处理操作 */} - - - + - - - - - + + + + @@ -1008,17 +1008,17 @@ export default function AlarmCenterPage() { - + @@ -1026,7 +1026,7 @@ export default function AlarmCenterPage() { {/* ========== 新增:生成工单弹窗 ========== */} { setCreateOrderModalVisible(false); @@ -1050,14 +1050,14 @@ export default function AlarmCenterPage() { @@ -1065,7 +1065,7 @@ export default function AlarmCenterPage() { diff --git a/src/components/alerts/AlarmRealtime.tsx b/src/components/alerts/AlarmRealtime.tsx index c96fa0d..e9a7ea5 100644 --- a/src/components/alerts/AlarmRealtime.tsx +++ b/src/components/alerts/AlarmRealtime.tsx @@ -5,6 +5,7 @@ import { } from 'antd'; import { EyeOutlined } from '@ant-design/icons'; import { useTranslation } from 'react-i18next'; +import i18n from '../i18n'; const AntdCard = Card as any; const { Search } = Input; @@ -12,9 +13,9 @@ const { Option } = Select; // 模拟数据 const mockAlarms = [ - { id: 1, deviceName: '1#环境传感器', level: 'warn', content: '温度超过阈值 62℃', time: '2026-05-07 10:24:00', status: 'unprocessed' }, - { id: 2, deviceName: '2#阵列区球机', level: 'error', content: '视频信号丢失', time: '2026-05-07 10:20:00', status: 'processing' }, - { id: 3, deviceName: '3#电池供电设备', level: 'info', content: '电量低于20%', time: '2026-05-07 09:50:00', status: 'processed' }, + { id: 1, deviceName: i18n.t('alerts.mockDeviceSensor'), level: 'warn', content: i18n.t('alerts.mockTempExceed', { value: 62 }), time: '2026-05-07 10:24:00', status: 'unprocessed' }, + { id: 2, deviceName: i18n.t('alerts.mockDeviceCamera'), level: 'error', content: i18n.t('alerts.mockVideoSignalLost'), time: '2026-05-07 10:20:00', status: 'processing' }, + { id: 3, deviceName: i18n.t('alerts.mockDeviceBattery'), level: 'info', content: i18n.t('alerts.mockBatteryLow', { value: 20 }), time: '2026-05-07 09:50:00', status: 'processed' }, ]; const AlarmRealtime = () => { @@ -46,22 +47,22 @@ const AlarmRealtime = () => { // 表格列 const columns = [ { title: t('common.time'), dataIndex: 'time', width: 180 }, - { title: '设备', dataIndex: 'deviceName' }, + { title: t('devices.device'), dataIndex: 'deviceName' }, { - title: '级别', + title: t('alerts.level'), render: (l: any) => { const color = l.level === 'warn' ? 'orange' : l.level === 'error' ? 'red' : 'blue'; const text = l.level === 'warn' ? t('constants.warning') : l.level === 'error' ? t('constants.critical') : t('constants.info'); return {text}; } }, - { title: '内容', dataIndex: 'content' }, + { title: t('alerts.content'), dataIndex: 'content' }, { title: t('roles.status'), render: (r: any) => { const statusMap = { unprocessed: { status: 'error', text: t('home.unhandled') }, - processing: { status: 'warning', text: '处理中' }, + processing: { status: 'warning', text: t('alerts.processing') }, processed: { status: 'success', text: t('constants.handled') }, closed: { status: 'default', text: t('constants.closed') }, }; @@ -73,7 +74,7 @@ const AlarmRealtime = () => { title: t('roles.actions'), render: (_: any, record: any) => ( ) }, @@ -84,11 +85,11 @@ const AlarmRealtime = () => { + } > {/* 表格 */} @@ -102,7 +103,7 @@ const AlarmRealtime = () => { {/* 告警详情弹窗 */} setDetailVisible(false)} footer={null} @@ -112,7 +113,7 @@ const AlarmRealtime = () => { {currentAlarm.time} {currentAlarm.deviceName} - {currentAlarm.content} + {currentAlarm.content} {currentAlarm.level === 'warn' ? t('constants.warning') : currentAlarm.level === 'error' ? t('constants.critical') : t('constants.info')} @@ -126,7 +127,7 @@ const AlarmRealtime = () => { } text={ currentAlarm.status === 'unprocessed' ? t('home.unhandled') : - currentAlarm.status === 'processing' ? '处理中' : t('constants.handled') + currentAlarm.status === 'processing' ? t('alerts.processing') : t('constants.handled') } /> diff --git a/src/components/alerts/AlarmRules.tsx b/src/components/alerts/AlarmRules.tsx index 17797ff..37a6115 100644 --- a/src/components/alerts/AlarmRules.tsx +++ b/src/components/alerts/AlarmRules.tsx @@ -8,14 +8,15 @@ import { PlusOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons'; // 从你的设备接入页面 导入 物模型数据 //import { modelList, modelDetailList } from './DeviceAccessPage'; import { useTranslation } from 'react-i18next'; +import i18n from '../i18n'; const AntdCard = Card as any; const { Search } = Input; // 模拟数据 const mockRules = [ - { id: 1, name: '温度超温', deviceType: 'sensor', modelId:11, propCode:'temp', level: 'warn', condition: 'temp > 60', status: true }, - { id: 2, name: '电池低电量', deviceType: 'battery', modelId:12, propCode:'battery', level: 'info', condition: 'battery < 20', status: false }, + { id: 1, name: i18n.t('alerts.mockRuleTemp'), deviceType: 'sensor', modelId:11, propCode:'temp', level: 'warn', condition: 'temp > 60', status: true }, + { id: 2, name: i18n.t('alerts.mockRuleBattery'), deviceType: 'battery', modelId:12, propCode:'battery', level: 'info', condition: 'battery < 20', status: false }, ]; const AlarmRules = () => { @@ -28,9 +29,9 @@ const AlarmRules = () => { // 下拉选项 const [deviceTypeOpts, setDeviceTypeOpts] = useState([ - { value: 'sensor', label: '传感器' }, + { value: 'sensor', label: t('alerts.deviceSensor') }, { value: 'camera', label: t('devices.camera') }, - { value: 'battery', label: '电池设备' }, + { value: 'battery', label: t('alerts.deviceBattery') }, ]); const [propList, setPropList] = useState([]); @@ -104,7 +105,7 @@ const AlarmRules = () => { //}, [form.getFieldValue('modelId')]); const columns = [ - { title: '规则名称', dataIndex: 'name' }, + { title: t('alerts.ruleName'), dataIndex: 'name' }, { title: t('devices.deviceType'), dataIndex: 'deviceType' }, { title: t('alerts.alertLevel'), @@ -114,17 +115,17 @@ const AlarmRules = () => { return {text}; } }, - { title: '触发条件', dataIndex: 'condition' }, + { title: t('alerts.triggerCondition'), dataIndex: 'condition' }, { title: t('roles.status'), - render: (r: any) => + render: (r: any) => }, { title: t('roles.actions'), render: (r: any) => ( - handleDelete(r.id)}> + handleDelete(r.id)}> ) } @@ -133,11 +134,11 @@ const AlarmRules = () => { return (
- + } > @@ -145,15 +146,15 @@ const AlarmRules = () => { setModalVisible(false)} onOk={handleSave} width={600} >
- - + + @@ -162,7 +163,7 @@ const AlarmRules = () => { - + - + - + - - + + diff --git a/src/components/alerts/AlarmStatistics.tsx b/src/components/alerts/AlarmStatistics.tsx index 8d8f050..7e30473 100644 --- a/src/components/alerts/AlarmStatistics.tsx +++ b/src/components/alerts/AlarmStatistics.tsx @@ -41,8 +41,8 @@ const AlarmStatistics = () => { const chart = echarts.init(barChartRef.current); const option = { - title: { text: '近7天告警趋势', textStyle: { fontSize: 14 } }, - xAxis: { type: 'category', data: ['1日', '2日', '3日', '4日', '5日', '6日', '7日'] }, + title: { text: t('alerts.sevenDayTrend'), textStyle: { fontSize: 14 } }, + xAxis: { type: 'category', data: [t('devices.daySuffix', { day: 1 }), t('devices.daySuffix', { day: 2 }), t('devices.daySuffix', { day: 3 }), t('devices.daySuffix', { day: 4 }), t('devices.daySuffix', { day: 5 }), t('devices.daySuffix', { day: 6 }), t('devices.daySuffix', { day: 7 })] }, yAxis: { type: 'value' }, series: [{ data: [5, 8, 12, 9, 15, 7, statData.total || 2], @@ -66,14 +66,14 @@ const AlarmStatistics = () => { const chart = echarts.init(pieChartRef.current); const pieData = [ - { value: statData.levelStats?.[1] || 0, name: '严重告警' }, - { value: statData.levelStats?.[2] || 0, name: '重要告警' }, - { value: statData.levelStats?.[3] || 0, name: '一般告警' }, - { value: statData.levelStats?.[4] || 0, name: '提示告警' }, + { value: statData.levelStats?.[1] || 0, name: t('alerts.criticalAlert') }, + { value: statData.levelStats?.[2] || 0, name: t('alerts.importantAlert') }, + { value: statData.levelStats?.[3] || 0, name: t('alerts.generalAlert') }, + { value: statData.levelStats?.[4] || 0, name: t('alerts.infoAlert') }, ].filter(i => i.value > 0); const option = { - title: { text: '告警级别分布' }, + title: { text: t('alerts.alertLevelDistribution') }, tooltip: { trigger: 'item' }, legend: { bottom: 0 }, series: [{ @@ -119,7 +119,7 @@ const AlarmStatistics = () => {
-
严重告警
+
{t('alerts.criticalAlert')}
{statData.levelStats?.[1] || 0}
diff --git a/src/components/alerts/AlarmSubscription.tsx b/src/components/alerts/AlarmSubscription.tsx index 9a9707c..30f6e76 100644 --- a/src/components/alerts/AlarmSubscription.tsx +++ b/src/components/alerts/AlarmSubscription.tsx @@ -5,6 +5,7 @@ import { } from 'antd'; import { PlusOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons'; import { useTranslation } from 'react-i18next'; +import i18n from '../i18n'; const AntdCard = Card as any; const { Option } = Select; @@ -12,9 +13,9 @@ const { Search } = Input; // 模拟数据 const mockSub = [ - { id: 1, name: '运维订阅', deviceType: 'all', level: 'all', channels: ['短信'] }, - { id: 2, name: '管理员订阅', deviceType: 'sensor', level: 'error', channels: ['短信', '邮件'] }, - { id: 3, name: '设备订阅', deviceType: 'camera', level: 'warn', channels: ['APP推送'] }, + { id: 1, name: i18n.t('alerts.mockSubOps'), deviceType: 'all', level: 'all', channels: ['短信'] }, + { id: 2, name: i18n.t('alerts.mockSubAdmin'), deviceType: 'sensor', level: 'error', channels: ['短信', '邮件'] }, + { id: 3, name: i18n.t('alerts.mockSubDevice'), deviceType: 'camera', level: 'warn', channels: ['APP推送'] }, ]; const AlarmSubscription = () => { @@ -25,6 +26,13 @@ const AlarmSubscription = () => { const [editingId, setEditingId] = useState(null); const [form] = Form.useForm(); + // 通知渠道显示映射(值为稳定编码,显示时翻译) + const channelLabelMap = { + '短信': t('alerts.sms'), + '邮件': t('alerts.email'), + 'APP推送': t('alerts.appPush'), + }; + // 搜索筛选 const handleSearch = (value) => { const res = list.filter(item => @@ -78,12 +86,12 @@ const AlarmSubscription = () => { }; const columns = [ - { title: '订阅名称', dataIndex: 'name' }, + { title: t('alerts.subscriptionName'), dataIndex: 'name' }, { title: t('devices.deviceType'), dataIndex: 'deviceType' }, { title: t('alerts.alertLevel'), dataIndex: 'level' }, { - title: '通知渠道', - render: (r) => r.channels.map(i => {i}) + title: t('alerts.notifyChannel'), + render: (r) => r.channels.map(i => {channelLabelMap[i] || i}) }, { title: t('roles.actions'), @@ -104,14 +112,14 @@ const AlarmSubscription = () => { title={t('alerts.alarmSubscription')} extra={ - - + + } > @@ -125,40 +133,40 @@ const AlarmSubscription = () => { {/* 新增/编辑 弹窗 */} setModalVisible(false)} onOk={handleSave} maskClosable={false} > - - + + - - + + + diff --git a/src/components/devices/DeviceControl.tsx b/src/components/devices/DeviceControl.tsx index 91c7ba7..2e08d54 100644 --- a/src/components/devices/DeviceControl.tsx +++ b/src/components/devices/DeviceControl.tsx @@ -66,13 +66,13 @@ interface Waypoint { const INITIAL_WAYPOINTS: Waypoint[] = []; const StatusTagMap = { - working: { text: '工作中', color: 'bg-[#00e5ff]/10 text-[#00e5ff] border-[#00e5ff]/30' }, + working: { text: t('devices.s59'), color: 'bg-[#00e5ff]/10 text-[#00e5ff] border-[#00e5ff]/30' }, online: { text: t('devices.online'), color: 'bg-[#00e5ff]/10 text-[#00e5ff] border-[#00e5ff]/30' }, offline: { text: t('devices.offline'), color: 'bg-[#ff4d4f]/10 text-[#ff4d4f] border-[#ff4d4f]/30' }, maintenance: { text: t('workOrder.maintainTask'), color: 'bg-[#ffad0d]/10 text-[#ffad0d] border-[#ffad0d]/30' }, - configured: { text: '视频已配', color: 'bg-[#00e5ff]/10 text-[#00e5ff] border-[#00e5ff]/30' }, - no_video: { text: '无视频', color: 'bg-[#6b7280]/10 text-[#9ca3af] border-[#6b7280]/30' }, - failed: { text: '连接失败', color: 'bg-[#ff4d4f]/10 text-[#ff4d4f] border-[#ff4d4f]/30' } + configured: { text: t('devices.fixed2Video'), color: 'bg-[#00e5ff]/10 text-[#00e5ff] border-[#00e5ff]/30' }, + no_video: { text: t('devices.noVideo'), color: 'bg-[#6b7280]/10 text-[#9ca3af] border-[#6b7280]/30' }, + failed: { text: t('devices.connectionFailed'), color: 'bg-[#ff4d4f]/10 text-[#ff4d4f] border-[#ff4d4f]/30' } }; const RobotControlCenter = ({ setView }) => { @@ -128,7 +128,7 @@ const RobotControlCenter = ({ setView }) => { const [hasControlRight, setHasContronRight] = useState(false);//是否有控制权限 //远程控制相关 - const [modalcontent, setmodalcontent] = useState({ title: '权限请求', content: '是否请求控制权限?' }); + const [modalcontent, setmodalcontent] = useState({ title: t('devices.s2'), content: t('devices.requestControlPermission2') }); const [modelType, setmodelType] = useState(''); const [taskForm, setTaskForm] = useState({ name: '', @@ -224,7 +224,7 @@ const RobotControlCenter = ({ setView }) => { // 下发无人机飞行指令:返航 / 取消返航 / 暂停 / 恢复 const sendFlightCommand = async (command: 'return_home' | 'return_home_cancel' | 'flighttask_pause' | 'flighttask_recovery') => { if (!selectedDevice?.gateway_sn) { - antdMessage.warning('未选中无人机或无人机SN不存在'); + antdMessage.warning(t('devices.uavUavSn')); return; } @@ -238,7 +238,7 @@ const RobotControlCenter = ({ setView }) => { const res = await postFlightCommmand(data); if (res.data.code == 0) { - antdMessage.success('指令下发成功'); + antdMessage.success(t('devices.fixed2SuccessFixed2')); // 切换状态 if (command === 'return_home') setIsReturningHome(true); @@ -249,7 +249,7 @@ const RobotControlCenter = ({ setView }) => { antdMessage.error(res.data.msg || '指令下发失败'); } } catch (err) { - antdMessage.error('接口请求异常'); + antdMessage.error(t('devices.fixed2AbnormalFixed2')); } finally { setFlightCommandLoading(false); } @@ -258,7 +258,7 @@ const RobotControlCenter = ({ setView }) => { // 更新任务状态:执行 / 挂起 / 继续 const updateTaskStatus = async (taskId: string, status: string) => { if (!taskId) { - antdMessage.warning('请先选择航线任务'); + antdMessage.warning(t('devices.waylineTasksFixed2')); return; } setLoading(true); @@ -279,7 +279,7 @@ const RobotControlCenter = ({ setView }) => { antdMessage.error(res.data.msg || '操作失败'); } } catch (err) { - antdMessage.error('操作接口异常'); + antdMessage.error(t('devices.fixed2ActionsFixed2Fixed2AbnormalFixed2')); } finally { setLoading(false); } @@ -343,7 +343,7 @@ const RobotControlCenter = ({ setView }) => { break; case 'switchResult'://连接失败 重新连接 antdMessage.success({ - content: '权限获取成功', + content: t('devices.successFixed2'), key: 'control', duration: 2, }); @@ -364,8 +364,8 @@ const RobotControlCenter = ({ setView }) => { // if(isControlled||isRequestControl) return; - showModal({ title: "权限请求", content: `${message.platform || '未知平台'}请求控制${requestDeviceId.current == connectedDeviceSnRef.current ? "" : requestDeviceId.current}权限,是否同意?` }, 'switchResult'); - // setmodalcontent({title:'权限切换请求',content:`${message.platform|| '未知平台'}请求控制权限`}); + showModal({ title: t('devices.s2'), content: `${message.platform || '未知平台'}请求控制${requestDeviceId.current == connectedDeviceSnRef.current ? "" : requestDeviceId.current}权限,是否同意?` }, 'switchResult'); + // setmodalcontent({title: t('devices.s10'),content:`${message.platform|| '未知平台'}请求控制权限`}); break; case 'detectionReport'://连接失败 重新连接 const obsData = message.data; @@ -431,12 +431,12 @@ const RobotControlCenter = ({ setView }) => { } else { setTaskDetail(null); - antdMessage.error('暂无详情'); + antdMessage.error(t('devices.noDetailFixed2')); } } catch (err) { setTaskDetail(null); - antdMessage.error('获取详情接口异常'); + antdMessage.error(t('devices.detailFixed2Fixed2AbnormalFixed2')); } finally { setLoading(false); } @@ -482,7 +482,7 @@ const RobotControlCenter = ({ setView }) => { const handleSelectRobot = async (robot) => { // 1. 防御判断(必须放最前面) if (!robot || !robot.serialNumber) { - antdMessage.warning("设备信息异常"); + antdMessage.warning(t('devices.fixed2DeviceFixed2InfoFixed2AbnormalFixed2')); return; } @@ -514,7 +514,7 @@ const RobotControlCenter = ({ setView }) => { if (!userParse) { setIsControlled(false); - antdMessage.warning("请先登录"); + antdMessage.warning(t('devices.loginFixed2')); return; } @@ -523,7 +523,7 @@ const RobotControlCenter = ({ setView }) => { if (!token || !userName) { setIsControlled(false); - antdMessage.warning("登录信息失效,请重新登录"); + antdMessage.warning(t('devices.fixed2LoginFixed2InfoLoginFixed2')); return; } if (robot.type == "plane") return; @@ -543,13 +543,13 @@ const RobotControlCenter = ({ setView }) => { } else { setHasContronRight(false); - antdMessage.warning("其他web端正在控制"); + antdMessage.warning(t('devices.fixed2OthersWebControlFixed2')); connectedDeviceSnRef.current = null; } } catch (err) { console.error("权限校验失败", err); setHasContronRight(false); - antdMessage.error("网络异常或请求失败,请重试"); + antdMessage.error(t('devices.abnormalRequestFailed')); connectedDeviceSnRef.current = null; } }; @@ -701,7 +701,7 @@ const RobotControlCenter = ({ setView }) => { antdMessage.loading({ key: "control", type: 'loading', - content: '权限请求中...', + content: t('devices.fixed2PermissionRequesting'), duration: 0, }); } else { @@ -746,11 +746,11 @@ const RobotControlCenter = ({ setView }) => { setPoints(points); } else { - antdMessage.warning("暂无航线数据"); + antdMessage.warning(t('devices.noWaylineDataFixed2')); setPoints([]); } }).catch(() => { - antdMessage.warning("获取航线失败"); + antdMessage.warning(t('devices.fetchWaylineFailed')); setPoints([]); }); }; @@ -813,7 +813,7 @@ const RobotControlCenter = ({ setView }) => { })); setPoints(mapPoints); if (mapClearRef.current) mapClearRef.current(); - antdMessage.success("已删除航点并同步地图"); + antdMessage.success(t('devices.deleteFixed2Waypoint')); }; const clearWaypoints = () => { @@ -822,15 +822,15 @@ const RobotControlCenter = ({ setView }) => { setTaskRunning(false); setPoints([]); if (mapClearRef.current) mapClearRef.current(); - antdMessage.success("已清空所有航点与地图轨迹"); + antdMessage.success(t('devices.waypoint2')); }; const createFlightTask = async () => { if (!taskForm.name) { - antdMessage.warning('请输入任务名称'); + antdMessage.warning(t('devices.inputTaskName')); return; } if (!waylineUuid) { - antdMessage.warning('请选择航线'); + antdMessage.warning(t('devices.pleaseSelectWayline')); return; } @@ -845,7 +845,7 @@ const RobotControlCenter = ({ setView }) => { }; if (taskForm.task_type !== 'immediate' && !params.begin_at) { - antdMessage.warning('请选择任务开始时间'); + antdMessage.warning(t('devices.selectTaskStartTimeFixed2')); return; } @@ -854,7 +854,7 @@ const RobotControlCenter = ({ setView }) => { // 请确保你已在顶部引入:import { createWaylineTask } from '@/api/api' const res = await saveDeviceTask(params); if (res.data.code === 0) { - antdMessage.success('创建成功'); + antdMessage.success(t('devices.createSuccess')); setCreateTaskModal(false); getAllPlan(selectedDevice); resetModals(); @@ -862,7 +862,7 @@ const RobotControlCenter = ({ setView }) => { antdMessage.error(res.data.msg || '创建失败'); } } catch (err) { - antdMessage.error('接口异常,请检查'); + antdMessage.error(t('devices.fixed2Fixed2Abnormal')); } finally { setLoading(false); } @@ -870,11 +870,11 @@ const RobotControlCenter = ({ setView }) => { const saveWaypoints = () => { if (waypoints.length === 0) { - antdMessage.warning("无航点可保存"); + antdMessage.warning(t('devices.fixed2NoWaypointsSaveFixed2')); return; } if (selectedDevice?.type === 'plane') { - antdMessage.warning("无人机不支持保存本地航线"); + antdMessage.warning(t('devices.fixed2UavSaveFixed2Local')); return; } @@ -887,7 +887,7 @@ const RobotControlCenter = ({ setView }) => { }; localStorage.setItem(key, JSON.stringify(data)); - antdMessage.success("✅ 航线已新增保存到本地"); + antdMessage.success(t('devices.addNewFixed2SaveLocalFixed2')); refreshRobotRoutes(); }; const resetModals = () => { @@ -946,7 +946,7 @@ const RobotControlCenter = ({ setView }) => { setWaylineList(_list || []); // 取list数组 } } catch (err) { - antdMessage.error('获取航线列表失败'); + antdMessage.error(t('devices.fetchWaylineListFailed')); } }; useEffect(() => { @@ -1012,12 +1012,12 @@ const RobotControlCenter = ({ setView }) => { //if (waypoints.length === 0) return; setTaskIssued(true); setTaskRunning(false); - antdMessage.success("GPS任务已下发"); + antdMessage.success(t('devices.gpsTaskIssued')); }; const toggleTaskRunning = () => { if (!taskIssued) { - antdMessage.warning("请先下发任务"); + antdMessage.warning(t('devices.taskSuspended2Fixed23')); return; } setTaskRunning(!taskRunning); @@ -1027,7 +1027,7 @@ const RobotControlCenter = ({ setView }) => { const stopGPSTask = () => { setTaskRunning(false); setTaskIssued(false); - antdMessage.success("任务已停止"); + antdMessage.success(t('devices.taskStopped')); }; const mapFocus = selectedDevice ? { @@ -1067,7 +1067,7 @@ const RobotControlCenter = ({ setView }) => { const handleControlModal = (data) => { if (!hasControlRight) return; if (data.control == true) { - showModal({ title: "权限请求", content: "是否请求控制权限?" }, 'switchPermission'); + showModal({ title: t('devices.s2'), content: t('devices.requestControlPermission2') }, 'switchPermission'); } else { setIsModalOpen(false); } @@ -1358,16 +1358,15 @@ const RobotControlCenter = ({ setView }) => {

- 设备列表 -

+ {t('devices.deviceList')}
- 在线设备 + {t('devices.onlineDevices')} {onlineCount}/{allDevices.length}
{/*
-

任务态势

+

{t('devices.fixed2TaskSuspended2')}

{selectedDevice.taskStatus}

*/}
@@ -1423,7 +1422,7 @@ const RobotControlCenter = ({ setView }) => {
- 视频链路已配置 + {t('devices.fixed2Video')}
@@ -1432,8 +1431,7 @@ const RobotControlCenter = ({ setView }) => {

- 电池电量 -

+ {t('devices.batteryFixed2')}

{

- 飞行高度 -

+ {t('devices.flightAltitude')}

{Number((uavState || selectedDevice).height || 0).toFixed(2)} m

- 信号/卫星 -

+ {t('devices.s62')}

{(uavState || selectedDevice).position_state?.gps_number || 0} GPS / @@ -1464,16 +1460,14 @@ const RobotControlCenter = ({ setView }) => {

- 环境风速 -

+ {t('devices.s72')}

{Number((uavState || selectedDevice).wind_speed || 0).toFixed(2)} m/s

- RTK 状态 -

+ {t('devices.fixed2RtkFixed2StatusFixed2')}

@@ -1485,7 +1479,7 @@ const RobotControlCenter = ({ setView }) => { )} {/*
-

当前位置

+

{t('devices.fixed2CurrentFixed2LocationFixed2')}

{selectedDevice?.position?.lon ? Number(selectedDevice.position.lon).toFixed(2) @@ -1495,11 +1489,11 @@ const RobotControlCenter = ({ setView }) => { : '--'}

-

当前模式

+

{t('devices.fixed2CurrentFixed2AbModeFixed2')}

{selectedDevice?.type === 'plane' ? '无人机待命' : selectedDevice?.type === 'mower' ? '割草待命' : '待命'}

-

电量/航向

+

{t('devices.fixed2Battery')}

{selectedDevice?.battery}% / 92°

@@ -1507,7 +1501,7 @@ const RobotControlCenter = ({ setView }) => {

{selectedDevice?.speed} m/s

- 启用遥控 + {t('devices.fixed2Enable')}
record.id || record.serialNumber || record.device_sn} size="small" pagination={false} columns={[ - { title: '装备名称', dataIndex: 'name', width: 140 }, + { title: t('devices.fixed2EquipmentFixed2NameFixed2'), dataIndex: 'name', width: 140 }, { title: t('common.type'), dataIndex: 'type', width: 90 }, { title: t('workOrder.currentStatus'), @@ -659,7 +660,7 @@ export default function DeviceOverviewPage() { return {text}; } }, { - title: '模式', dataIndex: 'mode', width: 70, render: (text) => { + title: t('devices.abMode'), dataIndex: 'mode', width: 70, render: (text) => { let color = 'default'; if (text === '作业中') color = 'processing'; if (text === t('devices.idle')) color = 'success'; @@ -683,8 +684,8 @@ export default function DeviceOverviewPage() { />; } }, { title: t('aiAnalysis.location'), dataIndex: 'position', width: 90 }, - //{ title: '当前任务', dataIndex: 'task', width: 100 }, - { title: '通讯状态', dataIndex: 'comm', width: 100, render: v => }, + //{ title: t('devices.fixed2CurrentFixed2TaskSuspended2Fixed2'), dataIndex: 'task', width: 100 }, + { title: t('devices.statusFixed2'), dataIndex: 'comm', width: 100, render: v => }, { title: t('roles.actions'), key: 'action', @@ -700,7 +701,7 @@ export default function DeviceOverviewPage() { onClick={() => openEditRobotModal(record)} title={t('roles.edit')} /> - + }> + {t('devices.fixed2ControlFixed2LogDesc6Alert')}} bordered={false} style={{ borderRadius: 8 }} extra={
}>
{t} }, - { title: '设备', dataIndex: 'device', width: 100, render: t => {t} }, + { title: t('devices.device'), dataIndex: 'device', width: 100, render: t => {t} }, { title: t('common.type'), dataIndex: 'type', width: 70, render: r => { let c = '#52c41a'; if (r === t('constants.sourceAlert')) c = '#fa8c16'; if (t === 'events') c = '#1890ff'; return
{t}
} }, { title: t('common.description'), dataIndex: 'desc', render: t => {t} }, { title: '', width: 70, render: (_, r) => r.level ? {r.level} : null } ]} /> -
+
- 设备控制面板} bordered={false} style={{ borderRadius: 8, marginBottom: 8 }} bodyStyle={{ padding: 12, paddingBottom: 0 }}> + {t('devices.deviceControlPanel')}} bordered={false} style={{ borderRadius: 8, marginBottom: 8 }} bodyStyle={{ padding: 12, paddingBottom: 0 }}> )} @@ -886,9 +887,7 @@ export default function DeviceOverviewPage() { ready={ready} /> ) : ( -
- 视频加载中... -
+
{t('video.videoLoading')}
) ) : ( // ========== 未点击播放时:显示播放大按钮 ========== @@ -931,7 +930,7 @@ export default function DeviceOverviewPage() { onClick={() => { setRobotPlayActive(false); }} - >停止视频 + >{t('devices.fixed2StopFixed2VideoFixed2')} )} @@ -939,66 +938,66 @@ export default function DeviceOverviewPage() {
- - + +
)}
- AI分析与操作建议} bordered={false} style={{ borderRadius: 12, marginBottom: 8 }}> + {t('devices.fixed2AiActions')}} bordered={false} style={{ borderRadius: 12, marginBottom: 8 }}> -
🔥
热斑疑似
2 处
-
杂草高发区
3 块
-
组件风险区
4 块
-
围栏异常
1 处
+
🔥
{t('devices.hotspotSuspect')}
2 {t('devices.s32')}
+
{t('devices.s33')}
3 {t('devices.s34')}
+
{t('devices.s35')}
4 {t('devices.s34')}
+
{t('devices.fenceAbnormal')}
1 {t('devices.s32')}
-
建议操作
+
{t('devices.suggestAction')}
-
⦿优先执行巡检航线 P-03
-
⦿先行安排除草任务 G-02
-
⦿建议生成清洗工单
+
⦿{t('devices.executeFixed2InspectionP03')}
+
⦿{t('devices.taskSuspended2G02')}
+
⦿{t('devices.generateCleaningOrderFixed2')}
- - + +
- +
{(selectedDevice?.productName?.includes(t('constants.uav')) || selectedDevice?.device_sn) && ( - +
-
风速
{fix2(realtimeDevice?.wind_speed)} m/s
+
{t('devices.s44')}
{fix2(realtimeDevice?.wind_speed)} m/s
{t('home.temperature')}
{fix2(realtimeDevice?.environment_temperature)}℃
-
降雨
{getVal(realtimeDevice?.rainfall)}
+
{t('devices.rainfall')}
{getVal(realtimeDevice?.rainfall)}
RTK
{realtimeDevice?.position_state?.is_fixed === 'fixing_successful' ? '固定' : '未固定'}
-
适宜执行
+
{t('devices.suitableExec')}
-
可正常作业
+
{t('devices.normal')}
)} {(!selectedDevice?.productName?.includes(t('constants.uav')) && !selectedDevice?.device_sn) && ( - 机器人作业状态} bordered={false} style={{ borderRadius: 12, marginBottom: 8, boxShadow: '0 2px 8px rgba(0,0,0,0.06)', border: '1px solid #f0f0f0' }}> + {t('devices.fixed2RobotStatusFixed2')}} bordered={false} style={{ borderRadius: 12, marginBottom: 8, boxShadow: '0 2px 8px rgba(0,0,0,0.06)', border: '1px solid #f0f0f0' }}>
累计作业里程} + title={{t('devices.statTotalDistance')}} value={deviceStatistics?.distance ? Number(deviceStatistics.distance).toFixed(2) : Number(deviceStatistics?.workArea / 0.7 || 0).toFixed(2)} suffix="m" valueStyle={{ fontSize: 20, fontWeight: 700, color: '#165DFF', marginTop: 4 }} @@ -1031,7 +1030,7 @@ export default function DeviceOverviewPage() { }} > 累计作业面积} + title={{t('devices.s50')}} value={Number(deviceStatistics?.workArea || 0).toFixed(2)} suffix="㎡" valueStyle={{ fontSize: 20, fontWeight: 700, color: '#165DFF', marginTop: 4 }} @@ -1050,7 +1049,7 @@ export default function DeviceOverviewPage() { }} > 累计作业时长} + title={{t('devices.s51')}} value={Number(deviceStatistics?.time || 0).toFixed(2)} suffix="min" valueStyle={{ fontSize: 20, fontWeight: 700, color: '#165DFF', marginTop: 4 }} @@ -1061,7 +1060,7 @@ export default function DeviceOverviewPage() { )} - 任务执行时间线} bordered={false} style={{ borderRadius: 12 }} extra={
}> + {t('devices.fixed2TaskSuspended2Fixed2ExecutionTime')}} bordered={false} style={{ borderRadius: 12 }} extra={
}> {taskTimeline.map((item, idx) => ( {item.time}
}> @@ -1070,14 +1069,14 @@ export default function DeviceOverviewPage() { ))} -
+
{/* 机器人编辑别名弹窗 */} setEditRobotModalOpen(false)} confirmLoading={editLoading} @@ -1087,28 +1086,26 @@ export default function DeviceOverviewPage() { {currentEditRobot && (
- 设备SN: + {t('devices.fixed2Fixed2DeviceSn')} {currentEditRobot.device_sn || currentEditRobot.serialNumber}
- 设备类型: + {t('devices.fixed2DeviceType')} {currentEditRobot.productName || t('constants.locationUnknown')}
- 设备别名 * + {t('devices.fixed2Device')}*
setEditAliasInput(e.target.value)} - placeholder="请输入自定义设备别名" + placeholder={t('devices.fixed2PleaseEnterDevice')} maxLength={30} allowClear /> -
- 别名用于设备列表、控制面板展示,便于快速区分设备 -
+
{t('devices.deviceListControlDeviceFixed2')}
)}
diff --git a/src/components/devices/DeviceParamModal.tsx b/src/components/devices/DeviceParamModal.tsx index ef2f3f6..06cd450 100644 --- a/src/components/devices/DeviceParamModal.tsx +++ b/src/components/devices/DeviceParamModal.tsx @@ -119,16 +119,16 @@ const DeviceParamModal: React.FC = ({ open, device, onCan setParamData(res.data); paramForm.setFieldsValue({ ...res.data }); } else { - utils.message.error('获取参数失败'); + utils.message.error(t('devices.fetchParamFail')); setParamData(null); paramForm.resetFields(); } } else { - utils.message.error('获取参数失败'); + utils.message.error(t('devices.fetchParamFail')); } } catch (err) { - utils.message.error('获取参数失败'); + utils.message.error(t('devices.fetchParamFail')); } finally { setLoading(false); } @@ -149,14 +149,14 @@ const DeviceParamModal: React.FC = ({ open, device, onCan }; const res = await deviceRunParamSave(payload); if (res.code === 200) { - utils.message.success('保存参数成功'); + utils.message.success(t('devices.paramSaveSuccess')); onSuccess?.(); onCancel?.(); } else { - utils.message.error(res.msg || '保存参数失败'); + utils.message.error(res.msg || t('devices.paramSaveFail')); } } catch (err) { - utils.message.error('操作异常'); + utils.message.error(t('devices.paramSaveException')); } finally { setLoading(false); } @@ -168,53 +168,53 @@ const DeviceParamModal: React.FC = ({ open, device, onCan onCancel?.(); }; const paramFieldConfigs = [ - { key: 'leftForwardGain', label: '左轮前进增益', type: 'InputNumber', editable: true, min: 0, max: 1, step: 0.01 }, - { key: 'leftBackwardGain', label: '左轮后退增益', type: 'InputNumber', editable: true, min: 0, max: 1, step: 0.01 }, - { key: 'rightForwardGain', label: '右轮前进增益', type: 'InputNumber', editable: true, min: 0, max: 1, step: 0.01 }, - { key: 'rightBackwardGain', label: '右轮后退增益', type: 'InputNumber', editable: true, min: 0, max: 1, step: 0.01 }, + { key: 'leftForwardGain', label: t('devices.paramLeftForwardGain'), type: 'InputNumber', editable: true, min: 0, max: 1, step: 0.01 }, + { key: 'leftBackwardGain', label: t('devices.paramLeftBackwardGain'), type: 'InputNumber', editable: true, min: 0, max: 1, step: 0.01 }, + { key: 'rightForwardGain', label: t('devices.paramRightForwardGain'), type: 'InputNumber', editable: true, min: 0, max: 1, step: 0.01 }, + { key: 'rightBackwardGain', label: t('devices.paramRightBackwardGain'), type: 'InputNumber', editable: true, min: 0, max: 1, step: 0.01 }, - { key: 'runSpeed', label: '运行速度', type: 'InputNumber', editable: true }, - { key: 'chipUidSign', label: '芯片UID固化标志位', type: 'InputNumber', editable: false }, - { key: 'chipUid', label: '芯片UID', type: 'Input', editable: false }, - { key: 'remoteConfig', label: '遥控器通道配置', type: 'InputNumber', editable: false }, - { key: 'knifeMotorMode', label: '割刀电机模式', type: 'InputNumber', editable: false }, - { key: 'walkMotorMode', label: '行走电机模式', type: 'InputNumber', editable: false }, - { key: 'leftMotorReverse', label: '左轮电机方向极性', type: 'InputNumber', editable: false }, - { key: 'rightMotorReverse', label: '右轮电机方向极性', type: 'InputNumber', editable: false }, - { key: 'swapChannel', label: '左右轮通道互换标志', type: 'InputNumber', editable: false }, - { key: 'use4G', label: '联网标志位', type: 'InputNumber', editable: false }, - { key: 'forwardSpeedLimit', label: '前进速度限制', type: 'InputNumber', editable: false }, - { key: 'turnSpeedLimit', label: '转向速度限制', type: 'InputNumber', editable: false }, - { key: 'knifePolarity', label: '割刀通道极性', type: 'InputNumber', editable: false }, - { key: 'fanPolarity', label: '风门通道极性', type: 'InputNumber', editable: false }, - { key: 'throttlePolarity', label: '油门通道极性', type: 'InputNumber', editable: false }, - { key: 'liftProtectTime', label: '底盘升降电机保护时间', type: 'InputNumber', editable: false }, - { key: 'dualRtk', label: 'RTK配置', type: 'InputNumber', editable: false }, - { key: 'knifeChannel', label: '割刀通道配置', type: 'InputNumber', editable: false }, - { key: 'fanChannel', label: '风门通道配置', type: 'InputNumber', editable: false }, - { key: 'throttleChannel', label: '油门通道配置', type: 'InputNumber', editable: false }, - { key: 'remoteType', label: '遥控器类型', type: 'InputNumber', editable: false }, - { key: 'relayBoard', label: '是否搭载继电器板', type: 'Checkbox', editable: false }, - { key: 'liftChannel', label: '底盘升通道配置', type: 'InputNumber', editable: false }, - { key: 'chassisChannel', label: '底盘降通道配置', type: 'InputNumber', editable: false }, - { key: 'armChannel', label: '抱闸通道配置', type: 'InputNumber', editable: false }, - { key: 'fuelPumpChannel', label: '燃油励磁通道配置', type: 'InputNumber', editable: false }, - { key: 'wifiName', label: 'wifi名称', type: 'Input', editable: false }, - { key: 'wifiPassword', label: 'wifi密码', type: 'Input', editable: false }, - { key: 'batteryType', label: '电池类型', type: 'InputNumber', editable: false }, - { key: 'driveType', label: '行走驱动配置', type: 'InputNumber', editable: false }, - { key: 'gearRatio', label: '转速比', type: 'InputNumber', editable: false, step: 0.01 }, - { key: 'robotLength', label: '机器人长度', type: 'InputNumber', editable: false, step: 0.01 }, - { key: 'robotWidth', label: '机器人宽度', type: 'InputNumber', editable: false, step: 0.01 }, - { key: 'robotHeight', label: '机器人高度', type: 'InputNumber', editable: false, step: 0.01 }, - { key: 'knifeWidth', label: '割刀宽度', type: 'InputNumber', editable: false, step: 0.01 }, - { key: 'tyreSize', label: '轮胎尺寸', type: 'InputNumber', editable: false, step: 0.01 }, - { key: 'firmwareVersion', label: '固件版本', type: 'Input', editable: false }, + { key: 'runSpeed', label: t('devices.paramRunSpeed'), type: 'InputNumber', editable: true }, + { key: 'chipUidSign', label: t('devices.paramChipUidSign'), type: 'InputNumber', editable: false }, + { key: 'chipUid', label: t('devices.paramChipUid'), type: 'Input', editable: false }, + { key: 'remoteConfig', label: t('devices.paramRemoteConfig'), type: 'InputNumber', editable: false }, + { key: 'knifeMotorMode', label: t('devices.paramKnifeMotorMode'), type: 'InputNumber', editable: false }, + { key: 'walkMotorMode', label: t('devices.paramWalkMotorMode'), type: 'InputNumber', editable: false }, + { key: 'leftMotorReverse', label: t('devices.paramLeftMotorReverse'), type: 'InputNumber', editable: false }, + { key: 'rightMotorReverse', label: t('devices.paramRightMotorReverse'), type: 'InputNumber', editable: false }, + { key: 'swapChannel', label: t('devices.paramSwapChannel'), type: 'InputNumber', editable: false }, + { key: 'use4G', label: t('devices.paramUse4G'), type: 'InputNumber', editable: false }, + { key: 'forwardSpeedLimit', label: t('devices.paramForwardSpeedLimit'), type: 'InputNumber', editable: false }, + { key: 'turnSpeedLimit', label: t('devices.paramTurnSpeedLimit'), type: 'InputNumber', editable: false }, + { key: 'knifePolarity', label: t('devices.paramKnifePolarity'), type: 'InputNumber', editable: false }, + { key: 'fanPolarity', label: t('devices.paramFanPolarity'), type: 'InputNumber', editable: false }, + { key: 'throttlePolarity', label: t('devices.paramThrottlePolarity'), type: 'InputNumber', editable: false }, + { key: 'liftProtectTime', label: t('devices.paramLiftProtectTime'), type: 'InputNumber', editable: false }, + { key: 'dualRtk', label: t('devices.paramDualRtk'), type: 'InputNumber', editable: false }, + { key: 'knifeChannel', label: t('devices.paramKnifeChannel'), type: 'InputNumber', editable: false }, + { key: 'fanChannel', label: t('devices.paramFanChannel'), type: 'InputNumber', editable: false }, + { key: 'throttleChannel', label: t('devices.paramThrottleChannel'), type: 'InputNumber', editable: false }, + { key: 'remoteType', label: t('devices.paramRemoteType'), type: 'InputNumber', editable: false }, + { key: 'relayBoard', label: t('devices.paramRelayBoard'), type: 'Checkbox', editable: false }, + { key: 'liftChannel', label: t('devices.paramLiftChannel'), type: 'InputNumber', editable: false }, + { key: 'chassisChannel', label: t('devices.paramChassisChannel'), type: 'InputNumber', editable: false }, + { key: 'armChannel', label: t('devices.paramArmChannel'), type: 'InputNumber', editable: false }, + { key: 'fuelPumpChannel', label: t('devices.paramFuelPumpChannel'), type: 'InputNumber', editable: false }, + { key: 'wifiName', label: t('devices.paramWifiName'), type: 'Input', editable: false }, + { key: 'wifiPassword', label: t('devices.paramWifiPassword'), type: 'Input', editable: false }, + { key: 'batteryType', label: t('devices.paramBatteryType'), type: 'InputNumber', editable: false }, + { key: 'driveType', label: t('devices.paramDriveType'), type: 'InputNumber', editable: false }, + { key: 'gearRatio', label: t('devices.paramGearRatio'), type: 'InputNumber', editable: false, step: 0.01 }, + { key: 'robotLength', label: t('devices.paramRobotLength'), type: 'InputNumber', editable: false, step: 0.01 }, + { key: 'robotWidth', label: t('devices.paramRobotWidth'), type: 'InputNumber', editable: false, step: 0.01 }, + { key: 'robotHeight', label: t('devices.paramRobotHeight'), type: 'InputNumber', editable: false, step: 0.01 }, + { key: 'knifeWidth', label: t('devices.paramKnifeWidth'), type: 'InputNumber', editable: false, step: 0.01 }, + { key: 'tyreSize', label: t('devices.paramTyreSize'), type: 'InputNumber', editable: false, step: 0.01 }, + { key: 'firmwareVersion', label: t('devices.paramFirmwareVersion'), type: 'Input', editable: false }, { key: 'crc16', label: 'CRC16', type: 'InputNumber', editable: false }, { key: 'tail1', label: 'tail1', type: 'InputNumber', editable: false }, { key: 'tail2', label: 'tail2', type: 'InputNumber', editable: false }, { key: 'remark', label: t('roles.remark'), type: 'Input', editable: false }, - { key: 'delFlag', label: '删除标志', type: 'Checkbox', editable: false }, + { key: 'delFlag', label: t('devices.paramDelFlag'), type: 'Checkbox', editable: false }, ]; return ( @@ -228,7 +228,7 @@ const DeviceParamModal: React.FC = ({ open, device, onCan background: 'linear-gradient(180deg, #52c41a 0%, #389e0d 100%)', borderRadius: 2, }}> - 设备全部参数查看与增益修改 + {t('devices.paramViewEditTitle')} - {deviceName} @@ -304,7 +304,7 @@ const DeviceParamModal: React.FC = ({ open, device, onCan ) : field.type === 'Checkbox' ? ( ) : ( - + )} diff --git a/src/components/devices/DeviceStatusPage.tsx b/src/components/devices/DeviceStatusPage.tsx index 94f1ffd..8e09d0f 100644 --- a/src/components/devices/DeviceStatusPage.tsx +++ b/src/components/devices/DeviceStatusPage.tsx @@ -417,7 +417,7 @@ export default function DeviceStatusPage() { // !isUAV ? device?.serialNumber : '', // `webrtc://1.95.137.212/live/livestream/${device?.serialNumber}`, //); - const getVal = (val) => val === "no_rain" ? "无" : (val || '-'); + const getVal = (val) => val === "no_rain" ? t('devices.rainNo') : (val || '-'); const realtimePosition = React.useMemo(() => { // 1. 无人机:优先用 MQTT 实时数据 @@ -580,7 +580,7 @@ export default function DeviceStatusPage() { // 让地图取最新经纬度 longitude: lng, latitude: lat, - name: device.deviceAlias || device.callsign || device.deviceName || '设备', + name: device.deviceAlias || device.callsign || device.deviceName || t('devices.device'), type: isUAV ? 'drone' : device.productName?.includes(t('constants.mower')) @@ -637,7 +637,7 @@ export default function DeviceStatusPage() { // 打点功能 const handleMarkPoint = () => { if (!realtimePosition) { - message.warning('无法获取实时位置,请检查设备连接'); + message.warning(t('devices.locationFail')); return; } const newPoint = { @@ -645,7 +645,7 @@ export default function DeviceStatusPage() { lat: Number(realtimePosition.lat), }; setMarkedPoints([...markedPoints, newPoint]); - messageApi.success(`已标记第 ${markedPoints.length + 1} 个点`); + messageApi.success(t('devices.pointMarked', { count: markedPoints.length + 1 })); }; // 清空标记点 @@ -660,7 +660,7 @@ export default function DeviceStatusPage() { //如果时弓子模式 if (isLoopMode) { if (markedPoints.length < 3) { - messageApi.warning('请至少标记 3 个点'); + messageApi.warning(t('devices.minThreePoints')); return; } @@ -716,11 +716,11 @@ export default function DeviceStatusPage() { planModel: 0, }); setCompleteFlag(true); - messageApi.success('路径规划完成'); + messageApi.success(t('devices.pathPlanDone')); } } catch (error) { console.error('路径规划失败:', error); - messageApi.error('路径规划失败,请重试'); + messageApi.error(t('devices.pathPlanFailRetry')); } } else { const positionPoints = [...markedPoints].map(item => { @@ -745,7 +745,7 @@ export default function DeviceStatusPage() { // 打开保存模态框 const handleOpenSaveModal = () => { if (!completeFlag) { - messageApi.warning('请先点击完成生成路径'); + messageApi.warning(t('devices.completePathFirst')); return; } setPlotName(''); @@ -755,7 +755,7 @@ export default function DeviceStatusPage() { // 保存功能 const handleSave = async () => { if (!plotName.trim()) { - messageApi.warning('请输入地块名称'); + messageApi.warning(t('devices.inputPlotName')); return; } @@ -826,7 +826,7 @@ export default function DeviceStatusPage() { if (!device) { return ( ); @@ -856,31 +856,31 @@ export default function DeviceStatusPage() { }} >
- 路径规划: - 已标记 {markedPoints.length} 个点 + {t('devices.pathPlanning')}: + {t('devices.markedPointsCount', { count: markedPoints.length })}
- 作业方式 + {t('devices.workMode')} - {isLoopMode ? '弓字' : 'AB点'} + {isLoopMode ? t('devices.bowMode') : t('devices.abMode')} @@ -910,7 +910,7 @@ export default function DeviceStatusPage() { flexWrap: 'wrap', }} > - 选择设备: + {t('devices.selectDevice')}: setPlotName(e.target.value)} - placeholder="请输入地块名称" + placeholder={t('devices.inputPlotName')} style={{ width: '100%', padding: '8px 12px', @@ -1390,7 +1390,7 @@ export default function DeviceStatusPage() { borderRadius: '50%', animation: 'spin 0.8s linear infinite', }} /> -
保存中...
+
{t('devices.saving')}
)}
- - AI 智能诊断与分析中心 - + {t('aiAnalysis.fixed2Ai2')} - - + +
{/* 第一排:顶部统计卡片 */} {[ - { title: 'PR值 (性能比)', value: '82.6%', trend: '较昨日 -1.3%', icon: , color: '#00B42A', bg: '#E8FFEA', trendColor: '#F53F3F', direction: 'down' }, - { title: '发电偏差率', value: '-4.35%', trend: '较昨日 -1.02%', icon: , color: '#F53F3F', bg: '#FFF1F0', trendColor: '#F53F3F', direction: 'down' }, - { title: '异常组串数量', value: '8', trend: '较昨日 +2 ↑', icon: , color: '#1D2129', bg: '#F5F7FA', trendColor: '#F53F3F', direction: 'up' }, - { title: '热斑疑似数量', value: '36', trend: '较昨日 +5 ↑', icon: , color: '#F53F3F', bg: '#FFF1F0', trendColor: '#F53F3F', direction: 'up' }, - { title: '电站健康度评分', value: '86.3', unit: '分', trend: '较昨日 -1.7', icon: , color: '#00B42A', bg: '#E8FFEA', trendColor: '#F53F3F', direction: 'down' }, - { title: '预测发电量', value: '4,821', unit: t('home.kWh'), trend: '较昨日 +2.6% ↑', icon: , color: '#165DFF', bg: '#E8F3FF', trendColor: '#00B42A', direction: 'up' }, + { title: t('aiAnalysis.prValue'), value: '82.6%', trend: t('devices.vsYesterday', { value: '-1.3%' }), icon: , color: '#00B42A', bg: '#E8FFEA', trendColor: '#F53F3F', direction: 'down' }, + { title: t('aiAnalysis.fixed2GenerationDeviation'), value: '-4.35%', trend: t('devices.vsYesterday', { value: '-1.02%' }), icon: , color: '#F53F3F', bg: '#FFF1F0', trendColor: '#F53F3F', direction: 'down' }, + { title: t('aiAnalysis.fixed2AbnormalStringFixed2QuantityFixed2'), value: '8', trend: t('devices.vsYesterday', { value: '+2 ↑' }), icon: , color: '#1D2129', bg: '#F5F7FA', trendColor: '#F53F3F', direction: 'up' }, + { title: t('aiAnalysis.fixed2HotspotSuspectFixed2QuantityFixed2'), value: '36', trend: t('devices.vsYesterday', { value: '+5 ↑' }), icon: , color: '#F53F3F', bg: '#FFF1F0', trendColor: '#F53F3F', direction: 'up' }, + { title: t('aiAnalysis.s25'), value: '86.3', unit: t('aiAnalysis.scoreUnit'), trend: t('devices.vsYesterday', { value: '-1.7' }), icon: , color: '#00B42A', bg: '#E8FFEA', trendColor: '#F53F3F', direction: 'down' }, + { title: t('aiAnalysis.fixed2PredictedGeneration'), value: '4,821', unit: t('home.kWh'), trend: t('devices.vsYesterday', { value: '+2.6% ↑' }), icon: , color: '#165DFF', bg: '#E8F3FF', trendColor: '#00B42A', direction: 'up' }, ].map((item, index) => (
无人机热成像巡检分析} - extra={巡检:2025-05-20 09:30 } + title={{t('aiAnalysis.fixed2UavInspection')}} + extra={{t('aiAnalysis.fixed2Inspection202505200930')}} bordered={false} bodyStyle={{ padding: '16px 20px' }} style={{ ...cardStyle, height: '100%' }} @@ -263,7 +261,7 @@ export default function AIDiagnosisPage() {
- {item.status === 'serious' ? '高危' : '注意'} + {item.status === 'serious' ? t('aiAnalysis.highDanger') : t('aiAnalysis.attention')}
@@ -273,11 +271,11 @@ export default function AIDiagnosisPage() {
- 热斑疑似点 -
高温区域
+ {t('aiAnalysis.fixed2HotspotSuspect')} +
{t('aiAnalysis.areaFixed2')}
- 温度分布: 25℃ ~ 85℃ + {t('aiAnalysis.fixed2Temperature2585')}
@@ -285,10 +283,10 @@ export default function AIDiagnosisPage() {
组串实时状态定位图} + title={{t('aiAnalysis.status')}} extra={} style={{ fontSize: 12, color: '#86909C' }}> - 总组串: 160 - 异常: 8 + {t('aiAnalysis.s9')}160 + {t('aiAnalysis.fixed2Abnormal')}8 } bordered={false} bodyStyle={{ padding: '16px 20px' }} @@ -305,20 +303,21 @@ export default function AIDiagnosisPage() { ))} {['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'].map((row) => ( -
{row}排
+
{row}{t('aiAnalysis.rowUnit')}
{Array.from({ length: 20 }).map((_, col) => { - let color = '#E8F3FF'; // 正常 + let color = '#E8F3FF'; let icon = null; - let tooltip = `组串 ${row}-${col + 1}: 正常`; + let statusKey = 'normal'; - // 模拟异常分布 - if (row === 'B' && col === 3) { color = '#F53F3F'; icon =
; tooltip = `组串 ${row}-${col + 1}: 严重异常 (热斑)`; } - if (row === 'B' && col === 17) { color = '#F53F3F'; icon =
; tooltip = `组串 ${row}-${col + 1}: 严重异常 (断路)`; } - if (row === 'C' && col === 15) { color = '#FF7D00'; icon =
; tooltip = `组串 ${row}-${col + 1}: 异常 (功率偏差)`; } - if (row === 'E' && col === 4) { color = '#FF7D00'; icon =
; tooltip = `组串 ${row}-${col + 1}: 异常 (遮挡)`; } - if (row === 'F' && col === 11) { color = '#F7BA1E'; icon =
; tooltip = `组串 ${row}-${col + 1}: 轻微异常`; } - if (row === 'G' && col === 1) { color = '#F7BA1E'; icon =
; tooltip = `组串 ${row}-${col + 1}: 轻微异常`; } - if (row === 'H' && col === 10) { color = '#86909C'; tooltip = `组串 ${row}-${col + 1}: 离线`; } + if (row === 'B' && col === 3) { color = '#F53F3F'; icon =
; statusKey = 'seriousHotspot'; } + if (row === 'B' && col === 17) { color = '#F53F3F'; icon =
; statusKey = 'seriousBreak'; } + if (row === 'C' && col === 15) { color = '#FF7D00'; icon =
; statusKey = 'abnormalPowerDeviation'; } + if (row === 'E' && col === 4) { color = '#FF7D00'; icon =
; statusKey = 'abnormalShading'; } + if (row === 'F' && col === 11) { color = '#F7BA1E'; icon =
; statusKey = 'minor'; } + if (row === 'G' && col === 1) { color = '#F7BA1E'; icon =
; statusKey = 'minor'; } + if (row === 'H' && col === 10) { color = '#86909C'; statusKey = 'offline'; } + + const tooltip = t('aiAnalysis.stringCellTooltip', { row, col: col + 1, status: t(`aiAnalysis.stringStatus.${statusKey}`) }); return ( @@ -335,11 +334,11 @@ export default function AIDiagnosisPage() {
{[ - { color: '#E8F3FF', label: '运行正常' }, - { color: '#F7BA1E', label: '轻微异常' }, - { color: '#FF7D00', label: '功率异常' }, - { color: '#F53F3F', label: '严重故障' }, - { color: '#86909C', label: '设备离线' }, + { color: '#E8F3FF', label: t('aiAnalysis.normalFixed2') }, + { color: '#F7BA1E', label: t('aiAnalysis.abnormalFixed22') }, + { color: '#FF7D00', label: t('aiAnalysis.abnormalFixed2') }, + { color: '#F53F3F', label: t('aiAnalysis.fixed2CriticalFixed2ErrorFixed2') }, + { color: '#86909C', label: t('aiAnalysis.fixed2DeviceFixed2OfflineFixed2') }, ].map(item => ( @@ -351,14 +350,13 @@ export default function AIDiagnosisPage() {
- 异常类型分布 -
+ {t('aiAnalysis.fixed2AbnormalFixed2Type')}
{[ - { label: '严重故障', value: 2, color: '#F53F3F', percent: '25%' }, - { label: '功率异常', value: 4, color: '#FF7D00', percent: '50%' }, - { label: '轻微异常', value: 2, color: '#F7BA1E', percent: '25%' }, - { label: '离线组串', value: 0, color: '#86909C', percent: '0%' }, + { label: t('aiAnalysis.fixed2CriticalFixed2ErrorFixed2'), value: 2, color: '#F53F3F', percent: '25%' }, + { label: t('aiAnalysis.abnormalFixed2'), value: 4, color: '#FF7D00', percent: '50%' }, + { label: t('aiAnalysis.abnormalFixed22'), value: 2, color: '#F7BA1E', percent: '25%' }, + { label: t('aiAnalysis.fixed2Offline'), value: 0, color: '#86909C', percent: '0%' }, ].map(item => (
@@ -372,7 +370,7 @@ export default function AIDiagnosisPage() { ))}
- +
@@ -384,14 +382,14 @@ export default function AIDiagnosisPage() {
组件衰减趋势分析} + title={{t('aiAnalysis.s13')}} bordered={false} bodyStyle={{ padding: '16px 20px' }} style={{ ...cardStyle, height: '100%' }} >
- 衰减率趋势 (%) - 当前均值: -5.32% + {t('aiAnalysis.degradationTrend')} + {t('aiAnalysis.fixed2Current532')}
- 衰减预警: - 12块组件衰减超标, 建议排查。 + {t('aiAnalysis.s15')} + {t('aiAnalysis.12')}
@@ -433,14 +431,14 @@ export default function AIDiagnosisPage() { 实时发电表现深度分析} + title={{t('aiAnalysis.generation')}} bordered={false} bodyStyle={{ padding: '16px 20px' }} style={{ ...cardStyle, height: '100%' }} >
-
发电量对比分析 (kWh)
+
{t('aiAnalysis.generationComparison')}
{[ - { label: '当日实际发电', value: '4,752', unit: t('home.kWh'), color: '#165DFF' }, - { label: '当日预测发电', value: '4,960', unit: t('home.kWh'), color: '#00B42A' }, - { label: '发电偏差率', value: '-4.35%', unit: '', color: '#F53F3F' }, - { label: '实时 PR 值', value: '82.6%', unit: '', color: '#1D2129' }, + { label: t('aiAnalysis.actualFixed2GenerationFixed2'), value: '4,752', unit: t('home.kWh'), color: '#165DFF' }, + { label: t('aiAnalysis.predictedGenerationFixed2'), value: '4,960', unit: t('home.kWh'), color: '#00B42A' }, + { label: t('aiAnalysis.fixed2GenerationDeviation'), value: '-4.35%', unit: '', color: '#F53F3F' }, + { label: t('aiAnalysis.pr'), value: '82.6%', unit: '', color: '#1D2129' }, ].map(item => (
{item.label}
@@ -496,23 +494,23 @@ export default function AIDiagnosisPage() { {/* AI诊断总结 */}
- AI 诊断总结 - 更新时间: 2025-05-20 09:45 + {t('aiAnalysis.fixed2Ai2')} + {t('aiAnalysis.fixed2UpdatedAt202505200945')}
-
- AI 建议与处置 -
+
{t('aiAnalysis.fixed2Ai')}
{g}, width: 68 }, + { title: t('aiAnalysis.s40'), dataIndex: 'gain', render: g => {g}, width: 68 }, { title: t('aiAnalysis.confidence'), dataIndex: 'confidence', width: 60 }, { title: t('roles.actions'), @@ -543,17 +539,17 @@ export default function AIDiagnosisPage() { size="small" style={{ fontSize: 11, borderRadius: 4, height: 22, padding: '0 6px' }} > - {record.isView ? '详情' : t('workOrder.dispatch')} + {record.isView ? t('common.detail') : t('workOrder.dispatch')} ) }, ]} dataSource={[ - { key: '1', measure: '处理热斑组件', priority: '高', gain: '+2.15%', confidence: '92%', isView: false }, - { key: '2', measure: '排查异常组串', priority: '高', gain: '+1.85%', confidence: '90%', isView: false }, - { key: '3', measure: '清洗受遮挡区域', priority: '中', gain: '+0.45%', confidence: '78%', isView: false }, - { key: '4', measure: '跟踪衰减组件', priority: '中', gain: '+0.35%', confidence: '86%', isView: false }, - { key: '5', measure: '监控逆变器效率', priority: '低', gain: '+0.10%', confidence: '93%', isView: true }, + { key: '1', measure: t('aiAnalysis.handleHotspotComponents'), priority: t('workOrder.high'), gain: '+2.15%', confidence: '92%', isView: false }, + { key: '2', measure: t('aiAnalysis.checkAbnormalStrings'), priority: t('workOrder.high'), gain: '+1.85%', confidence: '90%', isView: false }, + { key: '3', measure: t('aiAnalysis.cleanShadedAreas'), priority: t('workOrder.medium'), gain: '+0.45%', confidence: '78%', isView: false }, + { key: '4', measure: t('aiAnalysis.trackDegradedComponents'), priority: t('workOrder.medium'), gain: '+0.35%', confidence: '86%', isView: false }, + { key: '5', measure: t('aiAnalysis.monitorInverterEfficiency'), priority: t('workOrder.low'), gain: '+0.10%', confidence: '93%', isView: true }, ]} pagination={false} size="small" @@ -567,17 +563,17 @@ export default function AIDiagnosisPage() { {/* 底部:数据来源 */} 全栈数据融合来源} + title={{t('aiAnalysis.sourceFixed2')}} bordered={false} bodyStyle={{ padding: '16px 20px' }} style={cardStyle} > {[ - { title: '无人机载荷热成像', time: '2025-05-20 09:30', icon: , status: '同步中' }, - { title: 'SCADA 实时遥测数据', time: '2025-05-20 09:45:12', icon: , status: t('roles.normal') }, - { title: '云端气象分析站', time: '2025-05-20 09:45:00', icon: , status: t('roles.normal') }, - { title: '电站运维资产库', time: '最近更新: 2025-05-18', icon: , status: t('roles.normal') }, + { title: t('aiAnalysis.fixed2Uav'), time: '2025-05-20 09:30', icon: , status: t('aiAnalysis.syncing') }, + { title: t('aiAnalysis.scada'), time: '2025-05-20 09:45:12', icon: , status: t('roles.normal') }, + { title: t('aiAnalysis.s43'), time: '2025-05-20 09:45:00', icon: , status: t('roles.normal') }, + { title: t('aiAnalysis.oM'), time: t('aiAnalysis.lastUpdated', { value: '2025-05-18' }), icon: , status: t('roles.normal') }, ].map((source, idx) => (
@@ -586,10 +582,10 @@ export default function AIDiagnosisPage() {
{source.title}
-
数据点: {source.time}
+
{t('aiAnalysis.dataPoint', { time: source.time })}
- 状态: {source.status} + {t('aiAnalysis.statusLabel', { status: source.status })}
diff --git a/src/pages/Agri.jsx b/src/pages/Agri.jsx index cd7a8e7..f61a4fe 100644 --- a/src/pages/Agri.jsx +++ b/src/pages/Agri.jsx @@ -112,7 +112,7 @@ export default function Agri() { } } } catch (err) { - utils.message.error('获取模型列表失败'); + utils.message.error(t('page.listFixed2FailedFixed2')); } finally { setLoadingModels(false); } @@ -185,7 +185,7 @@ export default function Agri() { const handleSubmitDetect = async () => { if (detectFiles.length === 0) { - utils.message.warning('请先上传图片或视频'); + utils.message.warning(t('page.uploadImageVideoFixed2')); return; } @@ -215,7 +215,7 @@ export default function Agri() { addToHistory('detect', res.jobId); } } catch (err) { - utils.message.error('提交任务失败'); + utils.message.error(t('aiAnalysis.submitTaskFailed')); setDetectStatus(null); } }; @@ -4372,7 +4372,7 @@ export default function Agri() { setHotLoading(false); setSegResult(res); - utils.message.success('热成像分析完成'); + utils.message.success(t('aiAnalysis.thermalAnalysisComplete')); addToHistory('segmentation', res.analysisId || 'seg-' + Date.now()); }).catch(err => { }) @@ -4389,7 +4389,7 @@ export default function Agri() { setPolling(false); const resultRes = await getDetectJobResult(jobId); setDetectResult(resultRes); - utils.message.success('任务执行成功'); + utils.message.success(t('devices.taskExecSuccess')); } else if (['failed', 'cancelled'].includes(statusRes.status)) { clearInterval(pollTimer.current); setPolling(false); @@ -4405,7 +4405,7 @@ export default function Agri() { const handleSubmitSeg = async () => { if (!thermalFile) { - utils.message.warning('请先上传热成像文件'); + utils.message.warning(t('page.uploadThermal2')); return; } @@ -4422,10 +4422,10 @@ export default function Agri() { try { const res = await submitSegmentationAnalyze(formData); setSegResult(res); - utils.message.success('热成像分析完成'); + utils.message.success(t('aiAnalysis.thermalAnalysisComplete')); addToHistory('segmentation', res.analysisId || 'seg-' + Date.now()); } catch (err) { - utils.message.error('热成像分析失败'); + utils.message.error(t('aiAnalysis.thermalAnalysisFailed')); } finally { setSegLoading(false); } @@ -4447,7 +4447,7 @@ export default function Agri() { {[ { - title: '算法数量', + title: t('aiAnalysis.algoCount'), value: '2', unit: '检测 + 分割', icon: , @@ -4455,7 +4455,7 @@ export default function Agri() { bg: '#E8F3FF', }, { - title: '在线服务', + title: t('aiAnalysis.onlineServices'), value: '2', unit: 'health 实时检查', icon: , @@ -4463,7 +4463,7 @@ export default function Agri() { bg: '#E8FFEA', }, { - title: '模型数量', + title: t('aiAnalysis.modelCount'), value: models.length || 3, unit: '检测模型 registry', icon: , @@ -4471,7 +4471,7 @@ export default function Agri() { bg: '#E8F3FF', }, { - title: '历史记录', + title: t('aiAnalysis.historyRecords'), value: history.length, unit: '本页联调记录', icon: , @@ -4555,7 +4555,7 @@ export default function Agri() { const renderDetectWorkspace = () => (
- 检测模型 + {t('aiAnalysis.detectModel')} - - + + - +
{/* 左侧:分析图表 */}
- 污染趋势分析} bordered={false} style={cardStyle} bodyStyle={{ padding: 16 }}> + {t('clean.s6')}} bordered={false} style={cardStyle} bodyStyle={{ padding: 16 }}>
- 污染指数 + {t('clean.s7')} -
污染指数
-
阈值
+
{t('clean.s7')}
+
{t('alerts.threshold')}
- 发电损失构成 (近 7 天)} bordered={false} style={cardStyle} bodyStyle={{ padding: 16 }}> + {t('clean.fixed2Generation')}{t('clean.near7Days')}} bordered={false} style={cardStyle} bodyStyle={{ padding: 16 }}>
-
总损失电量
+
{t('clean.batteryFixed2')}
9,320
{t('home.kWh')}
- 清洗前后对比分析 (近一次)} bordered={false} style={cardStyle} bodyStyle={{ padding: 16 }}> + {t('clean.fixed2Cleaning2')}{t('clean.nearLast')}} bordered={false} style={cardStyle} bodyStyle={{ padding: 16 }}> - 清洗收益深度测算} bordered={false} style={cardStyle} bodyStyle={{ padding: 16 }}> + {t('clean.fixed2CleaningFixed2')}} bordered={false} style={cardStyle} bodyStyle={{ padding: 16 }}>
-
预计发电提升
+
{t('clean.generation')}
12,850 {t('home.kWh')}
-
电价 (含税)
-
0.568 元/kWh
+
{t('clean.electricityPrice')}
+
0.568 {t('clean.kwhFixed2')}
-
预计净收益
-
7,286 元
+
{t('clean.netRevenueFixed2')}
+
7,286 {t('clean.s16')}
-
ROI (投资回报)
+
{t('clean.roi')}
227%
- 回收周期 - 0.44 天 + {t('clean.s17')} + 0.44 {t('devices.day')}
@@ -396,19 +397,19 @@ export default function CleanOptimizationPage() { {/* 中间:地图分布与工单预览 */}
电站污染分布与建议清洗区块分布图} + title={{t('clean.recommendedCleanBlock')}} bordered={false} style={{ ...cardStyle, }} bodyStyle={{ padding: 0, position: 'relative', overflow: 'hidden', minHeight: 500 }} >
- 污染等级: - - - - - + {t('clean.s20')} + + + + +
@@ -435,7 +436,7 @@ export default function CleanOptimizationPage() { { id: '20', score: '17.8', top: '85%', left: '40%', color: '#00B42A' }, ].map(block => (
-
区块 {block.id}
+
{t('clean.block', { block: block.id })}
{block.score}
))} @@ -452,21 +453,21 @@ export default function CleanOptimizationPage() {
清洗工单智能预览} - extra={} + title={{t('clean.fixed2Cleaning2')}} + extra={} bordered={false} style={cardStyle} bodyStyle={{ padding: 0 }} >
{s} }, - { title: t('roles.actions'), key: 'action', render: () => }, + { title: t('roles.actions'), key: 'action', render: () => }, ]} dataSource={workOrderPreviewData} pagination={false} @@ -478,17 +479,17 @@ export default function CleanOptimizationPage() { {/* 右侧:决策建议与参考 */} - 智能清洗策略建议} bordered={false} style={cardStyle} bodyStyle={{ padding: 0 }}> + {t('clean.cleaning2')}} bordered={false} style={cardStyle} bodyStyle={{ padding: 0 }}>
- 算法模型:多因子加权综合优化模型 (v2.0) + {t('clean.algorithmModel')}
{p}, width: 65 }, - { title: '建议区块', dataIndex: 'area', key: 'area' }, - { title: '预计提升', dataIndex: 'boost', key: 'boost', render: (b) => {b} }, - { title: '天气适宜度', dataIndex: 'weather', key: 'weather', render: (w) => {w} }, + { title: t('clean.s48'), dataIndex: 'id', key: 'id', width: 85 }, + { title: t('workOrder.priority'), dataIndex: 'priority', key: 'priority', render: (p) => {p}, width: 65 }, + { title: t('clean.s49'), dataIndex: 'area', key: 'area' }, + { title: t('clean.s50'), dataIndex: 'boost', key: 'boost', render: (b) => {b} }, + { title: t('clean.fixed2WeatherAdvice2Fixed2SuitableExec2'), dataIndex: 'weather', key: 'weather', render: (w) => {w} }, ]} dataSource={smartCleanPlanData} pagination={false} @@ -497,18 +498,18 @@ export default function CleanOptimizationPage() { /> - 清洗技术方案推荐} bordered={false} style={cardStyle} bodyStyle={{ padding: 16 }}> + {t('clean.fixed2Cleaning2')}} bordered={false} style={cardStyle} bodyStyle={{ padding: 16 }}>
- 智能机器人方案 + {t('clean.robot')}
-
全自动清洗,适合大面积平整区块,效率极高。
+
{t('clean.cleaning')}
- 预计用水: - 18 吨 + {t('clean.s28')} + {t('clean.18')}
@@ -516,57 +517,57 @@ export default function CleanOptimizationPage() {
- 人工精细化方案 + {t('clean.fixed2Manual')}
-
灵活处理遮挡及严重污垢区域,适合复杂地形。
+
{t('clean.handleCriticalArea')}
- 预计用水: - 32 吨 + {t('clean.s28')} + {t('clean.32')}
- 天气与资源环境参考 (计划 01)} bordered={false} style={cardStyle} bodyStyle={{ padding: 16 }}> + {t('clean.fixed2WeatherAdvice2')}({t('clean.plan', { name: '01' })})} bordered={false} style={cardStyle} bodyStyle={{ padding: 16 }}>
-
05-26 (周一)
+
{`05-26 (${t('common.mondayShort')})`}
-
多云 18~28℃
+
{t('clean.fixed2Cloudy1828')}
-
05-27 (周二)
+
{`05-27 (${t('common.tuesdayShort')})`}
-
晴 19~30℃
+
{t('clean.1930')}
- 风速: 2.1m/s (适宜) + {t('clean.s36')}{`2.1m/s (${t('clean.suitable')})`}
- 降水: 10% (低) + {t('clean.s37')}{`10% (${t('clean.low')})`}
- 水源: 充足 (50t) + {t('clean.s38')}{`${t('clean.sufficient')} (50t)`}
- 清洗作业影响综合评估} bordered={false} style={cardStyle} bodyStyle={{ padding: 16 }}> + {t('clean.fixed2Cleaning')}} bordered={false} style={cardStyle} bodyStyle={{ padding: 16 }}> {[ - { icon: , bg: '#E8F3FF', label: '预计用水量', value: '18 吨', sub: '节水 30% (机器人)', subColor: '#00B42A' }, - { icon: , bg: '#E8F3FF', label: '预计总耗时', value: '12 小时', sub: '并行作业 2 组', subColor: '#86909C' }, - { icon: , bg: '#E8FFEA', label: '碳减排贡献', value: '6.85 吨', sub: 'CO₂ 减排量', subColor: '#86909C' }, - { icon: , bg: '#FFF7E8', label: '作业风险评估', value: t('aiAnalysis.lowRisk'), valueColor: '#00B42A', sub: '安全可控', subColor: '#86909C' }, + { icon: , bg: '#E8F3FF', label: t('clean.s52'), value: t('clean.tonUnit', { value: '18' }), sub: t('clean.waterSaveWithRobot', { value: '30%' }), subColor: '#00B42A' }, + { icon: , bg: '#E8F3FF', label: t('clean.s53'), value: t('clean.hourUnit', { value: '12' }), sub: t('clean.parallelJobs', { count: '2' }), subColor: '#86909C' }, + { icon: , bg: '#E8FFEA', label: t('clean.s54'), value: t('clean.tonUnit', { value: '6.85' }), sub: t('clean.co2Reduction'), subColor: '#86909C' }, + { icon: , bg: '#FFF7E8', label: t('clean.s55'), value: t('aiAnalysis.lowRisk'), valueColor: '#00B42A', sub: t('clean.safeControllable'), subColor: '#86909C' }, ].map((item, idx) => (
@@ -582,9 +583,7 @@ export default function CleanOptimizationPage() {
- - 以上决策建议基于历史数据、实时气象及发电损失模型综合计算。实际执行效果可能受天气骤变、现场突发障碍等不确定因素影响。 - + {t('clean.generationActualFixed2ExecuteWeatherAdvice2Ok')}
diff --git a/src/pages/Devices.tsx b/src/pages/Devices.tsx index 9c763df..a3a0c0d 100644 --- a/src/pages/Devices.tsx +++ b/src/pages/Devices.tsx @@ -25,7 +25,7 @@ export default function DeviceIndexPage() { const menuItems = [ - { key: 'overview', label: '装备总览', icon: }, + { key: 'overview', label: t('devices.equipmentOverview'), icon: }, { key: 'status', label: t('devices.deviceStatus'), icon: }, { key: 'wayLineTask', label: t('devices.waylineTask'), icon: }, { key: 'robotTask', label: t('devices.robotTask'), icon: }, @@ -69,7 +69,7 @@ export default function DeviceIndexPage() { return (
- 装备控制 + {t('devices.equipmentControl')}
( @@ -40,13 +40,13 @@ export default function Downloads() { ), }, { - title: '源设备', + title: t('video.sourceDevice'), dataIndex: 'deviceName', key: 'deviceName', render: (text: string) => {text}, }, { - title: '起止时间', + title: t('video.startEndTime'), key: 'duration', render: (record: any) => ( @@ -57,7 +57,7 @@ export default function Downloads() { ), }, { - title: '容量', + title: t('video.capacity'), dataIndex: 'size', key: 'size', render: (text: string) => {text}, @@ -72,7 +72,7 @@ export default function Downloads() { icon={} style={{ fontSize: 11, fontWeight: 'bold' }} > - 下载 + {t('common.download')} ), }, @@ -81,10 +81,10 @@ export default function Downloads() { return (
- 视频回放中心 + {t('video.videoReplayCenter')}
@@ -94,7 +94,7 @@ export default function Downloads() {
- 时间跨度 + {t('video.timeSpan')} @@ -102,7 +102,7 @@ export default function Downloads() { diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 547dd1c..76f0301 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -101,9 +101,9 @@ export default function StationDashboard() { ]; const aiAnalysis = [ - { type: "AI分析", content: "AI发现逆变器温度与组串电流存在轻微异常,建议优先复核A区设备。", desc: "发现2处组件存在异常热斑" }, - { type: t('constants.critical'), content: "INV-A02散热检修建议:建议检查INV-A02散热风扇、风道和运行负载,持续高温时转维修工单。", desc: "3个组串存在发电异常" }, - { type: t('constants.critical'), content: "A05阵列热斑处置建议:建议现场复核PV-A05-018至PV-A05-020组件接线和旁路二极管状态,必要时更换异常组件。", desc: "整体发电偏差-2.8%" } + { type: "AI分析", content: t('home.fixed2AiInverterFixed2TemperatureAbnormalADevice'), desc: "发现2处组件存在异常热斑" }, + { type: t('constants.critical'), content: t('home.invA02MaintenanceInvA02Repair'), desc: "3个组串存在发电异常" }, + { type: t('constants.critical'), content: t('home.a05HotspotPvA05018PvA05020StatusAbnormal'), desc: "整体发电偏差-2.8%" } ]; // 图表数据 @@ -200,12 +200,12 @@ export default function StationDashboard() { const res = await addWorkOrder(params); if (res.code === 0 || res.code === 200) { - messageApi.success('✅ 清洗工单生成成功'); + messageApi.success(t('home.cleaningOrderGeneratedSuccessfullyFixed2')); } else { messageApi.error(res.msg || '❌ 工单生成失败'); } } catch (error) { - messageApi.error('❌ 工单生成接口调用失败'); + messageApi.error(t('home.failedFixed2')); console.error(error); } }; @@ -222,7 +222,7 @@ export default function StationDashboard() { } } catch (err) { - messageApi.error('加载失败'); + messageApi.error(t('common.loadFailed')); } finally { } @@ -322,7 +322,7 @@ export default function StationDashboard() { series={[{ key: 'efficiency', name: '效率', color: '#165DFF' }]} /> - {t('home.maintenanceProgress')}} extra={} bordered={false} style={cardStyle} bodyStyle={{ padding: '12px' }}> + {t('home.maintenanceProgress')}} extra={} bordered={false} style={cardStyle} bodyStyle={{ padding: '12px' }}>
-
总任务
+
{t('home.taskSuspended2Fixed2')}
12
@@ -351,10 +351,10 @@ export default function StationDashboard() {
{t('home.stationOverview')}
- roles.normal - constants.sourceAlert - devices.offline - 维护中 + {t('roles.normal')} + {t('constants.sourceAlert')} + {t('devices.offline')} + {t('home.underMaintenance')}
} @@ -368,7 +368,7 @@ export default function StationDashboard() { style={{ width: '100%', height: '100%', border: 'none', borderRadius: '8px', position: "absolute" }} allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen - title="外部系统" + title={t('home.s20')} />*/} @@ -378,16 +378,16 @@ export default function StationDashboard() {
- {t('home.recentInspection')}} extra={} bordered={false} style={{ ...cardStyle, height: "100%" }} bodyStyle={{ padding: '0 12px' }}> + {t('home.recentInspection')}} extra={} bordered={false} style={{ ...cardStyle, height: "100%" }} bodyStyle={{ padding: '0 12px' }}>
t.substring(0, 10) }, - { title: t('roles.status'), dataIndex: 'status', key: 'status', render: () => ● 已完成 }, + { title: t('home.fixed2InspectionFixed2TypeFixed2'), dataIndex: 'name', key: 'name', ellipsis: true }, + { title: t('home.fixed2InspectionFixed2TimeFixed2'), dataIndex: 'time', key: 'time' }, + { title: t('home.fixed2Inspection'), dataIndex: 'person', key: 'person', render: (t) => t.substring(0, 10) }, + { title: t('roles.status'), dataIndex: 'status', key: 'status', render: () => {t('home.completedFixed2')} }, ]} /> @@ -401,8 +401,8 @@ export default function StationDashboard() {
-
热点检测
-
发现 2 处组件存在异常热斑
+
{t('home.detectFixed2')}
+
{t('home.s4')}2{t('home.abnormalFixed2HotspotFixed2')}
@@ -413,8 +413,8 @@ export default function StationDashboard() {
-
组串异常
-
3 个组串存在发电异常
+
{t('home.abnormalFixed2')}
+
3{t('home.generationFixed2AbnormalFixed2')}
@@ -425,8 +425,8 @@ export default function StationDashboard() {
-
发电偏差分析
-
整体发电偏差 -2.8%
+
{t('home.fixed2GenerationDeviation')}
+
{t('home.generationDeviationFixed2')}-2.8%
@@ -441,11 +441,11 @@ export default function StationDashboard() { {/* 右侧 */}
- {t('alerts.realtimeAlert')}} extra={} bordered={false} style={cardStyle} bodyStyle={{ padding: '12px' }}> + {t('alerts.realtimeAlert')}} extra={} bordered={false} style={cardStyle} bodyStyle={{ padding: '12px' }}>
- 告警内容 - 级别 - 发生时间 + {t('alerts.alarmContent')} + {t('alerts.level')} + {t('alerts.occurTime')}
{alarmList.slice(0, 2).map((alert, idx) => { let dotColor = alert.alarmLevel === 'ERROR' ? '#ff4d4f' : alert.alarmLevel === 'WARNING' ? '#fa8c16' : '#1677ff'; @@ -465,7 +465,7 @@ export default function StationDashboard() { })}
- {t('home.deviceOnlineStatus')}} extra={} bordered={false} style={cardStyle} bodyStyle={{ padding: '12px' }}> + {t('home.deviceOnlineStatus')}} extra={} bordered={false} style={cardStyle} bodyStyle={{ padding: '12px' }}> {[ { name: '组件阵列', count: '320/320', icon: }, @@ -490,7 +490,7 @@ export default function StationDashboard() { - {t('home.weatherInfo')}} extra={} bordered={false} style={cardStyle} bodyStyle={{ padding: '12px' }}> + {t('home.weatherInfo')}} extra={} bordered={false} style={cardStyle} bodyStyle={{ padding: '12px' }}>
@@ -510,7 +510,7 @@ export default function StationDashboard() { {/* ====================================== */} {/* 👇 清洗建议 + 生成工单按钮(已完善) */} {/* ====================================== */} - {t('clean.cleaningSuggestion')}} extra={} bordered={false} style={cardStyle} bodyStyle={{ padding: '12px' }}> + {t('clean.cleaningSuggestion')}} extra={} bordered={false} style={cardStyle} bodyStyle={{ padding: '12px' }}>
-
建议清洗时间: 3天内
-
预计提升发电量: 2.6 %
-
预计提升电量: 120 kWh/天
+
{t('home.cleaningFixed2Time')}{t('home.3')}
+
{t('home.fixed2Battery')}2.6 %
+
{t('home.battery')}{t('home.120')}
+ >{t('home.generateCleanOrder')}
diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx index 0e3db45..4cb9993 100644 --- a/src/pages/Login.tsx +++ b/src/pages/Login.tsx @@ -55,10 +55,10 @@ export default function Login() { } navigate('/', { replace: true }); } else { - message.error('登录失败:' + loginResult.msg); + message.error(t('login.loginFailed') + ':' + loginResult.msg); } } catch (err) { - message.error('登录失败:' + err.toString()); + message.error(t('login.loginFailed') + ':' + err.toString()); } finally { // 无论成功失败,关闭 loading setLoading(false); @@ -89,7 +89,7 @@ export default function Login() {
- 光伏电站智能监控与运维系统 + {t('login.systemName')}
@@ -117,7 +117,7 @@ export default function Login() { >

- 我已阅读并同意 {t('login.userAgreement')} 和 {t('login.privacyPolicy')} + {t('login.agreeText')} {t('login.userAgreement')} {t('login.and')} {t('login.privacyPolicy')}

@@ -139,7 +139,7 @@ export default function Login() { boxShadow: '0 4px 12px rgba(22, 119, 255, 0.25)' }} > - 登录 + {t('login.loginButton')} diff --git a/src/pages/Profile.tsx b/src/pages/Profile.tsx index 35f836e..8cbec15 100644 --- a/src/pages/Profile.tsx +++ b/src/pages/Profile.tsx @@ -38,6 +38,8 @@ const { TabPane } = Tabs; const { Title } = Typography; const Profile: React.FC = () => { + const { t } = useTranslation(); + const [loading, setLoading] = useState(false); const [profileData, setProfileData] = useState(null); const { userInfo } = useSelector((state: RootState) => state.user); @@ -80,7 +82,7 @@ const Profile: React.FC = () => { message.error(res.msg || t('profile.modifyFailed')); } } catch (error) { - message.error('请求失败'); + message.error(t('profile.requestFailed')); } finally { setLoading(false); } @@ -101,7 +103,7 @@ const Profile: React.FC = () => { message.error(res.msg || t('profile.modifyFailed')); } } catch (error) { - message.error('请求失败'); + message.error(t('profile.requestFailed')); } finally { setLoading(false); } @@ -136,7 +138,7 @@ const Profile: React.FC = () => { return (
- 个人中心 + {t('profile.title')} {/* 🔥 关键:给 Row 添加 align="stretch" 让两列自动等高 */} @@ -175,7 +177,7 @@ const Profile: React.FC = () => { borderColor: '#d9d9d9', }} > - 更换头像 + {t('profile.changeAvatar')}
@@ -187,25 +189,25 @@ const Profile: React.FC = () => {
- 用户名称: + {t('profile.username')}: {profileData?.userName}
- 手机号码: + {t('profile.phone')}: {profileData?.phonenumber || t('profile.notSet')}
- 用户邮箱: + {t('profile.email')}: {profileData?.email || t('profile.notSet')}
- 所属角色: + {t('profile.role')}: {profileData?.roles?.map((r: any) => r.roleName).join(', ') || t('profile.notSet')} @@ -234,7 +236,7 @@ const Profile: React.FC = () => { }} > {/* 基本资料 */} - +
{ name="sex" > - 男 - 女 + {t('profile.male')} + {t('profile.female')} } @@ -291,11 +293,11 @@ const Profile: React.FC = () => { } - placeholder="请输入邮箱" + placeholder={t('profile.emailPlaceholder')} style={{ borderRadius: 6 }} /> @@ -305,7 +307,7 @@ const Profile: React.FC = () => { name="remark" > @@ -323,7 +325,7 @@ const Profile: React.FC = () => { width: 160, }} > - 保存修改 + {t('profile.saveChanges')} @@ -331,7 +333,7 @@ const Profile: React.FC = () => { {/* 修改密码 */} - +
{ } - placeholder="请输入旧密码" + placeholder={t('profile.oldPasswordPlaceholder')} style={{ borderRadius: 6 }} /> @@ -355,13 +357,13 @@ const Profile: React.FC = () => { label={t('profile.newPassword')} name="newPassword" rules={[ - { required: true, message: '请输入新密码' }, - { min: 6, message: '密码长度不能小于6位' }, + { required: true, message: t('profile.newPasswordPlaceholder') }, + { min: 6, message: t('profile.passwordLengthError') }, ]} > } - placeholder="请输入新密码" + placeholder={t('profile.newPasswordPlaceholder')} style={{ borderRadius: 6 }} /> @@ -369,11 +371,11 @@ const Profile: React.FC = () => { } - placeholder="请再次输入新密码" + placeholder={t('profile.confirmPasswordPlaceholder')} style={{ borderRadius: 6 }} /> @@ -390,7 +392,7 @@ const Profile: React.FC = () => { width: 160, }} > - 确认修改 + {t('profile.confirmChange')} diff --git a/src/pages/RealTimeMonitor.tsx b/src/pages/RealTimeMonitor.tsx index 31142cd..34e03bb 100644 --- a/src/pages/RealTimeMonitor.tsx +++ b/src/pages/RealTimeMonitor.tsx @@ -77,9 +77,9 @@ const RealTimeMonitorPage = () => { }, { title: t('common.type'), dataIndex: 'type', render: (t) => {t} }, { title: t('workOrder.currentStatus'), dataIndex: 'status', render: getStatusTag }, - { title: '实时功率/数据', dataIndex: 'data', render: (d) => {d} }, + { title: t('page.fixed2RealtimePower'), dataIndex: 'data', render: (d) => {d} }, { - title: '通讯状态', dataIndex: 'communication', + title: t('page.statusFixed2'), dataIndex: 'communication', render: (c) => ( {c} @@ -103,8 +103,8 @@ const RealTimeMonitorPage = () => { return (
- 实时监控中心 - 全方位实时掌握电站设备运行状态与发电表现 + {t('router.realtimeMonitorCenter')} + {t('page.deviceFixed2StatusAndGeneration')}
{/* 左侧大栏 */} @@ -115,10 +115,10 @@ const RealTimeMonitorPage = () => { title={电站拓扑图 (实时)} extra={
- roles.normal - constants.sourceAlert - devices.offline - •• 通讯中 + {t('roles.normal')} + {t('constants.sourceAlert')} + {t('devices.offline')} + {t('page.s2')}
} bordered={false} @@ -137,7 +137,7 @@ const RealTimeMonitorPage = () => { }} allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen - title="外部系统" + title={t('home.s20')} />*/} @@ -148,19 +148,19 @@ const RealTimeMonitorPage = () => { {/* 设备实时监控列表 */} 设备实时监控列表} + title={{t('page.fixed2DeviceFixed2RealtimeMonitorListFixed2')}} bordered={false} style={{ ...cardStyle, borderRadius: 8 }} extra={ - 全部 56 - 正常 52 - 告警 1 - 离线 3 + {t('page.fixed2AllRecordings56')} + {t('page.fixed2Normal52')} + {t('page.fixed2Alert1')} + {t('page.fixed2Offline3')} - } style={{ width: 120 }} /> + } style={{ width: 120 }} /> } > @@ -172,7 +172,7 @@ const RealTimeMonitorPage = () => { size="small" />
- 共 56 条 + {t('page.56')}
@@ -185,12 +185,12 @@ const RealTimeMonitorPage = () => { {/* 6个统计卡片 */} {[ - { title: '当前总功率', value: '812.6', unit: 'kW', sub: '额定功率 1,000 kW', color: '#165DFF', icon: }, - { title: '并网状态', value: '并网正常', unit: '', sub: '频率 50.02 Hz', color: '#00B42A', icon: }, - { title: '电网电压', value: '380.2', unit: 'V', sub: 'A相 380.1 V', color: '#165DFF', icon: }, - { title: '今日累计发电量', value: '4,752', unit: t('home.kWh'), sub: '昨日 5,000 kWh', color: '#00B42A', icon: }, - { title: '当前辐照度', value: '815', unit: 'W/m²', sub: '较昨日 +5.6%', color: '#faad14', icon: }, - { title: '环境温度', value: '28.0', unit: '℃', sub: '较昨日 +1.2 ℃', color: '#165DFF', icon: }, + { title: t('page.fixed2CurrentFixed2TotalPowerFixed2'), value: '812.6', unit: 'kW', sub: '额定功率 1,000 kW', color: '#165DFF', icon: }, + { title: t('page.statusFixed2'), value: '并网正常', unit: '', sub: '频率 50.02 Hz', color: '#00B42A', icon: }, + { title: t('home.gridVoltage'), value: '380.2', unit: 'V', sub: 'A相 380.1 V', color: '#165DFF', icon: }, + { title: t('page.fixed2TodayAccumulatedBatteryFixed2'), value: '4,752', unit: t('home.kWh'), sub: '昨日 5,000 kWh', color: '#00B42A', icon: }, + { title: t('home.currentIrradiance'), value: '815', unit: 'W/m²', sub: '较昨日 +5.6%', color: '#faad14', icon: }, + { title: t('home.ambientTemperature'), value: '28.0', unit: '℃', sub: '较昨日 +1.2 ℃', color: '#165DFF', icon: }, ].map((item, idx) => (
@@ -211,7 +211,7 @@ const RealTimeMonitorPage = () => { {/* 实时功率曲线 */} - 实时功率曲线} extra={} bordered={false} style={cardStyle} bodyStyle={{ padding: '12px' }}> + {t('page.powerCurveFixed2')}} extra={} bordered={false} style={cardStyle} bodyStyle={{ padding: '12px' }}> { {/* 热力图与在线率 */} - 组串电流热力图 (单位: A)} extra={} bordered={false} style={cardStyle} bodyStyle={{ padding: '12px' }}> + 组串电流热力图 (单位: A)} extra={} bordered={false} style={cardStyle} bodyStyle={{ padding: '12px' }}>
{['INV-01', 'INV-02', 'INV-03', 'INV-04', 'INV-05'].map((name, i) => (
@@ -292,7 +292,7 @@ const RealTimeMonitorPage = () => {
- 设备在线率} bordered={false} style={{ ...cardStyle, height: "100%" }} bodyStyle={{ padding: '12px' }}> + {t('devices.deviceOnlineRate')}} bordered={false} style={{ ...cardStyle, height: "100%" }} bodyStyle={{ padding: '12px' }}>
{ />
92.1%
-
在线率
+
{t('devices.statOnlineRate')}
在线 52 (92.1%) 离线 3 (5.3%) 告警 1 (1.8%) - 总数 56 + {t('page.56')}
{/* 简易地图点位 */} diff --git a/src/pages/ReportCenter.tsx b/src/pages/ReportCenter.tsx index 847c82b..9f9fff3 100644 --- a/src/pages/ReportCenter.tsx +++ b/src/pages/ReportCenter.tsx @@ -27,63 +27,6 @@ const { Option } = Select; const { Text, Title } = Typography; // 模拟数据(完全匹配参考图) -const overviewCards = [ - { - title: '本月发电量', - value: '138.6', - unit: 'MWh', - trend: '环比 ↑ +6.2%', - color: '#00B42A', - iconBg: '#E8FFEA', - icon: - }, - { - title: 'PR均值', - value: '82.8', - unit: '%', - trend: '环比 ↑ +1.1%', - color: '#165DFF', - iconBg: '#E8F3FF', - icon: - }, - { - title: '运维工单完成率', - value: '93.4', - unit: '%', - trend: '本月累计 126 单', - color: '#165DFF', - iconBg: '#E8F3FF', - icon: - }, - { - title: '告警闭环率', - value: '94.2', - unit: '%', - trend: '严重告警 8 条', - color: '#F53F3F', - iconBg: '#FFF1F0', - icon: - }, - { - title: '清洗收益提升', - value: '+7.6', - unit: '%', - trend: '预估增发 12,850 kWh', - color: '#00B42A', - iconBg: '#E8FFEA', - icon: - }, - { - title: '自动生成报告', - value: '24', - unit: '份', - trend: '本月累计', - color: '#722ED1', - iconBg: '#F9F0FF', - icon: - }, -]; - const monthlyPowerData = [ { date: '05-01', generation: 6, predicted: 8, pr: 105 }, { date: '05-03', generation: 5.5, predicted: 7.8, pr: 102 }, @@ -117,34 +60,6 @@ const orderAlarmData = [ { date: '05-26-31', newOrder: 90, completed: 70, alarmCount: 18, closedRate: 92 }, ]; -const aiAnalysisData = [ - { type: '热斑识别', value: 36, color: '#F53F3F', icon: }, - { type: '异常组串', value: 8, color: '#FF7D00', icon: }, - { type: '推荐清洗区块', value: 8, color: '#00B42A', icon: }, - { type: '视频异常事件', value: 6, color: '#722ED1', icon: }, -]; -const aiPieData = aiAnalysisData.map(item => ({ - name: item.type, - value: item.value, - color: item.color, -})); - -const reportFileList = [ - { name: '2025年05月电站月报', status: '已生成' }, - { name: '周运维总结', status: '已生成' }, - { name: 'AI诊断日报', status: '生成中' }, - { name: '告警闭环周报', status: '已生成' }, -]; - -const reportTemplates = [ - { name: '月度经营报告', desc: '综合经营分析与收益评估模板', color: '#165DFF', bg: '#E8F3FF', icon: }, - { name: '设备健康分析报告', desc: '设备健康度与故障分析模板', color: '#165DFF', bg: '#E8F3FF', icon: }, - { name: '清洗优化效果报告', desc: '清洗前后对比与收益分析模板', color: '#165DFF', bg: '#E8F3FF', icon: }, - { name: '巡检与机器人作业报告', desc: '巡检与机器人作业统计模板', color: '#722ED1', bg: '#F9F0FF', icon: }, -]; - - - export default function ReportCenterPage() { const { t } = useTranslation(); const [activeTab, setActiveTab] = useState('overview'); @@ -152,6 +67,89 @@ export default function ReportCenterPage() { const [reportType, setReportType] = useState('all'); const [dateRange, setDateRange] = useState([dayjs('2025-05-01'), dayjs('2025-05-31')]); + const overviewCards = [ + { + title: t('report.monthlyGeneration'), + value: '138.6', + unit: 'MWh', + trend: t('report.monthOverMonthUp', { value: '+6.2%' }), + color: '#00B42A', + iconBg: '#E8FFEA', + icon: + }, + { + title: t('report.prAverage'), + value: '82.8', + unit: '%', + trend: t('report.monthOverMonthUp', { value: '+1.1%' }), + color: '#165DFF', + iconBg: '#E8F3FF', + icon: + }, + { + title: t('report.workOrderCompletionRate'), + value: '93.4', + unit: '%', + trend: t('report.monthlyTotalOrders', { count: 126 }), + color: '#165DFF', + iconBg: '#E8F3FF', + icon: + }, + { + title: t('report.alertClosureRate'), + value: '94.2', + unit: '%', + trend: t('report.criticalAlertCount', { count: 8 }), + color: '#F53F3F', + iconBg: '#FFF1F0', + icon: + }, + { + title: t('report.cleaningRevenueImprovement'), + value: '+7.6', + unit: '%', + trend: t('report.estimatedAdditionalGeneration', { value: '12,850' }), + color: '#00B42A', + iconBg: '#E8FFEA', + icon: + }, + { + title: t('report.autoGeneratedReports'), + value: '24', + unit: t('report.unitReport'), + trend: t('report.monthlyAccumulated'), + color: '#722ED1', + iconBg: '#F9F0FF', + icon: + }, + ]; + + const aiAnalysisData = [ + { type: t('aiAnalysis.hotspotRecognition'), value: 36, color: '#F53F3F', icon: }, + { type: t('aiAnalysis.abnormalString'), value: 8, color: '#FF7D00', icon: }, + { type: t('aiAnalysis.recommendedCleanBlock'), value: 8, color: '#00B42A', icon: }, + { type: t('aiAnalysis.videoAbnormalEvent'), value: 6, color: '#722ED1', icon: }, + ]; + const aiPieData = aiAnalysisData.map(item => ({ + name: item.type, + value: item.value, + color: item.color, + })); + + const reportFileList = [ + { name: t('report.stationMonthlyReportFile'), status: t('report.generated') }, + { name: t('report.weeklyOperationSummary'), status: t('report.generated') }, + { name: t('report.aiDiagnosisDaily'), status: t('report.generating') }, + { name: t('report.alertClosureWeekly'), status: t('report.generated') }, + ]; + + const reportTemplates = [ + { name: t('report.monthlyOperationReport'), desc: t('report.monthlyOperationReportDesc'), color: '#165DFF', bg: '#E8F3FF', icon: }, + { name: t('report.deviceHealthReport'), desc: t('report.deviceHealthReportDesc'), color: '#165DFF', bg: '#E8F3FF', icon: }, + { name: t('report.cleaningOptimizationReport'), desc: t('report.cleaningEffectReportDesc'), color: '#165DFF', bg: '#E8F3FF', icon: }, + { name: t('report.inspectionRobotReport'), desc: t('report.inspectionRobotReportDesc'), color: '#722ED1', bg: '#F9F0FF', icon: }, + ]; + const cardStyle = { borderRadius: 12, border: '1px solid #f0f0f0', @@ -159,11 +157,11 @@ export default function ReportCenterPage() { overflow: 'hidden' }; const generationRecord = [ - { name: '电站月报', time: '2025-05-31 08:15', operator: '张运维', type: '发电报表', status: '已生成' }, - { name: '告警周报', time: '2025-05-26 09:10', operator: '系统自动', type: t('report.alertReport'), status: '已生成' }, - { name: 'AI诊断日报', time: '2025-05-31 07:30', operator: '系统自动', type: 'AI分析报告', status: '生成中' }, - { name: '运维汇总报表', time: '2025-05-31 09:00', operator: '李工程师', type: '运维报表', status: '已生成' }, - { name: '清洗效果报告', time: '2025-05-28 10:20', operator: '王运维', type: '运维报表', status: '已生成' }, + { name: t('report.stationMonthly'), time: '2025-05-31 08:15', operator: '张运维', type: t('report.generationReport'), status: t('report.generated') }, + { name: t('report.alertWeekly'), time: '2025-05-26 09:10', operator: t('report.systemAuto'), type: t('report.alertReport'), status: t('report.generated') }, + { name: t('report.aiDiagnosisDaily'), time: '2025-05-31 07:30', operator: t('report.systemAuto'), type: t('report.aiAnalysisReport'), status: t('report.generating') }, + { name: t('report.operationSummary'), time: '2025-05-31 09:00', operator: '李工程师', type: t('report.operationReport'), status: t('report.generated') }, + { name: t('report.cleaningEffectReport'), time: '2025-05-28 10:20', operator: '王运维', type: t('report.operationReport'), status: t('report.generated') }, ]; return ( @@ -205,11 +203,11 @@ export default function ReportCenterPage() { {/* 页面头部标签 */} - - - + + + - +
@@ -270,7 +268,7 @@ export default function ReportCenterPage() { {/* 筛选栏 */}
- 报表周期: + {t('report.reportCycle')}:
- 报表类型: + {t('report.reportType')}:
- 时间范围: + {t('report.dateRange')}: - - + +
@@ -309,12 +307,12 @@ export default function ReportCenterPage() { {/* 左侧主内容区 */}
{/* 月度综合经营分析 */} - 月度综合经营分析}> + {t('report.monthlyOperationAnalysis')}}>
-
发电量 (MWh)
-
预测发电 (MWh)
+
{t('report.generation')} (MWh)
+
{t('report.predictedGeneration')} (MWh)
PR (%)
{[ - { label: '本月累计发电量', value: '138.6', unit: 'MWh' }, - { label: '理论发电量', value: '166.8', unit: 'MWh' }, - { label: 'PR均值', value: '82.8', unit: '%' }, - { label: '发电偏差', value: '-3.4', unit: '%', color: '#F53F3F' }, - { label: '平均辐照度', value: '5.3', unit: 'kWh/㎡/天' }, + { label: t('report.monthlyAccumulatedGeneration'), value: '138.6', unit: 'MWh' }, + { label: t('report.theoreticalGeneration'), value: '166.8', unit: 'MWh' }, + { label: t('report.prAverage'), value: '82.8', unit: '%' }, + { label: t('report.generationDeviation'), value: '-3.4', unit: '%', color: '#F53F3F' }, + { label: t('report.averageIrradiance'), value: '5.3', unit: t('report.unitIrradiancePerDay') }, ].map((item, idx) => (
{item.label} @@ -370,13 +368,13 @@ export default function ReportCenterPage() { bordered={false} style={{ ...cardStyle, height: "100%" }} bodyStyle={{ padding: '16px' }} - title={发电与气象统计} + title={{t('report.generationMeteorology')}} >
- - - + + +
工单与告警闭环分析} + title={{t('report.workOrderAlertClosure')}} >
- + - +
@@ -425,7 +423,7 @@ export default function ReportCenterPage() { bordered={false} style={{ ...cardStyle, height: "100%" }} bodyStyle={{ padding: '16px' }} - title={AI 分析成果统计} + title={{t('report.aiAnalysisStatistics')}} >
- {item.value} 个 + {item.value} {t('report.unitItem')}
))} @@ -483,16 +481,16 @@ export default function ReportCenterPage() { {/* 订阅与分发 */} - 订阅与分发}> + {t('report.subscriptionDispatch')}}>
-
订阅人员 (6)
+
{t('report.subscribers', { count: 6 })}
{[ - { name: '张运维', role: '运维主管', img: 'https://img.icons8.com/fluency/48/user-male-circle.png' }, - { name: '李工程师', role: '电站工程师', img: 'https://img.icons8.com/fluency/48/user-male-circle.png' }, - { name: '王运维', role: '设备运维', img: 'https://img.icons8.com/fluency/48/user-male-circle.png' }, - { name: '陈经理', role: '运维经理', img: 'https://img.icons8.com/fluency/48/user-male-circle.png' }, + { name: '张运维', role: t('report.roleOmSupervisor'), img: 'https://img.icons8.com/fluency/48/user-male-circle.png' }, + { name: '李工程师', role: t('report.roleStationEngineer'), img: 'https://img.icons8.com/fluency/48/user-male-circle.png' }, + { name: '王运维', role: t('report.roleDeviceOm'), img: 'https://img.icons8.com/fluency/48/user-male-circle.png' }, + { name: '陈经理', role: t('report.roleOmManager'), img: 'https://img.icons8.com/fluency/48/user-male-circle.png' }, ].map((p, idx) => ( @@ -503,20 +501,20 @@ export default function ReportCenterPage() {
-
分发渠道 (可多选)
+
{t('report.dispatchChannels')}
- + - + -
发送计划
+
{t('report.sendPlan')}
- 每周一 09:00 自动发送 + {t('report.weeklyAutoSend')}
@@ -528,17 +526,17 @@ export default function ReportCenterPage() { {/* 右侧边栏 */} {/* 报表目录 / 快捷下载 */} - 报表目录 / 快捷下载} extra={}> + {t('report.reportDirectory')}} extra={}>
{text} }, { title: t('roles.status'), dataIndex: 'status', width: 80, - render: (v) => {v} + render: (v) => {v} }, { title: t('roles.actions'), width: 120, @@ -558,7 +556,7 @@ export default function ReportCenterPage() { {/* 模板中心 */} - 模板中心} extra={}> + {t('report.templateCenter')}} extra={}> {reportTemplates.map((tpl, idx) => ( @@ -577,17 +575,17 @@ export default function ReportCenterPage() { {/* 报表生成记录 */} - 报表生成记录} extra={}> + {t('report.generationRecords')}} extra={}>
{v} + render: (v) => {v} }, { title: t('roles.actions'), width: 90, diff --git a/src/pages/Roles.tsx b/src/pages/Roles.tsx index 9c36353..1e53d6f 100644 --- a/src/pages/Roles.tsx +++ b/src/pages/Roles.tsx @@ -132,7 +132,7 @@ export default function Roles() { }; editRoleApi(params).then(res => { if (res.code == 200) { - messageApi.success('编辑角色成功'); + messageApi.success(t('roles.editSuccess')); getRole(); setRoleModalVisible(false); } else { @@ -150,7 +150,7 @@ export default function Roles() { }; addRole(params).then(res => { if (res.code == 200) { - messageApi.success('新增角色成功'); + messageApi.success(t('roles.addSuccess')); getRole(); setRoleModalVisible(false); } else { @@ -176,7 +176,7 @@ export default function Roles() { icon={} onClick={handleAddRole} > - 新增角色 + {t('roles.addRole')} @@ -196,7 +196,7 @@ export default function Roles() { onClick={() => handleEditRole(role)} style={{ color: '#1677ff' }} > - 编辑 + {t('roles.edit')} , , handleDeleteRole(role.roleId)} > ]} @@ -224,13 +224,13 @@ export default function Roles() { title={{role.roleName}} description={ - 权限标识:{role.roleKey} + {t('roles.permissionKey')}:{role.roleKey} - 状态:{role.status == 1 ? t('roles.normal') : t('roles.stopped')} + {t('roles.status')}:{role.status == 1 ? t('roles.normal') : t('roles.stopped')} - {role.admin && '【超级管理员】'} - {role.manager && '【机构管理员】'} + {role.admin && t('roles.superAdmin')} + {role.manager && t('roles.orgAdmin')} } @@ -251,7 +251,7 @@ export default function Roles() { pageSize={pagination.pageSize} total={pagination.total} showSizeChanger - showTotal={(total) => `共 ${total} 条`} + showTotal={(total) => `${t('common.total')} ${total} ${t('common.items')}`} onChange={handlePageChange} style={{ textAlign: 'right' }} /> @@ -259,7 +259,7 @@ export default function Roles() { {/* 新增/编辑弹窗 */} setRoleModalVisible(false)} onOk={handleSaveRole} @@ -268,14 +268,14 @@ export default function Roles() { >
- + - - + + - + @@ -288,7 +288,7 @@ export default function Roles() { - 菜单权限配置 + {t('roles.menuPermissionConfig')} {t('router.systemSetting')} - 告警规则配置、用户角色、通知管理与系统参数设置 + {t('systemSetting.fixed2AlertRulesRoleSettingsFixed2')} */} diff --git a/src/pages/Users.tsx b/src/pages/Users.tsx index 882881b..622635a 100644 --- a/src/pages/Users.tsx +++ b/src/pages/Users.tsx @@ -150,7 +150,7 @@ export default function UserList() { title: t('users.role'), render: (_, record) => { const roles = record.roles || []; - if (roles.length === 0) return 未分配; + if (roles.length === 0) return {t('users.notAssigned')}; return roles.map(item => ( @@ -172,7 +172,7 @@ export default function UserList() { handleDelete(r.userId)}> - + ) @@ -185,7 +185,7 @@ export default function UserList() {
@@ -198,7 +198,7 @@ export default function UserList() { pageSize: pagination.pageSize, total: pagination.total, showSizeChanger: true, - showTotal: t => `共 ${t} 条` + showTotal: (total) => `${t('common.total')} ${total} ${t('common.items')}` }} onChange={handleTableChange} bordered @@ -249,7 +249,7 @@ export default function UserList() {
- @@ -261,7 +261,7 @@ export default function UserList() { {/* 多角色选择 */} - {roles.map(item => ( 事件时间线} - extra={} + title={{t('video.eventTimeline')}} + extra={} bordered={false} style={cardStyle} bodyStyle={{ padding: '16px 20px' }} > {[ - { time: '10:23:41', title: '周界入侵检测', desc: '周界北侧区域检测到翻越围栏事件', color: '#F53F3F' }, - { time: '10:18:09', title: '人员闯入', desc: '设备区检测到人员闯入', color: '#F7BA1E' }, - { time: '10:05:33', title: '设备区域滞留', desc: '逆变器区人员滞留超过5分钟', color: '#F7BA1E' }, - { time: '09:52:17', title: '运动检测', desc: '气象站区域检测到异常运动', color: '#165DFF' }, - { time: '09:41:28', title: '周界入侵检测', desc: '周界南侧区域检测到攀爬事件', color: '#F53F3F' }, + { time: '10:23:41', title: t('video.detectFixed2'), desc: '周界北侧区域检测到翻越围栏事件', color: '#F53F3F' }, + { time: '10:18:09', title: t('video.s30'), desc: '设备区检测到人员闯入', color: '#F7BA1E' }, + { time: '10:05:33', title: t('video.fixed2DeviceFixed2Area'), desc: '逆变器区人员滞留超过5分钟', color: '#F7BA1E' }, + { time: '09:52:17', title: t('aiAnalysis.motionDetection'), desc: '气象站区域检测到异常运动', color: '#165DFF' }, + { time: '09:41:28', title: t('video.detectFixed2'), desc: '周界南侧区域检测到攀爬事件', color: '#F53F3F' }, ].map((item, idx) => ( {item.time}} dot={
}>
@@ -349,25 +348,25 @@ export default function VideoMonitorPage() { ))}
- +
最近告警快照} - extra={} + title={{t('video.alert')}} + extra={} bordered={false} style={cardStyle} bodyStyle={{ padding: '16px 20px' }} >
{[ - { title: '周界北侧', time: '10:23:41', type: '周界入侵', img: 'https://images.unsplash.com/photo-1504384308090-c894fdcc538d?auto=format&fit=crop&w=300&q=60', boxColor: '#F53F3F' }, - { title: '设备区', time: '10:18:09', type: '人员闯入', img: 'https://images.unsplash.com/photo-1581092160562-40aa08e78837?auto=format&fit=crop&w=300&q=60', boxColor: '#F7BA1E' }, - { title: '逆变器室', time: '10:05:33', type: '设备区域滞留', img: 'https://images.unsplash.com/photo-1581094794329-c8112a89af12?auto=format&fit=crop&w=300&q=60', boxColor: '#F7BA1E' }, - //{ title: '气象站', time: '09:52:17', type: '运动检测', img: 'https://images.unsplash.com/photo-1534088568595-a066f7104211?auto=format&fit=crop&w=300&q=60', boxColor: '#165DFF' }, + { title: t('video.s34'), time: '10:23:41', type: '周界入侵', img: 'https://images.unsplash.com/photo-1504384308090-c894fdcc538d?auto=format&fit=crop&w=300&q=60', boxColor: '#F53F3F' }, + { title: t('video.fixed2Device'), time: '10:18:09', type: '人员闯入', img: 'https://images.unsplash.com/photo-1581092160562-40aa08e78837?auto=format&fit=crop&w=300&q=60', boxColor: '#F7BA1E' }, + { title: t('video.fixed2Inverter'), time: '10:05:33', type: '设备区域滞留', img: 'https://images.unsplash.com/photo-1581094794329-c8112a89af12?auto=format&fit=crop&w=300&q=60', boxColor: '#F7BA1E' }, + //{ title: t('video.s0'), time: '09:52:17', type: '运动检测', img: 'https://images.unsplash.com/photo-1534088568595-a066f7104211?auto=format&fit=crop&w=300&q=60', boxColor: '#165DFF' }, ].map((item, idx) => (
@@ -384,7 +383,7 @@ export default function VideoMonitorPage() {
))} diff --git a/src/pages/WorkOrder.tsx b/src/pages/WorkOrder.tsx index 535a68e..e3a8538 100644 --- a/src/pages/WorkOrder.tsx +++ b/src/pages/WorkOrder.tsx @@ -57,12 +57,6 @@ const { Text, Title } = Typography; const { RangePicker } = DatePicker; const FormItem = Form.Item; -// 优先级数字映射 1高 2中 3低 -const priorityConfig = { - ERROR: { label: '高', color: '#ff4d4f', bg: '#fff1f0', border: '#ff4d4f' }, - WARNING: { label: '中', color: '#faad14', bg: '#fff7e6', border: '#faad14' }, - INFO: { label: '低', color: '#52c41a', bg: '#f6ffed', border: '#52c41a' } -}; @@ -104,9 +98,9 @@ const WorkOrderPage = () => { // 优先级选项 const priorityOptions = [ - { value: 1, label: '高' }, - { value: 2, label: '中' }, - { value: 3, label: '低' } + { value: 1, label: t('workOrder.high') }, + { value: 2, label: t('workOrder.medium') }, + { value: 3, label: t('workOrder.low') } ]; const orderStatusConfig = { 1: { label: t('constants.pending'), textColor: '#1890ff' }, @@ -129,6 +123,13 @@ const WorkOrderPage = () => { current: 1, pageSize: 10, }); + // 优先级数字映射 1高 2中 3低 + const priorityConfig = { + ERROR: { label: t('workOrder.high'), color: '#ff4d4f', bg: '#fff1f0', border: '#ff4d4f' }, + WARNING: { label: t('workOrder.medium'), color: '#faad14', bg: '#fff7e6', border: '#faad14' }, + INFO: { label: t('workOrder.low'), color: '#52c41a', bg: '#f6ffed', border: '#52c41a' } + }; + @@ -239,14 +240,14 @@ const WorkOrderPage = () => { }; const res = await dispatchWorkOrder(params); if (res.code === 200) { - messageApi.success('工单派单成功'); + messageApi.success(t('workOrder.fixed2DispatchOrderFixed2SuccessFixed2')); setDispatchUserModalVisible(false); dispatchForm.resetFields(); } else { messageApi.error(res.msg || '派单失败'); } } catch (err) { - messageApi.error('派单请求异常'); + messageApi.error(t('workOrder.fixed2DispatchAbnormalFixed2')); console.error(err); } }; @@ -417,7 +418,7 @@ const WorkOrderPage = () => { const stats = [ { - title: '今日新增工单', + title: t('workOrder.fixed2TodayRecordingsFixed2AddNew'), value: orderStat.todayAdd, subValue: orderStat.todayAddDiff, subText: '较昨日', @@ -425,7 +426,7 @@ const WorkOrderPage = () => { bg: '#e6f7ff' }, { - title: '待处理工单', + title: t('workOrder.pendingOrders'), value: orderStat.pending, subValue: orderStat.pendingHigh, subText: '高优先级', @@ -433,7 +434,7 @@ const WorkOrderPage = () => { bg: '#fff1f0' }, { - title: '执行中工单', + title: t('workOrder.fixed2InProgress'), value: orderStat.running, subValue: `${orderStat.finishRate}%`, subText: '按时率', @@ -441,7 +442,7 @@ const WorkOrderPage = () => { bg: '#f6ffed' }, { - title: '已完成工单', + title: t('workOrder.completedOrders'), value: orderStat.finishedMonth, subValue: '', subText: '本月累计', @@ -449,7 +450,7 @@ const WorkOrderPage = () => { bg: '#f6ffed' }, { - title: '平均处理时长', + title: t('workOrder.handle'), value: orderStat.avgHandleHour, unit: '小时', subValue: orderStat.avgDiff, @@ -458,7 +459,7 @@ const WorkOrderPage = () => { bg: '#e6f7ff' }, { - title: 'AI建议生成', + title: t('workOrder.fixed2Ai'), value: orderStat.aiCreate, unit: '单', subValue: '', @@ -511,7 +512,7 @@ const WorkOrderPage = () => { } } } catch (error) { - messageApi.error('获取工单列表失败'); + messageApi.error(t('workOrder.listFixed2Fixed2FailedFixed2')); } finally { setLoading(false); } @@ -588,7 +589,7 @@ const WorkOrderPage = () => { }; // 打开完成工单弹窗 const openCompleteModal = () => { - if (!selectedRowKey) return messageApi.warning('请先选择工单'); + if (!selectedRowKey) return messageApi.warning(t('workOrder.s3')); completeForm.setFieldsValue({ id: currentDetail?.id, orderNo: currentDetail?.orderNo, @@ -640,12 +641,12 @@ const WorkOrderPage = () => { // 4. 请求方法 completeWorkOrder 必须修改:传 formData,删除 headers 的 Content-Type await completeWorkOrder(formData); - messageApi.success('工单已完成'); + messageApi.success(t('workOrder.completedFixed2')); setCompleteModalVisible(false); getWorkOrderList(); getWorkOrderDetail(selectedRowKey); } catch (err) { - messageApi.error('工单完成失败'); + messageApi.error(t('workOrder.completeFailFixed2')); console.error(err); } }; @@ -659,7 +660,7 @@ const WorkOrderPage = () => { setCurrentDetail(res.data); } } catch (error) { - messageApi.error('获取工单详情失败'); + messageApi.error(t('workOrder.detailFixed2Fixed2FailedFixed2')); } }; @@ -676,7 +677,7 @@ const WorkOrderPage = () => { // 打开修改工单弹窗 const openEditModal = () => { - if (!selectedRowKey) return messageApi.warning('请先选择一条工单'); + if (!selectedRowKey) return messageApi.warning(t('workOrder.s7')); const levelText = levelNum2Text[currentDetail?.priorityLevel]; //数字转字符串 form.setFieldsValue({ orderTitle: currentDetail?.orderTitle, @@ -700,7 +701,7 @@ const WorkOrderPage = () => { orderIds: [selectedRowKey] }); } else { - return messageApi.warning('请先选择一条工单'); + return messageApi.warning(t('workOrder.s7')); } setDispatchModalVisible(true); @@ -719,12 +720,12 @@ const WorkOrderPage = () => { taskDescription: values.taskDescription, }; await updateWorkOrder(params); - messageApi.success('修改工单成功'); + messageApi.success(t('workOrder.fixed2ModifyOrderFixed2SuccessFixed2')); setEditModalVisible(false); getWorkOrderList(); getWorkOrderDetail(selectedRowKey); } catch (error) { - messageApi.error('修改工单失败'); + messageApi.error(t('workOrder.fixed2ModifyOrderFixed2FailedFixed2')); console.error(error); } }; @@ -742,7 +743,7 @@ const WorkOrderPage = () => { }; const res = await startWorkOrder(params); if (res.code == 200) { - messageApi.success('工单执行成功'); + messageApi.success(t('workOrder.executeSuccessFixed2')); setDispatchModalVisible(false); setSelectedRowKeys([]); // 清空选中 getWorkOrderList(); @@ -751,26 +752,26 @@ const WorkOrderPage = () => { } } catch (error) { - messageApi.error('工单执行失败'); + messageApi.error(t('workOrder.executeFailFixed2')); console.error(error); } }; // 挂起工单 const handleSuspend = async () => { - if (!selectedRowKey) return messageApi.warning('请选择工单'); + if (!selectedRowKey) return messageApi.warning(t('workOrder.pleaseSelectOrder')); try { await suspendWorkOrder(selectedRowKey); - messageApi.success('工单已挂起'); + messageApi.success(t('workOrder.suspendedFixed2')); getWorkOrderList(); } catch (e) { - messageApi.error('挂起失败'); + messageApi.error(t('workOrder.fixed2SuspendFixed2FailedFixed2')); } }; // 删除单行 const handleDelete = async (record) => { - if (!record?.id) return messageApi.warning('无效工单'); + if (!record?.id) return messageApi.warning(t('workOrder.invalidOrder')); try { await deleteWorkOrder(record.id); messageApi.success(t('common.deleteSuccess')); @@ -784,7 +785,7 @@ const WorkOrderPage = () => { // 批量删除 // 批量删除 - 传给后端 id数组 const handleBatchDelete = async () => { - if (selectedRowKeys.length === 0) return messageApi.warning('请选择要删除的工单'); + if (selectedRowKeys.length === 0) return messageApi.warning(t('workOrder.fixed2PleaseSelectDelete')); try { await deleteWorkOrder(selectedRowKeys); @@ -792,7 +793,7 @@ const WorkOrderPage = () => { setSelectedRowKeys([]); // 清空选中 getWorkOrderList(); // 刷新列表 } catch (e) { - messageApi.error('批量删除失败'); + messageApi.error(t('workOrder.batchDeleteFail')); console.error(e); } }; @@ -821,7 +822,7 @@ const WorkOrderPage = () => { // 表格列 const columns = [ - { title: '工单编号', dataIndex: 'orderNo', key: 'orderNo', width: 150 }, + { title: t('workOrder.orderNo'), dataIndex: 'orderNo', key: 'orderNo', width: 150 }, { title: t('workOrder.orderTitle'), dataIndex: 'orderTitle', key: 'orderTitle', width: 200, ellipsis: true }, { title: t('workOrder.source'), @@ -848,7 +849,7 @@ const WorkOrderPage = () => { ) }, - { title: '设备', dataIndex: 'deviceId', key: 'deviceId', width: 160, ellipsis: true }, + { title: t('devices.device'), dataIndex: 'deviceId', key: 'deviceId', width: 160, ellipsis: true }, { title: t('common.createTime'), dataIndex: 'createTime', key: 'createTime', width: 160 }, { title: t('roles.status'), @@ -872,9 +873,7 @@ const WorkOrderPage = () => { handleDelete(record)}> - + ) @@ -906,9 +905,9 @@ const WorkOrderPage = () => { {/* 顶部标题和页签 */}
- 工单任务管理 + {t('workOrder.fixed2WorkOrders')}
@@ -953,7 +952,7 @@ const WorkOrderPage = () => { - - } size="small" /> + + } size="small" /> @@ -980,7 +979,7 @@ const WorkOrderPage = () => { bordered={false} title={ - 工单列表 + {t('workOrder.listFixed2')} (共 {workOrderTotal} 条) {selectedRowKeys.length > 0 && ( <> @@ -988,9 +987,7 @@ const WorkOrderPage = () => { title={`确定删除已选中的 ${selectedRowKeys.length} 条工单吗?`} onConfirm={handleBatchDelete} > - +
- 工单状态分布} style={{ ...cardStyle, height: 380 }}> + {t('workOrder.fixed2OrderStatus')}} style={{ ...cardStyle, height: 380 }}>
{ height={260} />
-
总数
+
{t('workOrder.s5')}
{workOrderTotal}
@@ -1063,14 +1060,13 @@ const WorkOrderPage = () => {
任务排班 / 日历视图} + title={{t('workOrder.fixed2TaskSuspended2')}} extra={ } style={{ ...cardStyle, height: 380 }} @@ -1170,10 +1166,10 @@ const WorkOrderPage = () => { rowKey="id" size="small" columns={[ - { title: '工单编号', dataIndex: 'orderNo', width: 260 }, + { title: t('workOrder.orderNo'), dataIndex: 'orderNo', width: 260 }, { title: t('workOrder.orderTitle'), dataIndex: 'orderTitle', ellipsis: true }, { - title: '计划执行时间', + title: t('workOrder.executionTimeFixed2'), dataIndex: 'planStartTime', render: t => dayjs(t).format('YYYY-MM-DD HH:mm') }, @@ -1212,8 +1208,8 @@ const WorkOrderPage = () => {
- 工单详情 - 工单编号: {currentDetail?.orderNo || '--'} + {t('workOrder.detailFixed2')} + {t('workOrder.fixed2OrderNo')}{currentDetail?.orderNo || '--'}
@@ -1228,35 +1224,35 @@ const WorkOrderPage = () => {
- 来源: + {t('workOrder.fixed2Source')}{sourceTypeMap[currentDetail?.sourceType] || '--'}- 工单类型: + {t('workOrder.fixed2OrderType')}{getWorkOrderTypeText(currentDetail?.orderType)}- 执行开始时间: + {t('workOrder.fixed2ExecutionStartTime')} {currentDetail?.actualStartTime || '--'} - 关联设备: + {t('workOrder.fixed2RelatedDevice')}{currentDetail?.deviceName || '无'}- 关联区域: + {t('workOrder.fixed2RelatedArea')}{currentDetail?.siteName || '无'}- 当前状态: + {t('workOrder.fixed2CurrentStatus')} {orderStatusConfig[currentDetail?.orderStatus]?.label || '--'} - 任务描述: + {t('workOrder.fixed2TaskDescription')}
{getRelatedAlarmInfo(currentDetail?.alarmId)} @@ -1269,7 +1265,7 @@ const WorkOrderPage = () => { (currentDetail?.videoUrl && currentDetail.videoUrl.length > 0) ) && (
-
附件素材:
+
{t('workOrder.fixed2Attachments')}
{/* 图片预览组,支持点击放大 */} {currentDetail?.imgUrl?.length > 0 && ( @@ -1431,9 +1427,7 @@ const WorkOrderPage = () => { onClick={openExecuteModal} style={{ borderRadius: 8, height: 36, fontWeight: 600 }} icon={} - > - 执行工单 - + >{t('workOrder.execute')}
@@ -1524,16 +1518,14 @@ const WorkOrderPage = () => {
-
关联告警列表 (近 3 条)
+
{t('workOrder.relatedAlertList')}(近 3 条)
{weekAlarmList.length > 3 && ( + >{t('common.viewAll')} )}
{latestThreeAlarm.length === 0 ? ( @@ -1610,8 +1602,8 @@ const WorkOrderPage = () => { - +
{ rules={[{ required: true, message: t('workOrder.pleaseSelectDispatcher') }]} > - + +
{ > {/* { */} @@ -1780,13 +1772,13 @@ const WorkOrderPage = () => { setCompleteModalVisible(false)} footer={null} width={500}> - - - + + - + @@ -1811,12 +1803,12 @@ const WorkOrderPage = () => { })()} columns={[ { - title: '告警编号', + title: t('alerts.alarmNo'), dataIndex: 'alarmNo', width: 140 }, { - title: '告警标题', + title: t('alerts.alarmTitle'), dataIndex: 'alarmTitle', ellipsis: true }, diff --git a/src/router/index.tsx b/src/router/index.tsx index 4cec377..d74be6c 100644 --- a/src/router/index.tsx +++ b/src/router/index.tsx @@ -59,34 +59,36 @@ function PageFallback() { ); } -export const routeMeta: Record = { - '/': { title: 'router.overview', roles: ['admin', 'operator', 'viewer'], requiresAuth: true }, - '/overview': { title: 'router.overview', roles: ['admin', 'operator', 'viewer'], requiresAuth: true }, - '/devices': { title: 'router.devices', roles: ['admin', 'operator'], requiresAuth: true }, - '/video': { title: 'router.video', roles: ['admin', 'operator', 'viewer'], requiresAuth: true }, - '/monitor': { title: 'router.monitor', roles: ['admin', 'operator', 'viewer'], requiresAuth: true }, - '/alerts': { title: 'router.alerts', roles: ['admin', 'operator'], requiresAuth: true }, - '/AIanalysis': { title: 'router.aiAnalysis', roles: ['admin', 'operator'], requiresAuth: true }, - '/clean': { title: 'router.clean', roles: ['admin', 'operator'], requiresAuth: true }, - '/task': { title: 'router.task', roles: ['admin', 'operator'], requiresAuth: true }, - '/table': { title: 'router.table', roles: ['admin', 'operator'], requiresAuth: true }, - '/systemSetting': { title: 'router.systemSetting', roles: ['admin'], requiresAuth: true }, - '/systemSetting/log': { title: 'router.log', roles: ['admin'], requiresAuth: true }, - '/systemSetting/videoManage': { title: 'router.videoManage', roles: ['admin'], requiresAuth: true }, - '/downloads': { title: 'router.downloads', roles: ['admin', 'operator'], requiresAuth: true }, - '/manage/users': { title: 'router.users', roles: ['admin'], requiresAuth: true }, - '/manage/roles': { title: 'router.roles', roles: ['admin'], requiresAuth: true }, - '/manage/menuList': { title: 'router.menuList', roles: ['admin'], requiresAuth: true }, - '/organization': { title: 'router.organization', roles: ['admin'], requiresAuth: true }, - '/station': { title: 'router.station', roles: ['admin'], requiresAuth: true }, - '/RealtimeMonitor': { title: 'router.monitor', roles: ['admin', 'operator', 'viewer'], requiresAuth: true }, - '/profile': { title: 'router.profile', roles: ['admin', 'operator', 'viewer'], requiresAuth: true }, +// 占位页(国际化) +const NoticePolicyPlaceholder = () => { + const { t } = useTranslation(); + return ( +
+

{t('router.noticePolicy')}

+
+ ); }; +const DataSafetyPlaceholder = () => { + const { t } = useTranslation(); + return ( +
+

{t('router.dataSafety')}

+
+ ); +}; + +const ModuleDeveloping = () => { + const { t } = useTranslation(); + return ( +
+ 🚧 +

{t('router.moduleDeveloping')}

+
+ ); +}; + + // ====================== 全部懒加载的路由配置 ====================== const routes: RouteObject[] = [ { @@ -245,9 +247,9 @@ const routes: RouteObject[] = [ { path: 'menu', element: }> }, { path: 'org', element: }> }, { path: 'site', element: }> }, - { path: 'notice', element: }>

通知策略页面

}, + { path: 'notice', element: }> }, { path: 'deviceAccess', element: }> }, - { path: 'dataSafety', element: }>

数据与安全页面

}, + { path: 'dataSafety', element: }> }, { path: 'systemMaintenance', element: }> }, { path: 'monitor', element: }> }, { path: 'devices', element: }> }, @@ -321,12 +323,7 @@ const routes: RouteObject[] = [ }, { path: '*', - element: ( -
- 🚧 -

模块开发中

-
- ), + element: , }, ], }, diff --git a/src/types/index.ts b/src/types/index.ts index 4d7cb48..21e64e7 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,5 +1,5 @@ // ============================================================ -// 光伏电站智能监控与运维系统 - 全局类型定义 +// 光伏机器人环境感知服务平台 - 全局类型定义 // ============================================================ // ------------------- 通用 ------------------- diff --git a/tmp_additions.json b/tmp_additions.json new file mode 100644 index 0000000..23d13ea --- /dev/null +++ b/tmp_additions.json @@ -0,0 +1,4692 @@ +{ + "zhAdd": { + "alerts": { + "processing": "处理中", + "alarmNo": "告警编号", + "alarmTitle": "告警title", + "alarmContent": "告警content", + "alertDetail": "告警detail", + "rootCause": "rootcause", + "recommendedAction": "recommendedaction", + "notifySubscription": "notify订阅", + "sms": "短信", + "dingTalk": "dingtalk", + "email": "邮件", + "handleSuccess": "处理成功", + "handleFail": "处理失败", + "exportFileName": "导出file名称", + "reportValue": "reportvalue", + "noAlarmToCreateOrder": "编号告警TO创建工单", + "orderFromAlarm": "工单from告警", + "orderCreated": "工单已创建", + "orderCreateFail": "工单创建失败", + "todayTotalAlerts": "今日共告警", + "trendNoChange": "趋势编号change", + "trendUp": "趋势UP", + "trendDown": "趋势down", + "trendZero": "趋势zero", + "yesterdayValue": "昨日value", + "todayCriticalAlerts": "今日严重告警", + "todayPending": "今日待处理", + "todayClosed": "今日已关闭", + "alertTypeProportion": "告警类型proportion", + "alertStatusStatistics": "告警状态统计", + "sourceDevice": "来源设备", + "occurTime": "occur时间", + "alertDescription": "告警description", + "contactHandler": "联系处理人", + "handleOperation": "处理operation", + "pleaseSelectHandleStatus": "please选择处理状态", + "pleaseSelectHandleResult": "please选择处理结果", + "pleaseInputHandleRemark": "pleaseinput处理remark", + "confirmHandle": "确认处理", + "modifyAlert": "modify告警", + "pleaseInputAlarmTitle": "pleaseinput告警title", + "pleaseSelectAlertLevel": "please选择告警级别", + "pleaseSelectAlertType": "please选择告警类型", + "pleaseInputAlarmContent": "pleaseinput告警content", + "newOrder": "NEW工单", + "planStartTime": "计划开始时间", + "pleaseSelectPlanStartTime": "please选择计划开始时间", + "confirmCreateOrder": "确认创建工单", + "mockDeviceSensor": "模拟设备传感器", + "mockTempExceed": "模拟温度exceed", + "mockDeviceCamera": "模拟设备摄像头", + "mockVideoSignalLost": "模拟视频signallost", + "mockDeviceBattery": "模拟设备电量", + "mockBatteryLow": "模拟电量LOW", + "level": "级别", + "content": "content", + "searchDeviceContent": "搜索设备content", + "mockRuleTemp": "模拟规则温度", + "mockRuleBattery": "模拟规则电量", + "deviceSensor": "设备传感器", + "deviceBattery": "设备电量", + "ruleName": "规则名称", + "triggerCondition": "triggercondition", + "ruleConfig": "规则配置", + "addRule": "新增规则", + "editRule": "编辑规则", + "ruleNamePlaceholder": "规则名称placeholder", + "relatedModel": "relatedmodel", + "monitorProperty": "monitorproperty", + "compareSymbol": "comparesymbol", + "threshold": "threshold", + "thresholdPlaceholder": "thresholdplaceholder", + "sevenDayTrend": "seven日趋势", + "criticalAlert": "严重告警", + "importantAlert": "重要告警", + "generalAlert": "一般告警", + "infoAlert": "提示告警", + "alertLevelDistribution": "告警级别分布", + "mockSubOps": "模拟SUBOPS", + "mockSubAdmin": "模拟SUBadmin", + "mockSubDevice": "模拟SUB设备", + "appPush": "APPpush", + "subscriptionName": "订阅名称", + "notifyChannel": "notifychannel", + "searchSubscription": "搜索订阅", + "addSubscription": "新增订阅", + "editAlertSubscription": "编辑告警订阅", + "addAlertSubscription": "新增告警订阅", + "pleaseInputSubscriptionName": "pleaseinput订阅名称", + "allDevices": "ALL设备", + "allLevels": "ALLlevels", + "pleaseSelectChannel": "please选择channel", + "subtitle": "subtitle", + "AlarmHistory_14": "时间范围", + "AlarmHistory_15": "右侧详情抽屉", + "AlarmHistory_16": "通知订阅模块", + "AlarmOverview_17": "刷新列表", + "AlarmOverview_18": "刷新统计", + "AlarmOverview_25": "修复:等级是字符串,不是数字", + "AlarmOverview_26": "统计卡片", + "AlarmOverview_27": "只清除首尾空格", + "AlarmOverview_28": "新增:刷新图表全量告警", + "AlarmOverview_29": "表格", + "AlarmOverview_30": "三个图表", + "AlarmOverview_31": "告警详情抽屉", + "AlarmOverview_32": "告警头部", + "AlarmOverview_33": "告警基础信息", + "AlarmOverview_34": "告警描述", + "AlarmOverview_35": "根因分析", + "AlarmOverview_36": "推荐处理措施", + "AlarmOverview_37": "处理操作", + "AlarmOverview_38": "底部按钮", + "AlarmOverview_39": "修改告警弹窗", + "AlarmOverview_40": "新增:生成工单弹窗", + "AlarmRealtime_41": "告警详情弹窗", + "AlarmSubscription_42": "短信", + "AlarmSubscription_43": "邮件", + "AlarmSubscription_44": "推送", + "AlarmSubscription_45": "新增/编辑 弹窗" + }, + "common": { + "loadFailed": "load失败", + "loadDetailFailed": "loaddetail失败", + "requestError": "请求错误", + "deviceId": "设备ID", + "pleaseInputDeviceId": "pleaseinput设备ID", + "timeRange": "时间range", + "enabled": "enabled", + "modify": "modify", + "apiCallFail": "APIcall失败", + "quantity": "quantity", + "detail": "detail", + "storeValue": "storevalue", + "operationFailed": "operation失败", + "submitFail": "submit失败", + "confirmDeleteRecord": "确认删除record", + "deleteIrreversible": "删除irreversible", + "working": "作业中", + "settings": "设置" + }, + "workOrder": { + "generateOrder": "生成工单", + "newOrders": "NEWorders", + "dispatchSuccess": "dispatch成功", + "dispatchFail": "dispatch失败", + "dispatchRequestError": "dispatch请求错误", + "todayNewOrders": "今日NEWorders", + "vsYesterday": "较昨日", + "pendingOrders": "待处理orders", + "highPriority": "highpriority", + "runningOrders": "runningorders", + "onTimeRate": "ON时间rate", + "completedOrders": "已完成orders", + "monthlyTotal": "monthly共", + "avgHandleTime": "AVG处理时间", + "hourUnit": "hour单位", + "vsLastWeek": "较last周", + "aiSuggestedOrders": "AIsuggestedorders", + "orderUnit": "工单单位", + "autoGeneratedToday": "autogenerated今日", + "getListFailed": "GET列表失败", + "pleaseSelectOrder": "please选择工单", + "completeSuccess": "完成成功", + "completeFail": "完成失败", + "getDetailFailed": "GETdetail失败", + "pleaseSelectOneOrder": "please选择ONE工单", + "modifySuccess": "modify成功", + "modifyFail": "modify失败", + "executeSuccess": "执行成功", + "executeFail": "执行失败", + "suspendSuccess": "suspend成功", + "suspendFail": "suspend失败", + "invalidOrder": "无效工单", + "pleaseSelectOrderToDelete": "please选择工单TO删除", + "batchDeleteSuccess": "batch删除成功", + "batchDeleteFail": "batch删除失败", + "orderNo": "工单编号", + "taskManagement": "任务管理", + "overview": "概览", + "timeRange": "时间range", + "keyword": "keyword", + "pleaseInputOrderNoOrTitle": "pleaseinput工单编号ORtitle", + "list": "列表", + "totalWithCount": "共with数量", + "confirmBatchDelete": "确认batch删除", + "batchExecute": "batch执行", + "statusDistribution": "状态分布", + "totalCount": "共数量", + "scheduleCalendarView": "schedulecalendar查看", + "todaySuffix": "今日suffix", + "noScheduledOrders": "编号scheduledorders", + "scheduledOrderList": "scheduled工单列表", + "planStartTime": "计划开始时间", + "detail": "detail", + "noTitle": "编号title", + "nearItems": "nearitems", + "pleaseInputTaskDesc": "pleaseinput任务desc", + "pendingDispatch": "待处理dispatch", + "pleaseSelectPerson": "please选择person", + "selectedOrders": "已选orders", + "pleaseSelectExecuteDevice": "please选择执行设备", + "pleaseInputResult": "pleaseinput结果", + "pleaseSelectResult": "please选择结果", + "handledCompleted": "已处理已完成", + "fixed": "固定", + "noActionNeeded": "编号actionneeded", + "issueClosed": "issue已关闭", + "pleaseInputHandleRemark": "pleaseinput处理remark", + "alarmNo": "告警编号", + "alarmTitle": "告警title", + "WorkOrder_1275": "批量选中", + "WorkOrder_1276": "今日新增工单", + "WorkOrder_1277": "待处理总数", + "WorkOrder_1278": "待处理高优先级", + "WorkOrder_1280": "本月已完成", + "WorkOrder_1281": "按时完成率", + "WorkOrder_1282": "平均处理时长", + "WorkOrder_1283": "今日新增对比昨日", + "WorkOrder_1284": "平均时长对比上周", + "WorkOrder_1285": "普通用户", + "WorkOrder_1286": "选中人员", + "WorkOrder_1287": "如需真实昨日对比,单独请求昨日统计接口", + "WorkOrder_1288": "已处理完成", + "WorkOrder_1289": "数字转字符串", + "WorkOrder_1290": "清空选中", + "WorkOrder_1291": "新增加载7天告警", + "WorkOrder_1292": "顶部标题和页签", + "WorkOrder_1293": "左侧:列表", + "WorkOrder_1294": "底部图表", + "WorkOrder_1295": "日期切换栏", + "WorkOrder_1296": "横向时间表头", + "WorkOrder_1297": "单一行时间画布,不再区分人员", + "WorkOrder_1298": "更多工单弹窗:弹窗内只展示当前选中日期工单", + "WorkOrder_1299": "右侧:详情", + "WorkOrder_1300": "新增:图片", + "WorkOrder_1301": "视频附件预览区域", + "WorkOrder_1302": "图片预览组,支持点击放大", + "WorkOrder_1303": "底层灰色轨道", + "WorkOrder_1304": "进度填充", + "WorkOrder_1305": "修改工单弹窗", + "WorkOrder_1306": "派单弹窗:选择人员", + "WorkOrder_1307": "工单", + "WorkOrder_1308": "执行工单弹窗", + "WorkOrder_1309": "未知设备", + "WorkOrder_1310": "机器人设备分组", + "WorkOrder_1311": "无人机分组", + "WorkOrder_1312": "请选择执行开始时间", + "WorkOrder_1313": "完成工单弹窗", + "WorkOrder_1314": "已修复", + "WorkOrder_1315": "无需处理", + "WorkOrder_1316": "问题已闭环", + "WorkOrder_1317": "近7天全部告警弹窗" + }, + "devices": { + "working": "作业中", + "videoConfigured": "视频已配置", + "noVideo": "编号视频", + "connectionFailed": "连接失败", + "permissionRequestTitle": "权限请求title", + "requestControlPermission": "请求控制权限", + "selectNoDrone": "选择编号无人机", + "commandIssued": "commandissued", + "commandFailed": "command失败", + "apiRequestError": "API请求错误", + "selectTaskFirst": "选择任务first", + "taskExecutedSuccess": "任务executed成功", + "taskSuspended": "任务suspended", + "taskResumedSuccess": "任务resumed成功", + "taskReturnedHome": "任务returnedhome", + "taskStatusUpdateSuccess": "任务状态更新成功", + "operationApiError": "operationAPI错误", + "permissionObtained": "权限obtained", + "controlRequestContent": "控制请求content", + "unknownPlatform": "未知platform", + "obstacleDetected": "obstacle检测到", + "unknownObject": "未知object", + "noTaskDetail": "编号任务detail", + "fetchDetailError": "获取detail错误", + "deviceInfoError": "设备提示错误", + "pleaseLoginFirst": "please登录first", + "loginInfoExpired": "登录提示expired", + "otherWebControlling": "otherWEBcontrolling", + "networkErrorRetry": "network错误retry", + "routeLoaded": "路线loaded", + "droneFallbackName": "无人机fallback名称", + "droneAirspace": "无人机airspace", + "droneWayline": "无人机航线", + "onStandby": "ON待机", + "permissionRequesting": "权限requesting", + "noWaylineData": "编号航线数据", + "fetchWaylineFailed": "获取航线失败", + "waypointsAdded": "waypointsadded", + "waypointDeleted": "waypointdeleted", + "allWaypointsCleared": "ALLwaypointscleared", + "pleaseSelectWayline": "please选择航线", + "selectTaskStartTime": "选择任务开始时间", + "createSuccess": "创建成功", + "apiErrorCheck": "API错误check", + "noWaypointsToSave": "编号waypointsTO保存", + "planeNoLocalRoute": "plane编号local路线", + "routeSavedLocal": "路线savedlocal", + "waylineLabel": "航线label", + "fetchWaylineListFailed": "获取航线列表失败", + "routeLoadedWithName": "路线loadedwith名称", + "gpsTaskIssued": "GPS任务issued", + "issueTaskFirst": "issue任务first", + "taskPaused": "任务paused", + "taskStarted": "任务started", + "taskStopped": "任务停用", + "viewFront": "查看front", + "viewBack": "查看back", + "viewLeft": "查看左", + "viewRight": "查看右", + "viewTop": "查看TOP", + "deviceList": "设备列表", + "onlineDevices": "在线设备", + "inspectionMachine": "巡检machine", + "supportsGpsPlan": "supportsGPS计划", + "supportsRemoteGpsPlan": "supportsremoteGPS计划", + "batteryWithValue": "电量withvalue", + "videoLinkConfigured": "视频link已配置", + "batteryCapacity": "电量容量", + "flightAltitude": "flight高度", + "signalSatellite": "signalsatellite", + "envWindSpeed": "ENVwind速度", + "rtkStatus": "RTK状态", + "fixed": "固定", + "notFixed": "NOT固定", + "remoteControlAndSituation": "remote控制ANDsituation", + "gpsPlanning": "GPSplanning", + "leftWheelDrive": "左wheeldrive", + "normalRunning": "正常running", + "realtimeSpeed": "实时速度", + "workingCurrent": "作业中current", + "rightWheelDrive": "右wheeldrive", + "gpsWaypointPlan": "GPSwaypoint计划", + "savePlan": "保存计划", + "clearRoute": "clear路线", + "droneWaylineTask": "无人机航线任务", + "taskWaitingStart": "任务waiting开始", + "taskStartFailure": "任务开始failure", + "taskTerminated": "任务terminated", + "taskTimeout": "任务timeout", + "noWaylineTask": "编号航线任务", + "robotTaskData": "机器人任务数据", + "waypointList": "waypoint列表", + "noWaypoints": "编号waypoints", + "waypoint": "waypoint", + "robotLocalRoutes": "机器人local路线", + "noSavedRoutes": "编号saved路线", + "waypointsCount": "waypoints数量", + "issueTask": "issue任务", + "suspendTask": "suspend任务", + "cancelReturnHome": "取消返航home", + "videoAndConfig": "视频AND配置", + "noVideoStream": "编号视频stream", + "waylineDetail": "航线detail", + "taskIdLabel": "任务IDlabel", + "waylineUuidLabel": "航线uuidlabel", + "airportSnLabel": "airportSNlabel", + "landingDockSnLabel": "landingdockSNlabel", + "returnHomeAltitudeLabel": "返航home高度label", + "returnHomeModeLabel": "返航homemodelabel", + "waylinePrecisionLabel": "航线precisionlabel", + "lostControlActionLabel": "lost控制actionlabel", + "resumeFromBreakpointLabel": "恢复frombreakpointlabel", + "taskTypeLabel": "任务类型label", + "repeatModeLabel": "repeatmodelabel", + "minBatteryLabel": "MIN电量label", + "currentWaypointLabel": "currentwaypointlabel", + "rthOptimal": "RTHoptimal", + "rthPreset": "RTH预设", + "gpsMode": "GPSmode", + "rtkHighPrecision": "RTKhighprecision", + "modeAuto": "modeauto", + "modeManual": "modemanual", + "taskImmediate": "任务immediate", + "taskTimed": "任务timed", + "taskRecurring": "任务recurring", + "taskContinuous": "任务continuous", + "repeatNone": "repeat无", + "repeatDaily": "repeat日", + "repeatWeekly": "repeatweekly", + "repeatMonthlyByDate": "repeatmonthlyBY日期", + "repeatMonthlyByWeek": "repeatmonthlyBY周", + "taskResumed": "任务resumed", + "taskEnded": "任务ended", + "taskTimedOut": "任务timedOUT", + "createWaylineTask": "创建航线任务", + "waylineName": "航线名称", + "inputWaylineName": "input航线名称", + "selectWayline": "选择航线", + "autoFetch": "auto获取", + "optionalEmpty": "optionalempty", + "gpsModeDesc": "GPSmodedesc", + "rtkModeDesc": "RTKmodedesc", + "autoResumeFromBreakpoint": "auto恢复frombreakpoint", + "resumeAuto": "恢复auto", + "resumeManual": "恢复manual", + "minExecBatteryLabel": "MINexec电量label", + "taskWaylineTypeLabel": "任务航线类型label", + "taskStartTimeLabel": "任务开始时间label", + "selectStartTimePlaceholder": "选择开始时间placeholder", + "taskEndTimeLabel": "任务结束时间label", + "repeatInterval": "repeatinterval", + "boundDrone": "bound无人机", + "airportCode": "airport码", + "tlStartCleaning": "时间线开始清洗", + "timelineStatusStart": "时间线状态开始", + "tlStartInspection": "时间线开始巡检", + "tlStartMowing": "时间线开始除草", + "tlStandbyReady": "时间线待机就绪", + "timelineStatusReady": "时间线状态就绪", + "tlReturnSupply": "时间线返航补给", + "timelineStatusReturn": "时间线状态返航", + "tlFinishInspection": "时间线完成巡检", + "timelineStatusComplete": "时间线状态完成", + "logCommand": "LOGcommand", + "logDesc1": "LOGdesc1", + "logDesc2": "LOGdesc2", + "logDesc3": "LOGdesc3", + "logDesc4": "LOGdesc4", + "logDesc5": "LOGdesc5", + "logEvent": "LOGevent", + "logDesc6": "LOGdesc6", + "logLevelLowBattery": "LOG级别LOW电量", + "aliasRobotOnly": "alias机器人only", + "aliasNotEmpty": "aliasNOTempty", + "aliasModifiedSuccess": "aliasmodified成功", + "aliasModifyFail": "aliasmodify失败", + "aliasApiError": "aliasAPI错误", + "noPermission": "编号权限", + "stationStatus": "基站状态", + "dispatchableCount": "dispatchable数量", + "unitTai": "单位TAI", + "robotExecutingCount": "机器人执行中数量", + "mowingRobot": "除草机器人", + "robotInTaskCount": "机器人IN任务数量", + "washDrone": "wash无人机", + "unitJia": "单位JIA", + "standbyCount": "待机数量", + "todayTaskExecute": "今日任务执行", + "completionRate": "completionrate", + "unknownDevice": "未知设备", + "modeWorking": "mode作业中", + "modeDebugLocal": "modedebuglocal", + "modeDebugRemote": "modedebugremote", + "modeUpgrading": "modeupgrading", + "positionInStation": "positionIN基站", + "taskStandby2": "任务standby2", + "commGood": "comm良好", + "taskStarted2": "任务started2", + "taskPaused2": "任务paused2", + "returnCommandSent": "返航commandsent", + "rechargeCommandSent": "rechargecommandsent", + "emergencyStopTriggered": "emergencystoptriggered", + "rainNo": "rain编号", + "equipmentOverview": "equipment概览", + "equipmentSituationMap": "equipmentsituationMAP", + "legendTitle": "legendtitle", + "inspectionRoute": "巡检路线", + "robotPath": "机器人路径", + "cleaningPath": "清洗路径", + "robotPosition": "机器人position", + "dronePosition": "无人机position", + "supplyChargePoint": "补给charge点", + "obstacleForbiddenZone": "obstacleforbiddenzone", + "equipmentScheduleList": "equipmentschedule列表", + "equipmentName": "equipment名称", + "modeLabel": "modelabel", + "commStatus": "comm状态", + "deviceParamSetting": "设备参数设置", + "controlLogTitle": "控制LOGtitle", + "allLevels": "ALLlevels", + "deviceColHeader": "设备COLheader", + "viewMoreLogs": "查看morelogs", + "deviceControlPanel": "设备控制panel", + "detailBtn": "detailBTN", + "batteryWithPercent": "电量withpercent", + "productType": "product类型", + "deviceSnLabel": "设备SNlabel", + "activatedTime": "activated时间", + "firmwareVersion": "firmware版本", + "flightWindSpeed": "flightwind速度", + "rtkCount": "RTK数量", + "startBtn": "开始BTN", + "rechargeBtn": "rechargeBTN", + "realtimePicture": "实时picture", + "stationVideo": "基站视频", + "droneVideo": "无人机视频", + "stopVideoBtn": "stop视频BTN", + "switchManual": "切换manual", + "emergencyStopBtn": "emergencystopBTN", + "aiAnalysisTitle": "AI分析title", + "hotspotSuspect": "热斑suspect", + "countUnit": "数量单位", + "weedHighZone": "weedhighzone", + "blockUnit": "block单位", + "componentRiskZone": "组件风险zone", + "fenceAbnormal": "fence异常", + "suggestAction": "suggestaction", + "aiAction1": "AIaction1", + "aiAction2": "AIaction2", + "aiAction3": "AIaction3", + "generateTask": "生成任务", + "aiOptimizePath": "AIoptimize路径", + "oneClickCoordinated": "ONEclickcoordinated", + "envFlightConditions": "ENVflightconditions", + "windSpeed": "wind速度", + "rainfall": "rainfall", + "rtkEnvLabel": "RTKENVlabel", + "fixedEnv": "固定ENV", + "notFixedEnv": "NOT固定ENV", + "suitableExec": "suitableexec", + "canWorkNormally": "CAN工作normally", + "robotWorkStatus": "机器人工作状态", + "cumWorkMileage": "CUM工作mileage", + "cumWorkArea": "CUM工作area", + "cumWorkTime": "CUM工作时间", + "taskTimelineTitle": "任务时间线title", + "viewMore": "查看more", + "editRobotInfoTitle": "编辑机器人提示title", + "deviceSnColon": "设备SNcolon", + "deviceTypeColon": "设备类型colon", + "deviceAliasLabel": "设备aliaslabel", + "inputCustomAlias": "inputcustomalias", + "aliasHint": "aliashint", + "fetchParamFail": "获取参数失败", + "paramSaveSuccess": "参数保存成功", + "paramSaveFail": "参数保存失败", + "paramSaveException": "参数保存exception", + "paramLeftForwardGain": "参数左前增益", + "paramLeftBackwardGain": "参数左后增益", + "paramRightForwardGain": "参数右前增益", + "paramRightBackwardGain": "参数右后增益", + "paramRunSpeed": "参数RUN速度", + "paramChipUidSign": "参数芯片UID标识", + "paramChipUid": "参数芯片UID", + "paramRemoteConfig": "参数remote配置", + "paramKnifeMotorMode": "参数knifemotormode", + "paramWalkMotorMode": "参数walkmotormode", + "paramLeftMotorReverse": "参数左motorreverse", + "paramRightMotorReverse": "参数右motorreverse", + "paramSwapChannel": "参数swapchannel", + "paramUse4G": "参数use4g", + "paramForwardSpeedLimit": "参数前速度limit", + "paramTurnSpeedLimit": "参数turn速度limit", + "paramKnifePolarity": "参数knifepolarity", + "paramFanPolarity": "参数FANpolarity", + "paramThrottlePolarity": "参数throttlepolarity", + "paramLiftProtectTime": "参数liftprotect时间", + "paramDualRtk": "参数dualRTK", + "paramKnifeChannel": "参数knifechannel", + "paramFanChannel": "参数FANchannel", + "paramThrottleChannel": "参数throttlechannel", + "paramRemoteType": "参数remote类型", + "paramRelayBoard": "参数relayboard", + "paramLiftChannel": "参数liftchannel", + "paramChassisChannel": "参数chassischannel", + "paramArmChannel": "参数ARMchannel", + "paramFuelPumpChannel": "参数fuelpumpchannel", + "paramWifiName": "参数wifi名称", + "paramWifiPassword": "参数wifipassword", + "paramBatteryType": "参数电量类型", + "paramDriveType": "参数drive类型", + "paramGearRatio": "参数gearratio", + "paramRobotLength": "参数机器人length", + "paramRobotWidth": "参数机器人width", + "paramRobotHeight": "参数机器人height", + "paramKnifeWidth": "参数knifewidth", + "paramTyreSize": "参数tyresize", + "paramFirmwareVersion": "参数firmware版本", + "paramDelFlag": "参数DELflag", + "paramViewEditTitle": "参数查看编辑title", + "noData": "编号数据", + "locationFail": "位置失败", + "pointMarked": "点marked", + "minThreePoints": "MINthree点", + "pathPlanDone": "路径计划done", + "pathPlanFailRetry": "路径计划失败retry", + "completePathFirst": "完成路径first", + "inputPlotName": "inputplot名称", + "pathPlanning": "路径planning", + "markedPointsCount": "marked点数量", + "workMode": "工作mode", + "bowMode": "BOWmode", + "abMode": "ABmode", + "markPoint": "mark点", + "clearMarks": "clearmarks", + "complete": "完成", + "selectDevice": "选择设备", + "unknownDrone": "未知无人机", + "situationMap": "situationMAP", + "clearPlannedRoute": "clearplanned路线", + "realtimeVideoMonitor": "实时视频monitor", + "frontView": "front查看", + "backView": "back查看", + "leftView": "左查看", + "rightView": "右查看", + "panorama": "全景", + "robotStatus": "机器人状态", + "rtBattery": "RT电量", + "rtVoltage": "RTvoltage", + "rtLeftMotor": "RT左motor", + "rtRightMotor": "RT右motor", + "rtLeftSpeed": "RT左速度", + "rtRightSpeed": "RT右速度", + "rtLeftTemp": "RT左温度", + "rtRightTemp": "RT右温度", + "rtMainTemp": "RTmain温度", + "rtHeadingAngle": "RT航向angle", + "rtPitchAngle": "RTpitchangle", + "rtHeadingStatus": "RT航向状态", + "rtInitialized": "RT已初始化", + "rtPositioning": "RTpositioning", + "gpsSingle": "GPSsingle", + "dgpsDiff": "DGPSdiff", + "rtkFixed2": "RTKfixed2", + "rtkFloat": "RTK浮点", + "rtSatellites": "RTsatellites", + "satelliteCount": "satellite数量", + "rtMode": "RTmode", + "videoStopped": "视频停用", + "clickPlayHint": "clickplayhint", + "remoteControl": "remote控制", + "savePathTitle": "保存路径title", + "plotNameLabel": "plot名称label", + "saving": "saving", + "stationCameraNotFound": "基站摄像头NOTfound", + "indoor": "室内", + "outdoor": "室外", + "switchStationCameraFail": "切换基站摄像头失败", + "switchDroneLensFail": "切换无人机镜头失败", + "wideLens": "广角镜头", + "zoomLens": "变焦镜头", + "irLens": "红外镜头", + "noVideoSignal": "编号视频signal", + "rainYes": "rainYES", + "hangar": "hangar", + "coverOpen": "coveropen", + "coverClosed": "cover已关闭", + "inDock": "INdock", + "outDock": "OUTdock", + "droneStatus": "无人机状态", + "horizontalSpeed": "horizontal速度", + "verticalSpeed": "vertical速度", + "headingAngle": "航向angle", + "gpsUnit": "GPS单位", + "taskCompletedMsg": "任务已完成消息", + "confirmDeleteTitle": "确认删除title", + "confirmDeleteContent": "确认删除content", + "confirmDeleteOk": "确认删除OK", + "routeDeletedSuccess": "路线deleted成功", + "deleteFailRetry": "删除失败retry", + "deleteFailNetwork": "删除失败network", + "taskListRefreshFail": "任务列表刷新失败", + "routeNoPathPoints": "路线编号路径点", + "checkRouteFail": "check路线失败", + "taskExecuteSuccess": "任务执行成功", + "taskExecuteFail": "任务执行失败", + "taskExecuteException": "任务执行exception", + "taskCanceledSuccess": "任务canceled成功", + "taskCancelFail": "任务取消失败", + "taskCancelException": "任务取消exception", + "taskPausedSuccess": "任务paused成功", + "taskPauseFail": "任务暂停失败", + "taskPauseException": "任务暂停exception", + "taskResumeFail": "任务恢复失败", + "taskResumeException": "任务恢复exception", + "waylineFallback": "航线fallback", + "waylineListFetchFail": "航线列表获取失败", + "fillRequiredFields": "fillrequiredfields", + "createTaskSuccess": "创建任务成功", + "createTaskFail": "创建任务失败", + "createTaskException": "创建任务exception", + "trendYesterday": "趋势昨日", + "trendFlat": "趋势flat", + "taskListFetchFail": "任务列表获取失败", + "boardTaskFetchFail": "board任务获取失败", + "statusExecSuccess": "状态exec成功", + "statusPreparing": "状态preparing", + "statusExecFailed": "状态exec失败", + "statusWaitingStart": "状态waiting开始", + "statusStartFailed": "状态开始失败", + "statusRestored": "状态已恢复", + "statusTerminated": "状态terminated", + "statusTimeout": "状态timeout", + "deviceSn": "设备SN", + "requestFail": "请求失败", + "selectExecuteDroneFirst": "选择执行无人机first", + "waylineTaskCreated": "航线任务已创建", + "waylineExecFail": "航线exec失败", + "todayWaylinePlan": "今日航线计划", + "unitTiao": "单位tiao", + "inProgressDesc": "INprogressdesc", + "executingTasks": "执行中任务", + "unitGe": "单位GE", + "pendingApprovalTasks": "待处理approval任务", + "taskCompletionRate": "任务completionrate", + "aiOptimizeGain": "AIoptimize增益", + "coverageEquipment": "coverageequipment", + "coverageValue": "coveragevalue", + "allOnline": "ALL在线", + "noDroneSelected": "编号无人机已选", + "commandSent": "commandsent", + "commandSendFail": "commandsend失败", + "apiRequestException": "API请求exception", + "noDetail": "编号detail", + "fetchDetailException": "获取detailexception", + "taskExecSuccess": "任务exec成功", + "taskSuspended2": "任务suspended2", + "taskRestored2": "任务restored2", + "taskReturnedHome2": "任务returnedhome2", + "statusUpdateSuccess": "状态更新成功", + "operationFail": "operation失败", + "operationApiException": "operationAPIexception", + "waylineEditPlan": "航线编辑计划", + "optimize": "optimize", + "inspectRoute": "巡检路线", + "mowRoute": "除草路线", + "droneAirport": "无人机airport", + "chargePoint": "charge点", + "noFlyZone": "编号FLYzone", + "deviceTaskList": "设备任务列表", + "allDrones": "ALLdrones", + "taskOrchestrationAI": "任务orchestrationAI", + "typeDroneHotspot": "类型无人机热斑", + "levelHigh": "级别high", + "statusSuggestExec": "状态suggestexec", + "typeFenceInspect": "类型fence巡检", + "typeComponentClean": "类型组件清洗", + "levelMid": "级别MID", + "statusWaitExec": "状态waitexec", + "typeArrayMow": "类型array除草", + "typeAIRecheck": "类型AIrecheck", + "levelLow": "级别LOW", + "statusPendingApproval": "状态待处理approval", + "colNo": "COL编号", + "suggestTime": "suggest时间", + "aiSuggestionColon": "AI建议colon", + "aiAdvice1": "AIadvice1", + "aiAdvice2": "AIadvice2", + "aiAdvice3": "AIadvice3", + "oneClickSchedule": "ONEclickschedule", + "smartAssign": "smartassign", + "waylineParamsTitle": "航线参数title", + "currentWayline": "current航线", + "rthMode": "RTHmode", + "waylinePrecision": "航线precision", + "resumeBreakpoint": "恢复breakpoint", + "safeRthBattery": "safeRTH电量", + "repeatTypeLabel": "repeat类型label", + "lostControlAction": "lost控制action", + "obstacleAvoidSwitch": "obstacleavoid切换", + "nightFlightLimit": "nightflightlimit", + "resumeTask": "恢复任务", + "pauseTask": "暂停任务", + "weatherWindowTitle": "weatherwindowtitle", + "suitability": "suitability", + "suitableExec2": "suitableexec2", + "suitable": "suitable", + "precipStatus": "precip状态", + "hasRain": "HASrain", + "gnssQuality": "gnssquality", + "fixed2": "fixed2", + "notFixed2": "NOTfixed2", + "weatherAdvice1": "weatheradvice1", + "weatherAdvice2": "weatheradvice2", + "waylineList": "航线列表", + "selectDrone": "选择无人机", + "searchWaylinePlaceholder": "搜索航线placeholder", + "unknownTime": "未知时间", + "executeWayline": "执行航线", + "ganttViewTitle": "gantt查看title", + "dronePortA01": "无人机portA01", + "typeHotspotInspect": "类型热斑巡检", + "typeRecheck": "类型recheck", + "inspectRobotRbt": "巡检机器人RBT", + "mowRobotGr": "除草机器人GR", + "washDroneUav": "wash无人机", + "workTaskBoard": "工作任务board", + "startLabel": "开始label", + "endLabel": "结束label", + "pendingExec": "待处理exec", + "equipmentColon": "equipmentcolon", + "startTimeColon": "开始时间colon", + "completedTimeColon": "已完成时间colon", + "createWaylineTaskTitle": "创建航线任务title", + "airportSn": "airportSN", + "landingDockSn": "landingdockSN", + "returnHomeAltitude": "返航home高度", + "returnHomeMode": "返航homemode", + "minBattery": "MIN电量", + "repeatMode": "repeatmode", + "weekdayExec": "weekdayexec", + "weekSun": "周周日", + "weekMon": "周周一", + "weekTue": "周周二", + "weekWed": "周周三", + "weekThu": "周周四", + "weekFri": "周周五", + "weekSat": "周周六", + "monthDays": "月days", + "whichWeek": "which周", + "weekNo": "周编号", + "weekday": "weekday", + "execTimePoint": "exec时间点", + "inputTimestamp": "inputtimestamp", + "confirmCreate": "确认创建", + "executeWaylineTask": "执行航线任务", + "executeNow": "执行NOW", + "selectedWaylineColon": "已选航线colon", + "executeDeviceColon": "执行设备colon", + "noDeviceSelected": "编号设备已选", + "returnHomeAltitudeColon": "返航home高度colon", + "deviceData": "设备数据", + "equipmentControl": "equipment控制", + "realtimePowerData": "实时power数据", + "communicationStatus": "communication状态", + "communicationNormal": "communication正常", + "groupOf": "groupOF", + "weatherStation": "weather基站", + "stationTopology": "基站topology", + "communicating": "communicating", + "realtimeMonitorList": "实时monitor列表", + "allTypes": "ALLtypes", + "searchDeviceName": "搜索设备名称", + "dronePort": "无人机port", + "deviceOnlineRate": "设备在线rate", + "onlineRate": "在线rate", + "totalCount": "共数量", + "device_2": "上传文件必须用 data,不能用", + "device_3": "上传文件必加", + "device_4": "修正为正确的添加接口", + "DeviceControl_47": "连接中状态", + "DeviceControl_48": "权限请求的弹窗", + "DeviceControl_49": "是否有控制权限", + "DeviceControl_50": "连接失败 重新连接", + "DeviceControl_51": "这里的return会终止整个handleMessage函数的执行", + "DeviceControl_52": "清空设备信息", + "DeviceControl_53": "秒后台保活", + "DeviceControl_54": "关键:清空上一个设备的轨迹", + "DeviceControl_55": "取list数组", + "DeviceControl_56": "加上这句", + "DeviceControl_57": "是否存在", + "DeviceControl_58": "连接是否打开", + "DeviceControl_59": "消息类型", + "DeviceControl_60": "原始手柄数据", + "DeviceControl_61": "认证令牌", + "DeviceControl_62": "割草机视角切换", + "DeviceControl_63": "巡检机器人:3路视频", + "DeviceControl_64": "紧凑横向,不滚动,全部可见", + "DeviceControl_65": "主画面:小一点", + "DeviceControl_66": "割草机单路", + "DeviceControl_67": "修复:割草机4路视频", + "DeviceControl_68": "绝不压扁,保持", + "DeviceControl_69": "其他设备", + "DeviceControl_71": "左侧设备列表", + "DeviceControl_72": "任务态势", + "DeviceControl_73": "区域", + "DeviceControl_74": "中间操作区", + "DeviceControl_75": "无人机状态概览", + "DeviceControl_76": "当前位置", + "DeviceControl_77": "当前模式", + "DeviceControl_78": "无人机待命", + "DeviceControl_79": "割草待命", + "DeviceControl_80": "待命", + "DeviceControl_81": "电量/航向", + "DeviceControl_82": "启用遥控", + "DeviceControl_83": "遥控与态势", + "DeviceControl_85": "把视频嵌入到这里", + "DeviceControl_86": "视频区域 - 独立大窗口", + "DeviceControl_87": "状态卡片行", + "DeviceControl_88": "链路", + "DeviceControl_89": "遥控接管", + "DeviceControl_90": "规划态势", + "DeviceControl_91": "控制区:左轮", + "DeviceControl_92": "右轮", + "DeviceControl_93": "紧凑布局", + "DeviceControl_94": "左轮速度面板 - 缩小", + "DeviceControl_95": "顶部标题", + "DeviceControl_96": "仪表盘(居中放大", + "DeviceControl_98": "电流 一体化卡片(核心视觉", + "DeviceControl_99": "电流", + "DeviceControl_100": "手柄 - 缩小、居中", + "DeviceControl_101": "右轮速度面板 - 缩小", + "DeviceControl_103": "仪表盘(居中优化", + "DeviceControl_104": "核心数据卡片:速度", + "DeviceControl_105": "目标值(干净对称", + "DeviceControl_106": "实时速度", + "DeviceControl_107": "速度输出", + "DeviceControl_108": "规划", + "DeviceControl_109": "状态标签(和你原版完全一样", + "DeviceControl_110": "下发任务", + "DeviceControl_113": "无人机专用任务控制按钮", + "DeviceControl_114": "动态按钮:执行中", + "DeviceControl_115": "挂起 / 已挂起", + "DeviceControl_117": "暂停 / 恢复", + "DeviceControl_118": "动态:返航 / 取消返航", + "DeviceControl_119": "右侧视频面板(遥控页隐藏)- 保持不变", + "DeviceControl_120": "设备类型:机器人", + "DeviceControl_121": "显示 4 路视频", + "DeviceControl_122": "设备类型:割草机", + "DeviceControl_123": "显示 1 路视频", + "DeviceControl_124": "设备类型:无人机", + "DeviceControl_125": "遍历显示视频流", + "DeviceControl_126": "删除原来的悬浮视频窗口代码", + "DeviceControl_127": "旋转加载圈", + "DeviceControl_128": "飞行参数", + "DeviceControl_129": "时间选择", + "DeviceControl_130": "重复任务", + "deviceController_131": "保存当前设备", + "deviceController_132": "后台保活定时器", + "deviceController_133": "其他端正在控制", + "deviceController_134": "权限校验失败", + "deviceController_135": "无控制权限", + "deviceController_136": "请求控制中", + "deviceController_137": "控制请求超时", + "deviceController_138": "控制失败", + "deviceController_139": "控制成功", + "deviceController_140": "权限请求", + "deviceController_141": "请求控制设备", + "deviceController_142": "车辆", + "deviceController_143": "围栏", + "deviceController_144": "坑洞", + "deviceController_145": "草丛", + "deviceController_146": "检测到", + "deviceController_147": "未知物", + "DeviceOverviewPage_149": "默认都算", + "DeviceOverviewPage_150": "只有切设备才变", + "DeviceOverviewPage_151": "视频不闪", + "DeviceOverviewPage_152": "作业中状态数量", + "DeviceOverviewPage_153": "左侧文字区域", + "DeviceOverviewPage_154": "底部环比/备注", + "DeviceOverviewPage_155": "右侧圆形图标区域", + "DeviceOverviewPage_156": "关键:videoDevice 绝不随MQTT更新,视频永不闪烁", + "DeviceOverviewPage_157": "机器人编辑别名弹窗", + "DeviceOverviewPage_158": "设备参数查看与修改弹窗(通用组件", + "DeviceParamModal_159": "加载时表单区域简单遮罩提示(打开弹窗拉取参数时的loading反馈", + "DeviceStatusPage_160": "持久化无人机数据", + "DeviceStatusPage_161": "持久化机场数据", + "DeviceStatusPage_162": "完成点", + "DeviceStatusPage_163": "开始时间戳", + "DeviceStatusPage_164": "结束时间戳", + "DeviceStatusPage_165": "标记的点", + "DeviceStatusPage_166": "是否完成路径规划", + "DeviceStatusPage_167": "保存的路径数据", + "DeviceStatusPage_168": "保存模态框可见性", + "DeviceStatusPage_169": "地块名称", + "DeviceStatusPage_170": "是否正在保存", + "DeviceStatusPage_171": "地图", + "DeviceStatusPage_172": "拿到路由上的", + "DeviceStatusPage_173": "持久化数据最后覆盖,确保优先显示", + "DeviceStatusPage_174": "存储返回的路径结果", + "DeviceStatusPage_175": "页面顶部按钮区域", + "DeviceStatusPage_176": "无人机专用布局", + "DeviceStatusPage_177": "左侧:机场视频", + "DeviceStatusPage_178": "无人机场视频", + "DeviceStatusPage_179": "机场状态", + "DeviceStatusPage_180": "右侧:无人机视频", + "DeviceStatusPage_181": "无人机状态", + "DeviceStatusPage_182": "无人机视频", + "DeviceStatusPage_183": "视频层:停止时不渲染 / 未播放时为空,不影响状态面板", + "DeviceStatusPage_184": "视频未加载 / 停止播放的占位", + "DeviceStatusPage_185": "单视频(非全景)模式状态叠加层:始终渲染,和视频层解耦", + "DeviceStatusPage_186": "右边 - 占1格,上部地图,下部远程控制", + "DeviceStatusPage_187": "远程控制面板", + "DeviceStatusPage_188": "保存路径模态框", + "DraggablePanel_189": "拖拽手柄", + "DraggablePanel_190": "子内容", + "DroneVideoPlayer_191": "默认选择室外", + "DroneVideoPlayer_192": "默认选择广角", + "DroneVideoPlayer_193": "目前先默认用第一个摄像头", + "DroneVideoPlayer_194": "重置另一边的加载状态", + "DroneVideoPlayer_195": "确保在双视频模式下有最小宽度", + "DroneVideoPlayer_196": "视频控制按钮", + "DroneVideoPlayer_197": "状态详情叠加层 - 左上角", + "DroneVideoPlayer_198": "底部信息叠加 - 显示经纬度", + "mowerAllView_199": "三列之间间距", + "mowerAllView_200": "左列:左视", + "mowerAllView_201": "左视", + "mowerAllView_202": "中列:上-前视 下-后视", + "mowerAllView_203": "前视", + "mowerAllView_204": "后视", + "mowerAllView_205": "右视", + "RobotTaskPage_206": "是否执行任务", + "RobotTaskPage_207": "正在执行的任务", + "RobotTaskPage_208": "控制视频组件显示", + "RobotTaskPage_212": "机器人设备", + "RobotTaskPage_213": "任务完成关闭视频", + "RobotTaskPage_214": "清空地图轨迹", + "RobotTaskPage_215": "设置当前任务", + "RobotTaskPage_216": "弓子 2 回子", + "RobotTaskPage_217": "连接成功后自动打开视频", + "RobotTaskPage_218": "加宽一点", + "RobotTaskPage_219": "原始状态值", + "RobotTaskPage_220": "子元素收缩兜底,超长时允许压缩换行", + "RobotTaskPage_221": "开启自动换行,小屏按钮自动折行", + "RobotTaskPage_222": "优化", + "RobotTaskPage_223": "机器人状态悬浮面板 - 绝对定位左上角", + "RobotTaskPage_224": "避开左侧图例面板,防止重叠", + "RobotTaskPage_225": "限制最大宽度,不会无限拉长", + "RobotTaskPage_226": "视频开关", + "RobotTaskPage_227": "机器人任务模块,带Tab切换", + "RobotTaskPage_229": "删除按钮", + "RobotTaskPage_230": "执行按钮", + "RobotTaskPage_231": "传递 routeList 给弹窗", + "RobotTaskPage_232": "执行任务弹窗", + "RobotTaskPage_237": "周选择", + "RobotTaskPage_238": "月选择", + "RobotTaskPage_239": "按天:选择日期", + "WayLinePage_242": "航线列表", + "WayLinePage_243": "航线列表默认选中第一个", + "WayLinePage_244": "使用航线列表无人机", + "WayLinePage_245": "地图卡片", + "WayLinePage_246": "独立筛选 - 设备任务列表", + "WayLinePage_247": "继续任务", + "WayLinePage_248": "底部内容区", + "WayLinePage_249": "航线列表独立无人机筛选", + "WayLinePage_250": "执行按钮改为图标", + "WayLinePage_251": "任务名称:自动换行", + "WayLinePage_252": "时间:自动换行", + "WayLinePage_253": "创建任务弹窗", + "WayLinePage_254": "重复任务配置", + "WayLinePage_255": "对象绑定", + "WayLinePage_256": "每周", + "WayLinePage_257": "按月日期", + "WayLinePage_258": "按月星期", + "WayLinePage_260": "执行航线设置返航高度", + "XboxController_261": "只有这里才发", + "XboxController_262": "空依赖", + "XboxController_263": "强制触发一次", + "XboxController_264": "页面切换依赖", + "XboxController_265": "未连接设备", + "XboxController_266": "其他 Web 端已连接", + "XboxController_267": "控制中", + "XboxController_268": "点击获取控制权限" + }, + "systemSetting": { + "pleaseSelectRoles": "please选择角色", + "batchDeleteRolesTitle": "batch删除角色title", + "confirmDeleteSelectedRoles": "确认删除已选角色", + "batchDeleteSuccess": "batch删除成功", + "resetFail": "重置失败", + "pleaseSelectUsers": "please选择users", + "batchDeleteUsersTitle": "batch删除userstitle", + "confirmDeleteSelectedUsers": "确认删除已选users", + "userAndRole": "用户AND角色", + "belongingOrg": "belongingORG", + "belongingSite": "belongingsite", + "devicePerm": "设备perm", + "hasPerm": "HASperm", + "noPerm": "编号perm", + "confirmDeleteUser": "确认删除用户", + "totalCount": "共数量", + "permissionLabel": "权限label", + "levelLabel": "级别label", + "editRole": "编辑角色", + "roleLevel": "角色级别", + "pleaseInputRoleLevel": "pleaseinput角色级别", + "account": "account", + "nicknameShort": "nicknameshort", + "phoneShort": "phoneshort", + "editPasswordPlaceholder": "编辑passwordplaceholder", + "chooseRole": "choose角色", + "pleaseSelectOrg": "please选择ORG", + "pleaseSelectSite": "please选择site", + "deviceOperatePermLabel": "设备operatepermlabel", + "has": "HAS", + "none": "无", + "AlarmSettingsManagement_294": "错误编码下拉数据源", + "AlarmSettingsManagement_295": "工单模型下拉数据源", + "AlarmSettingsManagement_296": "获取告警模型列表失败", + "AlarmSettingsManagement_297": "确认删除", + "AlarmSettingsManagement_298": "您确定要删除这条告警模型吗", + "AlarmSettingsManagement_299": "请先勾选要删除的数据", + "AlarmSettingsManagement_300": "批量删除确认", + "AlarmSettingsManagement_301": "您确定要删除选中的", + "AlarmSettingsManagement_302": "条告警模型吗", + "AlarmSettingsManagement_303": "删除后不可恢复", + "AlarmSettingsManagement_304": "批量删除成功", + "AlarmSettingsManagement_305": "批量删除失败", + "AlarmSettingsManagement_306": "更新成功", + "AlarmSettingsManagement_309": "关联工单模型", + "AlarmSettingsManagement_310": "自动生成工单", + "AlarmSettingsManagement_311": "新增告警模型", + "AlarmSettingsManagement_313": "编辑告警模型", + "AlarmSettingsManagement_314": "错误编码(关联错误管理", + "AlarmSettingsManagement_315": "请选择错误编码", + "AlarmSettingsManagement_316": "工单模型ID(关联工单模型", + "AlarmSettingsManagement_317": "请选择工单模型", + "AlarmSettingsManagement_319": "是否自动生成工单", + "AllDeviceManagement_321": "清洗机器人", + "AllDeviceManagement_324": "电表", + "AllDeviceManagement_325": "运维机器人", + "AllDeviceManagement_326": "气象站", + "AllDeviceManagement_327": "设备编码", + "AllDeviceManagement_328": "运行", + "AllDeviceManagement_329": "编辑设备", + "AllDeviceManagement_330": "确定要删除设备", + "AllDeviceManagement_331": "请输入设备名称", + "BasicSettings_332": "设置已保存", + "BasicSettings_333": "当前版本", + "BasicSettings_334": "最新版", + "BasicSettings_335": "告警通道", + "BasicSettings_336": "数据备份状态", + "BasicSettings_337": "最近更新", + "BasicSettings_338": "超级管理员", + "BasicSettings_339": "全部权限", + "BasicSettings_340": "系统管理", + "BasicSettings_341": "运维工程师", + "BasicSettings_342": "工单管理", + "BasicSettings_343": "巡检主管", + "BasicSettings_344": "巡检管理", + "BasicSettings_345": "告警处理", + "BasicSettings_346": "数据分析师", + "BasicSettings_347": "数据分析", + "BasicSettings_348": "报表查看", + "BasicSettings_349": "接口", + "BasicSettings_350": "顶部统计卡片", + "BasicSettings_351": "左侧", + "BasicSettings_352": "基础配置", + "BasicSettings_353": "默认电站", + "BasicSettings_354": "首页默认页面", + "BasicSettings_355": "数据刷新频率", + "BasicSettings_356": "分钟", + "BasicSettings_357": "地图服务", + "BasicSettings_358": "高德地图", + "BasicSettings_359": "工单自动编号", + "BasicSettings_360": "建议自动生成", + "BasicSettings_361": "移动端同步", + "BasicSettings_362": "保存设置", + "BasicSettings_363": "恢复默认", + "BasicSettings_364": "通知与告警策略", + "BasicSettings_365": "已启用", + "BasicSettings_366": "系统通知", + "BasicSettings_367": "严重告警立即通知", + "BasicSettings_368": "发电下降超过", + "BasicSettings_369": "通信中断通知", + "BasicSettings_370": "每日汇总推送", + "BasicSettings_371": "夜间免打扰", + "BasicSettings_372": "用户与权限预览", + "BasicSettings_373": "用户名", + "BasicSettings_375": "张运维", + "BasicSettings_376": "李工程师", + "BasicSettings_377": "王班长", + "BasicSettings_378": "赵分析师", + "BasicSettings_379": "右侧", + "BasicSettings_380": "角色权限概览", + "BasicSettings_381": "用户", + "BasicSettings_382": "权限", + "BasicSettings_383": "设备接入状态", + "BasicSettings_384": "详情", + "BasicSettings_386": "数据保留", + "BasicSettings_387": "登录审计", + "BasicSettings_388": "已开启", + "BasicSettings_389": "自动备份", + "BasicSettings_390": "密钥", + "BasicSettings_391": "最近备份", + "BasicSettings_392": "证书", + "BasicSettings_393": "有效期至", + "BasicSettings_394": "双因素认证", + "BasicSettings_396": "管理权限", + "BasicSettings_397": "清理缓存", + "BasicSettings_398": "重启服务", + "BasicSettings_399": "备份下载", + "BasicSettings_400": "日志导出", + "CameraManagement_401": "东门入口摄像头", + "CameraManagement_402": "园区东门", + "CameraManagement_403": "高清球机", + "CameraManagement_404": "海康威视", + "CameraManagement_405": "西门入口摄像头", + "CameraManagement_406": "园区西门", + "CameraManagement_407": "高清枪机", + "CameraManagement_408": "大华", + "CameraManagement_409": "停车场摄像头", + "CameraManagement_410": "地下停车场", + "CameraManagement_411": "办公楼大堂摄像头", + "CameraManagement_412": "办公楼1层大堂", + "CameraManagement_413": "半球摄像机", + "CameraManagement_414": "宇视", + "CameraManagement_415": "仓库监控摄像头", + "CameraManagement_416": "物料仓库", + "CameraManagement_417": "摄像头类型", + "CameraManagement_418": "品牌", + "CameraManagement_419": "分辨率", + "CameraManagement_420": "云台", + "CameraManagement_421": "支持", + "CameraManagement_422": "不支持", + "CameraManagement_423": "夜视", + "CameraManagement_424": "预览", + "CameraManagement_425": "视频预览", + "CameraManagement_426": "视频预览占位", + "CameraManagement_427": "确定要删除摄像头", + "CameraManagement_428": "摄像头总数", + "CameraManagement_429": "离线/故障", + "CameraManagement_430": "请输入名称/编码", + "CameraManagement_431": "状态筛选", + "CameraManagement_432": "新增摄像头", + "CameraManagement_433": "编辑摄像头", + "CameraManagement_434": "请输入设备编码", + "CameraManagement_435": "请输入IP地址", + "CameraManagement_436": "例如", + "CameraManagement_437": "请选择类型", + "CameraManagement_438": "全景摄像机", + "CameraManagement_439": "请选择品牌", + "CameraManagement_440": "请选择分辨率", + "CameraManagement_441": "安装位置", + "CameraManagement_442": "请输入安装位置", + "CameraManagement_443": "云台控制", + "CameraManagement_444": "夜视功能", + "DeviceAccessPage_445": "替换这行", + "DeviceAccessPage_446": "录像状态", + "DeviceAccessPage_447": "周界入侵告警", + "DeviceAccessPage_448": "湿度", + "DeviceAccessPage_449": "超温告警", + "DeviceAccessPage_450": "电压", + "DeviceAccessPage_451": "低电量告警", + "DeviceAccessPage_452": "删除后刷新", + "DeviceAccessPage_454": "行为配置回显", + "DeviceAccessPage_455": "编辑才有", + "DeviceAccessPage_457": "默认给一个", + "DeviceAccessPage_458": "阵列区监控设备", + "DeviceAccessPage_460": "编辑设备成功", + "DeviceAccessPage_461": "新增设备成功", + "DeviceAccessPage_463": "产品编辑成功", + "DeviceAccessPage_464": "产品新增成功", + "DeviceAccessPage_465": "改成", + "DeviceAccessPage_466": "产品", + "DeviceAccessPage_467": "模型", + "DeviceAccessPage_468": "端口", + "DeviceAccessPage_469": "账号", + "DeviceAccessPage_470": "模型名称", + "DeviceAccessPage_471": "连接协议", + "DeviceAccessPage_472": "属性列表", + "DeviceAccessPage_473": "无属性", + "DeviceAccessPage_474": "指令列表", + "DeviceAccessPage_475": "无指令", + "DeviceAccessPage_476": "确定要删除该物模型吗", + "DeviceAccessPage_477": "产品管理", + "DeviceAccessPage_478": "物模型管理", + "DeviceAccessPage_479": "新增设备", + "DeviceAccessPage_480": "产品编码", + "DeviceAccessPage_481": "请输入编码", + "DeviceAccessPage_482": "产品名称", + "DeviceAccessPage_483": "请输入名称", + "DeviceAccessPage_484": "生产厂家", + "DeviceAccessPage_485": "请输入厂家", + "DeviceAccessPage_486": "协议编码", + "DeviceAccessPage_487": "请选择协议", + "DeviceAccessPage_488": "传输方式", + "DeviceAccessPage_489": "禁用", + "DeviceAccessPage_490": "新增产品", + "DeviceAccessPage_491": "协议", + "DeviceAccessPage_492": "上报", + "DeviceAccessPage_493": "订阅", + "DeviceAccessPage_494": "字段映射", + "DeviceAccessPage_495": "确定要删除该产品吗", + "DeviceAccessPage_496": "新增物模型", + "DeviceAccessPage_497": "设备配置", + "DeviceAccessPage_498": "产品配置", + "DeviceAccessPage_499": "物模型配置", + "DeviceAccessPage_500": "请输入设备", + "DeviceAccessPage_501": "关联产品", + "DeviceAccessPage_502": "请选择关联产品", + "DeviceAccessPage_503": "配置", + "DeviceAccessPage_504": "地址", + "DeviceAccessPage_505": "默认", + "DeviceAccessPage_506": "登录账号", + "DeviceAccessPage_507": "设备登录账号", + "DeviceAccessPage_508": "登录密码", + "DeviceAccessPage_509": "设备登录密码", + "DeviceAccessPage_510": "服务地址", + "DeviceAccessPage_511": "客户端唯一标识", + "DeviceAccessPage_512": "账号(选填", + "DeviceAccessPage_514": "密码(选填", + "DeviceAccessPage_515": "告警阈值配置", + "DeviceAccessPage_516": "上限", + "DeviceAccessPage_517": "上限值", + "DeviceAccessPage_518": "下限", + "DeviceAccessPage_519": "下限值", + "DeviceAccessPage_520": "当前物模型未设置告警属性,无法配置阈值", + "DeviceAccessPage_521": "产品编号", + "DeviceAccessPage_522": "智能网关", + "DeviceAccessPage_523": "厂商", + "DeviceAccessPage_524": "迈步科技", + "DeviceAccessPage_525": "传输类型", + "DeviceAccessPage_526": "绑定物模型", + "DeviceAccessPage_527": "请选择要绑定的物模型", + "DeviceAccessPage_528": "协议自动配置项", + "DeviceAccessPage_529": "属性配置(你原来的", + "DeviceAccessPage_530": "物模型属性配置", + "DeviceAccessPage_533": "请选择物模型以显示属性", + "DeviceAccessPage_534": "行为(指令)配置 新增", + "DeviceAccessPage_535": "物模型行为(指令)配置", + "DeviceAccessPage_536": "默认值", + "DeviceAccessPage_537": "该物模型暂无指令", + "DeviceAccessPage_538": "属性", + "DeviceAccessPage_539": "编码", + "DeviceAccessPage_540": "单位", + "DeviceAccessPage_541": "指令", + "DeviceAccessPage_542": "指令编码", + "DeviceAccessPage_543": "指令名称", + "DeviceAccessPage_544": "参数", + "DeviceAccessPage_545": "属性弹窗", + "DeviceAccessPage_546": "配置属性", + "DeviceAccessPage_547": "属性编码", + "DeviceAccessPage_548": "属性名称", + "DeviceAccessPage_549": "如 温度", + "DeviceAccessPage_550": "告警参数", + "DeviceAccessPage_551": "指令弹窗", + "DeviceAccessPage_552": "配置指令", + "DeviceAccessPage_553": "指令参数", + "DeviceAccessPage_554": "参数名", + "DeviceAccessPage_555": "枚举", + "DeviceAccessPage_556": "分隔", + "DeviceAccessPage_557": "标准字段", + "DeviceAccessPage_558": "厂商原始字段", + "DeviceAccessPage_559": "转换规则", + "DeviceManagement_560": "无人机管理", + "DeviceManagement_561": "智能装备", + "DeviceManagement_562": "摄像头管理", + "DeviceManagement_563": "机器人生成", + "DroneManagement_564": "获取无人机列表失败", + "DroneManagement_565": "请先在列表中选中需要绑定的无人机", + "DroneManagement_566": "绑定成功", + "DroneManagement_567": "绑定失败", + "DroneManagement_568": "操作异常", + "DroneManagement_569": "未选择有效设备,无法解绑", + "DroneManagement_570": "人员", + "DroneManagement_571": "场站", + "DroneManagement_572": "组织", + "DroneManagement_573": "确认解绑", + "DroneManagement_574": "确定要为选中的无人机清除", + "DroneManagement_575": "绑定吗", + "DroneManagement_576": "解绑成功", + "DroneManagement_577": "解绑失败", + "DroneManagement_578": "机场", + "DroneManagement_579": "所属组织", + "DroneManagement_580": "未绑定", + "DroneManagement_581": "所属场站", + "DroneManagement_582": "人员解绑", + "DroneManagement_583": "场站解绑", + "DroneManagement_584": "组织解绑", + "DroneManagement_585": "绑定无人机", + "DroneManagement_586": "批量解绑(用户", + "DroneManagement_587": "批量解绑(场站", + "DroneManagement_588": "批量解绑(组", + "DroneManagement_589": "已选无人机", + "DroneManagement_591": "请选择组织", + "DroneManagement_592": "请选择场站", + "DroneManagement_593": "请先选择组织", + "DroneManagement_594": "请选择负责人", + "DroneManagement_595": "请先选择场站后选择负责人", + "ErrorManagement_596": "搜索区域完全不变", + "ErrorManagement_597": "新增/编辑/查看弹窗(核心优化区域", + "ErrorManagement_598": "第一行:基础编码名称", + "ErrorManagement_599": "第二行:来源、等级、校验字段", + "ErrorManagement_600": "第三行:对比类型", + "ErrorManagement_601": "对比值(自动切换单输入/区间双输入", + "ErrorManagement_602": "区间类型", + "ErrorManagement_603": "显示两个输入框", + "FlvPlayer_606": "当前浏览器不支持 FLV MSE播放,请下载文件查看", + "FlvPlayer_607": "适配", + "FlvPlayer_608": "关键:大文件点播不能false,否则一次性全部下载立刻关闭", + "FlvPlayer_609": "视频编码不受浏览器MSE支持,请下载文件本地播放", + "FlvPlayer_610": "媒体源不支持", + "FlvPlayer_611": "播放异常,建议下载文件本地播放", + "FlvPlayer_612": "网络错误,请检查视频地址", + "FlvPlayer_613": "媒体解码错误,该FLV文件可能不兼容浏览器", + "FlvPlayer_614": "加载超时,浏览器MSE解析失败,请下载文件本地播放", + "FlvPlayer_615": "播放器初始化失败,请重试", + "FlvPlayer_616": "视频加载失败,请检查地址或下载", + "FlvPlayer_617": "播放失败", + "FlvPlayer_618": "重试", + "FlvPlayer_619": "下载视频", + "FlvPlayer_620": "点击手动播放视频", + "FlvPlayer_621": "使用", + "FlvPlayer_622": "解码,部分特殊编码FLV浏览器无法渲染,建议下载本地播放", + "FlvPlayer_623": "非FLV使用浏览器原生播放", + "LogManagement_624": "系统", + "LogManagement_625": "机器人智慧服务系统 Alpha 三服务(接入/状态/命令)启动完成", + "LogManagement_626": "命令服务", + "LogManagement_627": "向 ROBOT-001 下发移动命令", + "LogManagement_628": "执行结果:成功", + "LogManagement_629": "新增运维工程师账号", + "LogManagement_630": "李工", + "LogManagement_631": "角色:运维工程师", + "LogManagement_632": "心跳超时", + "LogManagement_633": "已自动置为离线", + "LogManagement_634": "自主巡检", + "LogManagement_635": "超时,已自动重试 1 次后仍失败", + "LogManagement_636": "修改系统名称与数据刷新频率", + "LogManagement_637": "状态服务", + "LogManagement_638": "设备 ROBOT-001 上报状态:电量 85%,模式", + "LogManagement_639": "逆变器 INV-03 输出功率较昨日下降 16%,触发预警", + "LogManagement_640": "调整 CAM-04 热成像通道码率参数", + "LogManagement_641": "每日自动备份完成,备份文件", + "LogManagement_642": "未知设备尝试注册(secret 校验失败),已拒绝,来源", + "LogManagement_643": "角色授权", + "LogManagement_644": "报表中心-导出", + "LogManagement_645": "日志已刷新", + "LogManagement_646": "级别", + "LogManagement_648": "消息", + "LogManagement_650": "已导出", + "LogManagement_651": "条日志", + "LogManagement_652": "系统运行、设备控制与管理员操作流水,仅超级管理员可查看", + "LogManagement_653": "全部级别", + "LogManagement_654": "搜索消息 / 操作人 / 来源", + "MenuList_655": "菜单加载失败", + "MenuList_656": "新增子菜单", + "MenuList_657": "新增菜单", + "MenuList_658": "关闭分页", + "MenuList_659": "菜单弹窗", + "MenuList_660": "编辑菜单", + "MenuList_661": "无(一级菜单", + "MenuList_662": "请输入菜单名称", + "MenuList_663": "显示排序", + "MenuList_664": "数字越小越靠前", + "MenuList_665": "图标选择弹窗", + "MenuList_666": "选择菜单图标", + "OnlineUser_667": "研发部门", + "OnlineUser_668": "内网", + "OnlineUser_669": "默认选中在线用户", + "OnlineUser_670": "搜索完成", + "OnlineUser_671": "已重置搜索条件", + "OnlineUser_672": "已向会话", + "OnlineUser_673": "发送强退指令", + "OnlineUser_674": "序号", + "OnlineUser_675": "会话编号", + "OnlineUser_676": "登录名称", + "OnlineUser_677": "所属部门", + "OnlineUser_678": "主机", + "OnlineUser_679": "登录地点", + "OnlineUser_680": "操作系统", + "OnlineUser_681": "浏览器", + "OnlineUser_682": "登录时间", + "OnlineUser_683": "强退", + "OnlineUser_684": "系统监控", + "OnlineUser_685": "顶部", + "OnlineUser_686": "其他监控", + "OnlineUser_688": "搜索栏", + "OnlineUser_689": "登录地址", + "OnlineUser_690": "请输入登录地址", + "OnlineUser_691": "请输入用户名称", + "OnlineUser_694": "在线用户表格", + "OnlineUser_695": "其他页面(你可以随便改内容", + "OnlineUser_696": "其他监控内容", + "OnlineUser_697": "这里可以放服务状态、CPU监控、内存监控、接口监控等内容", + "Organization_698": "确定删除该组织", + "Organization_699": "新增组织", + "Organization_700": "统一分页", + "Organization_701": "编辑组织", + "Organization_702": "请输入组织名称", + "Organization_703": "组织编码", + "Organization_704": "请输入组织编码", + "Organization_705": "请输入负责人", + "Organization_706": "请输入联系电话", + "Organization_707": "请输入邮箱", + "Organization_708": "请输入地址", + "Organization_709": "请输入备注", + "SerialNumGenerate_710": "请选择要删除的数据", + "SerialNumGenerate_711": "请先生成", + "SerialNumGenerate_713": "未查询到数据", + "SerialNumGenerate_714": "查询失败", + "SerialNumGenerate_715": "序列号", + "SerialNumGenerate_716": "型号", + "SerialNumGenerate_717": "国家", + "SerialNumGenerate_718": "省份", + "SerialNumGenerate_719": "二维码", + "SerialNumGenerate_721": "机器人二维码管理", + "SerialNumGenerate_722": "数据管理", + "SerialNumGenerate_723": "确定删除选中项", + "SerialNumGenerate_724": "筛选栏", + "SerialNumGenerate_725": "请输入序列号", + "SerialNumGenerate_726": "列表", + "SerialNumGenerate_727": "完全自适应", + "SerialNumGenerate_728": "右侧固定", + "SerialNumGenerate_729": "不溢出", + "SerialNumGenerate_730": "新增弹窗", + "SerialNumGenerate_731": "生成机器人二维码", + "SerialNumGenerate_732": "选择型号", + "SerialNumGenerate_733": "选择配置", + "SerialNumGenerate_734": "选择类型", + "SerialNumGenerate_735": "选择国家", + "SerialNumGenerate_736": "请先选择国家", + "SerialNumGenerate_737": "生成序列号", + "SerialNumGenerate_738": "请先生成二维码", + "SerialNumGenerate_739": "查看二维码", + "SerialNumGenerate_740": "二维码详情", + "SerialNumGenerate_741": "机器人类型", + "SerialNumGenerate_742": "类型名称", + "SerialNumGenerate_743": "存储值", + "SerialNumGenerate_744": "机器人型号", + "SerialNumGenerate_745": "型号名称", + "SerialNumGenerate_746": "配置名称", + "SmartDeviceManagement_747": "获取智能装备列表失败", + "SmartDeviceManagement_748": "装备编辑成功", + "SmartDeviceManagement_749": "无权限", + "SmartDeviceManagement_750": "保存参数成功", + "SmartDeviceManagement_751": "授权失败", + "SmartDeviceManagement_752": "请先在列表中选中需要绑定的智能装备", + "SmartDeviceManagement_753": "确定要为选中的智能装备清除", + "SmartDeviceManagement_754": "设备码", + "SmartDeviceManagement_755": "清除人员绑定", + "SmartDeviceManagement_756": "清除场站绑定", + "SmartDeviceManagement_757": "清除组织绑定", + "SmartDeviceManagement_758": "授权", + "SmartDeviceManagement_759": "绑定装备", + "SmartDeviceManagement_760": "批量解绑(组织", + "SmartDeviceManagement_761": "绑定智能装备", + "SmartDeviceManagement_762": "已选智能装备", + "SmartDeviceManagement_763": "请选择装备", + "SmartDeviceManagement_764": "编辑智能装备信息", + "SmartDeviceManagement_765": "请填写设备名称", + "SmartDeviceManagement_766": "设备序列号", + "SmartDeviceManagement_767": "请填写设备码", + "SmartDeviceManagement_768": "请输入设备序列号", + "SmartDeviceManagement_769": "请选择在线状态", + "SmartDeviceManagement_770": "选择在线状态", + "SmartDeviceManagement_771": "设备授权", + "SmartDeviceManagement_772": "设置设备操作授权,授权到期后设备将失去对应操作权限", + "SmartDeviceManagement_773": "授权时间", + "SmartDeviceManagement_774": "请输入授权剩余时间", + "SmartDeviceManagement_775": "授权时间不能小于", + "SmartDeviceManagement_776": "保存授权", + "StationManage_777": "分区设置", + "StationManage_778": "边界绘制", + "StationManage_779": "请选择组织筛选", + "StationManage_780": "新增场站", + "StationManage_781": "场站弹窗", + "StationManage_782": "编辑场站", + "StationManage_783": "请选择所属组织", + "StationManage_784": "详细地址", + "StationManage_785": "消息推送配置", + "StationManage_786": "钉钉告警推送", + "StationManage_787": "开启", + "StationManage_788": "钉钉工单推送", + "StationManage_789": "邮件推送", + "StationManage_790": "告警弹窗推送", + "StationManage_791": "钉钉开关开启才显示Token、密钥", + "StationManage_792": "钉钉机器人", + "StationManage_793": "开启钉钉推送时必须填写", + "StationManage_794": "请输入钉钉机器人", + "StationManage_795": "钉钉加密密钥", + "StationManage_796": "开启钉钉推送时必须填写加密密钥", + "StationManage_797": "钉钉机器人加签密钥", + "StationManage_798": "分区弹窗", + "StationManage_799": "分区名称", + "StationManage_800": "分区编码", + "StationManage_801": "分区类型", + "StationManage_802": "清扫区", + "StationManage_803": "巡检区", + "StationManage_804": "保存分区", + "StationManage_805": "地图绘制弹窗", + "StationManage_806": "地图区域绘制(可自行接入高德/百度地图", + "StationManage_807": "开始绘制", + "StationManage_808": "保存边界", + "SystemMaintain_809": "错误管理", + "SystemMaintain_810": "告警模型管理", + "SystemMaintain_811": "工单模型管理", + "UserPage_814": "角色弹窗", + "UserPage_815": "用户弹窗", + "UserPage_816": "自动适应布局:隐藏角色时,状态自动占满一行", + "VideoManagement_817": "读取通道失败", + "VideoManagement_818": "无法连接视频服务", + "VideoManagement_819": "读取文件失败", + "VideoManagement_820": "请先选择完整的时间路径", + "VideoManagement_821": "大小", + "VideoManagement_822": "录像通道与回放管理 · 共", + "VideoManagement_823": "个通道", + "VideoManagement_824": "通道", + "VideoManagement_825": "选择设备通道", + "VideoManagement_826": "请先选择视频通道", + "VideoManagement_827": "请选择 年 / 月 / 日 加载录像", + "VideoManagement_828": "该日暂无录像文件", + "VideoManagement_829": "个文件", + "WorkOrderSettingsManagement_830": "获取工单模型列表失败", + "WorkOrderSettingsManagement_831": "您确定要删除这条工单模型吗", + "WorkOrderSettingsManagement_832": "条工单模型吗", + "WorkOrderSettingsManagement_833": "工单名称", + "WorkOrderSettingsManagement_835": "轻微", + "WorkOrderSettingsManagement_836": "路径/航线", + "WorkOrderSettingsManagement_837": "自动生成任务", + "WorkOrderSettingsManagement_838": "新增工单模型", + "WorkOrderSettingsManagement_839": "编辑工单模型", + "WorkOrderSettingsManagement_840": "请输入工单名称", + "WorkOrderSettingsManagement_842": "请选择设备", + "WorkOrderSettingsManagement_843": "根据设备类型显示不同的路径选择", + "WorkOrderSettingsManagement_844": "预设路径", + "WorkOrderSettingsManagement_845": "请选择预设路径", + "WorkOrderSettingsManagement_846": "无人机航线", + "WorkOrderSettingsManagement_847": "请选择无人机航线", + "SystemSetting_1265": "告警规则配置、用户角色、通知管理与系统参数设置" + }, + "aiAnalysis": { + "objectDetection": "objectdetection", + "detectDesc": "detectdesc", + "thermalSegmentation": "热成像segmentation", + "segmentationDesc": "segmentationdesc", + "getModelsFailed": "GETmodels失败", + "uploadFirst": "上传first", + "submitTaskFailed": "submit任务失败", + "thermalAnalysisComplete": "热成像分析完成", + "taskSuccess": "任务成功", + "taskExecutionError": "任务execution错误", + "cancelled": "cancelled", + "uploadThermalFirst": "上传热成像first", + "thermalAnalysisFailed": "热成像分析失败", + "detectTaskName": "detect任务名称", + "segmentationTaskName": "segmentation任务名称", + "algoCount": "algo数量", + "detectAndSeg": "detectANDSEG", + "onlineServices": "在线services", + "healthCheck": "healthcheck", + "modelCount": "model数量", + "detectModelRegistry": "detectmodelregistry", + "historyRecords": "历史records", + "pageDebugRecords": "页面debugrecords", + "detectModel": "detectmodel", + "default": "default", + "inputType": "input类型", + "image": "image", + "inferencePreset": "inference预设", + "quality": "quality", + "dragFile": "dragfile", + "supportedFormats": "supportedformats", + "processing": "处理中", + "submitDetectTask": "submitdetect任务", + "serverDemo": "serverdemo", + "refreshModels": "刷新models", + "thermalFileLabel": "热成像filelabel", + "uploadThermal": "上传热成像", + "arrayMask": "arraymask", + "panelGeojson": "panelgeojson", + "submitThermalAnalysis": "submit热成像分析", + "readServerHistory": "readserver历史", + "resultDisplay": "结果display", + "resultViewHint": "结果查看hint", + "inputPreview": "inputpreview", + "resultImageVideo": "结果image视频", + "showResultAfterTask": "show结果after任务", + "resultJson": "结果json", + "noResult": "编号结果", + "thermalInteractiveView": "热成像interactive查看", + "clickHotspotHint": "click热斑hint", + "hotspotCount": "热斑数量", + "panelArea": "panelarea", + "analysisId": "分析ID", + "pointDetails": "点details", + "selectHotspotPanel": "选择热斑panel", + "hotspot": "热斑", + "blockNumber": "blocknumber", + "originalPixels": "originalpixels", + "overlayPixels": "overlaypixels", + "area": "area", + "waitingSegmentation": "waitingsegmentation", + "hotspotPanelList": "热斑panel列表", + "panelLabel": "panellabel", + "centerTitle": "中心title", + "algorithmList": "algorithm列表", + "integratedCount": "integrated数量", + "detectWorkspace": "detectworkspace", + "segWorkspace": "SEGworkspace", + "asyncTask": "async任务", + "syncAnalysis": "sync分析", + "detectWorkspaceDesc": "detectworkspacedesc", + "segWorkspaceDesc": "SEGworkspacedesc", + "historyAnalysis": "历史分析", + "noHistory": "编号历史", + "detect": "detect", + "segment": "segment", + "hotspotRecognition": "热斑recognition", + "abnormalString": "异常组串", + "recommendedCleanBlock": "recommended清洗block", + "videoAbnormalEvent": "视频异常event", + "videoIntelligentAnalysis": "视频intelligent分析", + "perimeterIntrusion": "perimeterintrusion", + "perimeterIntrusionDesc": "perimeterintrusiondesc", + "personnelIntrusion": "personnelintrusion", + "personnelIntrusionDesc": "personnelintrusiondesc", + "fireSmokeDetection": "firesmokedetection", + "fireSmokeDesc": "firesmokedesc", + "areaLoitering": "arealoitering", + "areaLoiteringDesc": "arealoiteringdesc", + "motionDetection": "motiondetection", + "motionDetectionDesc": "motiondetectiondesc", + "perimeterIntrusionTimeline": "perimeterintrusion时间线", + "personnelIntrusionTimeline": "personnelintrusion时间线", + "areaLoiteringTimeline": "arealoitering时间线", + "motionDetectionTimeline": "motiondetection时间线", + "perimeterClimbTimeline": "perimeterclimb时间线", + "perimeterIntrusionType": "perimeterintrusion类型", + "AIAnalysis_983": "处理热斑组件", + "AIAnalysis_984": "排查异常组串", + "AIAnalysis_985": "清洗受遮挡区域", + "AIAnalysis_986": "跟踪衰减较大组件", + "AIAnalysis_987": "持续监控逆变器效率", + "AIAnalysis_988": "方阵", + "AIAnalysis_989": "智能诊断与分析中心", + "AIAnalysis_990": "刷新数据", + "AIAnalysis_991": "导出报告", + "AIAnalysis_992": "第一排:顶部统计卡片", + "AIAnalysis_993": "值 (性能比", + "AIAnalysis_994": "较昨日", + "AIAnalysis_995": "发电偏差率", + "AIAnalysis_996": "异常组串数量", + "AIAnalysis_997": "热斑疑似数量", + "AIAnalysis_998": "电站健康度评分", + "AIAnalysis_999": "预测发电量", + "AIAnalysis_1000": "第二排:热成像分析 和 组串异常定位", + "AIAnalysis_1001": "无人机热成像巡检分析", + "AIAnalysis_1002": "模拟热斑点", + "AIAnalysis_1003": "高危", + "AIAnalysis_1004": "注意", + "AIAnalysis_1005": "热斑疑似点", + "AIAnalysis_1006": "高温区域", + "AIAnalysis_1007": "温度分布", + "AIAnalysis_1008": "组串实时状态定位图", + "AIAnalysis_1009": "总组串", + "AIAnalysis_1012": "组串", + "AIAnalysis_1013": "严重异常 (热斑", + "AIAnalysis_1014": "严重异常 (断路", + "AIAnalysis_1015": "异常 (功率偏差", + "AIAnalysis_1016": "异常 (遮挡", + "AIAnalysis_1017": "轻微异常", + "AIAnalysis_1019": "运行正常", + "AIAnalysis_1020": "功率异常", + "AIAnalysis_1021": "严重故障", + "AIAnalysis_1022": "设备离线", + "AIAnalysis_1023": "异常类型分布", + "AIAnalysis_1024": "离线组串", + "AIAnalysis_1025": "查看详细定位表", + "AIAnalysis_1026": "第三排:三个主要分析区域", + "AIAnalysis_1027": "组件衰减趋势分析", + "AIAnalysis_1028": "衰减率趋势", + "AIAnalysis_1029": "当前均值", + "AIAnalysis_1030": "平均衰减率", + "AIAnalysis_1031": "最佳组件", + "AIAnalysis_1032": "最差组件", + "AIAnalysis_1033": "衰减预警", + "AIAnalysis_1034": "块组件衰减超标", + "AIAnalysis_1035": "建议排查", + "AIAnalysis_1036": "实时发电表现深度分析", + "AIAnalysis_1037": "发电量对比分析", + "AIAnalysis_1038": "实际发电", + "AIAnalysis_1039": "预测发电", + "AIAnalysis_1040": "理论发电", + "AIAnalysis_1041": "当日实际发电", + "AIAnalysis_1042": "当日预测发电", + "AIAnalysis_1043": "实时 PR 值", + "AIAnalysis_1044": "诊断总结", + "AIAnalysis_1046": "诊断项", + "AIAnalysis_1047": "影响", + "AIAnalysis_1048": "检测到 36 处热斑疑似点", + "AIAnalysis_1049": "组串失配", + "AIAnalysis_1050": "串组串存在异常", + "AIAnalysis_1051": "遮挡风险", + "AIAnalysis_1052": "局部遮挡影响 2% 发电量", + "AIAnalysis_1053": "组件衰减", + "AIAnalysis_1054": "逆变器异常", + "AIAnalysis_1055": "所有逆变器运行正常", + "AIAnalysis_1056": "建议与处置", + "AIAnalysis_1057": "建议措施", + "AIAnalysis_1058": "收益", + "AIAnalysis_1059": "跟踪衰减组件", + "AIAnalysis_1060": "监控逆变器效率", + "AIAnalysis_1061": "底部:数据来源", + "AIAnalysis_1062": "全栈数据融合来源", + "AIAnalysis_1063": "无人机载荷热成像", + "AIAnalysis_1064": "同步中", + "AIAnalysis_1065": "实时遥测数据", + "AIAnalysis_1066": "云端气象分析站", + "AIAnalysis_1067": "电站运维资产库", + "AIAnalysis_1068": "数据点" + }, + "video": { + "fileDetails": "filedetails", + "sourceDevice": "来源设备", + "startEndTime": "开始结束时间", + "capacity": "容量", + "videoReplayCenter": "视频replay中心", + "allRecordings": "ALLrecordings", + "todayRecordings": "今日recordings", + "timeSpan": "时间span", + "onlineSearch": "在线搜索", + "labelMainFront": "labelmainfront", + "labelRear": "labelrear", + "labelLeft": "label左", + "labelRight": "label右", + "labelFrontDrive": "labelfrontdrive", + "labelRearMount": "labelrearmount", + "labelLeftWing": "label左wing", + "labelRightWing": "label右wing", + "labelCargo": "labelcargo", + "labelMainProbe": "labelmainprobe", + "labelPanorama": "label全景", + "labelDetail": "labeldetail", + "currentDutyDevice": "currentduty设备", + "videoStreamChannels": "视频streamchannels", + "refreshSignal": "刷新signal", + "fullScreenWall": "fullscreenwall", + "ptzServoControl": "PTZservo控制", + "systemIntegratedCommand": "systemintegratedcommand", + "twoWayIntercom": "TWOWAYintercom", + "triggerRemoteRepellent": "triggerremoterepellent", + "aiVisualTracking": "AIvisualtracking", + "activeVideoStreams": "active视频streams", + "positionArray1": "positionarray1", + "domeCamera": "dome摄像头", + "recording": "recording", + "positionInverterRoom": "position逆变器room", + "positionStationGate": "position基站gate", + "positionPerimeterNorth": "positionperimeternorth", + "positionWeatherStation": "positionweather基站", + "positionEquipmentArea": "positionequipmentarea", + "positionStorageBattery": "positionstorage电量", + "positionEnergyStorage": "positionenergystorage", + "positionPerimeterSouth": "positionperimetersouth", + "positionArrayWest": "positionarraywest", + "cameraName": "摄像头名称", + "recordingStatus": "recording状态", + "lastEventTime": "lastevent时间", + "onlineCameras": "在线cameras", + "unitDevice": "单位设备", + "onlineRateTrend": "在线rate趋势", + "offlineCameras": "离线cameras", + "offlineRateTrend": "离线rate趋势", + "todayAlertEvents": "今日告警events", + "unitIncident": "单位incident", + "vsYesterdayPercent": "较昨日percent", + "perimeterAlerts": "perimeter告警", + "storageUsage": "storageusage", + "storageUsageTrend": "storageusage趋势", + "videoMonitorCenter": "视频monitor中心", + "customLayout": "customlayout", + "mainView": "main查看", + "liveLabel": "livelabel", + "cameraDeviceList": "摄像头设备列表", + "searchCameraPlaceholder": "搜索摄像头placeholder", + "allStatuses": "ALLstatuses", + "eventTimeline": "event时间线", + "allTypes": "ALLtypes", + "viewMore": "查看more", + "recentAlertSnapshots": "recent告警snapshots", + "VideoPlayer_849": "前视图", + "VideoPlayer_850": "后视图", + "VideoPlayer_851": "左视图", + "VideoPlayer_852": "右视图", + "VideoPlayer_853": "视频源连接失败", + "Video_1269": "安防型", + "Video_1270": "运输型", + "Video_1271": "巡检型", + "VideoMonitorPage_1272": "中间视频监控中心", + "VideoMonitorPage_1273": "底部三栏", + "VideoMonitorPage_1274": "模拟检测框" + }, + "login": { + "and": "AND", + "Login_1228": "左侧:表单区域", + "Login_1230": "右侧:梯形斜切", + "Login_1231": "图片铺满" + }, + "profile": { + "requestFailed": "请求失败", + "basicInfo": "basic提示", + "phoneInvalid": "phone无效", + "emailInvalid": "邮件无效", + "emailPlaceholder": "邮件placeholder", + "remarkPlaceholder": "remarkplaceholder", + "changePassword": "changepassword", + "oldPasswordPlaceholder": "OLDpasswordplaceholder", + "newPasswordPlaceholder": "NEWpasswordplaceholder", + "passwordLengthError": "passwordlength错误", + "confirmPasswordRequired": "确认passwordrequired", + "confirmPasswordPlaceholder": "确认passwordplaceholder", + "Profile_1232": "关键:给 Row 添加", + "Profile_1233": "让两列自动等高", + "Profile_1234": "左侧信息卡片", + "Profile_1236": "自动填充高度", + "Profile_1237": "右侧表单卡片", + "Profile_1238": "基本资料", + "Profile_1239": "修改密码" + }, + "home": { + "realtimePower": "实时power", + "totalPower": "共power", + "ratedPower": "ratedpower", + "gridConnection": "grid连接", + "gridConnected": "gridconnected", + "frequency": "frequency", + "gridVoltage": "gridvoltage", + "phaseA": "phaseA", + "todayAccumulated": "今日accumulated", + "currentIrradiance": "current辐照度", + "ambientTemperature": "ambient温度", + "powerCurve": "powercurve", + "today": "今日", + "inverterPowerCompare": "逆变器powercompare", + "stringCurrentHeatmap": "组串currentheatmap", + "current": "current", + "Home_1158": "江苏省", + "Home_1159": "南通市", + "Home_1160": "崇川区", + "Home_1161": "西南风", + "Home_1162": "升压站安防巡检任务", + "Home_1163": "草稿", + "Home_1164": "区清洗任务", + "Home_1166": "区巡检任务", + "Home_1167": "已派发", + "Home_1168": "分析", + "Home_1169": "发现逆变器温度与组串电流存在轻微异常,建议优先复核A区设备", + "Home_1170": "发现2处组件存在异常热斑", + "Home_1171": "散热检修建议:建议检查INV-A02散热风扇、风道和运行负载,持续高温时转维修工单", + "Home_1172": "个组串存在发电异常", + "Home_1173": "阵列热斑处置建议:建议现场复核PV-A05-018至PV-A05-020组件接线和旁路二极管状态,必要时更换异常组件", + "Home_1174": "整体发电偏差", + "Home_1176": "待开始", + "Home_1177": "主键ID(新增为空", + "Home_1178": "工单编号(后端生成", + "Home_1179": "光伏组件清洗工单", + "Home_1183": "电站", + "Home_1184": "设备类型:电站", + "Home_1185": "根据电站清洁度建议进行组件清洗,提升发电效率", + "Home_1186": "系统自动生成清洗建议,预计提升发电量", + "Home_1187": "清洗设备、水枪、抹布", + "Home_1188": "影响发电量(%) 数字", + "Home_1190": "工单生成失败", + "Home_1191": "工单生成接口调用失败", + "Home_1192": "加载失败", + "Home_1193": "必须加这个,message才能正常显示", + "Home_1198": "主体布局", + "Home_1199": "左侧图表", + "Home_1200": "今日发电功率趋势", + "Home_1201": "效率", + "Home_1203": "总任务", + "Home_1204": "中间", + "Home_1205": "外部系统", + "Home_1206": "巡检类型", + "Home_1207": "巡检时间", + "Home_1208": "巡检人员", + "Home_1210": "热点检测", + "Home_1211": "发现", + "Home_1212": "处组件存在异常热斑", + "Home_1213": "组串异常", + "Home_1214": "发电偏差分析", + "Home_1215": "告警内容", + "Home_1216": "发生时间", + "Home_1217": "组件阵列", + "Home_1218": "环境传感器", + "Home_1219": "详细", + "Home_1221": "生成工单按钮(已完善", + "Home_1222": "清洁度", + "Home_1223": "建议清洗时间", + "Home_1224": "天内", + "Home_1225": "预计提升发电量", + "Home_1226": "预计提升电量" + }, + "router": { + "realtimeMonitorCenter": "实时monitor中心", + "realtimeMonitorDesc": "实时monitordesc" + }, + "report": { + "monthlyGeneration": "monthlygeneration", + "monthOverMonthUp": "月over月UP", + "prAverage": "PRaverage", + "workOrderCompletionRate": "工作工单completionrate", + "monthlyTotalOrders": "monthly共orders", + "alertClosureRate": "告警closurerate", + "criticalAlertCount": "严重告警数量", + "cleaningRevenueImprovement": "清洗revenueimprovement", + "estimatedAdditionalGeneration": "estimatedadditionalgeneration", + "autoGeneratedReports": "autogeneratedreports", + "unitReport": "单位report", + "monthlyAccumulated": "monthlyaccumulated", + "stationMonthlyReportFile": "基站monthlyreportfile", + "generated": "generated", + "weeklyOperationSummary": "weeklyoperationsummary", + "aiDiagnosisDaily": "AI诊断日", + "generating": "生成中", + "alertClosureWeekly": "告警closureweekly", + "monthlyOperationReport": "monthlyoperationreport", + "monthlyOperationReportDesc": "monthlyoperationreportdesc", + "deviceHealthReport": "设备healthreport", + "deviceHealthReportDesc": "设备healthreportdesc", + "cleaningOptimizationReport": "清洗优化report", + "cleaningEffectReportDesc": "清洗effectreportdesc", + "inspectionRobotReport": "巡检机器人report", + "inspectionRobotReportDesc": "巡检机器人reportdesc", + "stationMonthly": "基站monthly", + "alertWeekly": "告警weekly", + "systemAuto": "systemauto", + "aiAnalysisReport": "AI分析report", + "operationSummary": "operationsummary", + "operationReport": "operationreport", + "cleaningEffectReport": "清洗effectreport", + "overview": "概览", + "reportCycle": "reportcycle", + "reportType": "report类型", + "exportPdf": "导出PDF", + "exportExcel": "导出excel", + "monthlyOperationAnalysis": "monthlyoperation分析", + "generation": "generation", + "predictedGeneration": "predictedgeneration", + "monthlyAccumulatedGeneration": "monthlyaccumulatedgeneration", + "theoreticalGeneration": "theoreticalgeneration", + "generationDeviation": "generationdeviation", + "averageIrradiance": "average辐照度", + "unitIrradiancePerDay": "单位辐照度PER日", + "generationMeteorology": "generationmeteorology", + "workOrderAlertClosure": "工作工单告警closure", + "closureRate": "closurerate", + "alarmCount": "告警数量", + "aiAnalysisStatistics": "AI分析统计", + "unitItem": "单位item", + "subscriptionDispatch": "订阅dispatch", + "subscribers": "subscribers", + "roleOmSupervisor": "角色OMsupervisor", + "roleStationEngineer": "角色基站engineer", + "roleDeviceOm": "角色设备OM", + "roleOmManager": "角色OMmanager", + "dispatchChannels": "dispatchchannels", + "email": "邮件", + "systemNotification": "systemnotification", + "sendPlan": "send计划", + "weeklyAutoSend": "weeklyautosend", + "reportDirectory": "report目录", + "reportName": "report名称", + "templateCenter": "template中心", + "generationRecords": "generationrecords", + "generateTime": "生成时间", + "generator": "generator", + "reportDownloader_5": "适配割草(植被高度)、清洗(灰损率)的不同字段", + "reportDownloader_6": "核心指标", + "reportDownloader_7": "最佳作业窗口", + "reportDownloader_8": "任务列表", + "reportDownloader_9": "算法执行流程", + "ReportCenter_1249": "王运维", + "ReportCenter_1250": "页面头部标签", + "ReportCenter_1251": "左侧主内容区", + "ReportCenter_1252": "月度综合经营分析", + "ReportCenter_1253": "第二排:三个图表", + "ReportCenter_1254": "订阅与分发", + "ReportCenter_1255": "陈经理", + "ReportCenter_1256": "右侧边栏", + "ReportCenter_1257": "报表目录 / 快捷下载", + "ReportCenter_1258": "模板中心", + "ReportCenter_1259": "报表生成记录" + }, + "roles": { + "editSuccess": "编辑成功", + "addSuccess": "新增成功", + "manageMembers": "管理members", + "confirmDeleteRole": "确认删除角色", + "permissionKey": "权限KEY", + "superAdmin": "superadmin", + "orgAdmin": "ORGadmin", + "editRole": "编辑角色", + "roleNamePlaceholder": "角色名称placeholder", + "rolePermission": "角色权限", + "roleKeyPlaceholder": "角色KEYplaceholder", + "roleSortLabel": "角色排序label", + "menuPermissionConfig": "菜单权限配置", + "Roles_1260": "统一卡片容器(和菜单管理一致", + "Roles_1261": "顶部操作栏(和菜单管理统一", + "Roles_1262": "角色卡片列表", + "Roles_1263": "分页组件(正确触发", + "Roles_1264": "新增/编辑弹窗" + }, + "users": { + "notAssigned": "NOTassigned", + "languagePlaceholder": "语言placeholder", + "rolePlaceholder": "角色placeholder", + "siteId": "siteID", + "Users_1266": "回填多选角色", + "Users_1267": "直接提交数组", + "Users_1268": "多角色选择", + "userSlice_1321": "保存用户信息", + "userSlice_1322": "刷新自动恢复" + }, + "page": { + "api_1": "只需要加这一行", + "request_10": "使用全局", + "AgoraRtc_11": "先清理上一次", + "AgoraRtc_12": "视频连接失败", + "Draggable_269": "仅标题栏可拖拽,内容区不触发拖拽", + "Draggable_270": "整体8px外边框", + "Draggable_271": "拖拽标题栏", + "Draggable_272": "关闭按钮", + "Draggable_273": "关闭按钮,阻止拖拽冒泡", + "Draggable_274": "传入的视频内容", + "Empty_275": "自定义文案", + "Empty_276": "组件高度", + "Map_278": "规划航线", + "Map_279": "完成点实时轨迹", + "Map_280": "标记点实体", + "Map_281": "跟随小车的实例", + "Map_282": "直接存 Cartesian3(性能最高", + "Map_283": "强制刷新", + "Map_284": "垂直俯视", + "Map_285": "加这个(只在需要时渲染", + "Map_286": "飞机", + "Map_288": "割草", + "Map_290": "默认车", + "recharts_291": "访问量", + "recharts_292": "浏览量", + "recharts_293": "成交量", + "useWebRTCStream_848": "必须加", + "WeatherSunny_854": "每根", + "circQueue_855": "队列容量 (最多存储项目数", + "circQueue_856": "队列存储区,使用数组存任意对象", + "circQueue_857": "队头索引", + "circQueue_858": "队尾索引", + "circQueue_859": "队满标志", + "circQueue_860": "是否启用深拷贝 (默认启用", + "circQueue_861": "队列最大内存限制 (单位:字节", + "circQueue_862": "当前已使用内存大小 (单位:字节", + "circQueue_863": "判断队列是否为空", + "circQueue_864": "判断队列是否已满", + "circQueue_865": "估算对象大小 (以 JSON 字符串的 UTF-8 编码长度作为近似值", + "circQueue_866": "中用于计算字节长度", + "circQueue_867": "若对象不可序列化,返回 0 (避免出错", + "circQueue_868": "深拷贝对象 (基于 JSON 的方式,仅支持可序列化对象", + "circQueue_869": "入队操作:将对象放入队尾", + "circQueue_870": "估算项目内存大小", + "circQueue_871": "若队满或超过内存限制则入队失败", + "circQueue_872": "深拷贝入队数据 (避免外部修改影响队列", + "circQueue_873": "存入队尾", + "circQueue_874": "环形增长", + "circQueue_875": "增加当前内存使用", + "circQueue_876": "若尾追头,表示队满", + "circQueue_877": "出队操作:从队头取出一个对象", + "circQueue_878": "返回深拷贝", + "circQueue_879": "队空返回", + "circQueue_880": "读取队头项目", + "circQueue_881": "更新内存使用", + "circQueue_882": "出队必然队不满", + "circQueue_883": "返回深拷贝,保护队列内部数据", + "circQueue_884": "丢弃数据:从队尾向前丢弃 len 项 (反向移动", + "circQueue_885": "队空则不能继续丢弃", + "circQueue_886": "定位最后一项", + "circQueue_887": "减去内存大小", + "circQueue_888": "回退", + "circQueue_889": "获取当前队列中元素个数", + "circQueue_890": "循环直到成功入队或超时", + "circQueue_891": "超时退出", + "circQueue_892": "异步轮询", + "circQueue_893": "自旋加 sleep 挂起", + "circQueue_894": "成功取出则返回", + "circQueue_895": "序列化接口,用于", + "circQueue_896": "复制每个元素", + "circQueue_897": "移动到下一个元素", + "tracePoint_898": "防止值重复", + "tracePoint_899": "成员变量(私有", + "tracePoint_900": "循环队列", + "tracePoint_901": "用于存放等待处理的点", + "tracePoint_902": "轨迹点数组", + "tracePoint_903": "存放已完成点", + "tracePoint_904": "当前点", + "tracePoint_905": "完成点索引", + "tracePoint_906": "指向当前点之前的最后一个要插入的完成点的位置", + "tracePoint_907": "用于防止多线程同时更新", + "tracePoint_908": "模式管理器", + "tracePoint_909": "构造函数", + "tracePoint_910": "限制", + "tracePoint_911": "存放轨迹", + "tracePoint_912": "已完成点", + "tracePoint_913": "默认为定位模式", + "tracePoint_914": "复位", + "tracePoint_915": "重置轨迹点", + "tracePoint_916": "清空队列", + "tracePoint_917": "清空轨迹点数组", + "tracePoint_918": "重置完成点索引", + "tracePoint_919": "重置为定位模式", + "tracePoint_920": "释放锁", + "tracePoint_921": "获取轨迹", + "tracePoint_922": "尝试锁定", + "tracePoint_923": "防止多线程同时更新", + "tracePoint_924": "深拷贝", + "tracePoint_925": "避免外部修改导致内部出错", + "tracePoint_926": "解锁", + "tracePoint_927": "锁定中", + "tracePoint_928": "返回空", + "tracePoint_929": "更新当前点", + "tracePoint_930": "只保留完成点部分", + "tracePoint_931": "再插入新的当前点", + "tracePoint_932": "触发事件", + "tracePoint_933": "驱动外部更新", + "tracePoint_934": "支持自动扩展数组", + "tracePoint_935": "如果轨迹数组正在操作", + "tracePoint_936": "先存放至队列中", + "tracePoint_937": "添加完成点", + "tracePoint_938": "先处理队列中积压的点", + "tracePoint_939": "如果队列不为空", + "tracePoint_940": "取出队列中的点", + "tracePoint_941": "错误处理", + "tracePoint_942": "可以输出到", + "tracePoint_943": "文件", + "tracePoint_944": "打印", + "tracePoint_945": "无论成功失败都解锁", + "tracePoint_946": "避免无法使用", + "tracePoint_947": "若当前点存在,说明当前数组末尾已插入一个当前点", + "tracePoint_948": "截断轨迹数组到当前点,将新完成点插入在当前点之前", + "tracePoint_949": "截断避免多添加了无效点", + "tracePoint_950": "然后再当前点之前插入完成点", + "tracePoint_951": "首次添加轨迹点需要先尾插", + "tracePoint_952": "更新当前点索引", + "tracePoint_953": "释放锁资源", + "tryLock_954": "原子尝试锁", + "tryLock_955": "非阻塞且线程安全", + "tryLock_956": "可用于异步多线程", + "utils_957": "最优", + "utils_958": "苏州", + "utils_959": "天气接口调用失败", + "utils_960": "兜底", + "utils_961": "暴雨", + "utils_962": "只取有标题", + "utils_963": "有字段的列", + "utils_964": "转义CSV引号", + "utils_965": "错误 红", + "utils_966": "警告 橙", + "utils_967": "信息 蓝", + "Agri_968": "输入预览", + "Agri_969": "结果图/视频", + "Agri_970": "结果", + "Agri_971": "左侧:结果图", + "Agri_972": "热点标记", + "Agri_973": "请上传热成像文件", + "Agri_974": "热点标记点", + "Agri_975": "右侧:点位详情", + "Agri_976": "总容器:垂直布局,占满卡片内容区", + "Agri_977": "上半部分:点位详情", + "Agri_978": "分割线", + "Agri_979": "下半部分:热点/面板列表", + "Agri_980": "左侧:算法列表", + "Agri_981": "中间:工作区", + "Agri_982": "右侧:历史记录", + "RealTimeMonitor_1240": "左侧大栏", + "RealTimeMonitor_1241": "电站拓扑图", + "RealTimeMonitor_1242": "设备实时监控列表", + "RealTimeMonitor_1243": "右侧小栏", + "RealTimeMonitor_1244": "个统计卡片", + "RealTimeMonitor_1245": "实时功率曲线", + "RealTimeMonitor_1246": "逆变器功率对比", + "RealTimeMonitor_1247": "热力图与在线率", + "RealTimeMonitor_1248": "简易地图点位", + "index_1318": "加这个", + "stationSlice_1319": "当前选中场站(完整对象", + "stationSlice_1320": "方便直接用", + "index_1323": "多租户登录时传入", + "index_1324": "过期时间(秒", + "index_1327": "按钮权限标识", + "index_1328": "不在菜单中展示" + }, + "clean": { + "CleanOptimization_1069": "计划", + "CleanOptimization_1070": "区块", + "CleanOptimization_1071": "待创建", + "CleanOptimization_1072": "王工", + "CleanOptimization_1073": "赵工", + "CleanOptimization_1074": "清洗前发电量", + "CleanOptimization_1075": "清洗后发电量", + "CleanOptimization_1076": "灰尘积累", + "CleanOptimization_1077": "鸟粪遮挡", + "CleanOptimization_1078": "沙尘沉积", + "CleanOptimization_1079": "污染指数", + "CleanOptimization_1080": "中等", + "CleanOptimization_1081": "建议清洗区块数", + "CleanOptimization_1082": "占比", + "CleanOptimization_1083": "预计发电提升", + "CleanOptimization_1084": "占当前日发电量", + "CleanOptimization_1085": "预计收益提升", + "CleanOptimization_1086": "按电价", + "CleanOptimization_1087": "元/kWh 计算", + "CleanOptimization_1088": "上次清洗时间", + "CleanOptimization_1089": "距今 12 天", + "CleanOptimization_1090": "清洗成本预估", + "CleanOptimization_1091": "预计单次清洗总成本", + "CleanOptimization_1092": "标题与操作栏", + "CleanOptimization_1093": "清洗优化管理", + "CleanOptimization_1094": "最近 7 天", + "CleanOptimization_1095": "最近 30 天", + "CleanOptimization_1096": "左侧:分析图表", + "CleanOptimization_1097": "污染趋势分析", + "CleanOptimization_1098": "阈值", + "CleanOptimization_1099": "发电损失构成", + "CleanOptimization_1100": "近 7 天", + "CleanOptimization_1101": "总损失电量", + "CleanOptimization_1102": "清洗前后对比分析", + "CleanOptimization_1103": "近一次", + "CleanOptimization_1104": "清洗收益深度测算", + "CleanOptimization_1105": "电价 (含税", + "CleanOptimization_1106": "预计净收益", + "CleanOptimization_1107": "投资回报", + "CleanOptimization_1108": "回收周期", + "CleanOptimization_1109": "中间:地图分布与工单预览", + "CleanOptimization_1110": "电站污染分布与建议清洗区块分布图", + "CleanOptimization_1111": "污染等级", + "CleanOptimization_1112": "优先清洗", + "CleanOptimization_1113": "较高", + "CleanOptimization_1114": "较低", + "CleanOptimization_1115": "清洁", + "CleanOptimization_1116": "地图背景图与浮动标签", + "CleanOptimization_1117": "模拟区块叠加层", + "CleanOptimization_1118": "具体的污染区块标签", + "CleanOptimization_1119": "右侧悬浮控制按钮", + "CleanOptimization_1120": "清洗工单智能预览", + "CleanOptimization_1121": "查看更多", + "CleanOptimization_1122": "预计工时", + "CleanOptimization_1123": "计划时间", + "CleanOptimization_1124": "创建工单", + "CleanOptimization_1125": "右侧:决策建议与参考", + "CleanOptimization_1126": "智能清洗策略建议", + "CleanOptimization_1127": "算法模型:多因子加权综合优化模型", + "CleanOptimization_1128": "推荐计划", + "CleanOptimization_1129": "建议区块", + "CleanOptimization_1130": "预计提升", + "CleanOptimization_1131": "天气适宜度", + "CleanOptimization_1132": "清洗技术方案推荐", + "CleanOptimization_1133": "智能机器人方案", + "CleanOptimization_1134": "全自动清洗,适合大面积平整区块,效率极高", + "CleanOptimization_1135": "预计用水", + "CleanOptimization_1136": "人工精细化方案", + "CleanOptimization_1137": "灵活处理遮挡及严重污垢区域,适合复杂地形", + "CleanOptimization_1138": "天气与资源环境参考", + "CleanOptimization_1142": "风速", + "CleanOptimization_1143": "适宜", + "CleanOptimization_1144": "降水", + "CleanOptimization_1145": "水源", + "CleanOptimization_1146": "充足", + "CleanOptimization_1147": "清洗作业影响综合评估", + "CleanOptimization_1148": "预计用水量", + "CleanOptimization_1149": "节水 30% (机器人", + "CleanOptimization_1150": "预计总耗时", + "CleanOptimization_1151": "小时", + "CleanOptimization_1152": "并行作业 2 组", + "CleanOptimization_1153": "碳减排贡献", + "CleanOptimization_1154": "减排量", + "CleanOptimization_1155": "作业风险评估", + "CleanOptimization_1156": "安全可控", + "CleanOptimization_1157": "以上决策建议基于历史数据、实时气象及发电损失模型综合计算。实际执行效果可能受天气骤变、现场突发障碍等不确定因素影响" + } + }, + "enAdd": { + "alerts": { + "processing": "processing", + "alarmNo": "alarm No", + "alarmTitle": "alarm Title", + "alarmContent": "alarm Content", + "alertDetail": "alert Detail", + "rootCause": "root Cause", + "recommendedAction": "recommended Action", + "notifySubscription": "notify Subscription", + "sms": "sms", + "dingTalk": "ding Talk", + "email": "email", + "handleSuccess": "handle Success", + "handleFail": "handle Fail", + "exportFileName": "export File Name", + "reportValue": "report Value", + "noAlarmToCreateOrder": "no Alarm To Create Order", + "orderFromAlarm": "order From Alarm", + "orderCreated": "order Created", + "orderCreateFail": "order Create Fail", + "todayTotalAlerts": "today Total Alerts", + "trendNoChange": "trend No Change", + "trendUp": "trend Up", + "trendDown": "trend Down", + "trendZero": "trend Zero", + "yesterdayValue": "yesterday Value", + "todayCriticalAlerts": "today Critical Alerts", + "todayPending": "today Pending", + "todayClosed": "today Closed", + "alertTypeProportion": "alert Type Proportion", + "alertStatusStatistics": "alert Status Statistics", + "sourceDevice": "source Device", + "occurTime": "occur Time", + "alertDescription": "alert Description", + "contactHandler": "contact Handler", + "handleOperation": "handle Operation", + "pleaseSelectHandleStatus": "please Select Handle Status", + "pleaseSelectHandleResult": "please Select Handle Result", + "pleaseInputHandleRemark": "please Input Handle Remark", + "confirmHandle": "confirm Handle", + "modifyAlert": "modify Alert", + "pleaseInputAlarmTitle": "please Input Alarm Title", + "pleaseSelectAlertLevel": "please Select Alert Level", + "pleaseSelectAlertType": "please Select Alert Type", + "pleaseInputAlarmContent": "please Input Alarm Content", + "newOrder": "new Order", + "planStartTime": "plan Start Time", + "pleaseSelectPlanStartTime": "please Select Plan Start Time", + "confirmCreateOrder": "confirm Create Order", + "mockDeviceSensor": "mock Device Sensor", + "mockTempExceed": "mock Temp Exceed", + "mockDeviceCamera": "mock Device Camera", + "mockVideoSignalLost": "mock Video Signal Lost", + "mockDeviceBattery": "mock Device Battery", + "mockBatteryLow": "mock Battery Low", + "level": "level", + "content": "content", + "searchDeviceContent": "search Device Content", + "mockRuleTemp": "mock Rule Temp", + "mockRuleBattery": "mock Rule Battery", + "deviceSensor": "device Sensor", + "deviceBattery": "device Battery", + "ruleName": "rule Name", + "triggerCondition": "trigger Condition", + "ruleConfig": "rule Config", + "addRule": "add Rule", + "editRule": "edit Rule", + "ruleNamePlaceholder": "rule Name Placeholder", + "relatedModel": "related Model", + "monitorProperty": "monitor Property", + "compareSymbol": "compare Symbol", + "threshold": "threshold", + "thresholdPlaceholder": "threshold Placeholder", + "sevenDayTrend": "seven Day Trend", + "criticalAlert": "critical Alert", + "importantAlert": "important Alert", + "generalAlert": "general Alert", + "infoAlert": "info Alert", + "alertLevelDistribution": "alert Level Distribution", + "mockSubOps": "mock Sub Ops", + "mockSubAdmin": "mock Sub Admin", + "mockSubDevice": "mock Sub Device", + "appPush": "app Push", + "subscriptionName": "subscription Name", + "notifyChannel": "notify Channel", + "searchSubscription": "search Subscription", + "addSubscription": "add Subscription", + "editAlertSubscription": "edit Alert Subscription", + "addAlertSubscription": "add Alert Subscription", + "pleaseInputSubscriptionName": "please Input Subscription Name", + "allDevices": "all Devices", + "allLevels": "all Levels", + "pleaseSelectChannel": "please Select Channel", + "subtitle": "subtitle", + "AlarmHistory_14": "Time 范围", + "AlarmHistory_15": "右侧详情抽屉", + "AlarmHistory_16": "通知订阅模块", + "AlarmOverview_17": "Refresh 列表", + "AlarmOverview_18": "Refresh 统计", + "AlarmOverview_25": "修复:等级是字符串,不是数字", + "AlarmOverview_26": "统计卡片", + "AlarmOverview_27": "只清除首尾空格", + "AlarmOverview_28": "Add : Refresh 图表全量告警", + "AlarmOverview_29": "表格", + "AlarmOverview_30": "三个图表", + "AlarmOverview_31": "告警详情抽屉", + "AlarmOverview_32": "告警头部", + "AlarmOverview_33": "告警基础信息", + "AlarmOverview_34": "告警 Description", + "AlarmOverview_35": "根因分析", + "AlarmOverview_36": "推荐 Handle 措施", + "AlarmOverview_37": "Handle Actions", + "AlarmOverview_38": "底部 Button", + "AlarmOverview_39": "Modify 告警弹窗", + "AlarmOverview_40": "Add : Generate Order 弹窗", + "AlarmRealtime_41": "告警详情弹窗", + "AlarmSubscription_42": "短信", + "AlarmSubscription_43": "邮件", + "AlarmSubscription_44": "推送", + "AlarmSubscription_45": "Add / Edit 弹窗" + }, + "common": { + "loadFailed": "load Failed", + "loadDetailFailed": "load Detail Failed", + "requestError": "request Error", + "deviceId": "device Id", + "pleaseInputDeviceId": "please Input Device Id", + "timeRange": "time Range", + "enabled": "enabled", + "modify": "modify", + "apiCallFail": "api Call Fail", + "quantity": "quantity", + "detail": "detail", + "storeValue": "store Value", + "operationFailed": "operation Failed", + "submitFail": "submit Fail", + "confirmDeleteRecord": "confirm Delete Record", + "deleteIrreversible": "delete Irreversible", + "working": "working", + "settings": "settings" + }, + "workOrder": { + "generateOrder": "generate Order", + "newOrders": "new Orders", + "dispatchSuccess": "dispatch Success", + "dispatchFail": "dispatch Fail", + "dispatchRequestError": "dispatch Request Error", + "todayNewOrders": "today New Orders", + "vsYesterday": "vs Yesterday", + "pendingOrders": "pending Orders", + "highPriority": "high Priority", + "runningOrders": "running Orders", + "onTimeRate": "on Time Rate", + "completedOrders": "completed Orders", + "monthlyTotal": "monthly Total", + "avgHandleTime": "avg Handle Time", + "hourUnit": "hour Unit", + "vsLastWeek": "vs Last Week", + "aiSuggestedOrders": "ai Suggested Orders", + "orderUnit": "order Unit", + "autoGeneratedToday": "auto Generated Today", + "getListFailed": "get List Failed", + "pleaseSelectOrder": "please Select Order", + "completeSuccess": "complete Success", + "completeFail": "complete Fail", + "getDetailFailed": "get Detail Failed", + "pleaseSelectOneOrder": "please Select One Order", + "modifySuccess": "modify Success", + "modifyFail": "modify Fail", + "executeSuccess": "execute Success", + "executeFail": "execute Fail", + "suspendSuccess": "suspend Success", + "suspendFail": "suspend Fail", + "invalidOrder": "invalid Order", + "pleaseSelectOrderToDelete": "please Select Order To Delete", + "batchDeleteSuccess": "batch Delete Success", + "batchDeleteFail": "batch Delete Fail", + "orderNo": "order No", + "taskManagement": "task Management", + "overview": "overview", + "timeRange": "time Range", + "keyword": "keyword", + "pleaseInputOrderNoOrTitle": "please Input Order No Or Title", + "list": "list", + "totalWithCount": "total With Count", + "confirmBatchDelete": "confirm Batch Delete", + "batchExecute": "batch Execute", + "statusDistribution": "status Distribution", + "totalCount": "total Count", + "scheduleCalendarView": "schedule Calendar View", + "todaySuffix": "today Suffix", + "noScheduledOrders": "no Scheduled Orders", + "scheduledOrderList": "scheduled Order List", + "planStartTime": "plan Start Time", + "detail": "detail", + "noTitle": "no Title", + "nearItems": "near Items", + "pleaseInputTaskDesc": "please Input Task Desc", + "pendingDispatch": "pending Dispatch", + "pleaseSelectPerson": "please Select Person", + "selectedOrders": "selected Orders", + "pleaseSelectExecuteDevice": "please Select Execute Device", + "pleaseInputResult": "please Input Result", + "pleaseSelectResult": "please Select Result", + "handledCompleted": "handled Completed", + "fixed": "fixed", + "noActionNeeded": "no Action Needed", + "issueClosed": "issue Closed", + "pleaseInputHandleRemark": "please Input Handle Remark", + "alarmNo": "alarm No", + "alarmTitle": "alarm Title", + "WorkOrder_1275": "批量选中", + "WorkOrder_1276": "今日 Add 工单", + "WorkOrder_1277": "Pending 总数", + "WorkOrder_1278": "Pending High Priority", + "WorkOrder_1280": "本月 Completed", + "WorkOrder_1281": "按时 Completion", + "WorkOrder_1282": "平均 Handle 时长", + "WorkOrder_1283": "今日 Add 对比 Yesterday", + "WorkOrder_1284": "平均时长对比上周", + "WorkOrder_1285": "普通用户", + "WorkOrder_1286": "选中人员", + "WorkOrder_1287": "如需真实 Yesterday 对比,单独请求 Yesterday 统计接口", + "WorkOrder_1288": "Handled Complete", + "WorkOrder_1289": "数字转字符串", + "WorkOrder_1290": "清空选中", + "WorkOrder_1291": "Add 加载7天告警", + "WorkOrder_1292": "顶部标题和页签", + "WorkOrder_1293": "左侧:列表", + "WorkOrder_1294": "底部图表", + "WorkOrder_1295": "Date 切换栏", + "WorkOrder_1296": "横向 Time 表头", + "WorkOrder_1297": "单一行 Time 画布,不再区分人员", + "WorkOrder_1298": "More 工单弹窗:弹窗内只展示当前选中 Date 工单", + "WorkOrder_1299": "右侧:详情", + "WorkOrder_1300": "Add :图片", + "WorkOrder_1301": "视频附件预览区域", + "WorkOrder_1302": "图片预览组,支持点击 Zoom In", + "WorkOrder_1303": "底层灰色轨道", + "WorkOrder_1304": "进度填充", + "WorkOrder_1305": "Modify Order 弹窗", + "WorkOrder_1306": "Dispatch 弹窗:选择人员", + "WorkOrder_1307": "工单", + "WorkOrder_1308": "Execute Order 弹窗", + "WorkOrder_1309": "Unknown 设备", + "WorkOrder_1310": "Robot 设备分组", + "WorkOrder_1311": "UAV 分组", + "WorkOrder_1312": "Please select Exec Start Time", + "WorkOrder_1313": "Complete 工单弹窗", + "WorkOrder_1314": "已修复", + "WorkOrder_1315": "无需 Handle", + "WorkOrder_1316": "问题已闭环", + "WorkOrder_1317": "近7天 All 告警弹窗" + }, + "devices": { + "working": "working", + "videoConfigured": "video Configured", + "noVideo": "no Video", + "connectionFailed": "connection Failed", + "permissionRequestTitle": "permission Request Title", + "requestControlPermission": "request Control Permission", + "selectNoDrone": "select No Drone", + "commandIssued": "command Issued", + "commandFailed": "command Failed", + "apiRequestError": "api Request Error", + "selectTaskFirst": "select Task First", + "taskExecutedSuccess": "task Executed Success", + "taskSuspended": "task Suspended", + "taskResumedSuccess": "task Resumed Success", + "taskReturnedHome": "task Returned Home", + "taskStatusUpdateSuccess": "task Status Update Success", + "operationApiError": "operation Api Error", + "permissionObtained": "permission Obtained", + "controlRequestContent": "control Request Content", + "unknownPlatform": "unknown Platform", + "obstacleDetected": "obstacle Detected", + "unknownObject": "unknown Object", + "noTaskDetail": "no Task Detail", + "fetchDetailError": "fetch Detail Error", + "deviceInfoError": "device Info Error", + "pleaseLoginFirst": "please Login First", + "loginInfoExpired": "login Info Expired", + "otherWebControlling": "other Web Controlling", + "networkErrorRetry": "network Error Retry", + "routeLoaded": "route Loaded", + "droneFallbackName": "drone Fallback Name", + "droneAirspace": "drone Airspace", + "droneWayline": "drone Wayline", + "onStandby": "on Standby", + "permissionRequesting": "permission Requesting", + "noWaylineData": "no Wayline Data", + "fetchWaylineFailed": "fetch Wayline Failed", + "waypointsAdded": "waypoints Added", + "waypointDeleted": "waypoint Deleted", + "allWaypointsCleared": "all Waypoints Cleared", + "pleaseSelectWayline": "please Select Wayline", + "selectTaskStartTime": "select Task Start Time", + "createSuccess": "create Success", + "apiErrorCheck": "api Error Check", + "noWaypointsToSave": "no Waypoints To Save", + "planeNoLocalRoute": "plane No Local Route", + "routeSavedLocal": "route Saved Local", + "waylineLabel": "wayline Label", + "fetchWaylineListFailed": "fetch Wayline List Failed", + "routeLoadedWithName": "route Loaded With Name", + "gpsTaskIssued": "gps Task Issued", + "issueTaskFirst": "issue Task First", + "taskPaused": "task Paused", + "taskStarted": "task Started", + "taskStopped": "task Stopped", + "viewFront": "view Front", + "viewBack": "view Back", + "viewLeft": "view Left", + "viewRight": "view Right", + "viewTop": "view Top", + "deviceList": "device List", + "onlineDevices": "online Devices", + "inspectionMachine": "inspection Machine", + "supportsGpsPlan": "supports Gps Plan", + "supportsRemoteGpsPlan": "supports Remote Gps Plan", + "batteryWithValue": "battery With Value", + "videoLinkConfigured": "video Link Configured", + "batteryCapacity": "battery Capacity", + "flightAltitude": "flight Altitude", + "signalSatellite": "signal Satellite", + "envWindSpeed": "env Wind Speed", + "rtkStatus": "rtk Status", + "fixed": "fixed", + "notFixed": "not Fixed", + "remoteControlAndSituation": "remote Control And Situation", + "gpsPlanning": "gps Planning", + "leftWheelDrive": "left Wheel Drive", + "normalRunning": "normal Running", + "realtimeSpeed": "realtime Speed", + "workingCurrent": "working Current", + "rightWheelDrive": "right Wheel Drive", + "gpsWaypointPlan": "gps Waypoint Plan", + "savePlan": "save Plan", + "clearRoute": "clear Route", + "droneWaylineTask": "drone Wayline Task", + "taskWaitingStart": "task Waiting Start", + "taskStartFailure": "task Start Failure", + "taskTerminated": "task Terminated", + "taskTimeout": "task Timeout", + "noWaylineTask": "no Wayline Task", + "robotTaskData": "robot Task Data", + "waypointList": "waypoint List", + "noWaypoints": "no Waypoints", + "waypoint": "waypoint", + "robotLocalRoutes": "robot Local Routes", + "noSavedRoutes": "no Saved Routes", + "waypointsCount": "waypoints Count", + "issueTask": "issue Task", + "suspendTask": "suspend Task", + "cancelReturnHome": "cancel Return Home", + "videoAndConfig": "video And Config", + "noVideoStream": "no Video Stream", + "waylineDetail": "wayline Detail", + "taskIdLabel": "task Id Label", + "waylineUuidLabel": "wayline Uuid Label", + "airportSnLabel": "airport Sn Label", + "landingDockSnLabel": "landing Dock Sn Label", + "returnHomeAltitudeLabel": "return Home Altitude Label", + "returnHomeModeLabel": "return Home Mode Label", + "waylinePrecisionLabel": "wayline Precision Label", + "lostControlActionLabel": "lost Control Action Label", + "resumeFromBreakpointLabel": "resume From Breakpoint Label", + "taskTypeLabel": "task Type Label", + "repeatModeLabel": "repeat Mode Label", + "minBatteryLabel": "min Battery Label", + "currentWaypointLabel": "current Waypoint Label", + "rthOptimal": "rth Optimal", + "rthPreset": "rth Preset", + "gpsMode": "gps Mode", + "rtkHighPrecision": "rtk High Precision", + "modeAuto": "mode Auto", + "modeManual": "mode Manual", + "taskImmediate": "task Immediate", + "taskTimed": "task Timed", + "taskRecurring": "task Recurring", + "taskContinuous": "task Continuous", + "repeatNone": "repeat None", + "repeatDaily": "repeat Daily", + "repeatWeekly": "repeat Weekly", + "repeatMonthlyByDate": "repeat Monthly By Date", + "repeatMonthlyByWeek": "repeat Monthly By Week", + "taskResumed": "task Resumed", + "taskEnded": "task Ended", + "taskTimedOut": "task Timed Out", + "createWaylineTask": "create Wayline Task", + "waylineName": "wayline Name", + "inputWaylineName": "input Wayline Name", + "selectWayline": "select Wayline", + "autoFetch": "auto Fetch", + "optionalEmpty": "optional Empty", + "gpsModeDesc": "gps Mode Desc", + "rtkModeDesc": "rtk Mode Desc", + "autoResumeFromBreakpoint": "auto Resume From Breakpoint", + "resumeAuto": "resume Auto", + "resumeManual": "resume Manual", + "minExecBatteryLabel": "min Exec Battery Label", + "taskWaylineTypeLabel": "task Wayline Type Label", + "taskStartTimeLabel": "task Start Time Label", + "selectStartTimePlaceholder": "select Start Time Placeholder", + "taskEndTimeLabel": "task End Time Label", + "repeatInterval": "repeat Interval", + "boundDrone": "bound Drone", + "airportCode": "airport Code", + "tlStartCleaning": "tl Start Cleaning", + "timelineStatusStart": "timeline Status Start", + "tlStartInspection": "tl Start Inspection", + "tlStartMowing": "tl Start Mowing", + "tlStandbyReady": "tl Standby Ready", + "timelineStatusReady": "timeline Status Ready", + "tlReturnSupply": "tl Return Supply", + "timelineStatusReturn": "timeline Status Return", + "tlFinishInspection": "tl Finish Inspection", + "timelineStatusComplete": "timeline Status Complete", + "logCommand": "log Command", + "logDesc1": "log Desc1", + "logDesc2": "log Desc2", + "logDesc3": "log Desc3", + "logDesc4": "log Desc4", + "logDesc5": "log Desc5", + "logEvent": "log Event", + "logDesc6": "log Desc6", + "logLevelLowBattery": "log Level Low Battery", + "aliasRobotOnly": "alias Robot Only", + "aliasNotEmpty": "alias Not Empty", + "aliasModifiedSuccess": "alias Modified Success", + "aliasModifyFail": "alias Modify Fail", + "aliasApiError": "alias Api Error", + "noPermission": "no Permission", + "stationStatus": "station Status", + "dispatchableCount": "dispatchable Count", + "unitTai": "unit Tai", + "robotExecutingCount": "robot Executing Count", + "mowingRobot": "mowing Robot", + "robotInTaskCount": "robot In Task Count", + "washDrone": "wash Drone", + "unitJia": "unit Jia", + "standbyCount": "standby Count", + "todayTaskExecute": "today Task Execute", + "completionRate": "completion Rate", + "unknownDevice": "unknown Device", + "modeWorking": "mode Working", + "modeDebugLocal": "mode Debug Local", + "modeDebugRemote": "mode Debug Remote", + "modeUpgrading": "mode Upgrading", + "positionInStation": "position In Station", + "taskStandby2": "task Standby2", + "commGood": "comm Good", + "taskStarted2": "task Started2", + "taskPaused2": "task Paused2", + "returnCommandSent": "return Command Sent", + "rechargeCommandSent": "recharge Command Sent", + "emergencyStopTriggered": "emergency Stop Triggered", + "rainNo": "rain No", + "equipmentOverview": "equipment Overview", + "equipmentSituationMap": "equipment Situation Map", + "legendTitle": "legend Title", + "inspectionRoute": "inspection Route", + "robotPath": "robot Path", + "cleaningPath": "cleaning Path", + "robotPosition": "robot Position", + "dronePosition": "drone Position", + "supplyChargePoint": "supply Charge Point", + "obstacleForbiddenZone": "obstacle Forbidden Zone", + "equipmentScheduleList": "equipment Schedule List", + "equipmentName": "equipment Name", + "modeLabel": "mode Label", + "commStatus": "comm Status", + "deviceParamSetting": "device Param Setting", + "controlLogTitle": "control Log Title", + "allLevels": "all Levels", + "deviceColHeader": "device Col Header", + "viewMoreLogs": "view More Logs", + "deviceControlPanel": "device Control Panel", + "detailBtn": "detail Btn", + "batteryWithPercent": "battery With Percent", + "productType": "product Type", + "deviceSnLabel": "device Sn Label", + "activatedTime": "activated Time", + "firmwareVersion": "firmware Version", + "flightWindSpeed": "flight Wind Speed", + "rtkCount": "rtk Count", + "startBtn": "start Btn", + "rechargeBtn": "recharge Btn", + "realtimePicture": "realtime Picture", + "stationVideo": "station Video", + "droneVideo": "drone Video", + "stopVideoBtn": "stop Video Btn", + "switchManual": "switch Manual", + "emergencyStopBtn": "emergency Stop Btn", + "aiAnalysisTitle": "ai Analysis Title", + "hotspotSuspect": "hotspot Suspect", + "countUnit": "count Unit", + "weedHighZone": "weed High Zone", + "blockUnit": "block Unit", + "componentRiskZone": "component Risk Zone", + "fenceAbnormal": "fence Abnormal", + "suggestAction": "suggest Action", + "aiAction1": "ai Action1", + "aiAction2": "ai Action2", + "aiAction3": "ai Action3", + "generateTask": "generate Task", + "aiOptimizePath": "ai Optimize Path", + "oneClickCoordinated": "one Click Coordinated", + "envFlightConditions": "env Flight Conditions", + "windSpeed": "wind Speed", + "rainfall": "rainfall", + "rtkEnvLabel": "rtk Env Label", + "fixedEnv": "fixed Env", + "notFixedEnv": "not Fixed Env", + "suitableExec": "suitable Exec", + "canWorkNormally": "can Work Normally", + "robotWorkStatus": "robot Work Status", + "cumWorkMileage": "cum Work Mileage", + "cumWorkArea": "cum Work Area", + "cumWorkTime": "cum Work Time", + "taskTimelineTitle": "task Timeline Title", + "viewMore": "view More", + "editRobotInfoTitle": "edit Robot Info Title", + "deviceSnColon": "device Sn Colon", + "deviceTypeColon": "device Type Colon", + "deviceAliasLabel": "device Alias Label", + "inputCustomAlias": "input Custom Alias", + "aliasHint": "alias Hint", + "fetchParamFail": "fetch Param Fail", + "paramSaveSuccess": "param Save Success", + "paramSaveFail": "param Save Fail", + "paramSaveException": "param Save Exception", + "paramLeftForwardGain": "param Left Forward Gain", + "paramLeftBackwardGain": "param Left Backward Gain", + "paramRightForwardGain": "param Right Forward Gain", + "paramRightBackwardGain": "param Right Backward Gain", + "paramRunSpeed": "param Run Speed", + "paramChipUidSign": "param Chip Uid Sign", + "paramChipUid": "param Chip Uid", + "paramRemoteConfig": "param Remote Config", + "paramKnifeMotorMode": "param Knife Motor Mode", + "paramWalkMotorMode": "param Walk Motor Mode", + "paramLeftMotorReverse": "param Left Motor Reverse", + "paramRightMotorReverse": "param Right Motor Reverse", + "paramSwapChannel": "param Swap Channel", + "paramUse4G": "param Use4G", + "paramForwardSpeedLimit": "param Forward Speed Limit", + "paramTurnSpeedLimit": "param Turn Speed Limit", + "paramKnifePolarity": "param Knife Polarity", + "paramFanPolarity": "param Fan Polarity", + "paramThrottlePolarity": "param Throttle Polarity", + "paramLiftProtectTime": "param Lift Protect Time", + "paramDualRtk": "param Dual Rtk", + "paramKnifeChannel": "param Knife Channel", + "paramFanChannel": "param Fan Channel", + "paramThrottleChannel": "param Throttle Channel", + "paramRemoteType": "param Remote Type", + "paramRelayBoard": "param Relay Board", + "paramLiftChannel": "param Lift Channel", + "paramChassisChannel": "param Chassis Channel", + "paramArmChannel": "param Arm Channel", + "paramFuelPumpChannel": "param Fuel Pump Channel", + "paramWifiName": "param Wifi Name", + "paramWifiPassword": "param Wifi Password", + "paramBatteryType": "param Battery Type", + "paramDriveType": "param Drive Type", + "paramGearRatio": "param Gear Ratio", + "paramRobotLength": "param Robot Length", + "paramRobotWidth": "param Robot Width", + "paramRobotHeight": "param Robot Height", + "paramKnifeWidth": "param Knife Width", + "paramTyreSize": "param Tyre Size", + "paramFirmwareVersion": "param Firmware Version", + "paramDelFlag": "param Del Flag", + "paramViewEditTitle": "param View Edit Title", + "noData": "no Data", + "locationFail": "location Fail", + "pointMarked": "point Marked", + "minThreePoints": "min Three Points", + "pathPlanDone": "path Plan Done", + "pathPlanFailRetry": "path Plan Fail Retry", + "completePathFirst": "complete Path First", + "inputPlotName": "input Plot Name", + "pathPlanning": "path Planning", + "markedPointsCount": "marked Points Count", + "workMode": "work Mode", + "bowMode": "bow Mode", + "abMode": "ab Mode", + "markPoint": "mark Point", + "clearMarks": "clear Marks", + "complete": "complete", + "selectDevice": "select Device", + "unknownDrone": "unknown Drone", + "situationMap": "situation Map", + "clearPlannedRoute": "clear Planned Route", + "realtimeVideoMonitor": "realtime Video Monitor", + "frontView": "front View", + "backView": "back View", + "leftView": "left View", + "rightView": "right View", + "panorama": "panorama", + "robotStatus": "robot Status", + "rtBattery": "rt Battery", + "rtVoltage": "rt Voltage", + "rtLeftMotor": "rt Left Motor", + "rtRightMotor": "rt Right Motor", + "rtLeftSpeed": "rt Left Speed", + "rtRightSpeed": "rt Right Speed", + "rtLeftTemp": "rt Left Temp", + "rtRightTemp": "rt Right Temp", + "rtMainTemp": "rt Main Temp", + "rtHeadingAngle": "rt Heading Angle", + "rtPitchAngle": "rt Pitch Angle", + "rtHeadingStatus": "rt Heading Status", + "rtInitialized": "rt Initialized", + "rtPositioning": "rt Positioning", + "gpsSingle": "gps Single", + "dgpsDiff": "dgps Diff", + "rtkFixed2": "rtk Fixed2", + "rtkFloat": "rtk Float", + "rtSatellites": "rt Satellites", + "satelliteCount": "satellite Count", + "rtMode": "rt Mode", + "videoStopped": "video Stopped", + "clickPlayHint": "click Play Hint", + "remoteControl": "remote Control", + "savePathTitle": "save Path Title", + "plotNameLabel": "plot Name Label", + "saving": "saving", + "stationCameraNotFound": "station Camera Not Found", + "indoor": "indoor", + "outdoor": "outdoor", + "switchStationCameraFail": "switch Station Camera Fail", + "switchDroneLensFail": "switch Drone Lens Fail", + "wideLens": "wide Lens", + "zoomLens": "zoom Lens", + "irLens": "ir Lens", + "noVideoSignal": "no Video Signal", + "rainYes": "rain Yes", + "hangar": "hangar", + "coverOpen": "cover Open", + "coverClosed": "cover Closed", + "inDock": "in Dock", + "outDock": "out Dock", + "droneStatus": "drone Status", + "horizontalSpeed": "horizontal Speed", + "verticalSpeed": "vertical Speed", + "headingAngle": "heading Angle", + "gpsUnit": "gps Unit", + "taskCompletedMsg": "task Completed Msg", + "confirmDeleteTitle": "confirm Delete Title", + "confirmDeleteContent": "confirm Delete Content", + "confirmDeleteOk": "confirm Delete Ok", + "routeDeletedSuccess": "route Deleted Success", + "deleteFailRetry": "delete Fail Retry", + "deleteFailNetwork": "delete Fail Network", + "taskListRefreshFail": "task List Refresh Fail", + "routeNoPathPoints": "route No Path Points", + "checkRouteFail": "check Route Fail", + "taskExecuteSuccess": "task Execute Success", + "taskExecuteFail": "task Execute Fail", + "taskExecuteException": "task Execute Exception", + "taskCanceledSuccess": "task Canceled Success", + "taskCancelFail": "task Cancel Fail", + "taskCancelException": "task Cancel Exception", + "taskPausedSuccess": "task Paused Success", + "taskPauseFail": "task Pause Fail", + "taskPauseException": "task Pause Exception", + "taskResumeFail": "task Resume Fail", + "taskResumeException": "task Resume Exception", + "waylineFallback": "wayline Fallback", + "waylineListFetchFail": "wayline List Fetch Fail", + "fillRequiredFields": "fill Required Fields", + "createTaskSuccess": "create Task Success", + "createTaskFail": "create Task Fail", + "createTaskException": "create Task Exception", + "trendYesterday": "trend Yesterday", + "trendFlat": "trend Flat", + "taskListFetchFail": "task List Fetch Fail", + "boardTaskFetchFail": "board Task Fetch Fail", + "statusExecSuccess": "status Exec Success", + "statusPreparing": "status Preparing", + "statusExecFailed": "status Exec Failed", + "statusWaitingStart": "status Waiting Start", + "statusStartFailed": "status Start Failed", + "statusRestored": "status Restored", + "statusTerminated": "status Terminated", + "statusTimeout": "status Timeout", + "deviceSn": "device Sn", + "requestFail": "request Fail", + "selectExecuteDroneFirst": "select Execute Drone First", + "waylineTaskCreated": "wayline Task Created", + "waylineExecFail": "wayline Exec Fail", + "todayWaylinePlan": "today Wayline Plan", + "unitTiao": "unit Tiao", + "inProgressDesc": "in Progress Desc", + "executingTasks": "executing Tasks", + "unitGe": "unit Ge", + "pendingApprovalTasks": "pending Approval Tasks", + "taskCompletionRate": "task Completion Rate", + "aiOptimizeGain": "ai Optimize Gain", + "coverageEquipment": "coverage Equipment", + "coverageValue": "coverage Value", + "allOnline": "all Online", + "noDroneSelected": "no Drone Selected", + "commandSent": "command Sent", + "commandSendFail": "command Send Fail", + "apiRequestException": "api Request Exception", + "noDetail": "no Detail", + "fetchDetailException": "fetch Detail Exception", + "taskExecSuccess": "task Exec Success", + "taskSuspended2": "task Suspended2", + "taskRestored2": "task Restored2", + "taskReturnedHome2": "task Returned Home2", + "statusUpdateSuccess": "status Update Success", + "operationFail": "operation Fail", + "operationApiException": "operation Api Exception", + "waylineEditPlan": "wayline Edit Plan", + "optimize": "optimize", + "inspectRoute": "inspect Route", + "mowRoute": "mow Route", + "droneAirport": "drone Airport", + "chargePoint": "charge Point", + "noFlyZone": "no Fly Zone", + "deviceTaskList": "device Task List", + "allDrones": "all Drones", + "taskOrchestrationAI": "task Orchestration AI", + "typeDroneHotspot": "type Drone Hotspot", + "levelHigh": "level High", + "statusSuggestExec": "status Suggest Exec", + "typeFenceInspect": "type Fence Inspect", + "typeComponentClean": "type Component Clean", + "levelMid": "level Mid", + "statusWaitExec": "status Wait Exec", + "typeArrayMow": "type Array Mow", + "typeAIRecheck": "type AI Recheck", + "levelLow": "level Low", + "statusPendingApproval": "status Pending Approval", + "colNo": "col No", + "suggestTime": "suggest Time", + "aiSuggestionColon": "ai Suggestion Colon", + "aiAdvice1": "ai Advice1", + "aiAdvice2": "ai Advice2", + "aiAdvice3": "ai Advice3", + "oneClickSchedule": "one Click Schedule", + "smartAssign": "smart Assign", + "waylineParamsTitle": "wayline Params Title", + "currentWayline": "current Wayline", + "rthMode": "rth Mode", + "waylinePrecision": "wayline Precision", + "resumeBreakpoint": "resume Breakpoint", + "safeRthBattery": "safe Rth Battery", + "repeatTypeLabel": "repeat Type Label", + "lostControlAction": "lost Control Action", + "obstacleAvoidSwitch": "obstacle Avoid Switch", + "nightFlightLimit": "night Flight Limit", + "resumeTask": "resume Task", + "pauseTask": "pause Task", + "weatherWindowTitle": "weather Window Title", + "suitability": "suitability", + "suitableExec2": "suitable Exec2", + "suitable": "suitable", + "precipStatus": "precip Status", + "hasRain": "has Rain", + "gnssQuality": "gnss Quality", + "fixed2": "fixed2", + "notFixed2": "not Fixed2", + "weatherAdvice1": "weather Advice1", + "weatherAdvice2": "weather Advice2", + "waylineList": "wayline List", + "selectDrone": "select Drone", + "searchWaylinePlaceholder": "search Wayline Placeholder", + "unknownTime": "unknown Time", + "executeWayline": "execute Wayline", + "ganttViewTitle": "gantt View Title", + "dronePortA01": "drone Port A01", + "typeHotspotInspect": "type Hotspot Inspect", + "typeRecheck": "type Recheck", + "inspectRobotRbt": "inspect Robot Rbt", + "mowRobotGr": "mow Robot Gr", + "washDroneUav": "wash Drone Uav", + "workTaskBoard": "work Task Board", + "startLabel": "start Label", + "endLabel": "end Label", + "pendingExec": "pending Exec", + "equipmentColon": "equipment Colon", + "startTimeColon": "start Time Colon", + "completedTimeColon": "completed Time Colon", + "createWaylineTaskTitle": "create Wayline Task Title", + "airportSn": "airport Sn", + "landingDockSn": "landing Dock Sn", + "returnHomeAltitude": "return Home Altitude", + "returnHomeMode": "return Home Mode", + "minBattery": "min Battery", + "repeatMode": "repeat Mode", + "weekdayExec": "weekday Exec", + "weekSun": "week Sun", + "weekMon": "week Mon", + "weekTue": "week Tue", + "weekWed": "week Wed", + "weekThu": "week Thu", + "weekFri": "week Fri", + "weekSat": "week Sat", + "monthDays": "month Days", + "whichWeek": "which Week", + "weekNo": "week No", + "weekday": "weekday", + "execTimePoint": "exec Time Point", + "inputTimestamp": "input Timestamp", + "confirmCreate": "confirm Create", + "executeWaylineTask": "execute Wayline Task", + "executeNow": "execute Now", + "selectedWaylineColon": "selected Wayline Colon", + "executeDeviceColon": "execute Device Colon", + "noDeviceSelected": "no Device Selected", + "returnHomeAltitudeColon": "return Home Altitude Colon", + "deviceData": "device Data", + "equipmentControl": "equipment Control", + "realtimePowerData": "realtime Power Data", + "communicationStatus": "communication Status", + "communicationNormal": "communication Normal", + "groupOf": "group Of", + "weatherStation": "weather Station", + "stationTopology": "station Topology", + "communicating": "communicating", + "realtimeMonitorList": "realtime Monitor List", + "allTypes": "all Types", + "searchDeviceName": "search Device Name", + "dronePort": "drone Port", + "deviceOnlineRate": "device Online Rate", + "onlineRate": "online Rate", + "totalCount": "total Count", + "device_2": "Upload 文件必须用 data,不能用", + "device_3": "Upload 文件必加", + "device_4": "修正为正确的添加接口", + "DeviceControl_47": "连接中 Status", + "DeviceControl_48": "权限请求的弹窗", + "DeviceControl_49": "是否有 Control 权限", + "DeviceControl_50": "连接 Failed 重新连接", + "DeviceControl_51": "这里的return会终止整个handleMessage函数的 Execute", + "DeviceControl_52": "清空设备信息", + "DeviceControl_53": "秒后台保活", + "DeviceControl_54": "关键:清空上一个设备的轨迹", + "DeviceControl_55": "取list数组", + "DeviceControl_56": "加上这句", + "DeviceControl_57": "是否存在", + "DeviceControl_58": "连接是否打开", + "DeviceControl_59": "消息 Type", + "DeviceControl_60": "原始手柄数据", + "DeviceControl_61": "认证令牌", + "DeviceControl_62": "Mower 视角切换", + "DeviceControl_63": "Inspection Robot :3路视频", + "DeviceControl_64": "紧凑横向,不滚动, All 可见", + "DeviceControl_65": "主画面:小一点", + "DeviceControl_66": "Mower 单路", + "DeviceControl_67": "修复: Mower 4路视频", + "DeviceControl_68": "绝不压扁,保持", + "DeviceControl_69": "Other 设备", + "DeviceControl_71": "左侧设备列表", + "DeviceControl_72": "任务态势", + "DeviceControl_73": "区域", + "DeviceControl_74": "中间 Actions 区", + "DeviceControl_75": "UAV Status 概览", + "DeviceControl_76": "当前 Location", + "DeviceControl_77": "当前模式", + "DeviceControl_78": "UAV 待命", + "DeviceControl_79": "割草待命", + "DeviceControl_80": "待命", + "DeviceControl_81": "Battery /航向", + "DeviceControl_82": "Enable 遥控", + "DeviceControl_83": "遥控与态势", + "DeviceControl_85": "把视频嵌入到这里", + "DeviceControl_86": "视频区域 - 独立大窗口", + "DeviceControl_87": "Status 卡片行", + "DeviceControl_88": "链路", + "DeviceControl_89": "遥控接管", + "DeviceControl_90": "规划态势", + "DeviceControl_91": "Control 区:左轮", + "DeviceControl_92": "右轮", + "DeviceControl_93": "紧凑布局", + "DeviceControl_94": "左轮 Speed 面板 - Zoom Out", + "DeviceControl_95": "顶部标题", + "DeviceControl_96": "仪表盘(居中 Zoom In", + "DeviceControl_98": "电流 一体化卡片(核心视觉", + "DeviceControl_99": "电流", + "DeviceControl_100": "手柄 - Zoom Out 、居中", + "DeviceControl_101": "右轮 Speed 面板 - Zoom Out", + "DeviceControl_103": "仪表盘(居中优化", + "DeviceControl_104": "核心数据卡片: Speed", + "DeviceControl_105": "目标值(干净对称", + "DeviceControl_106": "实时 Speed", + "DeviceControl_107": "Speed 输出", + "DeviceControl_108": "规划", + "DeviceControl_109": "Status 标签(和你原版完全一样", + "DeviceControl_110": "下发任务", + "DeviceControl_113": "UAV 专用任务 Control Button", + "DeviceControl_114": "动态 Button : In Progress", + "DeviceControl_115": "Suspend / Suspended", + "DeviceControl_117": "Pause / Resume", + "DeviceControl_118": "动态: Return / Cancel Return", + "DeviceControl_119": "右侧视频面板(遥控页隐藏)- 保持不变", + "DeviceControl_120": "Device Type : Robot", + "DeviceControl_121": "显示 4 路视频", + "DeviceControl_122": "Device Type : Mower", + "DeviceControl_123": "显示 1 路视频", + "DeviceControl_124": "Device Type : UAV", + "DeviceControl_125": "遍历显示视频流", + "DeviceControl_126": "Delete 原来的悬浮视频窗口代码", + "DeviceControl_127": "旋转加载圈", + "DeviceControl_128": "飞行参数", + "DeviceControl_129": "Time 选择", + "DeviceControl_130": "重复任务", + "deviceController_131": "Save 当前设备", + "deviceController_132": "后台保活定时器", + "deviceController_133": "Other 端正在 Control", + "deviceController_134": "权限校验 Failed", + "deviceController_135": "无 Control 权限", + "deviceController_136": "请求 Control Medium", + "deviceController_137": "Control 请求超时", + "deviceController_138": "Control Failed", + "deviceController_139": "Control Success", + "deviceController_140": "权限请求", + "deviceController_141": "请求 Control 设备", + "deviceController_142": "车辆", + "deviceController_143": "围栏", + "deviceController_144": "坑洞", + "deviceController_145": "草丛", + "deviceController_146": "检测到", + "deviceController_147": "Unknown 物", + "DeviceOverviewPage_149": "默认都算", + "DeviceOverviewPage_150": "只有切设备才变", + "DeviceOverviewPage_151": "视频不闪", + "DeviceOverviewPage_152": "Working Status 数量", + "DeviceOverviewPage_153": "左侧文字区域", + "DeviceOverviewPage_154": "底部环比/ Remark", + "DeviceOverviewPage_155": "右侧圆形图标区域", + "DeviceOverviewPage_156": "关键:videoDevice 绝不随MQTT更新,视频永不闪烁", + "DeviceOverviewPage_157": "Robot Edit 别名弹窗", + "DeviceOverviewPage_158": "设备参数 View 与 Modify 弹窗(通用组件", + "DeviceParamModal_159": "加载时表单区域简单遮罩 Info (打开弹窗拉取参数时的loading反馈", + "DeviceStatusPage_160": "持久化 UAV 数据", + "DeviceStatusPage_161": "持久化机场数据", + "DeviceStatusPage_162": "Complete 点", + "DeviceStatusPage_163": "Start Time 戳", + "DeviceStatusPage_164": "End Time 戳", + "DeviceStatusPage_165": "标记的点", + "DeviceStatusPage_166": "是否 Complete 路径规划", + "DeviceStatusPage_167": "Save 的路径数据", + "DeviceStatusPage_168": "Save 模态框可见性", + "DeviceStatusPage_169": "地块 Name", + "DeviceStatusPage_170": "是否正在 Save", + "DeviceStatusPage_171": "地图", + "DeviceStatusPage_172": "拿到路由上的", + "DeviceStatusPage_173": "持久化数据最后覆盖,确保优先显示", + "DeviceStatusPage_174": "存储 Back 的路径结果", + "DeviceStatusPage_175": "页面顶部 Button 区域", + "DeviceStatusPage_176": "UAV 专用布局", + "DeviceStatusPage_177": "左侧:机场视频", + "DeviceStatusPage_178": "UAV 场视频", + "DeviceStatusPage_179": "机场 Status", + "DeviceStatusPage_180": "右侧: UAV 视频", + "DeviceStatusPage_181": "UAV Status", + "DeviceStatusPage_182": "UAV 视频", + "DeviceStatusPage_183": "视频层: Stop 时不渲染 / 未 Play 时为空,不影响 Status 面板", + "DeviceStatusPage_184": "视频未加载 / Stop Play 的占位", + "DeviceStatusPage_185": "单视频(非全景)模式 Status 叠加层:始终渲染,和视频层解耦", + "DeviceStatusPage_186": "右边 - 占1格,上部地图,下部 Remote Control", + "DeviceStatusPage_187": "Remote Control 面板", + "DeviceStatusPage_188": "Save 路径模态框", + "DraggablePanel_189": "拖拽手柄", + "DraggablePanel_190": "子内容", + "DroneVideoPlayer_191": "默认选择室外", + "DroneVideoPlayer_192": "默认选择广角", + "DroneVideoPlayer_193": "目前先默认用第一个 Camera", + "DroneVideoPlayer_194": "Reset 另一边的加载 Status", + "DroneVideoPlayer_195": "确保在双视频模式下有最小宽度", + "DroneVideoPlayer_196": "视频 Control Button", + "DroneVideoPlayer_197": "Status 详情叠加层 - 左上角", + "DroneVideoPlayer_198": "底部信息叠加 - 显示经 Latitude", + "mowerAllView_199": "三列之间间距", + "mowerAllView_200": "左列:左视", + "mowerAllView_201": "左视", + "mowerAllView_202": "中列:上-前视 下-后视", + "mowerAllView_203": "前视", + "mowerAllView_204": "后视", + "mowerAllView_205": "右视", + "RobotTaskPage_206": "是否 Execute Task", + "RobotTaskPage_207": "正在 Execute 的任务", + "RobotTaskPage_208": "Control 视频组件显示", + "RobotTaskPage_212": "Robot 设备", + "RobotTaskPage_213": "任务 Complete Close 视频", + "RobotTaskPage_214": "清空地图轨迹", + "RobotTaskPage_215": "设置当前任务", + "RobotTaskPage_216": "弓子 2 回子", + "RobotTaskPage_217": "连接 Success 后自动打开视频", + "RobotTaskPage_218": "加宽一点", + "RobotTaskPage_219": "原始 Status 值", + "RobotTaskPage_220": "子元素收缩兜底,超长时允许压缩换行", + "RobotTaskPage_221": "开启自动换行,小屏 Button 自动折行", + "RobotTaskPage_222": "优化", + "RobotTaskPage_223": "Robot Status 悬浮面板 - 绝对定位左上角", + "RobotTaskPage_224": "避开左侧图例面板,防止重叠", + "RobotTaskPage_225": "限制最大宽度,不会无限拉长", + "RobotTaskPage_226": "视频开关", + "RobotTaskPage_227": "Robot Task 模块,带Tab切换", + "RobotTaskPage_229": "Delete Button", + "RobotTaskPage_230": "Execute Button", + "RobotTaskPage_231": "传递 routeList 给弹窗", + "RobotTaskPage_232": "Execute Task 弹窗", + "RobotTaskPage_237": "周选择", + "RobotTaskPage_238": "月选择", + "RobotTaskPage_239": "按天:选择 Date", + "WayLinePage_242": "航线列表", + "WayLinePage_243": "航线列表默认选中第一个", + "WayLinePage_244": "使用航线列表 UAV", + "WayLinePage_245": "地图卡片", + "WayLinePage_246": "独立筛选 - 设备任务列表", + "WayLinePage_247": "Resume 任务", + "WayLinePage_248": "底部内容区", + "WayLinePage_249": "航线列表独立 UAV 筛选", + "WayLinePage_250": "Execute Button 改为图标", + "WayLinePage_251": "Task Name :自动换行", + "WayLinePage_252": "Time :自动换行", + "WayLinePage_253": "Create Task 弹窗", + "WayLinePage_254": "重复任务配置", + "WayLinePage_255": "对象绑定", + "WayLinePage_256": "每周", + "WayLinePage_257": "按月 Date", + "WayLinePage_258": "按月星期", + "WayLinePage_260": "Execute Route 设置 Return Altitude", + "XboxController_261": "只有这里才发", + "XboxController_262": "空依赖", + "XboxController_263": "强制触发一次", + "XboxController_264": "页面切换依赖", + "XboxController_265": "未连接设备", + "XboxController_266": "Other Web 端已连接", + "XboxController_267": "Control Medium", + "XboxController_268": "点击获取 Control 权限" + }, + "systemSetting": { + "pleaseSelectRoles": "please Select Roles", + "batchDeleteRolesTitle": "batch Delete Roles Title", + "confirmDeleteSelectedRoles": "confirm Delete Selected Roles", + "batchDeleteSuccess": "batch Delete Success", + "resetFail": "reset Fail", + "pleaseSelectUsers": "please Select Users", + "batchDeleteUsersTitle": "batch Delete Users Title", + "confirmDeleteSelectedUsers": "confirm Delete Selected Users", + "userAndRole": "user And Role", + "belongingOrg": "belonging Org", + "belongingSite": "belonging Site", + "devicePerm": "device Perm", + "hasPerm": "has Perm", + "noPerm": "no Perm", + "confirmDeleteUser": "confirm Delete User", + "totalCount": "total Count", + "permissionLabel": "permission Label", + "levelLabel": "level Label", + "editRole": "edit Role", + "roleLevel": "role Level", + "pleaseInputRoleLevel": "please Input Role Level", + "account": "account", + "nicknameShort": "nickname Short", + "phoneShort": "phone Short", + "editPasswordPlaceholder": "edit Password Placeholder", + "chooseRole": "choose Role", + "pleaseSelectOrg": "please Select Org", + "pleaseSelectSite": "please Select Site", + "deviceOperatePermLabel": "device Operate Perm Label", + "has": "has", + "none": "none", + "AlarmSettingsManagement_294": "Error Code 下拉数据源", + "AlarmSettingsManagement_295": "工单模型下拉数据源", + "AlarmSettingsManagement_296": "获取告警模型列表 Failed", + "AlarmSettingsManagement_297": "Confirm Delete", + "AlarmSettingsManagement_298": "您 OK 要 Delete 这条告警模型吗", + "AlarmSettingsManagement_299": "请先勾选要 Delete 的数据", + "AlarmSettingsManagement_300": "Batch delete Confirm", + "AlarmSettingsManagement_301": "您 OK 要 Delete 选中的", + "AlarmSettingsManagement_302": "条告警模型吗", + "AlarmSettingsManagement_303": "Delete 后不可 Resume", + "AlarmSettingsManagement_304": "批量 Deleted", + "AlarmSettingsManagement_305": "批量 Delete failed", + "AlarmSettingsManagement_306": "更新 Success", + "AlarmSettingsManagement_309": "关联工单模型", + "AlarmSettingsManagement_310": "自动 Generate Order", + "AlarmSettingsManagement_311": "Add 告警模型", + "AlarmSettingsManagement_313": "Edit 告警模型", + "AlarmSettingsManagement_314": "Error Code (关联错误管理", + "AlarmSettingsManagement_315": "Please select Error Code", + "AlarmSettingsManagement_316": "工单模型ID(关联工单模型", + "AlarmSettingsManagement_317": "Please select 工单模型", + "AlarmSettingsManagement_319": "是否自动 Generate Order", + "AllDeviceManagement_321": "Clean Robot", + "AllDeviceManagement_324": "电表", + "AllDeviceManagement_325": "O&M Robot", + "AllDeviceManagement_326": "气象站", + "AllDeviceManagement_327": "设备编码", + "AllDeviceManagement_328": "运行", + "AllDeviceManagement_329": "Edit 设备", + "AllDeviceManagement_330": "OK 要 Delete 设备", + "AllDeviceManagement_331": "Please enter Device Name", + "BasicSettings_332": "设置已 Save", + "BasicSettings_333": "当前版本", + "BasicSettings_334": "最新版", + "BasicSettings_335": "告警通道", + "BasicSettings_336": "数据备份 Status", + "BasicSettings_337": "最近更新", + "BasicSettings_338": "超级管理员", + "BasicSettings_339": "All 权限", + "BasicSettings_340": "系统管理", + "BasicSettings_341": "O&M 工程师", + "BasicSettings_342": "工单管理", + "BasicSettings_343": "Inspection 主管", + "BasicSettings_344": "Inspection 管理", + "BasicSettings_345": "告警 Handle", + "BasicSettings_346": "数据分析师", + "BasicSettings_347": "数据分析", + "BasicSettings_348": "报表 View", + "BasicSettings_349": "接口", + "BasicSettings_350": "顶部统计卡片", + "BasicSettings_351": "左侧", + "BasicSettings_352": "基础配置", + "BasicSettings_353": "默认电站", + "BasicSettings_354": "首页默认页面", + "BasicSettings_355": "数据 Refresh 频率", + "BasicSettings_356": "分钟", + "BasicSettings_357": "地图服务", + "BasicSettings_358": "高德地图", + "BasicSettings_359": "工单自动编号", + "BasicSettings_360": "建议自动生成", + "BasicSettings_361": "移动端同步", + "BasicSettings_362": "Save 设置", + "BasicSettings_363": "Resume 默认", + "BasicSettings_364": "通知与告警策略", + "BasicSettings_365": "已 Enable", + "BasicSettings_366": "系统通知", + "BasicSettings_367": "Critical 告警立即通知", + "BasicSettings_368": "发电 Down 超过", + "BasicSettings_369": "通信中断通知", + "BasicSettings_370": "每日汇总推送", + "BasicSettings_371": "夜间免打扰", + "BasicSettings_372": "用户与权限预览", + "BasicSettings_373": "用户名", + "BasicSettings_375": "张 O&M", + "BasicSettings_376": "李工程师", + "BasicSettings_377": "王班长", + "BasicSettings_378": "赵分析师", + "BasicSettings_379": "右侧", + "BasicSettings_380": "Role 权限概览", + "BasicSettings_381": "用户", + "BasicSettings_382": "权限", + "BasicSettings_383": "Device Access Status", + "BasicSettings_384": "详情", + "BasicSettings_386": "数据保留", + "BasicSettings_387": "Login 审计", + "BasicSettings_388": "已开启", + "BasicSettings_389": "自动备份", + "BasicSettings_390": "密钥", + "BasicSettings_391": "最近备份", + "BasicSettings_392": "证书", + "BasicSettings_393": "有效期至", + "BasicSettings_394": "双因素认证", + "BasicSettings_396": "管理权限", + "BasicSettings_397": "清理缓存", + "BasicSettings_398": "重启服务", + "BasicSettings_399": "备份 Download", + "BasicSettings_400": "日志 Export", + "CameraManagement_401": "东门入口 Camera", + "CameraManagement_402": "园区东门", + "CameraManagement_403": "HD 球机", + "CameraManagement_404": "海康威视", + "CameraManagement_405": "西门入口 Camera", + "CameraManagement_406": "园区西门", + "CameraManagement_407": "HD 枪机", + "CameraManagement_408": "大华", + "CameraManagement_409": "停车场 Camera", + "CameraManagement_410": "地下停车场", + "CameraManagement_411": "办公楼大堂 Camera", + "CameraManagement_412": "办公楼1层大堂", + "CameraManagement_413": "半球摄像机", + "CameraManagement_414": "宇视", + "CameraManagement_415": "仓库监控 Camera", + "CameraManagement_416": "物料仓库", + "CameraManagement_417": "Camera Type", + "CameraManagement_418": "品牌", + "CameraManagement_419": "分辨率", + "CameraManagement_420": "云台", + "CameraManagement_421": "支持", + "CameraManagement_422": "不支持", + "CameraManagement_423": "夜视", + "CameraManagement_424": "预览", + "CameraManagement_425": "视频预览", + "CameraManagement_426": "视频预览占位", + "CameraManagement_427": "OK 要 Delete Camera", + "CameraManagement_428": "Camera 总数", + "CameraManagement_429": "Offline / Fault", + "CameraManagement_430": "Please enter Name /编码", + "CameraManagement_431": "Status 筛选", + "CameraManagement_432": "Add Camera", + "CameraManagement_433": "Edit Camera", + "CameraManagement_434": "Please enter 设备编码", + "CameraManagement_435": "Please enter IP", + "CameraManagement_436": "例如", + "CameraManagement_437": "Please select Type", + "CameraManagement_438": "全景摄像机", + "CameraManagement_439": "Please select 品牌", + "CameraManagement_440": "Please select 分辨率", + "CameraManagement_441": "安装 Location", + "CameraManagement_442": "Please enter 安装 Location", + "CameraManagement_443": "云台 Control", + "CameraManagement_444": "夜视功能", + "DeviceAccessPage_445": "替换这行", + "DeviceAccessPage_446": "录像 Status", + "DeviceAccessPage_447": "周界入侵告警", + "DeviceAccessPage_448": "湿度", + "DeviceAccessPage_449": "超温告警", + "DeviceAccessPage_450": "电压", + "DeviceAccessPage_451": "Low Battery 告警", + "DeviceAccessPage_452": "Delete 后 Refresh", + "DeviceAccessPage_454": "行为配置回显", + "DeviceAccessPage_455": "Edit 才有", + "DeviceAccessPage_457": "默认给一个", + "DeviceAccessPage_458": "阵列区监控设备", + "DeviceAccessPage_460": "Edit 设备 Success", + "DeviceAccessPage_461": "Add 设备 Success", + "DeviceAccessPage_463": "产品 Updated", + "DeviceAccessPage_464": "产品 Added", + "DeviceAccessPage_465": "改成", + "DeviceAccessPage_466": "产品", + "DeviceAccessPage_467": "模型", + "DeviceAccessPage_468": "端口", + "DeviceAccessPage_469": "Account", + "DeviceAccessPage_470": "模型 Name", + "DeviceAccessPage_471": "连接协议", + "DeviceAccessPage_472": "属性列表", + "DeviceAccessPage_473": "无属性", + "DeviceAccessPage_474": "指令列表", + "DeviceAccessPage_475": "无指令", + "DeviceAccessPage_476": "OK 要 Delete 该物模型吗", + "DeviceAccessPage_477": "产品管理", + "DeviceAccessPage_478": "物模型管理", + "DeviceAccessPage_479": "Add 设备", + "DeviceAccessPage_480": "产品编码", + "DeviceAccessPage_481": "Please enter 编码", + "DeviceAccessPage_482": "产品 Name", + "DeviceAccessPage_483": "Please enter Name", + "DeviceAccessPage_484": "生产厂家", + "DeviceAccessPage_485": "Please enter 厂家", + "DeviceAccessPage_486": "协议编码", + "DeviceAccessPage_487": "Please select 协议", + "DeviceAccessPage_488": "传输方式", + "DeviceAccessPage_489": "禁用", + "DeviceAccessPage_490": "Add 产品", + "DeviceAccessPage_491": "协议", + "DeviceAccessPage_492": "上报", + "DeviceAccessPage_493": "订阅", + "DeviceAccessPage_494": "字段映射", + "DeviceAccessPage_495": "OK 要 Delete 该产品吗", + "DeviceAccessPage_496": "Add 物模型", + "DeviceAccessPage_497": "设备配置", + "DeviceAccessPage_498": "产品配置", + "DeviceAccessPage_499": "物模型配置", + "DeviceAccessPage_500": "Please enter 设备", + "DeviceAccessPage_501": "关联产品", + "DeviceAccessPage_502": "Please select 关联产品", + "DeviceAccessPage_503": "配置", + "DeviceAccessPage_504": "地址", + "DeviceAccessPage_505": "默认", + "DeviceAccessPage_506": "Login Account", + "DeviceAccessPage_507": "设备 Login Account", + "DeviceAccessPage_508": "Login Password", + "DeviceAccessPage_509": "设备 Login Password", + "DeviceAccessPage_510": "服务地址", + "DeviceAccessPage_511": "客户端唯一标识", + "DeviceAccessPage_512": "Account (选填", + "DeviceAccessPage_514": "Password (选填", + "DeviceAccessPage_515": "告警阈值配置", + "DeviceAccessPage_516": "上限", + "DeviceAccessPage_517": "上限值", + "DeviceAccessPage_518": "下限", + "DeviceAccessPage_519": "下限值", + "DeviceAccessPage_520": "当前物模型 Not set 告警属性,无法配置阈值", + "DeviceAccessPage_521": "产品编号", + "DeviceAccessPage_522": "智能网关", + "DeviceAccessPage_523": "厂商", + "DeviceAccessPage_524": "迈步科技", + "DeviceAccessPage_525": "传输 Type", + "DeviceAccessPage_526": "绑定物模型", + "DeviceAccessPage_527": "Please select 要绑定的物模型", + "DeviceAccessPage_528": "协议自动配置项", + "DeviceAccessPage_529": "属性配置(你原来的", + "DeviceAccessPage_530": "物模型属性配置", + "DeviceAccessPage_533": "Please select 物模型以显示属性", + "DeviceAccessPage_534": "行为(指令)配置 Add", + "DeviceAccessPage_535": "物模型行为(指令)配置", + "DeviceAccessPage_536": "默认值", + "DeviceAccessPage_537": "该物模型暂无指令", + "DeviceAccessPage_538": "属性", + "DeviceAccessPage_539": "编码", + "DeviceAccessPage_540": "单位", + "DeviceAccessPage_541": "指令", + "DeviceAccessPage_542": "指令编码", + "DeviceAccessPage_543": "指令 Name", + "DeviceAccessPage_544": "参数", + "DeviceAccessPage_545": "属性弹窗", + "DeviceAccessPage_546": "配置属性", + "DeviceAccessPage_547": "属性编码", + "DeviceAccessPage_548": "属性 Name", + "DeviceAccessPage_549": "如 Temperature", + "DeviceAccessPage_550": "告警参数", + "DeviceAccessPage_551": "指令弹窗", + "DeviceAccessPage_552": "配置指令", + "DeviceAccessPage_553": "指令参数", + "DeviceAccessPage_554": "参数名", + "DeviceAccessPage_555": "枚举", + "DeviceAccessPage_556": "分隔", + "DeviceAccessPage_557": "标准字段", + "DeviceAccessPage_558": "厂商原始字段", + "DeviceAccessPage_559": "转换规则", + "DeviceManagement_560": "UAV 管理", + "DeviceManagement_561": "智能装备", + "DeviceManagement_562": "Camera 管理", + "DeviceManagement_563": "Robot 生成", + "DroneManagement_564": "获取 UAV 列表 Failed", + "DroneManagement_565": "请先在列表中选中需要绑定的 UAV", + "DroneManagement_566": "绑定 Success", + "DroneManagement_567": "绑定 Failed", + "DroneManagement_568": "Actions Abnormal", + "DroneManagement_569": "未选择有效设备,无法解绑", + "DroneManagement_570": "人员", + "DroneManagement_571": "场站", + "DroneManagement_572": "组织", + "DroneManagement_573": "Confirm 解绑", + "DroneManagement_574": "OK 要为选中的 UAV 清除", + "DroneManagement_575": "绑定吗", + "DroneManagement_576": "解绑 Success", + "DroneManagement_577": "解绑 Failed", + "DroneManagement_578": "机场", + "DroneManagement_579": "所属组织", + "DroneManagement_580": "未绑定", + "DroneManagement_581": "所属场站", + "DroneManagement_582": "人员解绑", + "DroneManagement_583": "场站解绑", + "DroneManagement_584": "组织解绑", + "DroneManagement_585": "绑定 UAV", + "DroneManagement_586": "批量解绑(用户", + "DroneManagement_587": "批量解绑(场站", + "DroneManagement_588": "批量解绑(组", + "DroneManagement_589": "Selected UAV", + "DroneManagement_591": "Please select 组织", + "DroneManagement_592": "Please select 场站", + "DroneManagement_593": "请先选择组织", + "DroneManagement_594": "Please select Leader", + "DroneManagement_595": "请先选择场站后选择 Leader", + "ErrorManagement_596": "Search 区域完全不变", + "ErrorManagement_597": "Add / Edit / View 弹窗(核心优化区域", + "ErrorManagement_598": "第一行:基础编码 Name", + "ErrorManagement_599": "第二行:来源、等级、 Check Field", + "ErrorManagement_600": "第三行:对比 Type", + "ErrorManagement_601": "对比值(自动切换单输入/区间双输入", + "ErrorManagement_602": "区间 Type", + "ErrorManagement_603": "显示两个输入框", + "FlvPlayer_606": "当前浏览器不支持 FLV MSE Play ,请 Download 文件 View", + "FlvPlayer_607": "适配", + "FlvPlayer_608": "关键:大文件点播不能false,否则一次性 All Download 立刻 Close", + "FlvPlayer_609": "视频编码不受浏览器MSE支持,请 Download 文件 Local Play", + "FlvPlayer_610": "媒体源不支持", + "FlvPlayer_611": "Play Abnormal ,建议 Download 文件 Local Play", + "FlvPlayer_612": "网络错误,请检查 Video URL", + "FlvPlayer_613": "媒体解码错误,该FLV文件可能不兼容浏览器", + "FlvPlayer_614": "加载超时,浏览器MSE解析 Failed ,请 Download 文件 Local Play", + "FlvPlayer_615": "Play 器初始化 Failed ,请重试", + "FlvPlayer_616": "视频加载 Failed ,请检查地址或 Download", + "FlvPlayer_617": "Play Failed", + "FlvPlayer_618": "重试", + "FlvPlayer_619": "Download 视频", + "FlvPlayer_620": "点击手动 Play 视频", + "FlvPlayer_621": "使用", + "FlvPlayer_622": "解码,部分特殊编码FLV浏览器无法渲染,建议 Download Local Play", + "FlvPlayer_623": "非FLV使用浏览器原生 Play", + "LogManagement_624": "系统", + "LogManagement_625": "Robot 智慧服务系统 Alpha 三服务(接入/ Status /命令)启动 Complete", + "LogManagement_626": "命令服务", + "LogManagement_627": "向 ROBOT-001 下发移动命令", + "LogManagement_628": "Execute 结果: Success", + "LogManagement_629": "Add O&M 工程师 Account", + "LogManagement_630": "李工", + "LogManagement_631": "Role : O&M 工程师", + "LogManagement_632": "心跳超时", + "LogManagement_633": "已自动置为 Offline", + "LogManagement_634": "自主 Inspection", + "LogManagement_635": "超时,已自动重试 1 次后仍 Failed", + "LogManagement_636": "Modify System Name 与数据 Refresh 频率", + "LogManagement_637": "Status 服务", + "LogManagement_638": "设备 ROBOT-001 上报 Status : Battery 85%,模式", + "LogManagement_639": "Inverter INV-03 输出功率 vs Yesterday Down 16%,触发预警", + "LogManagement_640": "调整 CAM-04 热成像通道码率参数", + "LogManagement_641": "每日自动备份 Complete ,备份文件", + "LogManagement_642": "Unknown 设备尝试注册(secret 校验 Failed ),已拒绝,来源", + "LogManagement_643": "Role 授权", + "LogManagement_644": "Report Center - Export", + "LogManagement_645": "日志已 Refresh", + "LogManagement_646": "级别", + "LogManagement_648": "消息", + "LogManagement_650": "已 Export", + "LogManagement_651": "条日志", + "LogManagement_652": "系统运行、 Device Control 与管理员 Actions 流水,仅超级管理员可 View", + "LogManagement_653": "All 级别", + "LogManagement_654": "Search 消息 / Operator / 来源", + "MenuList_655": "Menu 加载 Failed", + "MenuList_656": "Add 子 Menu", + "MenuList_657": "Add Menu", + "MenuList_658": "Close 分页", + "MenuList_659": "Menu 弹窗", + "MenuList_660": "Edit Menu", + "MenuList_661": "无(一级 Menu", + "MenuList_662": "Please enter Menu Name", + "MenuList_663": "显示 Sort", + "MenuList_664": "数字越小越靠前", + "MenuList_665": "图标选择弹窗", + "MenuList_666": "选择 Menu Icon", + "OnlineUser_667": "研发部门", + "OnlineUser_668": "内网", + "OnlineUser_669": "默认选中 Online Users", + "OnlineUser_670": "Search Complete", + "OnlineUser_671": "已 Reset Search 条件", + "OnlineUser_672": "已向会话", + "OnlineUser_673": "发送强退指令", + "OnlineUser_674": "序号", + "OnlineUser_675": "会话编号", + "OnlineUser_676": "Login Name", + "OnlineUser_677": "所属部门", + "OnlineUser_678": "主机", + "OnlineUser_679": "Login 地点", + "OnlineUser_680": "Actions 系统", + "OnlineUser_681": "浏览器", + "OnlineUser_682": "Login Time", + "OnlineUser_683": "强退", + "OnlineUser_684": "系统监控", + "OnlineUser_685": "顶部", + "OnlineUser_686": "Other 监控", + "OnlineUser_688": "Search 栏", + "OnlineUser_689": "Login 地址", + "OnlineUser_690": "Please enter Login 地址", + "OnlineUser_691": "Please enter Username", + "OnlineUser_694": "Online Users 表格", + "OnlineUser_695": "Other 页面(你可以随便改内容", + "OnlineUser_696": "Other 监控内容", + "OnlineUser_697": "这里可以放服务 Status 、CPU监控、内存监控、接口监控等内容", + "Organization_698": "OK Delete 该组织", + "Organization_699": "Add 组织", + "Organization_700": "统一分页", + "Organization_701": "Edit 组织", + "Organization_702": "Please enter Org Name", + "Organization_703": "组织编码", + "Organization_704": "Please enter 组织编码", + "Organization_705": "Please enter Leader", + "Organization_706": "Please enter Contact", + "Organization_707": "Please enter Email", + "Organization_708": "Please enter 地址", + "Organization_709": "Please enter Remark", + "SerialNumGenerate_710": "Please select 要 Delete 的数据", + "SerialNumGenerate_711": "请先生成", + "SerialNumGenerate_713": "未 Search 到数据", + "SerialNumGenerate_714": "Search Failed", + "SerialNumGenerate_715": "序列号", + "SerialNumGenerate_716": "型号", + "SerialNumGenerate_717": "国家", + "SerialNumGenerate_718": "省份", + "SerialNumGenerate_719": "二维码", + "SerialNumGenerate_721": "Robot 二维码管理", + "SerialNumGenerate_722": "数据管理", + "SerialNumGenerate_723": "OK Delete 选中项", + "SerialNumGenerate_724": "筛选栏", + "SerialNumGenerate_725": "Please enter 序列号", + "SerialNumGenerate_726": "列表", + "SerialNumGenerate_727": "完全自适应", + "SerialNumGenerate_728": "右侧固定", + "SerialNumGenerate_729": "不溢出", + "SerialNumGenerate_730": "Add 弹窗", + "SerialNumGenerate_731": "生成 Robot 二维码", + "SerialNumGenerate_732": "选择型号", + "SerialNumGenerate_733": "选择配置", + "SerialNumGenerate_734": "选择 Type", + "SerialNumGenerate_735": "选择国家", + "SerialNumGenerate_736": "请先选择国家", + "SerialNumGenerate_737": "生成序列号", + "SerialNumGenerate_738": "请先生成二维码", + "SerialNumGenerate_739": "View 二维码", + "SerialNumGenerate_740": "二维码详情", + "SerialNumGenerate_741": "Robot Type", + "SerialNumGenerate_742": "Type Name", + "SerialNumGenerate_743": "存储值", + "SerialNumGenerate_744": "Robot 型号", + "SerialNumGenerate_745": "型号 Name", + "SerialNumGenerate_746": "配置 Name", + "SmartDeviceManagement_747": "获取智能装备列表 Failed", + "SmartDeviceManagement_748": "装备 Updated", + "SmartDeviceManagement_749": "无权限", + "SmartDeviceManagement_750": "Save 参数 Success", + "SmartDeviceManagement_751": "授权 Failed", + "SmartDeviceManagement_752": "请先在列表中选中需要绑定的智能装备", + "SmartDeviceManagement_753": "OK 要为选中的智能装备清除", + "SmartDeviceManagement_754": "设备码", + "SmartDeviceManagement_755": "清除人员绑定", + "SmartDeviceManagement_756": "清除场站绑定", + "SmartDeviceManagement_757": "清除组织绑定", + "SmartDeviceManagement_758": "授权", + "SmartDeviceManagement_759": "绑定装备", + "SmartDeviceManagement_760": "批量解绑(组织", + "SmartDeviceManagement_761": "绑定智能装备", + "SmartDeviceManagement_762": "Selected 智能装备", + "SmartDeviceManagement_763": "Please select 装备", + "SmartDeviceManagement_764": "Edit 智能装备信息", + "SmartDeviceManagement_765": "请填写 Device Name", + "SmartDeviceManagement_766": "设备序列号", + "SmartDeviceManagement_767": "请填写设备码", + "SmartDeviceManagement_768": "Please enter 设备序列号", + "SmartDeviceManagement_769": "Please select Online Status", + "SmartDeviceManagement_770": "选择 Online Status", + "SmartDeviceManagement_771": "设备授权", + "SmartDeviceManagement_772": "设置设备 Actions 授权,授权到期后设备将失去对应 Actions 权限", + "SmartDeviceManagement_773": "授权 Time", + "SmartDeviceManagement_774": "Please enter 授权剩余 Time", + "SmartDeviceManagement_775": "授权 Time 不能小于", + "SmartDeviceManagement_776": "Save 授权", + "StationManage_777": "分区设置", + "StationManage_778": "边界绘制", + "StationManage_779": "Please select 组织筛选", + "StationManage_780": "Add 场站", + "StationManage_781": "场站弹窗", + "StationManage_782": "Edit 场站", + "StationManage_783": "Please select 所属组织", + "StationManage_784": "详细地址", + "StationManage_785": "消息推送配置", + "StationManage_786": "钉钉告警推送", + "StationManage_787": "开启", + "StationManage_788": "钉钉工单推送", + "StationManage_789": "邮件推送", + "StationManage_790": "告警弹窗推送", + "StationManage_791": "钉钉开关开启才显示Token、密钥", + "StationManage_792": "钉钉 Robot", + "StationManage_793": "开启钉钉推送时必须填写", + "StationManage_794": "Please enter 钉钉 Robot", + "StationManage_795": "钉钉加密密钥", + "StationManage_796": "开启钉钉推送时必须填写加密密钥", + "StationManage_797": "钉钉 Robot 加签密钥", + "StationManage_798": "分区弹窗", + "StationManage_799": "分区 Name", + "StationManage_800": "分区编码", + "StationManage_801": "分区 Type", + "StationManage_802": "清扫区", + "StationManage_803": "Inspection 区", + "StationManage_804": "Save 分区", + "StationManage_805": "地图绘制弹窗", + "StationManage_806": "地图区域绘制(可自行接入高德/百度地图", + "StationManage_807": "开始绘制", + "StationManage_808": "Save 边界", + "SystemMaintain_809": "错误管理", + "SystemMaintain_810": "告警模型管理", + "SystemMaintain_811": "工单模型管理", + "UserPage_814": "Role 弹窗", + "UserPage_815": "用户弹窗", + "UserPage_816": "自动适应布局:隐藏 Role 时, Status 自动占满一行", + "VideoManagement_817": "读取通道 Failed", + "VideoManagement_818": "无法连接视频服务", + "VideoManagement_819": "读取文件 Failed", + "VideoManagement_820": "请先选择完整的 Time 路径", + "VideoManagement_821": "大小", + "VideoManagement_822": "录像通道与 Replay 管理 · 共", + "VideoManagement_823": "个通道", + "VideoManagement_824": "通道", + "VideoManagement_825": "Select Device 通道", + "VideoManagement_826": "请先选择视频通道", + "VideoManagement_827": "Please select 年 / 月 / 日 加载录像", + "VideoManagement_828": "该日暂无录像文件", + "VideoManagement_829": "个文件", + "WorkOrderSettingsManagement_830": "获取工单模型列表 Failed", + "WorkOrderSettingsManagement_831": "您 OK 要 Delete 这条工单模型吗", + "WorkOrderSettingsManagement_832": "条工单模型吗", + "WorkOrderSettingsManagement_833": "工单 Name", + "WorkOrderSettingsManagement_835": "轻微", + "WorkOrderSettingsManagement_836": "路径/航线", + "WorkOrderSettingsManagement_837": "自动生成任务", + "WorkOrderSettingsManagement_838": "Add 工单模型", + "WorkOrderSettingsManagement_839": "Edit 工单模型", + "WorkOrderSettingsManagement_840": "Please enter 工单 Name", + "WorkOrderSettingsManagement_842": "请 Select Device", + "WorkOrderSettingsManagement_843": "根据 Device Type 显示不同的路径选择", + "WorkOrderSettingsManagement_844": "预设路径", + "WorkOrderSettingsManagement_845": "Please select 预设路径", + "WorkOrderSettingsManagement_846": "UAV 航线", + "WorkOrderSettingsManagement_847": "Please select UAV 航线", + "SystemSetting_1265": "Alert Rules 配置、用户 Role 、通知管理与系统参数设置" + }, + "aiAnalysis": { + "objectDetection": "object Detection", + "detectDesc": "detect Desc", + "thermalSegmentation": "thermal Segmentation", + "segmentationDesc": "segmentation Desc", + "getModelsFailed": "get Models Failed", + "uploadFirst": "upload First", + "submitTaskFailed": "submit Task Failed", + "thermalAnalysisComplete": "thermal Analysis Complete", + "taskSuccess": "task Success", + "taskExecutionError": "task Execution Error", + "cancelled": "cancelled", + "uploadThermalFirst": "upload Thermal First", + "thermalAnalysisFailed": "thermal Analysis Failed", + "detectTaskName": "detect Task Name", + "segmentationTaskName": "segmentation Task Name", + "algoCount": "algo Count", + "detectAndSeg": "detect And Seg", + "onlineServices": "online Services", + "healthCheck": "health Check", + "modelCount": "model Count", + "detectModelRegistry": "detect Model Registry", + "historyRecords": "history Records", + "pageDebugRecords": "page Debug Records", + "detectModel": "detect Model", + "default": "default", + "inputType": "input Type", + "image": "image", + "inferencePreset": "inference Preset", + "quality": "quality", + "dragFile": "drag File", + "supportedFormats": "supported Formats", + "processing": "processing", + "submitDetectTask": "submit Detect Task", + "serverDemo": "server Demo", + "refreshModels": "refresh Models", + "thermalFileLabel": "thermal File Label", + "uploadThermal": "upload Thermal", + "arrayMask": "array Mask", + "panelGeojson": "panel Geojson", + "submitThermalAnalysis": "submit Thermal Analysis", + "readServerHistory": "read Server History", + "resultDisplay": "result Display", + "resultViewHint": "result View Hint", + "inputPreview": "input Preview", + "resultImageVideo": "result Image Video", + "showResultAfterTask": "show Result After Task", + "resultJson": "result Json", + "noResult": "no Result", + "thermalInteractiveView": "thermal Interactive View", + "clickHotspotHint": "click Hotspot Hint", + "hotspotCount": "hotspot Count", + "panelArea": "panel Area", + "analysisId": "analysis Id", + "pointDetails": "point Details", + "selectHotspotPanel": "select Hotspot Panel", + "hotspot": "hotspot", + "blockNumber": "block Number", + "originalPixels": "original Pixels", + "overlayPixels": "overlay Pixels", + "area": "area", + "waitingSegmentation": "waiting Segmentation", + "hotspotPanelList": "hotspot Panel List", + "panelLabel": "panel Label", + "centerTitle": "center Title", + "algorithmList": "algorithm List", + "integratedCount": "integrated Count", + "detectWorkspace": "detect Workspace", + "segWorkspace": "seg Workspace", + "asyncTask": "async Task", + "syncAnalysis": "sync Analysis", + "detectWorkspaceDesc": "detect Workspace Desc", + "segWorkspaceDesc": "seg Workspace Desc", + "historyAnalysis": "history Analysis", + "noHistory": "no History", + "detect": "detect", + "segment": "segment", + "hotspotRecognition": "hotspot Recognition", + "abnormalString": "abnormal String", + "recommendedCleanBlock": "recommended Clean Block", + "videoAbnormalEvent": "video Abnormal Event", + "videoIntelligentAnalysis": "video Intelligent Analysis", + "perimeterIntrusion": "perimeter Intrusion", + "perimeterIntrusionDesc": "perimeter Intrusion Desc", + "personnelIntrusion": "personnel Intrusion", + "personnelIntrusionDesc": "personnel Intrusion Desc", + "fireSmokeDetection": "fire Smoke Detection", + "fireSmokeDesc": "fire Smoke Desc", + "areaLoitering": "area Loitering", + "areaLoiteringDesc": "area Loitering Desc", + "motionDetection": "motion Detection", + "motionDetectionDesc": "motion Detection Desc", + "perimeterIntrusionTimeline": "perimeter Intrusion Timeline", + "personnelIntrusionTimeline": "personnel Intrusion Timeline", + "areaLoiteringTimeline": "area Loitering Timeline", + "motionDetectionTimeline": "motion Detection Timeline", + "perimeterClimbTimeline": "perimeter Climb Timeline", + "perimeterIntrusionType": "perimeter Intrusion Type", + "AIAnalysis_983": "Handle 热斑组件", + "AIAnalysis_984": "排查 Abnormal 组串", + "AIAnalysis_985": "Clean 受遮挡区域", + "AIAnalysis_986": "跟踪衰减较大组件", + "AIAnalysis_987": "持续监控 Inverter 效率", + "AIAnalysis_988": "方阵", + "AIAnalysis_989": "智能诊断与分析中心", + "AIAnalysis_990": "Refresh 数据", + "AIAnalysis_991": "Export 报告", + "AIAnalysis_992": "第一排:顶部统计卡片", + "AIAnalysis_993": "值 (性能比", + "AIAnalysis_994": "vs Yesterday", + "AIAnalysis_995": "发电偏差率", + "AIAnalysis_996": "Abnormal 组串数量", + "AIAnalysis_997": "热斑疑似数量", + "AIAnalysis_998": "电站健康度评分", + "AIAnalysis_999": "Predicted 发 Battery", + "AIAnalysis_1000": "第二排: Thermal Analysis 和 组串 Abnormal 定位", + "AIAnalysis_1001": "UAV 热成像 Inspection 分析", + "AIAnalysis_1002": "模拟热斑点", + "AIAnalysis_1003": "高危", + "AIAnalysis_1004": "注意", + "AIAnalysis_1005": "热斑疑似点", + "AIAnalysis_1006": "高温区域", + "AIAnalysis_1007": "Temperature 分布", + "AIAnalysis_1008": "组串实时 Status 定位图", + "AIAnalysis_1009": "总组串", + "AIAnalysis_1012": "组串", + "AIAnalysis_1013": "Critical Abnormal (热斑", + "AIAnalysis_1014": "Critical Abnormal (断路", + "AIAnalysis_1015": "Abnormal (功率偏差", + "AIAnalysis_1016": "Abnormal (遮挡", + "AIAnalysis_1017": "轻微 Abnormal", + "AIAnalysis_1019": "运行 Normal", + "AIAnalysis_1020": "功率 Abnormal", + "AIAnalysis_1021": "Critical Fault", + "AIAnalysis_1022": "设备 Offline", + "AIAnalysis_1023": "Abnormal Type 分布", + "AIAnalysis_1024": "Offline 组串", + "AIAnalysis_1025": "View 详细定位表", + "AIAnalysis_1026": "第三排:三个主要分析区域", + "AIAnalysis_1027": "组件衰减趋势分析", + "AIAnalysis_1028": "衰减率趋势", + "AIAnalysis_1029": "当前均值", + "AIAnalysis_1030": "平均衰减率", + "AIAnalysis_1031": "最佳组件", + "AIAnalysis_1032": "最差组件", + "AIAnalysis_1033": "衰减预警", + "AIAnalysis_1034": "块组件衰减超标", + "AIAnalysis_1035": "建议排查", + "AIAnalysis_1036": "实时发电表现深度分析", + "AIAnalysis_1037": "发 Battery 对比分析", + "AIAnalysis_1038": "Actual 发电", + "AIAnalysis_1039": "Predicted 发电", + "AIAnalysis_1040": "理论发电", + "AIAnalysis_1041": "当日 Actual 发电", + "AIAnalysis_1042": "当日 Predicted 发电", + "AIAnalysis_1043": "实时 PR 值", + "AIAnalysis_1044": "诊断总结", + "AIAnalysis_1046": "诊断项", + "AIAnalysis_1047": "影响", + "AIAnalysis_1048": "检测到 36 处热斑疑似点", + "AIAnalysis_1049": "组串失配", + "AIAnalysis_1050": "串组串存在 Abnormal", + "AIAnalysis_1051": "遮挡风险", + "AIAnalysis_1052": "局部遮挡影响 2% 发 Battery", + "AIAnalysis_1053": "组件衰减", + "AIAnalysis_1054": "Inverter Abnormal", + "AIAnalysis_1055": "所有 Inverter 运行 Normal", + "AIAnalysis_1056": "建议与处置", + "AIAnalysis_1057": "建议措施", + "AIAnalysis_1058": "收益", + "AIAnalysis_1059": "跟踪衰减组件", + "AIAnalysis_1060": "监控 Inverter 效率", + "AIAnalysis_1061": "底部:数据来源", + "AIAnalysis_1062": "全栈数据融合来源", + "AIAnalysis_1063": "UAV 载荷热成像", + "AIAnalysis_1064": "同步中", + "AIAnalysis_1065": "实时遥测数据", + "AIAnalysis_1066": "云端气象分析站", + "AIAnalysis_1067": "电站 O&M 资产库", + "AIAnalysis_1068": "数据点" + }, + "video": { + "fileDetails": "file Details", + "sourceDevice": "source Device", + "startEndTime": "start End Time", + "capacity": "capacity", + "videoReplayCenter": "video Replay Center", + "allRecordings": "all Recordings", + "todayRecordings": "today Recordings", + "timeSpan": "time Span", + "onlineSearch": "online Search", + "labelMainFront": "label Main Front", + "labelRear": "label Rear", + "labelLeft": "label Left", + "labelRight": "label Right", + "labelFrontDrive": "label Front Drive", + "labelRearMount": "label Rear Mount", + "labelLeftWing": "label Left Wing", + "labelRightWing": "label Right Wing", + "labelCargo": "label Cargo", + "labelMainProbe": "label Main Probe", + "labelPanorama": "label Panorama", + "labelDetail": "label Detail", + "currentDutyDevice": "current Duty Device", + "videoStreamChannels": "video Stream Channels", + "refreshSignal": "refresh Signal", + "fullScreenWall": "full Screen Wall", + "ptzServoControl": "ptz Servo Control", + "systemIntegratedCommand": "system Integrated Command", + "twoWayIntercom": "two Way Intercom", + "triggerRemoteRepellent": "trigger Remote Repellent", + "aiVisualTracking": "ai Visual Tracking", + "activeVideoStreams": "active Video Streams", + "positionArray1": "position Array1", + "domeCamera": "dome Camera", + "recording": "recording", + "positionInverterRoom": "position Inverter Room", + "positionStationGate": "position Station Gate", + "positionPerimeterNorth": "position Perimeter North", + "positionWeatherStation": "position Weather Station", + "positionEquipmentArea": "position Equipment Area", + "positionStorageBattery": "position Storage Battery", + "positionEnergyStorage": "position Energy Storage", + "positionPerimeterSouth": "position Perimeter South", + "positionArrayWest": "position Array West", + "cameraName": "camera Name", + "recordingStatus": "recording Status", + "lastEventTime": "last Event Time", + "onlineCameras": "online Cameras", + "unitDevice": "unit Device", + "onlineRateTrend": "online Rate Trend", + "offlineCameras": "offline Cameras", + "offlineRateTrend": "offline Rate Trend", + "todayAlertEvents": "today Alert Events", + "unitIncident": "unit Incident", + "vsYesterdayPercent": "vs Yesterday Percent", + "perimeterAlerts": "perimeter Alerts", + "storageUsage": "storage Usage", + "storageUsageTrend": "storage Usage Trend", + "videoMonitorCenter": "video Monitor Center", + "customLayout": "custom Layout", + "mainView": "main View", + "liveLabel": "live Label", + "cameraDeviceList": "camera Device List", + "searchCameraPlaceholder": "search Camera Placeholder", + "allStatuses": "all Statuses", + "eventTimeline": "event Timeline", + "allTypes": "all Types", + "viewMore": "view More", + "recentAlertSnapshots": "recent Alert Snapshots", + "VideoPlayer_849": "前视图", + "VideoPlayer_850": "后视图", + "VideoPlayer_851": "左视图", + "VideoPlayer_852": "右视图", + "VideoPlayer_853": "视频源连接 Failed", + "Video_1269": "安防型", + "Video_1270": "运输型", + "Video_1271": "Inspection 型", + "VideoMonitorPage_1272": "中间 Video Monitor 中心", + "VideoMonitorPage_1273": "底部三栏", + "VideoMonitorPage_1274": "模拟检测框" + }, + "login": { + "and": "and", + "Login_1228": "左侧:表单区域", + "Login_1230": "右侧:梯形斜切", + "Login_1231": "图片铺满" + }, + "profile": { + "requestFailed": "request Failed", + "basicInfo": "basic Info", + "phoneInvalid": "phone Invalid", + "emailInvalid": "email Invalid", + "emailPlaceholder": "email Placeholder", + "remarkPlaceholder": "remark Placeholder", + "changePassword": "change Password", + "oldPasswordPlaceholder": "old Password Placeholder", + "newPasswordPlaceholder": "new Password Placeholder", + "passwordLengthError": "password Length Error", + "confirmPasswordRequired": "confirm Password Required", + "confirmPasswordPlaceholder": "confirm Password Placeholder", + "Profile_1232": "关键:给 Row 添加", + "Profile_1233": "让两列自动等高", + "Profile_1234": "左侧信息卡片", + "Profile_1236": "自动填充 Altitude", + "Profile_1237": "右侧表单卡片", + "Profile_1238": "基本资料", + "Profile_1239": "Modify Password" + }, + "home": { + "realtimePower": "realtime Power", + "totalPower": "total Power", + "ratedPower": "rated Power", + "gridConnection": "grid Connection", + "gridConnected": "grid Connected", + "frequency": "frequency", + "gridVoltage": "grid Voltage", + "phaseA": "phase A", + "todayAccumulated": "today Accumulated", + "currentIrradiance": "current Irradiance", + "ambientTemperature": "ambient Temperature", + "powerCurve": "power Curve", + "today": "today", + "inverterPowerCompare": "inverter Power Compare", + "stringCurrentHeatmap": "string Current Heatmap", + "current": "current", + "Home_1158": "江苏省", + "Home_1159": "南通市", + "Home_1160": "崇川区", + "Home_1161": "西南风", + "Home_1162": "升压站安防 Inspection 任务", + "Home_1163": "草稿", + "Home_1164": "区 Cleaning Task", + "Home_1166": "区 Inspection 任务", + "Home_1167": "已派发", + "Home_1168": "分析", + "Home_1169": "发现 Inverter Temperature 与组串电流存在轻微 Abnormal ,建议优先复核A区设备", + "Home_1170": "发现2处组件存在 Abnormal 热斑", + "Home_1171": "散热 Maintenance 建议:建议检查INV-A02散热风扇、风道和运行负载,持续高温时转 Repair 工单", + "Home_1172": "个组串存在发电 Abnormal", + "Home_1173": "阵列热斑处置建议:建议现场复核PV-A05-018至PV-A05-020组件接线和旁路二极管 Status ,必要时更换 Abnormal 组件", + "Home_1174": "整体发电偏差", + "Home_1176": "待开始", + "Home_1177": "主键ID( Add 为空", + "Home_1178": "工单编号(后端生成", + "Home_1179": "光伏组件 Clean 工单", + "Home_1183": "电站", + "Home_1184": "Device Type :电站", + "Home_1185": "根据电站清洁度建议进行组件 Clean ,提升发电效率", + "Home_1186": "系统自动生成 Cleaning Suggestion ,预计提升发 Battery", + "Home_1187": "Clean 设备、水枪、抹布", + "Home_1188": "影响发 Battery (%) 数字", + "Home_1190": "工单生成 Failed", + "Home_1191": "工单生成接口调用 Failed", + "Home_1192": "加载 Failed", + "Home_1193": "必须加这个,message才能 Normal 显示", + "Home_1198": "主体布局", + "Home_1199": "左侧图表", + "Home_1200": "今日发电功率趋势", + "Home_1201": "效率", + "Home_1203": "总任务", + "Home_1204": "中间", + "Home_1205": "外部系统", + "Home_1206": "Inspection Type", + "Home_1207": "Inspection Time", + "Home_1208": "Inspection 人员", + "Home_1210": "热点检测", + "Home_1211": "发现", + "Home_1212": "处组件存在 Abnormal 热斑", + "Home_1213": "组串 Abnormal", + "Home_1214": "发电偏差分析", + "Home_1215": "告警内容", + "Home_1216": "发生 Time", + "Home_1217": "组件阵列", + "Home_1218": "环境传感器", + "Home_1219": "详细", + "Home_1221": "Generate Order Button (已完善", + "Home_1222": "清洁度", + "Home_1223": "建议 Clean Time", + "Home_1224": "天内", + "Home_1225": "预计提升发 Battery", + "Home_1226": "预计提升 Battery" + }, + "router": { + "realtimeMonitorCenter": "realtime Monitor Center", + "realtimeMonitorDesc": "realtime Monitor Desc" + }, + "report": { + "monthlyGeneration": "monthly Generation", + "monthOverMonthUp": "month Over Month Up", + "prAverage": "pr Average", + "workOrderCompletionRate": "work Order Completion Rate", + "monthlyTotalOrders": "monthly Total Orders", + "alertClosureRate": "alert Closure Rate", + "criticalAlertCount": "critical Alert Count", + "cleaningRevenueImprovement": "cleaning Revenue Improvement", + "estimatedAdditionalGeneration": "estimated Additional Generation", + "autoGeneratedReports": "auto Generated Reports", + "unitReport": "unit Report", + "monthlyAccumulated": "monthly Accumulated", + "stationMonthlyReportFile": "station Monthly Report File", + "generated": "generated", + "weeklyOperationSummary": "weekly Operation Summary", + "aiDiagnosisDaily": "ai Diagnosis Daily", + "generating": "generating", + "alertClosureWeekly": "alert Closure Weekly", + "monthlyOperationReport": "monthly Operation Report", + "monthlyOperationReportDesc": "monthly Operation Report Desc", + "deviceHealthReport": "device Health Report", + "deviceHealthReportDesc": "device Health Report Desc", + "cleaningOptimizationReport": "cleaning Optimization Report", + "cleaningEffectReportDesc": "cleaning Effect Report Desc", + "inspectionRobotReport": "inspection Robot Report", + "inspectionRobotReportDesc": "inspection Robot Report Desc", + "stationMonthly": "station Monthly", + "alertWeekly": "alert Weekly", + "systemAuto": "system Auto", + "aiAnalysisReport": "ai Analysis Report", + "operationSummary": "operation Summary", + "operationReport": "operation Report", + "cleaningEffectReport": "cleaning Effect Report", + "overview": "overview", + "reportCycle": "report Cycle", + "reportType": "report Type", + "exportPdf": "export Pdf", + "exportExcel": "export Excel", + "monthlyOperationAnalysis": "monthly Operation Analysis", + "generation": "generation", + "predictedGeneration": "predicted Generation", + "monthlyAccumulatedGeneration": "monthly Accumulated Generation", + "theoreticalGeneration": "theoretical Generation", + "generationDeviation": "generation Deviation", + "averageIrradiance": "average Irradiance", + "unitIrradiancePerDay": "unit Irradiance Per Day", + "generationMeteorology": "generation Meteorology", + "workOrderAlertClosure": "work Order Alert Closure", + "closureRate": "closure Rate", + "alarmCount": "alarm Count", + "aiAnalysisStatistics": "ai Analysis Statistics", + "unitItem": "unit Item", + "subscriptionDispatch": "subscription Dispatch", + "subscribers": "subscribers", + "roleOmSupervisor": "role Om Supervisor", + "roleStationEngineer": "role Station Engineer", + "roleDeviceOm": "role Device Om", + "roleOmManager": "role Om Manager", + "dispatchChannels": "dispatch Channels", + "email": "email", + "systemNotification": "system Notification", + "sendPlan": "send Plan", + "weeklyAutoSend": "weekly Auto Send", + "reportDirectory": "report Directory", + "reportName": "report Name", + "templateCenter": "template Center", + "generationRecords": "generation Records", + "generateTime": "generate Time", + "generator": "generator", + "reportDownloader_5": "适配割草(植被 Altitude )、 Clean (灰损率)的不同字段", + "reportDownloader_6": "核心指标", + "reportDownloader_7": "最佳作业窗口", + "reportDownloader_8": "任务列表", + "reportDownloader_9": "算法 Execute 流程", + "ReportCenter_1249": "王 O&M", + "ReportCenter_1250": "页面头部标签", + "ReportCenter_1251": "左侧主内容区", + "ReportCenter_1252": "月度综合经营分析", + "ReportCenter_1253": "第二排:三个图表", + "ReportCenter_1254": "订阅与分发", + "ReportCenter_1255": "陈经理", + "ReportCenter_1256": "右侧边栏", + "ReportCenter_1257": "报表 Directory / 快捷 Download", + "ReportCenter_1258": "模板中心", + "ReportCenter_1259": "报表生成记录" + }, + "roles": { + "editSuccess": "edit Success", + "addSuccess": "add Success", + "manageMembers": "manage Members", + "confirmDeleteRole": "confirm Delete Role", + "permissionKey": "permission Key", + "superAdmin": "super Admin", + "orgAdmin": "org Admin", + "editRole": "edit Role", + "roleNamePlaceholder": "role Name Placeholder", + "rolePermission": "role Permission", + "roleKeyPlaceholder": "role Key Placeholder", + "roleSortLabel": "role Sort Label", + "menuPermissionConfig": "menu Permission Config", + "Roles_1260": "统一卡片容器(和 Menu Management 一致", + "Roles_1261": "顶部 Actions 栏(和 Menu Management 统一", + "Roles_1262": "Role 卡片列表", + "Roles_1263": "分页组件(正确触发", + "Roles_1264": "Add / Edit 弹窗" + }, + "users": { + "notAssigned": "not Assigned", + "languagePlaceholder": "language Placeholder", + "rolePlaceholder": "role Placeholder", + "siteId": "site Id", + "Users_1266": "回填多选 Role", + "Users_1267": "直接 Submit 数组", + "Users_1268": "多 Role 选择", + "userSlice_1321": "Save 用户信息", + "userSlice_1322": "Refresh 自动 Resume" + }, + "page": { + "api_1": "只需要加这一行", + "request_10": "使用全局", + "AgoraRtc_11": "先清理上一次", + "AgoraRtc_12": "视频连接 Failed", + "Draggable_269": "仅标题栏可拖拽,内容区不触发拖拽", + "Draggable_270": "整体8px外边框", + "Draggable_271": "拖拽标题栏", + "Draggable_272": "Close Button", + "Draggable_273": "Close Button ,阻止拖拽冒泡", + "Draggable_274": "传入的视频内容", + "Empty_275": "自定义文案", + "Empty_276": "组件 Altitude", + "Map_278": "规划航线", + "Map_279": "Complete 点实时轨迹", + "Map_280": "标记点实体", + "Map_281": "跟随小车的实例", + "Map_282": "直接存 Cartesian3(性能最高", + "Map_283": "强制 Refresh", + "Map_284": "垂直俯视", + "Map_285": "加这个(只在需要时渲染", + "Map_286": "飞机", + "Map_288": "割草", + "Map_290": "默认车", + "recharts_291": "访问量", + "recharts_292": "浏览量", + "recharts_293": "成交量", + "useWebRTCStream_848": "必须加", + "WeatherSunny_854": "每根", + "circQueue_855": "队列容量 (最多存储项目数", + "circQueue_856": "队列存储区,使用数组存任意对象", + "circQueue_857": "队头索引", + "circQueue_858": "队尾索引", + "circQueue_859": "队满标志", + "circQueue_860": "是否 Enable 深拷贝 (默认 Enable", + "circQueue_861": "队列最大内存限制 (单位:字节", + "circQueue_862": "当前已使用内存大小 (单位:字节", + "circQueue_863": "判断队列是否为空", + "circQueue_864": "判断队列是否已满", + "circQueue_865": "估算对象大小 (以 JSON 字符串的 UTF-8 编码长度作为近似值", + "circQueue_866": "中用于计算字节长度", + "circQueue_867": "若对象不可序列化, Back 0 (避免出错", + "circQueue_868": "深拷贝对象 (基于 JSON 的方式,仅支持可序列化对象", + "circQueue_869": "入队 Actions :将对象放入队尾", + "circQueue_870": "估算项目内存大小", + "circQueue_871": "若队满或超过内存限制则入队 Failed", + "circQueue_872": "深拷贝入队数据 (避免外部 Modify 影响队列", + "circQueue_873": "存入队尾", + "circQueue_874": "环形增长", + "circQueue_875": "增加当前 Memory", + "circQueue_876": "若尾追头,表示队满", + "circQueue_877": "出队 Actions :从队头取出一个对象", + "circQueue_878": "Back 深拷贝", + "circQueue_879": "队空 Back", + "circQueue_880": "读取队头项目", + "circQueue_881": "更新 Memory", + "circQueue_882": "出队必然队不满", + "circQueue_883": "Back 深拷贝,保护队列内部数据", + "circQueue_884": "丢弃数据:从队尾向前丢弃 len 项 (反向移动", + "circQueue_885": "队空则不能 Resume 丢弃", + "circQueue_886": "定位最后一项", + "circQueue_887": "减去内存大小", + "circQueue_888": "回退", + "circQueue_889": "获取当前队列中元素个数", + "circQueue_890": "循环直到 Success 入队或超时", + "circQueue_891": "超时退出", + "circQueue_892": "异步轮询", + "circQueue_893": "自旋加 sleep Suspend", + "circQueue_894": "Success 取出则 Back", + "circQueue_895": "序列化接口,用于", + "circQueue_896": "复制每个元素", + "circQueue_897": "移动到下一个元素", + "tracePoint_898": "防止值重复", + "tracePoint_899": "成员变量(私有", + "tracePoint_900": "循环队列", + "tracePoint_901": "用于存放等 Pending 的点", + "tracePoint_902": "轨迹点数组", + "tracePoint_903": "存放 Completed 点", + "tracePoint_904": "当前点", + "tracePoint_905": "Complete 点索引", + "tracePoint_906": "指向当前点之前的最后一个要插入的 Complete 点的 Location", + "tracePoint_907": "用于防止多线程同时更新", + "tracePoint_908": "模式管理器", + "tracePoint_909": "构造函数", + "tracePoint_910": "限制", + "tracePoint_911": "存放轨迹", + "tracePoint_912": "Completed 点", + "tracePoint_913": "默认为定位模式", + "tracePoint_914": "复位", + "tracePoint_915": "Reset 轨迹点", + "tracePoint_916": "清空队列", + "tracePoint_917": "清空轨迹点数组", + "tracePoint_918": "Reset Complete 点索引", + "tracePoint_919": "Reset 为定位模式", + "tracePoint_920": "释放锁", + "tracePoint_921": "获取轨迹", + "tracePoint_922": "尝试锁定", + "tracePoint_923": "防止多线程同时更新", + "tracePoint_924": "深拷贝", + "tracePoint_925": "避免外部 Modify 导致内部出错", + "tracePoint_926": "解锁", + "tracePoint_927": "锁定中", + "tracePoint_928": "Back 空", + "tracePoint_929": "更新当前点", + "tracePoint_930": "只保留 Complete 点部分", + "tracePoint_931": "再插入新的当前点", + "tracePoint_932": "触发事件", + "tracePoint_933": "驱动外部更新", + "tracePoint_934": "支持自动扩展数组", + "tracePoint_935": "如果轨迹数组正在 Actions", + "tracePoint_936": "先存放至队列中", + "tracePoint_937": "添加 Complete 点", + "tracePoint_938": "先 Handle 队列中积压的点", + "tracePoint_939": "如果队列不为空", + "tracePoint_940": "取出队列中的点", + "tracePoint_941": "错误 Handle", + "tracePoint_942": "可以输出到", + "tracePoint_943": "文件", + "tracePoint_944": "打印", + "tracePoint_945": "无论 Success Failed 都解锁", + "tracePoint_946": "避免无法使用", + "tracePoint_947": "若当前点存在,说明当前数组末尾已插入一个当前点", + "tracePoint_948": "截断轨迹数组到当前点,将新 Complete 点插入在当前点之前", + "tracePoint_949": "截断避免多添加了无效点", + "tracePoint_950": "然后再当前点之前插入 Complete 点", + "tracePoint_951": "首次添加轨迹点需要先尾插", + "tracePoint_952": "更新当前点索引", + "tracePoint_953": "释放锁资源", + "tryLock_954": "原子尝试锁", + "tryLock_955": "非阻塞且线程安全", + "tryLock_956": "可用于异步多线程", + "utils_957": "最优", + "utils_958": "苏州", + "utils_959": "天气接口调用 Failed", + "utils_960": "兜底", + "utils_961": "暴雨", + "utils_962": "只取有标题", + "utils_963": "有字段的列", + "utils_964": "转义CSV引号", + "utils_965": "错误 红", + "utils_966": "警告 橙", + "utils_967": "信息 蓝", + "Agri_968": "输入预览", + "Agri_969": "结果图/视频", + "Agri_970": "结果", + "Agri_971": "左侧:结果图", + "Agri_972": "热点标记", + "Agri_973": "请 Upload 热成像文件", + "Agri_974": "热点标记点", + "Agri_975": "右侧:点位详情", + "Agri_976": "总容器:垂直布局,占满卡片内容区", + "Agri_977": "上半部分:点位详情", + "Agri_978": "分割线", + "Agri_979": "下半部分:热点/面板列表", + "Agri_980": "左侧:算法列表", + "Agri_981": "中间:工作区", + "Agri_982": "右侧:历史记录", + "RealTimeMonitor_1240": "左侧大栏", + "RealTimeMonitor_1241": "电站拓扑图", + "RealTimeMonitor_1242": "设备 Real-time Monitor 列表", + "RealTimeMonitor_1243": "右侧小栏", + "RealTimeMonitor_1244": "个统计卡片", + "RealTimeMonitor_1245": "实时功率曲线", + "RealTimeMonitor_1246": "Inverter 功率对比", + "RealTimeMonitor_1247": "热力图与 Online Rate", + "RealTimeMonitor_1248": "简易地图点位", + "index_1318": "加这个", + "stationSlice_1319": "当前选中场站(完整对象", + "stationSlice_1320": "方便直接用", + "index_1323": "多租户 Login 时传入", + "index_1324": "过期 Time (秒", + "index_1327": "Button Permission", + "index_1328": "不在 Menu 中展示" + }, + "clean": { + "CleanOptimization_1069": "计划", + "CleanOptimization_1070": "区块", + "CleanOptimization_1071": "待创建", + "CleanOptimization_1072": "王工", + "CleanOptimization_1073": "赵工", + "CleanOptimization_1074": "Clean 前发 Battery", + "CleanOptimization_1075": "Clean 后发 Battery", + "CleanOptimization_1076": "灰尘积累", + "CleanOptimization_1077": "鸟粪遮挡", + "CleanOptimization_1078": "沙尘沉积", + "CleanOptimization_1079": "污染指数", + "CleanOptimization_1080": "中等", + "CleanOptimization_1081": "建议 Clean 区块数", + "CleanOptimization_1082": "占比", + "CleanOptimization_1083": "预计发电提升", + "CleanOptimization_1084": "占当前日发 Battery", + "CleanOptimization_1085": "Est Revenue 提升", + "CleanOptimization_1086": "按电价", + "CleanOptimization_1087": "元/ kWh 计算", + "CleanOptimization_1088": "上次 Clean Time", + "CleanOptimization_1089": "距今 12 天", + "CleanOptimization_1090": "Clean Cost 预估", + "CleanOptimization_1091": "预计单次 Clean 总成本", + "CleanOptimization_1092": "标题与 Actions 栏", + "CleanOptimization_1093": "Cleaning Optimization 管理", + "CleanOptimization_1094": "最 (7d)", + "CleanOptimization_1095": "最近 30 天", + "CleanOptimization_1096": "左侧:分析图表", + "CleanOptimization_1097": "污染趋势分析", + "CleanOptimization_1098": "阈值", + "CleanOptimization_1099": "发电损失构成", + "CleanOptimization_1100": "(7d)", + "CleanOptimization_1101": "总损失 Battery", + "CleanOptimization_1102": "Clean 前后对比分析", + "CleanOptimization_1103": "近一次", + "CleanOptimization_1104": "Clean 收益深度测算", + "CleanOptimization_1105": "电价 (含税", + "CleanOptimization_1106": "预计 Net Revenue", + "CleanOptimization_1107": "投资回报", + "CleanOptimization_1108": "回收周期", + "CleanOptimization_1109": "中间:地图分布与工单预览", + "CleanOptimization_1110": "电站污染分布与建议 Clean 区块分布图", + "CleanOptimization_1111": "污染等级", + "CleanOptimization_1112": "优先 Clean", + "CleanOptimization_1113": "较高", + "CleanOptimization_1114": "较低", + "CleanOptimization_1115": "清洁", + "CleanOptimization_1116": "地图背景图与浮动标签", + "CleanOptimization_1117": "模拟区块叠加层", + "CleanOptimization_1118": "具体的污染区块标签", + "CleanOptimization_1119": "右侧悬浮 Control Button", + "CleanOptimization_1120": "Clean 工单智能预览", + "CleanOptimization_1121": "View More", + "CleanOptimization_1122": "预计工时", + "CleanOptimization_1123": "计划 Time", + "CleanOptimization_1124": "创建工单", + "CleanOptimization_1125": "右侧:决策建议与参考", + "CleanOptimization_1126": "智能 Clean 策略建议", + "CleanOptimization_1127": "算法模型:多因子加权综合优化模型", + "CleanOptimization_1128": "推荐计划", + "CleanOptimization_1129": "建议区块", + "CleanOptimization_1130": "预计提升", + "CleanOptimization_1131": "天气适宜度", + "CleanOptimization_1132": "Clean 技术方案推荐", + "CleanOptimization_1133": "智能 Robot 方案", + "CleanOptimization_1134": "全自动 Clean ,适合大面积平整区块,效率极高", + "CleanOptimization_1135": "预计用水", + "CleanOptimization_1136": "人工精细化方案", + "CleanOptimization_1137": "灵活 Handle 遮挡及 Critical 污垢区域,适合复杂地形", + "CleanOptimization_1138": "天气与资源环境参考", + "CleanOptimization_1142": "风速", + "CleanOptimization_1143": "适宜", + "CleanOptimization_1144": "降水", + "CleanOptimization_1145": "水源", + "CleanOptimization_1146": "充足", + "CleanOptimization_1147": "Clean 作业影响综合评估", + "CleanOptimization_1148": "预计用水量", + "CleanOptimization_1149": "节水 30% ( Robot", + "CleanOptimization_1150": "预计总耗时", + "CleanOptimization_1151": "小时", + "CleanOptimization_1152": "并行作业 2 组", + "CleanOptimization_1153": "碳减排贡献", + "CleanOptimization_1154": "减排量", + "CleanOptimization_1155": "作业风险评估", + "CleanOptimization_1156": "安全可控", + "CleanOptimization_1157": "以上决策建议基于历史数据、实时气象及发电损失模型综合计算。 Actual Execute 效果可能受天气骤变、现场突发障碍等不 OK 因素影响" + } + } +} \ No newline at end of file diff --git a/tmp_analyze2.cjs b/tmp_analyze2.cjs new file mode 100644 index 0000000..18cda70 --- /dev/null +++ b/tmp_analyze2.cjs @@ -0,0 +1,80 @@ +const fs = require('fs'); +const path = require('path'); + +const root = 'src'; +const exts = ['.tsx', '.ts', '.jsx', '.js']; +const excludeDirs = ['locales', 'node_modules']; +const cjk = /[\u4e00-\u9fff]/; + +function walk(dir) { + const out = []; + for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { + const full = path.join(dir, entry.name); + if (entry.isDirectory()) { + if (excludeDirs.includes(entry.name)) continue; + out.push(...walk(full)); + } else if (exts.includes(path.extname(entry.name))) { + out.push(full); + } + } + return out; +} + +// Load locale keys (flattened) +function load(localePath) { + let src = fs.readFileSync(localePath, 'utf8'); + src = src.replace(/const\s+\w+\s*=\s*\{/, 'var obj = {'); + src = src.replace(/export\s+default\s+\w+;?/, ''); + src = src.replace(/\bconst\b/g, 'var'); + return new Function(src + '\nreturn obj;')(); +} +function flatten(obj, prefix = '', res = {}) { + for (const k of Object.keys(obj)) { + const key = prefix ? prefix + '.' + k : k; + if (obj[k] && typeof obj[k] === 'object' && !Array.isArray(obj[k])) flatten(obj[k], key, res); + else res[key] = obj[k]; + } + return res; +} +const zhF = flatten(load('src/locales/zh/index.ts')); +const enF = flatten(load('src/locales/en/index.ts')); + +// 1) Find all t('...') / i18n.t('...') referenced keys +const files = walk(root); +const refKeys = new Set(); +const refRegex = /(?:i18n\.t|[^a-zA-Z]t)\(\s*['"`]([a-zA-Z][a-zA-Z0-9_.]*?)['"`]/g; +for (const f of files) { + const content = fs.readFileSync(f, 'utf8'); + let m; + while ((m = refRegex.exec(content))) { + refKeys.add(m[1]); + } +} +const refArr = [...refKeys].sort(); +const missingInZh = refArr.filter(k => !(k in zhF)); +const missingInEn = refArr.filter(k => !(k in enF)); +console.log('=== t() referenced keys missing in ZH (' + missingInZh.length + ') ==='); +console.log(missingInZh.join('\n')); +console.log('\n=== t() referenced keys missing in EN (' + missingInEn.length + ') ==='); +console.log(missingInEn.join('\n')); +console.log('\nTotal referenced keys:', refArr.length); + +// 2) Find user-facing hardcoded Chinese strings. +// Exclude lines that are only comments or console.* (developer-facing). +const userHits = []; +for (const f of files) { + const lines = fs.readFileSync(f, 'utf8').split('\n'); + lines.forEach((line, i) => { + const t = line.trim(); + if (!cjk.test(line)) return; + if (t.startsWith('//') || t.startsWith('*') || t.startsWith('/*')) return; + if (/^\{\/\*/.test(t)) return; // {/* ... */} + // exclude console.* (browser console only) + if (/\bconsole\.(log|error|warn|info|debug)\s*\(/.test(line)) return; + userHits.push({ file: f, line: i + 1, text: t.slice(0, 220) }); + }); +} +console.log('\n=== USER-FACING hardcoded CJK lines (' + userHits.length + ') ==='); +for (const h of userHits) { + console.log(h.file + ':' + h.line + ' | ' + h.text); +} diff --git a/tmp_analyze2_output.txt b/tmp_analyze2_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..3f6ce9372ae37139b35e84f56a0bf3f70f0a9caa GIT binary patch literal 386560 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 "var obj = {", remove "export default zh;" + src = src.replace(/const\s+\w+\s*=\s*\{/, 'var obj = {'); + src = src.replace(/export\s+default\s+\w+;?/, ''); + src = src.replace(/\bconst\b/g, 'var'); + const moduleWrap = new Function(src + '\nreturn obj;'); + return moduleWrap(); +} + +function flatten(obj, prefix = '', res = {}) { + for (const k of Object.keys(obj)) { + const key = prefix ? prefix + '.' + k : k; + if (obj[k] && typeof obj[k] === 'object' && !Array.isArray(obj[k])) { + flatten(obj[k], key, res); + } else { + res[key] = obj[k]; + } + } + return res; +} + +const zhRaw = load('src/locales/zh/index.ts'); +const enRaw = load('src/locales/en/index.ts'); + +const zhF = flatten(zhRaw); +const enF = flatten(enRaw); + +const zhKeys = Object.keys(zhF).sort(); +const enKeys = Object.keys(enF).sort(); + +const onlyZh = zhKeys.filter(k => !(k in enF)); +const onlyEn = enKeys.filter(k => !(k in zhF)); + +console.log('=== Keys in ZH but MISSING in EN (' + onlyZh.length + ') ==='); +console.log(onlyZh.join('\n')); +console.log(''); +console.log('=== Keys in EN but MISSING in ZH (' + onlyEn.length + ') ==='); +console.log(onlyEn.join('\n')); +console.log(''); +console.log('ZH total keys:', zhKeys.length, ' EN total keys:', enKeys.length); diff --git a/tmp_dbg.cjs b/tmp_dbg.cjs new file mode 100644 index 0000000..27c9464 --- /dev/null +++ b/tmp_dbg.cjs @@ -0,0 +1,34 @@ +const fs = require('fs'); +const path = require('path'); +const root = 'src'; +const exts = ['.tsx', '.ts', '.jsx', '.js']; +const excludeDirs = ['locales', 'node_modules']; +function walk(dir) { + const out = []; + for (const e of fs.readdirSync(dir, { withFileTypes: true })) { + const full = path.join(dir, e.name); + if (e.isDirectory()) { if (!excludeDirs.includes(e.name)) out.push(...walk(full)); } + else if (exts.includes(path.extname(e.name))) out.push(full); + } + return out; +} +const refRegex = /(?:i18n\.t|[^a-zA-Z]t)\(\s*['"`]([a-zA-Z][a-zA-Z0-9_.]*?)['"`]/g; +const files = walk(root); +let count = 0; +const byFile = {}; +for (const f of files) { + const content = fs.readFileSync(f, 'utf8'); + let m; + const found = []; + while ((m = refRegex.exec(content))) { found.push(m[1]); } + if (found.length) { byFile[f] = found; count += found.length; } +} +console.log('Total raw t() matches:', count); +// show a few files with their matches +let shown = 0; +for (const f of Object.keys(byFile)) { + if (shown > 25) break; + console.log('\n' + f + ' (' + byFile[f].length + '):'); + console.log(' ' + byFile[f].slice(0, 12).join(', ')); + shown++; +} diff --git a/tmp_dbg3.cjs b/tmp_dbg3.cjs new file mode 100644 index 0000000..b0d24d1 --- /dev/null +++ b/tmp_dbg3.cjs @@ -0,0 +1,15 @@ +const fs = require('fs'); +const rxJSX = />([^<>`{}()]*[一-鿿][^<>`{}()]*)'>{t(\''+'NS.'+inner.trim()+'\')}<'); +s = s.replace(rxAttr, (m,attr,q,val)=>{ if(SKIP_ATTR.has(attr)) return m; if(val.includes('${')) return m; return attr+'={t(\'NS.'+val.trim()+'\')}'; }); +s = s.replace(rxMsg, (m,fn,q,val)=> fn+"(t('NS."+val.trim()+"'))"); +s = s.replace(rxProp, (m,pre,prop,q,val)=>{ if(!UI_PROP.has(prop)) return m; return pre+prop+": t('NS."+val.trim()+"')"; }); +const lines = s.split('\n'); +for (let i=68;i<82;i++){ console.log((i+1)+': '+lines[i]); } diff --git a/tmp_finalcheck.cjs b/tmp_finalcheck.cjs new file mode 100644 index 0000000..292cbec --- /dev/null +++ b/tmp_finalcheck.cjs @@ -0,0 +1,23 @@ +const fs = require('fs'); +function load(p){let s=fs.readFileSync(p,'utf8');s=s.replace(/const\s+\w+\s*=\s*\{/,'var obj = {');s=s.replace(/export\s+default\s+\w+;?/,'');s=s.replace(/\bconst\b/g,'var');return new Function(s+'\nreturn obj;')();} +const zh=load('src/locales/zh/index.ts'); const en=load('src/locales/en/index.ts'); +function flat(o,p,r){p=p||'';r=r||{};for(const k of Object.keys(o)){const key=p?p+'.'+k:k;if(o[k]&&typeof o[k]==='object'&&!Array.isArray(o[k]))flat(o[k],key,r);else r[key]=o[k];}return r;} +const zf=flat(zh), ef=flat(en); +console.log('zh flat keys:',Object.keys(zf).length,' en flat keys:',Object.keys(ef).length); +for(const k of ['common.completed','home.underMaintenance','roles.abnormal']) console.log(' en has '+k+':',k in ef,' (zh:',k in zf,')'); +// duplicate base-key detection per namespace (text scan) +function dupScan(file){ + const text=fs.readFileSync(file,'utf8'); + const re=/^\t([A-Za-z]+):\s*\{/gm; let m; const dups=[]; + while((m=re.exec(text))){ + const ns=m[1]; const start=m.index+m[0].length; + // find matching closing brace + let depth=1,i=start; while(i0){const c=text[i];if(c==='{')depth++;else if(c==='}')depth--;i++;} + const block=text.slice(start,i-1); + const keys={}; let dm; const kr=/^\s*([A-Za-z0-9_'"][A-Za-z0-9_'"]*):/gm; + while((dm=kr.exec(block))){const key=dm[1].replace(/'/g,''); if(key in keys) dups.push(ns+'.'+key); keys[key]=1;} + } + return dups; +} +const dz=dupScan('src/locales/zh/index.ts'); const de=dupScan('src/locales/en/index.ts'); +console.log('zh duplicate base keys:',dz.length, dz.slice(0,10)); console.log('en duplicate base keys:',de.length, de.slice(0,10)); diff --git a/tmp_findcjk.cjs b/tmp_findcjk.cjs new file mode 100644 index 0000000..b7056da --- /dev/null +++ b/tmp_findcjk.cjs @@ -0,0 +1,51 @@ +const fs = require('fs'); +const path = require('path'); + +const root = 'src'; +const exts = ['.tsx', '.ts', '.jsx', '.js']; +const excludeDirs = ['locales', 'node_modules']; + +// Regex to match a run of CJK characters (and surrounding simple chars) inside a string-ish +// We capture any quoted string (single/double/backtick) containing CJK. +const cjk = /[\u4e00-\u9fff]/; + +function walk(dir) { + const out = []; + for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { + const full = path.join(dir, entry.name); + if (entry.isDirectory()) { + if (excludeDirs.includes(entry.name)) continue; + out.push(...walk(full)); + } else if (exts.includes(path.extname(entry.name))) { + out.push(full); + } + } + return out; +} + +const files = walk(root); +let total = 0; +const perFile = []; +for (const f of files) { + const lines = fs.readFileSync(f, 'utf8').split('\n'); + const hits = []; + lines.forEach((line, i) => { + // skip pure comment lines (// ... or * ...) + const trimmed = line.trim(); + if (trimmed.startsWith('//') || trimmed.startsWith('*')) return; + if (!cjk.test(line)) return; + hits.push({ line: i + 1, text: line.trim() }); + }); + if (hits.length) { + perFile.push({ file: f, hits }); + total += hits.length; + } +} + +console.log('Total files with CJK:', perFile.length, 'Total CJK lines:', total); +for (const pf of perFile) { + console.log('\n### ' + pf.file + ' (' + pf.hits.length + ')'); + for (const h of pf.hits) { + console.log(' L' + h.line + ': ' + h.text.slice(0, 200)); + } +} diff --git a/tmp_fixkeys.cjs b/tmp_fixkeys.cjs new file mode 100644 index 0000000..411b23a --- /dev/null +++ b/tmp_fixkeys.cjs @@ -0,0 +1,25 @@ +// Quote any object key that starts with a digit (invalid as unquoted JS key). +// Flat key string (ns.123abc) is unchanged, so t('ns.123abc') still resolves. +const fs = require('fs'); +function fix(file) { + let s = fs.readFileSync(file, 'utf8'); + let n = 0; + s = s.replace(/^(\s*)([0-9][A-Za-z0-9_]*)(:\s)/gm, (m, ind, key, colon) => { n++; return ind + "'" + key + "'" + colon; }); + fs.writeFileSync(file, s, 'utf8'); + console.log(file + ' -> quoted ' + n + ' digit-leading keys'); +} +fix('src/locales/zh/index.ts'); +fix('src/locales/en/index.ts'); + +// verify both parse +function load(p) { + let s = fs.readFileSync(p, 'utf8'); + s = s.replace(/const\s+\w+\s*=\s*\{/, 'var obj = {'); + s = s.replace(/export\s+default\s+\w+;?/, ''); + s = s.replace(/\bconst\b/g, 'var'); + return new Function(s + '\nreturn obj;')(); +} +try { const z = load('src/locales/zh/index.ts'); console.log('zh parses OK, top keys:', Object.keys(z).length); } +catch (e) { console.log('zh PARSE FAIL:', e.message.split('\n')[0]); } +try { const e2 = load('src/locales/en/index.ts'); console.log('en parses OK, top keys:', Object.keys(e2).length); } +catch (e) { console.log('en PARSE FAIL:', e.message.split('\n')[0]); } diff --git a/tmp_gen.cjs b/tmp_gen.cjs new file mode 100644 index 0000000..506d7d4 --- /dev/null +++ b/tmp_gen.cjs @@ -0,0 +1,563 @@ +// Generaate missing locale keys (best-effort) for zh + en. +const fs = require('fs'); +const path = require('path'); + +const root = 'src'; +const exts = ['.tsx', '.ts', '.jsx', '.js']; +const excludeDirs = ['locales', 'node_modules', 'i18n']; + +function walk(dir) { + const out = []; + for (const e of fs.readdirSync(dir, { withFileTypes: true })) { + const full = path.join(dir, e.name); + if (e.isDirectory()) { if (!excludeDirs.includes(e.name)) out.push(...walk(full)); } + else if (exts.includes(path.extname(e.name))) out.push(full); + } + return out; +} + +function load(p) { + let s = fs.readFileSync(p, 'utf8'); + s = s.replace(/const\s+\w+\s*=\s*\{/, 'var obj = {'); + s = s.replace(/export\s+default\s+\w+;?/, ''); + s = s.replace(/\bconst\b/g, 'var'); + return new Function(s + '\nreturn obj;')(); +} +function flat(o, p, r) { + p = p || ''; r = r || {}; + for (const k of Object.keys(o)) { + const key = p ? p + '.' + k : k; + if (o[k] && typeof o[k] === 'object' && !Array.isArray(o[k])) flat(o[k], key, r); + else r[key] = o[k]; + } + return r; +} +function unflat() { return {}; } + +const zhObj = load('src/locales/zh/index.ts'); +const enObj = load('src/locales/en/index.ts'); +const zhMap = flat(zhObj); +const enMap = flat(enObj); + +// ---- token dictionaries ---- +// camelCase token -> Chinese (for inferring zh of missing keys) +const tokZh = { + tl: '时间线', timeline: '时间线', mock: '模拟', device: '设备', devices: '设备', + sensor: '传感器', camera: '摄像头', station: '基站', battery: '电量', temp: '温度', temperature: '温度', + uav: '无人机', drone: '无人机', mower: '割草机', robot: '机器人', video: '视频', + status: '状态', task: '任务', tasks: '任务', complete: '完成', completed: '已完成', completing: '完成中', + start: '开始', end: '结束', startcleaning: '开始清洗', startinspection: '开始巡检', startmowing: '开始除草', + standby: '待机', ready: '就绪', return: '返航', supply: '补给', finish: '完成', inspection: '巡检', inspect: '巡检', + mow: '除草', maintenance: '维护', repair: '维修', clean: '清洗', cleaning: '清洗', + msg: '消息', message: '消息', no: '编号', alarm: '告警', rule: '规则', rules: '规则', + param: '参数', params: '参数', left: '左', right: '右', forward: '前', backward: '后', gain: '增益', + speed: '速度', chip: '芯片', uid: 'UID', sign: '标识', route: '路线', routes: '路线', path: '路径', point: '点', points: '点', + indoor: '室内', outdoor: '室外', wide: '广角', zoom: '变焦', lens: '镜头', ir: '红外', switch: '切换', + fail: '失败', failed: '失败', success: '成功', fetch: '获取', save: '保存', create: '创建', created: '已创建', + generate: '生成', export: '导出', import: '导入', download: '下载', upload: '上传', search: '搜索', reset: '重置', + confirm: '确认', cancel: '取消', view: '查看', select: '选择', selected: '已选', name: '名称', type: '类型', + time: '时间', date: '日期', total: '共', count: '数量', today: '今日', yesterday: '昨日', + week: '周', month: '月', day: '日', year: '年', online: '在线', offline: '离线', + error: '错误', errors: '错误', warning: '警告', info: '提示', critical: '严重', important: '重要', general: '一般', + pending: '待处理', closed: '已关闭', handled: '已处理', restored: '已恢复', ignored: '已忽略', falsealarm: '误报', + level: '级别', source: '来源', result: '结果', trend: '趋势', distribution: '分布', statistics: '统计', + subscription: '订阅', history: '历史', realtime: '实时', overview: '概览', analysis: '分析', diagnosis: '诊断', + defect: '缺陷', thermal: '热成像', component: '组件', string: '组串', inverter: '逆变器', efficiency: '效率', + risk: '风险', confidence: '置信度', location: '位置', suggestion: '建议', plan: '计划', execute: '执行', + mowing: '除草', inspect: '巡检', work: '工作', order: '工单', user: '用户', role: '角色', roles: '角色', + permission: '权限', menu: '菜单', organization: '组织', notice: '通知', data: '数据', safety: '安全', + access: '接入', protocol: '协议', longitude: '经度', latitude: '纬度', timezone: '时区', capacity: '容量', + address: '地址', leader: '负责人', contact: '联系', icon: '图标', sort: '排序', parent: '上级', + directory: '目录', button: '按钮', theme: '主题', light: '浅色', dark: '深色', language: '语言', version: '版本', + serial: '序列', verify: '验证', bind: '绑定', code: '码', list: '列表', refresh: '刷新', delete: '删除', + edit: '编辑', add: '新增', update: '更新', enable: '启用', disable: '停用', normal: '正常', abnormal: '异常', + loading: '加载中', generating: '生成中', processing: '处理中', exported: '已导出', uploadfile: '上传文件', + uploadimage: '上传图片', startanalysis: '开始分析', analysisresult: '分析结果', detected: '检测到', + noresult: '无结果', none: '无', unknown: '未知', invalid: '无效', gps: 'GPS', dgps: 'DGPS', rtk: 'RTK', + fixed: '固定', float: '浮点', config: '配置', configured: '已配置', connection: '连接', connect: '连接', + request: '请求', permissionreq: '权限请求', recover: '恢复', resume: '恢复', pause: '暂停', stopped: '停用', + working: '作业中', workingrobots: '作业中机器人', stat: '统计', alert: '告警', alerts: '告警', + deviceenvironment: '设备环境', currenttemp: '当前温度', humidity: '环境湿度', positionaccuracy: '定位精度', + networklatency: '网络延迟', waterrisk: '积水风险', preset: '预设', searchplaceholder: '搜索', unnamed: '未命名', + deleteroute: '删除航线', executeroute: '执行航线', taskboard: '任务看板', pendingtask: '待执行任务', + executing: '执行中', executingellipsis: '执行中', pausedtask: '已暂停任务', executeroutetask: '执行路线任务', + routeinfo: '路线信息', routename: '路线名称', selectexecdevice: '选择执行设备', panorama: '全景', + wdsun: '日', wdmon: '一', wtue: '二', wdwend: '三', wdthu: '四', wdfri: '五', wdsat: '六', + deviceserial: '设备编号', fillrequired: '请填写', selectexectime: '选择执行时间', taskcreated: '任务创建成功', + createfailed: '创建失败', createtaskerror: '创建任务异常', newtask: '新建任务', inputtaskname: '请输入任务名称', + executeroutelabel: '执行路线', selectroute: '选择路线', repeatplan: '重复计划', execdate: '执行日期', + selectexecdate: '选择执行日期', exectime: '执行时间', savetask: '保存任务', devicecontrol: '设备控制', + realtimevideo: '实时视频', takeoff: '起飞', land: '降落', returnhome: '返航', emergencystop: '紧急停止', + rotateleft: '左旋', rotateright: '右旋', gimbalpitch: '云台俯仰', gimbályaw: '云台偏航', zoomin: '放大', zoomout: '缩小', + wayline: '航线', waylinetask: '航线任务', robottask: '机器人任务', devicename: '设备名称', devicetype: '设备类型', + devicemodel: '设备型号', onlinestatus: '在线状态', lastheartbeat: '最后心跳', batterylevel: '电量', + signalstrength: '信号强度', control: '控制', uploadwayline: '上传航线', downloadwayline: '下载航线', + executetask: '执行任务', taskname: '任务名称', taskstatus: '任务状态', flighttime: '飞行时间', altitude: '高度', + distance: '距离', starttime: '开始时间', endtime: '结束时间', drone: '无人机', camera2: '摄像头', + creator: '创建者', executionplan: '执行计划', executioncycle: '执行周期', statusnew: '新建', + statuspaused: '暂停中', statusfinished: '执行成功', statusfailed: '执行失败', confirmdeletetask: '确定要删除该任务吗', + confirmcanceltask: '确定要取消该任务吗', confirmpausetask: '确定要暂停该任务吗', clearrealtime: '清空实时', + cleartrack: '清空实时轨迹', clearplanroute: '清空规划航线', controlmode: '控制模式', localmode: '本地', + remotemode: '远程', heading: '航向', initialized: '已初始化', uninitialized: '未初始化', taskpool: '任务池', + taskexecutionplan: '任务执行计划', taskhistory: '历史任务', createtask: '创建任务', startdate: '开始日期', + enddate: '结束日期', allrobots: '全部机器人', operationsuggestion: '作业编排建议', good: '良好', + devicelocation: '设备定位', devicevideo: '设备视频', robotpanorama: '机器人全景', + dayplan: '按天计划', weekplan: '按周计划', monthplan: '按月计划', daysuffix: '日', unknownplan: '未知计划', + mon: '周一', tue: '周二', wed: '周三', thu: '周四', fri: '周五', sat: '周六', sun: '周日', + vs: '较', unit: '单位', unitplan: '条', unitrobot: '台', unittimes: '次', completionrate: '完成率', + alertscount: '告警数量', costsaving: '节省成本', monthlyaccumulated: '本月累计', daily: '日', target: '目标', + installedcapacity: '装机容量', generationtrend: '发电趋势', irradiance: '辐照度', actual: '实际', predict: '预测', + invertterefficiency: '逆变器效率', maintenanceprogress: '运维进度', stationoverview: '电站概览', + recentinspection: '近期巡检', aianalysissummary: 'AI分析摘要', realtimealerts: '实时告警', + deviceonlinestatus: '设备在线状态', weatherinfo: '天气信息', cleaningsuggestion: '清洗建议', + cleanordergenerated: '清洗工单生成成功', pleaseelectstation: '请选择电站', generatecleanorder: '生成清洗工单', + kwh: 'kWh', kwp: 'kWp', undermaintenance: '维护中', abnormal2: '异常', normal2: '正常', + moduleinfo: '模块信息', heat: '热', hotspot: '热斑', component0: '组件', datapoint: '数据点', datapoints: '数据点', + exportfilename: '导出文件', processing2: '处理中', loadfailed: '加载失败', loaddetailfailed: '加载详情失败', + requesterror: '请求错误', handlesuccess: '处理成功', handlefail: '处理失败', alarmno: '告警编号', + devicedatas: '设备数据', working2: '作业中', mockdevice: '模拟设备', mocksensor: '模拟传感器', + mocktemp: '模拟温度', mocktempexceed: '模拟温度超限', mockbattery: '模拟电量', mockbatterylow: '模拟电量低', + mockvideosignal: '模拟视频信号', mockvideosignallost: '模拟视频信号丢失', mockrule: '模拟规则', + mocksub: '模拟订阅', mocksubops: '模拟运维', mocksubadmin: '模拟管理员', mocksubdevice: '模拟设备', + sms: '短信', email: '邮件', apppush: 'App推送', subscriptionname: '订阅名称', notifychannel: '通知渠道', + devicesensor: '设备传感器', devicebattery: '设备电量', alarmlevel: '告警级别', alarmtype: '告警类型', + alarmsource: '告警来源', alarmtime: '告警时间', handlestatus: '处理状态', handleresult: '处理结果', + handletime: '处理时间', handler: '处理人', handleremark: '处理备注', handle: '处理', closealert: '关闭告警', + confirmalert: '确认告警', exportalert: '导出告警', critical2: '严重', important2: '重要', general2: '一般', + info2: '提示', reopen: '重新打开', sevenDayTrend: '近7天趋势', sevenDayTrend2: '近7天趋势', + criticalAlert: '严重告警', importantAlert: '重要告警', generalAlert: '一般告警', infoAlert: '提示告警', + daySuffix2: '日', daySuffix3: '日', exported2: '已导出', loadDetailFailed2: '加载详情失败', + requestError2: '请求错误', handleSuccess2: '处理成功', handleFail2: '处理失败', alarmNo2: '告警编号', + locationUnknown: '未知位置', none2: '无', good2: '良好', + taskCompletedMsg: '任务完成消息', confirmDeleteTitle: '确认删除', confirmDeleteContent: '确认删除内容', + confirmDeleteOk: '确认删除', routeDeletedSuccess: '路线删除成功', deleteFailRetry: '删除失败重试', + deleteFailNetwork: '删除失败网络', taskListRefreshFail: '任务列表刷新失败', routeNoPathPoints: '路线无路径点', + waylineFallback: '航线兜底', waylineListFetchFail: '航线列表获取失败', fillRequiredFields: '请填写必填项', + createTaskSuccess: '创建任务成功', createTaskFail: '创建任务失败', createTaskException: '创建任务异常', + trendYesterday: '较昨日', trendFlat: '持平', taskListFetchFail: '任务列表获取失败', + fetchParamFail: '参数获取失败', paramSaveSuccess: '参数保存成功', paramSaveFail: '参数保存失败', + paramSaveException: '参数保存异常', paramLeftForwardGain: '左前增益', paramLeftBackwardGain: '左后增益', + paramRightForwardGain: '右前增益', paramRightBackwardGain: '右后增益', paramRunSpeed: '运行速度', + paramChipUidSign: '芯片UID标识', timelineStatusStart: '开始', timelineStatusReady: '就绪', + timelineStatusReturn: '返航', timelineStatusComplete: '完成', tlStartCleaning: '开始清洗', + tlStartInspection: '开始巡检', tlStartMowing: '开始除草', tlStandbyReady: '待机就绪', + tlReturnSupply: '返航补给', tlFinishInspection: '完成巡检', tlFinishMowing: '完成除草', + stationCameraNotFound: '基站摄像头未找到', switchStationCameraFail: '切换基站摄像头失败', + switchDroneLensFail: '切换无人机镜头失败', wideLens: '广角镜头', zoomLens: '变焦镜头', irLens: '红外镜头', + rainNo: '无雨', pointMarked: '已标记点', minThreePoints: '至少三个点', pathPlanDone: '路径规划完成', + pathPlanFailRetry: '路径规划失败重试', completePathFirst: '请先完成路径', inputPlotName: '输入区域名称', + connectionFailed: '连接失败', permissionRequestTitle: '权限请求', requestControlPermission: '请求控制权限', + selectNoDrone: '未选择无人机', commandIssued: '指令已下发', commandFailed: '指令下发失败', + videoConfigured: '视频已配置', noVideo: '无视频', working3: '作业中', online2: '在线', offline2: '离线', + // generic fallbacks + page: '页面', setting: '设置', settings: '设置', manage: '管理', management: '管理', center: '中心', + report2: '报表', log2: '日志', profile: '个人中心', login: '登录', logout: '退出', station2: '电站', + monitor2: '监控', ai: 'AI', optimization: '优化', task2: '任务', order2: '工单', alert2: '告警', + device2: '设备', system2: '系统', user2: '用户', role2: '角色', +}; + +// Comprehensive English-token -> Chinese lexicon (covers tokens from missing keys) +const lex = { + a01: '', ab: '', abnormal: '异常', account: '账号', accumulated: '累计', action: '操作', activated: '已激活', active: '启用', + add: '新增', added: '已添加', additional: '额外', admin: '管理员', advice: '建议', after: '之后', ai: 'AI', airport: '机场', airspace: '空域', + alarm: '告警', alert: '告警', alerts: '告警', algo: '算法', algorithm: '算法', alias: '别名', all: '全部', altitude: '高度', ambient: '环境', + analysis: '分析', and: '与', angle: '角度', api: 'API', app: '应用', approval: '审批', area: '区域', arm: '布防', array: '阵列', assign: '分配', + assigned: '已分配', async: '异步', auto: '自动', average: '平均', avg: '平均', avoid: '避开', back: '返回', backward: '后退', basic: '基础', + batch: '批量', battery: '电量', belonging: '所属', block: '区块', board: '看板', bound: '边界', bow: '船首', breakpoint: '断点', btn: '按钮', + by: '按', calendar: '日历', call: '呼叫', camera: '摄像头', cameras: '摄像头', can: '可', cancel: '取消', canceled: '已取消', cancelled: '已取消', + capacity: '容量', cargo: '货物', cause: '原因', center: '中心', change: '变更', channel: '渠道', channels: '渠道', charge: '充电', chassis: '底盘', + check: '检查', chip: '芯片', choose: '选择', clean: '清洗', cleaning: '清洗', clear: '清空', cleared: '已清空', click: '点击', climb: '爬升', + closed: '已关闭', closure: '闭合', code: '码', col: '列', colon: '冒号', comm: '通信', command: '指令', communicating: '通信中', communication: '通信', + compare: '对比', complete: '完成', completed: '已完成', completion: '完成', component: '组件', condition: '条件', conditions: '条件', + config: '配置', configured: '已配置', confirm: '确认', connected: '已连接', connection: '连接', contact: '联系', content: '内容', continuous: '连续', + control: '控制', controlling: '控制中', coordinated: '协同', count: '数量', cover: '覆盖', coverage: '覆盖', create: '创建', created: '已创建', + critical: '严重', cum: '累计', current: '当前', curve: '曲线', custom: '自定义', cycle: '周期', daily: '每日', data: '数据', date: '日期', + day: '日', days: '天', debug: '调试', default: '默认', del: '删除', delete: '删除', deleted: '已删除', demo: '演示', desc: '描述', description: '描述', + detail: '详情', details: '详情', detect: '检测', detected: '已检测', detection: '检测', deviation: '偏差', device: '设备', devices: '设备', + dgps: 'DGPS', diagnosis: '诊断', diff: '差异', ding: '钉钉', directory: '目录', dispatch: '派单', dispatchable: '可派单', display: '显示', + distribution: '分布', dock: '停靠', dome: '穹顶', done: '已完成', down: '下降', drag: '拖拽', drive: '驱动', drone: '无人机', drones: '无人机', + dual: '双', duty: '值班', edit: '编辑', effect: '效果', email: '邮件', emergency: '紧急', empty: '空', enabled: '已启用', end: '结束', ended: '已结束', + energy: '能量', engineer: '工程师', env: '环境', equipment: '设备', error: '错误', estimated: '预计', event: '事件', events: '事件', exceed: '超限', + excel: 'Excel', exception: '异常', exec: '执行', execute: '执行', executed: '已执行', executing: '执行中', execution: '执行', expired: '已过期', + export: '导出', fail: '失败', failed: '失败', failure: '失败', fallback: '兜底', fan: '风扇', fence: '围栏', fetch: '获取', fields: '字段', file: '文件', + fill: '填写', finish: '完成', fire: '火警', firmware: '固件', first: '先', fixed: '固定', flag: '标志', flat: '持平', flight: '飞行', float: '浮点', + fly: '飞行', forbidden: '禁止', formats: '格式', forward: '前进', found: '找到', frequency: '频率', fri: '周五', from: '从', front: '前', fuel: '燃料', + full: '全', gain: '增益', gantt: '甘特', gate: '闸门', ge: '地理', gear: '档位', general: '一般', generate: '生成', generated: '已生成', + generating: '生成中', generation: '发电', generator: '发电机', geojson: 'GeoJSON', get: '获取', gnss: 'GNSS', good: '良好', gps: 'GPS', gr: '电网', + grid: '电网', group: '分组', handle: '处理', handled: '已处理', handler: '处理人', hangar: '机库', has: '有', header: '表头', heading: '航向', + health: '健康', heatmap: '热力图', height: '高度', high: '高', hint: '提示', history: '历史', home: '首页', home2: '首页', horizontal: '水平', + hotspot: '热斑', hour: '小时', id: 'ID', image: '图片', immediate: '立即', important: '重要', improvement: '提升', in: '内', incident: '事件', + indoor: '室内', inference: '推理', info: '提示', initialized: '已初始化', input: '输入', inspect: '巡检', inspection: '巡检', integrated: '集成', + intelligent: '智能', interactive: '交互', intercom: '对讲', interval: '间隔', intrusion: '入侵', invalid: '无效', inverter: '逆变器', ir: '红外', + irradiance: '辐照度', irreversible: '不可逆', issue: '问题', issued: '已下发', item: '项', items: '项', json: 'JSON', key: '键', keyword: '关键词', + knife: '刀', label: '标签', landing: '降落', language: '语言', last: '最后', layout: '布局', left: '左', legend: '图例', length: '长度', lens: '镜头', + level: '级别', levels: '级别', lift: '抬升', limit: '限制', link: '链接', list: '列表', live: '直播', load: '加载', loaded: '已加载', local: '本地', + location: '位置', log: '日志', login: '登录', logs: '日志', loitering: '徘徊', lost: '丢失', low: '低', machine: '机器', main: '主', manage: '管理', + management: '管理', manager: '管理员', manual: '手动', map: '地图', mark: '标记', marked: '已标记', marks: '标记', mask: '掩码', members: '成员', + menu: '菜单', meteorology: '气象', mid: '中间', mileage: '里程', min: '最小', mock: '模拟', mode: '模式', model: '模型', models: '模型', + modified: '已修改', modify: '修改', mon: '周一', monitor: '监控', month: '月', monthly: '每月', more: '更多', motion: '运动', motor: '电机', mount: '挂载', + mow: '除草', mowing: '除草', msg: '消息', name: '名称', near: '附近', needed: '需要', network: '网络', new: '新', nickname: '昵称', night: '夜间', + no: '无', none: '无', normal: '正常', normally: '正常', north: '北', not: '未', notification: '通知', notify: '通知', now: '现在', number: '编号', + object: '对象', obstacle: '障碍', obtained: '已获取', occur: '发生', of: '的', offline: '离线', ok: '确定', old: '旧', om: '运维', on: '开', one: '一', + online: '在线', only: '仅', open: '打开', operate: '操作', operation: '操作', ops: '运维', optimal: '最优', optimization: '优化', optimize: '优化', + optional: '可选', or: '或', orchestration: '编排', order: '工单', orders: '工单', org: '组织', original: '原始', other: '其他', out: '外', outdoor: '室外', + over: '超过', overlay: '叠加', overview: '概览', page: '页面', panel: '面板', panorama: '全景', param: '参数', params: '参数', password: '密码', + path: '路径', pause: '暂停', paused: '已暂停', pdf: 'PDF', pending: '待处理', per: '每', percent: '百分比', perimeter: '周长', perm: '权限', + permission: '权限', person: '人员', personnel: '人员', phase: '相', phone: '电话', picture: '图片', pitch: '俯仰', pixels: '像素', placeholder: '占位', + plan: '计划', plane: '飞机', planned: '已计划', planning: '规划', platform: '平台', play: '播放', please: '请', plot: '区域', point: '点', points: '点', + polarity: '极性', port: '端口', position: '位置', positioning: '定位', power: '功率', pr: '光伏', precip: '降水', precision: '精度', predicted: '预测', + preparing: '准备中', preset: '预设', preview: '预览', priority: '优先级', probe: '探测', processing: '处理中', product: '产品', progress: '进度', + property: '属性', proportion: '比例', protect: '保护', ptz: '云台', pump: '泵', push: '推送', quality: '质量', quantity: '数量', rain: '雨', + rainfall: '降雨', range: '范围', rate: '率', rated: '额定', ratio: '比例', rbt: '机器人', read: '读取', ready: '就绪', realtime: '实时', rear: '后', + recent: '近期', recharge: '充电', recheck: '复检', recognition: '识别', recommended: '建议', record: '记录', recording: '录制', records: '记录', + recurring: '重复', refresh: '刷新', registry: '注册', related: '关联', relay: '中继', remark: '备注', remote: '远程', repeat: '重复', repellent: '驱避', + replay: '回放', report: '报表', reports: '报表', request: '请求', requesting: '请求中', required: '必填', reset: '重置', restored: '已恢复', result: '结果', + resume: '恢复', resumed: '已恢复', retry: '重试', return: '返航', returned: '已返航', revenue: '收益', reverse: '反向', right: '右', risk: '风险', + robot: '机器人', role: '角色', roles: '角色', room: '房间', root: '根', route: '路线', routes: '路线', rt: '实时', rth: '返航', rtk: 'RTK', rule: '规则', + run: '运行', running: '运行中', safe: '安全', sat: '卫星', satellite: '卫星', satellites: '卫星', save: '保存', saved: '已保存', saving: '节省', + schedule: '计划', scheduled: '已计划', screen: '屏幕', search: '搜索', seg: '分割', segment: '段', segmentation: '分割', select: '选择', selected: '已选', + send: '发送', sensor: '传感器', sent: '已发送', server: '服务器', services: '服务', servo: '伺服', setting: '设置', settings: '设置', seven: '七', + short: '短', show: '显示', sign: '标识', signal: '信号', single: '单', site: '站点', situation: '态势', size: '大小', smart: '智能', smoke: '烟', sms: '短信', + sn: '序列号', snapshots: '快照', sort: '排序', source: '来源', south: '南', span: '跨度', speed: '速度', standby: '待机', start: '开始', started: '已开始', + station: '电站', statistics: '统计', status: '状态', statuses: '状态', stop: '停止', stopped: '已停止', storage: '存储', store: '存储', stream: '流', + streams: '流', string: '组串', sub: '订阅', submit: '提交', subscribers: '订阅者', subscription: '订阅', subtitle: '字幕', success: '成功', suffix: '后缀', + suggest: '建议', suggested: '建议', suggestion: '建议', suitability: '适宜性', suitable: '适宜', summary: '摘要', sun: '太阳', super: '超级', + supervisor: '主管', supply: '补给', supported: '支持', supports: '支持', suspect: '疑似', suspend: '暂停', suspended: '已暂停', swap: '切换', + switch: '切换', symbol: '符号', sync: '同步', system: '系统', tai: '台', talk: '通话', task: '任务', tasks: '任务', temp: '温度', temperature: '温度', + template: '模板', terminated: '已终止', theoretical: '理论', thermal: '热成像', three: '三', threshold: '阈值', throttle: '油门', thu: '周四', + tiao: '条', time: '时间', timed: '定时', timeline: '时间线', timeout: '超时', timestamp: '时间戳', title: '标题', tl: '时间线', to: '到', today: '今日', + top: '顶部', topology: '拓扑', total: '总', tracking: '跟踪', trend: '趋势', trigger: '触发', triggered: '已触发', tue: '周二', turn: '转向', two: '二', + type: '类型', types: '类型', tyre: '轮胎', uav: '无人机', uid: 'UID', unit: '单位', unknown: '未知', up: '上升', update: '更新', upgrading: '升级中', + upload: '上传', usage: '用量', use4g: '4G', user: '用户', users: '用户', uuid: 'UUID', value: '值', version: '版本', vertical: '垂直', video: '视频', + view: '查看', visual: '可视', voltage: '电压', vs: '较', wait: '等待', waiting: '等待中', walk: '行走', wall: '墙', wash: '清洗', way: '路径', + wayline: '航线', waypoint: '航点', waypoints: '航点', weather: '天气', web: '网页', wed: '周三', weed: '杂草', week: '周', weekday: '工作日', + weekly: '每周', west: '西', wheel: '轮', which: '哪个', wide: '广角', width: '宽度', wifi: 'WiFi', wind: '风', window: '窗口', wing: '机翼', with: '带', + work: '工作', working: '作业中', workspace: '工作区', yes: '是', yesterday: '昨日', zero: '零', zone: '区域', zoom: '缩放', +}; +const NOUNS_WITH_NO = new Set(['alarm', 'device', 'order', 'task', 'route', 'serial', 'station', 'work', 'user', 'role', 'id', 'sn', 'uav', 'drone', 'robot']); + +// Chinese phrase/char -> English (for translating hardcoded chinese strings) +const zhEn = { + '保存': 'Save', '保存修改': 'Save Changes', '取消': 'Cancel', '确认': 'Confirm', '确定': 'OK', '删除': 'Delete', + '编辑': 'Edit', '新增': 'Add', '查询': 'Search', '搜索': 'Search', '重置': 'Reset', '提交': 'Submit', '关闭': 'Close', + '返回': 'Back', '导出': 'Export', '导入': 'Import', '下载': 'Download', '上传': 'Upload', '刷新': 'Refresh', + '查看': 'View', '查看全部': 'View All', '查看详情': 'View Details', '操作': 'Action', '状态': 'Status', '类型': 'Type', + '名称': 'Name', '描述': 'Description', '备注': 'Remark', '时间': 'Time', '日期': 'Date', '创建时间': 'Created At', + '更新时间': 'Updated At', '启用': 'Enable', '停用': 'Disable', '正常': 'Normal', '加载中': 'Loading', + '加载中...': 'Loading...', '暂无数据': 'No Data', '成功': 'Success', '失败': 'Failed', '删除成功': 'Deleted', + '删除失败': 'Delete failed', '编辑成功': 'Updated', '编辑失败': 'Update failed', '新增成功': 'Added', + '新增失败': 'Add failed', '保存成功': 'Saved', '保存失败': 'Save failed', '确定删除?': 'Are you sure to delete?', + '请选择': 'Please select', '请输入': 'Please enter', '全部': 'All', '是': 'Yes', '否': 'No', '男': 'Male', '女': 'Female', + '未知': 'Unknown', '未设置': 'Not set', '共': 'Total', '条': 'Items', '全选': 'Select all', '批量删除': 'Batch delete', + '已选': 'Selected', '更多': 'More', '展开': 'Expand', '收起': 'Collapse', '已完成': 'Completed', + '星期一': 'Monday', '星期二': 'Tuesday', '星期三': 'Wednesday', '星期四': 'Thursday', '星期五': 'Friday', + '星期六': 'Saturday', '星期日': 'Sunday', + '光伏机器人环境感知服务平台': 'PV Plant Intelligent Monitoring & O&M System', + '请选择电站': 'Please select a station', '全屏': 'Full Screen', '退出全屏': 'Exit Full Screen', '退出登录': 'Logout', + '电站地址': 'Station Address', '电站类型': 'Station Type', '运行状态': 'Running Status', '正常运行': 'Normal Running', + '地面电站': 'Ground Station', '1MW 光伏电站': '1MW PV Plant', '河南省林州': 'Linzhou, Henan', + '模块开发中': 'Module Developing', + '请输入账号': 'Enter account', '请输入密码': 'Enter password', '账号': 'Account', '密码': 'Password', + '登录': 'Login', '登录成功': 'Login success', '登录失败': 'Login failed', '欢迎': 'Welcome', + '用户协议': 'User Agreement', '隐私政策': 'Privacy Policy', '我已阅读并同意': 'I have read and agree', + '请阅读并同意用户协议': 'Please read and agree to the user agreement', + '总览首页': 'Overview', '设备管理': 'Device Management', '设备总览': 'Device Overview', '设备状态': 'Device Status', + '航线任务': 'Wayline Task', '机器人任务': 'Robot Task', '在线视频': 'Online Video', '实时监控': 'Real-time Monitor', + '告警中心': 'Alert Center', 'AI诊断分析': 'AI Diagnosis', '清洗优化': 'Cleaning Optimization', '工单任务': 'Work Order', + '报表中心': 'Report Center', '系统设置': 'System Settings', '日志管理': 'Log Management', '视频管理': 'Video Management', + '视频下载': 'Video Download', '用户管理': 'User Management', '角色管理': 'Role Management', '菜单管理': 'Menu Management', + '组织管理': 'Organization Management', '场站管理': 'Station Management', '通知策略页面': 'Notice Policy', + '数据与安全页面': 'Data & Safety', '个人中心': 'Profile', '请选择菜单': 'Please select a menu', + '当前功率': 'Current Power', '系统可用率': 'System Availability', '告警数量': 'Alert Count', + '节省运维成本': 'O&M Cost Saving', '日目标': 'Daily Target', '装机容量': 'Installed Capacity', '昨日': 'Yesterday', + '未处理': 'Unhandled', '已关闭': 'Closed', '本月累计节省': 'Monthly Accumulated Saving', '发电趋势': 'Generation Trend', + '辐照度与温度趋势': 'Irradiance & Temperature Trend', '逆变器效率对比': 'Inverter Efficiency', '运维任务进度': 'O&M Progress', + '电站概览': 'Station Overview', '近期巡检记录': 'Recent Inspection', 'AI 分析摘要': 'AI Analysis Summary', + '实时告警': 'Real-time Alerts', '设备在线状态': 'Device Online Status', '天气信息': 'Weather Info', + '清洗建议': 'Cleaning Suggestion', '实际': 'Actual', '预测': 'Predicted', '辐照度': 'Irradiance', '温度': 'Temperature', + '汇流箱': 'Combiner Box', '逆变器': 'Inverter', '巡检机器人': 'Inspection Robot', '异常': 'Abnormal', '维护中': 'Under Maintenance', + '生成清洗工单': 'Generate Cleaning Order', '请先选择电站': 'Please select station first', '清洗工单生成成功': 'Cleaning order created', + 'kWh': 'kWh', 'kWp': 'kWp', + '设备名称': 'Device Name', '设备类型': 'Device Type', '设备型号': 'Device Model', '在线状态': 'Online Status', + '最后心跳': 'Last Heartbeat', '电量': 'Battery', '信号强度': 'Signal Strength', '位置': 'Location', '控制': 'Control', + '上传航线': 'Upload Wayline', '下载航线': 'Download Wayline', '执行任务': 'Execute Task', '任务名称': 'Task Name', + '任务状态': 'Task Status', '飞行时间': 'Flight Time', '高度': 'Altitude', '速度': 'Speed', '距离': 'Distance', + '开始时间': 'Start Time', '结束时间': 'End Time', '无人机': 'UAV', '割草机': 'Mower', '机器人': 'Robot', '摄像头': 'Camera', + '在线': 'Online', '离线': 'Offline', '待机': 'Standby', '运行中': 'Running', '充电中': 'Charging', '空闲': 'Idle', + '忙碌': 'Busy', '故障': 'Fault', '设备控制': 'Device Control', '实时视频': 'Real-time Video', '起飞': 'Takeoff', + '降落': 'Land', '返航': 'Return', '暂停': 'Pause', '继续': 'Resume', '紧急停止': 'Emergency Stop', '前进': 'Forward', + '后退': 'Backward', '左转': 'Turn Left', '右转': 'Turn Right', '上升': 'Up', '下降': 'Down', '左旋': 'Rotate Left', + '右旋': 'Rotate Right', '云台俯仰': 'Gimbal Pitch', '云台偏航': 'Gimbal Yaw', '放大': 'Zoom In', '缩小': 'Zoom Out', + '创建者': 'Creator', '执行计划': 'Execution Plan', '周日': 'Sun', '周一': 'Mon', '周二': 'Tue', '周三': 'Wed', + '周四': 'Thu', '周五': 'Fri', '周六': 'Sat', '执行周期': 'Execution Cycle', '新建': 'New', '暂停中': 'Paused', + '执行成功': 'Finished', '执行失败': 'Failed', '确定要删除该任务吗?': 'Delete this task?', '确定要取消该任务吗?': 'Cancel this task?', + '确定要暂停该任务吗?': 'Pause this task?', '恢复': 'Resume', '今日机器人计划': 'Today Robot Plan', + '进行中': 'In Progress', '作业中机器人': 'Working Robots', '台': 'Units', '作业中': 'Working', '累计作业里程': 'Total Distance', + '较昨日': 'vs Yesterday', '任务完成率': 'Task Completion Rate', '异常告警': 'Abnormal Alerts', '次': 'Times', + '在线率': 'Online Rate', '良好': 'Good', '机器人路径规划与监控': 'Robot Path Planning & Monitor', + '清空实时轨迹': 'Clear Track', '清空规划航线': 'Clear Plan', '控制模式:': 'Control Mode:', '本地': 'Local', '远程': 'Remote', + '航向:': 'Heading:', '已初始化': 'Initialized', '未初始化': 'Uninitialized', '定位:': 'Location:', '视频:': 'Video:', + '任务池': 'Task Pool', '任务执行计划': 'Task Execution Plan', '历史任务': 'Task History', '创建任务': 'Create Task', + '开始日期': 'Start Date', '结束日期': 'End Date', '全部机器人': 'All Robots', '作业编排建议': 'Operation Suggestion', + '开始除草任务': 'Start Mowing', '预计耗时 2 小时,当前进度 45%': 'ETA 2h, progress 45%', '预定巡检任务': 'Scheduled Inspection', + '待审批,建议执行区域 C': 'Pending, suggest area C', '设备例行维护': 'Routine Maintenance', '所有割草机返回充电站': 'All mowers return', + '智能生成今日计划': 'Auto-generate Today Plan', '设备环境与状态': 'Device Environment & Status', '当前温度': 'Current Temp', + '环境湿度': 'Humidity', '定位精度': 'Position Accuracy', 'RTK 固定': 'RTK Fixed', '网络延迟': 'Network Latency', + '注意:区域 B 存在积水风险,建议避开': 'Caution: area B flooding risk, avoid', '预设路线': 'Preset Routes', + '搜索...': 'Search...', '未命名路线': 'Unnamed Route', '删除航线': 'Delete Route', '执行航线': 'Execute Route', + '任务看板': 'Task Board', '待执行': 'Pending', '正在执行...': 'Executing...', '已暂停': 'Paused', '执行路线任务': 'Execute Route Task', + '执行': 'Execute', '路线信息': 'Route Info', '路线名称:': 'Route Name:', '请选择执行设备': 'Select Device', '机器人全景视频 - ': 'Robot Panorama - ', + '设备编号': 'Device No', '路线': 'Route', '请填写:': 'Please fill:', '请选择执行时间': 'Select Exec Time', + '任务创建成功': 'Task Created', '创建失败': 'Create Failed', '创建任务异常': 'Create Task Error', '新建任务': 'New Task', + '请输入任务名称': 'Enter Task Name', '执行路线': 'Execute Route', '选择路线': 'Select Route', '重复计划': 'Repeat Plan', + '天': 'Day', '周': 'Week', '月': 'Month', '执行日期': 'Exec Date', '选择执行日期': 'Select Exec Date', '执行时间': 'Exec Time', + '保存任务': 'Save Task', + '视频监控': 'Video Monitor', '实时监控': 'Real-time Monitor', '视频墙': 'Video Wall', '视频下载': 'Video Download', + '截图': 'Snapshot', '录制': 'Record', '播放': 'Play', '停止': 'Stop', '静音': 'Mute', '取消静音': 'Unmute', '直播': 'Live', + '回放': 'Replay', '选择设备': 'Select Device', '选择摄像头': 'Select Camera', '暂无视频': 'No Video', '视频加载中...': 'Loading video...', + '画质': 'Quality', '流畅': 'Fluency', '标清': 'SD', '高清': 'HD', '超清': 'FHD', '下载时间': 'Download Time', + '文件大小': 'File Size', '文件名': 'File Name', '下载状态': 'Download Status', '下载中': 'Downloading', '已下载': 'Downloaded', + '下载失败': 'Download Failed', '监控模式': 'Monitor Mode', '单画面': 'Single', '四画面': 'Quad', '九画面': 'Nine', '十六画面': 'Sixteen', + '告警概览': 'Alert Overview', '实时告警': 'Real-time Alert', '历史告警': 'Alert History', '告警规则': 'Alert Rules', + '告警统计': 'Alert Statistics', '告警订阅': 'Alert Subscription', '告警级别': 'Alert Level', '告警类型': 'Alert Type', + '告警来源': 'Alert Source', '告警时间': 'Alert Time', '处理状态': 'Handle Status', '处理结果': 'Handle Result', + '处理时间': 'Handle Time', '处理人': 'Handler', '处理备注': 'Handle Remark', '处理': 'Handle', '关闭告警': 'Close Alert', + '确认告警': 'Confirm Alert', '导出告警': 'Export Alert', '严重': 'Critical', '重要': 'Important', '一般': 'General', + '提示': 'Info', '待处理': 'Pending', '已处理': 'Handled', '已恢复': 'Restored', '已忽略': 'Ignored', '无法处理': 'Unable', + '厂家处理中': 'Manufacturer', '误报': 'False Alarm', '告警总数': 'Total Alerts', '今日告警': 'Today Alerts', + '未处理告警': 'Unhandled Alerts', '处理率': 'Handle Rate', '无人机故障': 'UAV Fault', '割草机故障': 'Mower Fault', + '其他设备故障': 'Other Device Fault', '服务器故障': 'Server Failure', '系统错误': 'System Error', '告警趋势': 'Alert Trend', + '告警分布': 'Alert Distribution', + 'AI诊断分析': 'AI Diagnosis', '热成像分析': 'Thermal Analysis', '缺陷检测': 'Defect Detection', '温度分析': 'Temp Analysis', + '热斑检测': 'Hotspot Detection', '组件分析': 'Component Analysis', '组串分析': 'String Analysis', '逆变器分析': 'Inverter Analysis', + '上传图片': 'Upload Image', '开始分析': 'Start Analysis', '分析结果': 'Analysis Result', '缺陷类型': 'Defect Type', + '缺陷等级': 'Defect Level', '置信度': 'Confidence', '位置': 'Location', '处理建议': 'Suggestion', '异常温度': 'Abnormal Temp', + '最高温度': 'Max Temp', '平均温度': 'Avg Temp', '最低温度': 'Min Temp', '正常温度': 'Normal Temp', '热斑区域': 'Hotspot Area', + '风险等级': 'Risk Level', '高风险': 'High Risk', '中风险': 'Medium Risk', '低风险': 'Low Risk', '无风险': 'No Risk', + '检测到缺陷': 'Defects Detected', '未检测到缺陷': 'No Defect', '分析历史': 'Analysis History', '重新分析': 'Re-analyze', + '清洗优化': 'Cleaning Optimization', '清洗任务': 'Cleaning Task', '清洗建议': 'Cleaning Suggestion', '清洗计划': 'Cleaning Plan', + '清洗记录': 'Cleaning Record', '清洗效果': 'Cleaning Effect', '清洗前效率': 'Eff Before', '清洗后效率': 'Eff After', + '效率提升': 'Eff Improvement', '预计收益': 'Est Revenue', '清洗成本': 'Clean Cost', '净收益': 'Net Revenue', '优先级': 'Priority', + '建议日期': 'Suggest Date', '清洗区域': 'Area', '清洗方式': 'Method', '人工清洗': 'Manual', '机器人清洗': 'Robot', + '水清洗': 'Water', '干清洗': 'Dry', '状态': 'Status', '待清洗': 'Pending', '清洗中': 'Cleaning', '已清洗': 'Cleaned', + '已取消': 'Cancelled', '生成工单': 'Generate Order', '污损率': 'Soiling Rate', '功率损失': 'Power Loss', + '上次清洗日期': 'Last Clean', '建议清洗日期': 'Next Clean', + '工单任务': 'Work Order', '工单标题': 'Order Title', '工单类型': 'Order Type', '工单状态': 'Order Status', + '执行开始时间': 'Exec Start Time', '关联设备': 'Related Device', '关联区域': 'Related Area', '当前状态': 'Current Status', + '任务描述': 'Task Description', '附件素材': 'Attachments', '执行工单': 'Execute Order', '修改': 'Modify', '挂起': 'Suspend', + '完成': 'Complete', '派单': 'Dispatch', '修改工单': 'Modify Order', '工单派单': 'Dispatch Order', '近7天全部关联告警': '7d Alerts', + '请输入工单标题': 'Enter Title', '请选择工单类型': 'Select Type', '请选择优先级': 'Select Priority', '确认修改': 'Confirm Modify', + '确认派单': 'Confirm Dispatch', '确认执行': 'Confirm Execute', '确认完成': 'Confirm Complete', '选择接收人员': 'Select Receiver', + '请选择派单人员': 'Select Dispatcher', '选择执行设备': 'Select Device', '确定挂起?': 'Suspend?', '已创建': 'Created', + '执行中': 'In Progress', '已挂起': 'Suspended', '已完成': 'Completed', '待处理': 'Pending', '高': 'High', '中': 'Medium', '低': 'Low', + 'AI 建议与关联信息': 'AI Suggestion', '建议携带': 'Suggested Equipment', '红外热像设备': 'Thermal Camera', + '优先复检组件': 'Prioritize Recheck', '预计影响发电': 'Est Impact', '关联告警趋势': 'Alert Trend', '关联告警列表': 'Alert List', + '近7天无告警记录': 'No alerts in 7d', '近 7 天': '(7d)', '高告警': 'High', '中告警': 'Medium', '低告警': 'Low', + '巡检': 'Inspection', '检修': 'Maintenance', '清洗': 'Cleaning', '维修': 'Repair', '运维': 'O&M', 'AI': 'AI', + '割草机故障': 'Mower Fault', '无人机故障': 'UAV Fault', '光伏组件故障': 'PV Module Fault', '清洗': 'Clean', '维护': 'Maintain', + '维修': 'Repair', '其他': 'Other', + '报表中心': 'Report Center', '发电量报表': 'Generation Report', '设备报表': 'Device Report', '告警报表': 'Alert Report', + '工单报表': 'Work Order Report', '巡检报表': 'Inspection Report', '清洗报表': 'Cleaning Report', '效率报表': 'Efficiency Report', + '自定义报表': 'Custom Report', '日报': 'Daily', '周报': 'Weekly', '月报': 'Monthly', '季报': 'Quarterly', '年报': 'Yearly', + '日期范围': 'Date Range', '生成报表': 'Generate Report', '导出报表': 'Export Report', '总发电量': 'Total Generation', + '峰值功率': 'Peak Power', '平均功率': 'Avg Power', '告警总数': 'Total Alerts', '工单总数': 'Total Orders', '完成率': 'Completion', + '平均效率': 'Avg Efficiency', + '系统设置': 'System Settings', '基础设置': 'Basic Settings', '用户管理': 'User Management', '菜单管理': 'Menu Management', + '组织管理': 'Organization Management', '场站管理': 'Station Management', '通知策略': 'Notice Policy', '设备接入': 'Device Access', + '数据与安全': 'Data & Safety', '系统维护': 'System Maintenance', '在线用户': 'Online Users', '设备管理': 'Device Management', + '日志管理': 'Log Management', '视频管理': 'Video Management', '系统名称': 'System Name', '系统版本': 'System Version', + '系统描述': 'System Description', '默认语言': 'Default Language', '系统Logo': 'System Logo', '主题': 'Theme', '浅色': 'Light', + '深色': 'Dark', '跟随系统': 'Follow System', '用户账号': 'Username', '用户昵称': 'Nickname', '手机号': 'Phone', '邮箱': 'Email', + '角色': 'Role', '密码': 'Password', '不填则不修改': 'Leave blank to keep', '语言': 'Language', '分配角色': 'Assign Role', + '性别': 'Gender', '状态': 'Status', '新增用户': 'Add User', '编辑用户': 'Edit User', '重置密码': 'Reset Password', + '确定将密码重置为 123456 吗?': 'Reset password to 123456?', '密码已重置为:123456': 'Password reset to 123456', + '中文': 'Chinese', '英语': 'English', '菜单名称': 'Menu Name', '菜单类型': 'Menu Type', '路由地址': 'Route Path', + '菜单图标': 'Menu Icon', '排序': 'Sort', '权限标识': 'Permission', '上级菜单': 'Parent Menu', '目录': 'Directory', + '菜单': 'Menu', '按钮': 'Button', '组织名称': 'Org Name', '组织类型': 'Org Type', '上级组织': 'Parent Org', '负责人': 'Leader', + '联系电话': 'Contact', '场站名称': 'Station Name', '场站类型': 'Station Type', '场站地址': 'Station Address', + '装机容量': 'Capacity', '场站状态': 'Station Status', '经度': 'Longitude', '纬度': 'Latitude', '时区': 'Timezone', + '日志类型': 'Log Type', '登录日志': 'Login Log', '操作日志': 'Operation Log', '错误日志': 'Error Log', '系统日志': 'System Log', + '操作人': 'Operator', '操作内容': 'Content', '操作时间': 'Time', 'IP地址': 'IP', '视频名称': 'Video Name', '视频地址': 'Video URL', + '视频类型': 'Video Type', '视频状态': 'Video Status', '备份数据库': 'Backup DB', '清除缓存': 'Clear Cache', + '系统信息': 'System Info', '磁盘使用': 'Disk', '内存使用': 'Memory', 'CPU使用率': 'CPU', '服务器状态': 'Server Status', + '运行时长': 'Uptime', '接入协议': 'Protocol', '接入状态': 'Status', '接入时间': 'Time', 'SN码': 'SN', '验证码': 'Verify Code', + '绑定设备': 'Bind Device', '错误编码': 'Error Code', '错误名称': 'Error Name', '错误来源': 'Error Source', '错误等级': 'Error Level', + '错误描述': 'Error Desc', '校验字段': 'Check Field', '对比规则': 'Compare Rule', '对比数值': 'Compare Value', + '区间最小值': 'Min', '区间最大值': 'Max', '请输入错误编码': 'Enter Error Code', '请输入错误名称': 'Enter Error Name', + '请选择来源': 'Select Source', '请选择等级': 'Select Level', '请选择错误来源': 'Select Error Source', + '请选择错误等级': 'Select Error Level', '请填写校验字段名': 'Enter Field', '请选择对比规则': 'Select Rule', + '请填写最小值': 'Enter Min', '请填写最大值': 'Enter Max', '请填写对比值': 'Enter Value', + '例如:ERR-ROBOT-001': 'e.g. ERR-ROBOT-001', '如:battery、speed、temperature': 'e.g. battery, speed', + '最小值': 'Min', '最大值': 'Max', '数值/文本,多个用逗号分隔': 'comma separated', '详细描述触发该错误的场景': 'Describe scenario', + '出现该错误后的处理方案': 'Handling plan', '新增错误规则': 'Add Error Rule', '编辑错误规则': 'Edit Error Rule', + '查看错误规则详情': 'View Error Rule', '确定要删除错误 "': 'Delete error "', '获取错误列表失败': 'Get error list failed', + '操作失败': 'Operation failed', + '个人中心': 'Profile', '获取个人信息失败': 'Get profile failed', '修改成功': 'Modified', '修改失败': 'Modify failed', + '密码修改成功': 'Password changed', '头像上传成功': 'Avatar uploaded', '上传失败': 'Upload failed', + '两次输入的密码不一致': 'Passwords mismatch', '更换头像': 'Change Avatar', '用户名称': 'Username', '用户昵称': 'Nickname', + '手机号码': 'Phone', '用户邮箱': 'Email', '所属角色': 'Role', '旧密码': 'Old Password', '新密码': 'New Password', + '确认密码': 'Confirm Password', '请输入用户昵称': 'Enter Nickname', '请输入手机号码': 'Enter Phone', '保存修改': 'Save Changes', + '确认修改': 'Confirm Modify', + '用户管理': 'User Management', '用户账号': 'Username', '用户昵称': 'Nickname', '手机号': 'Phone', '邮箱': 'Email', + '角色': 'Role', '状态': 'Status', '操作': 'Actions', '编辑': 'Edit', '删除': 'Delete', '用户账号': 'Username', + '用户昵称': 'Nickname', '手机号码': 'Phone', '密码': 'Password', '语言': 'Language', '分配角色': 'Assign Role', + '状态': 'Status', '性别': 'Gender', '不填则不修改': 'Leave blank', '中文': 'Chinese', '英语': 'English', '正常': 'Normal', + '停用': 'Disabled', '男': 'Male', '女': 'Female', '未知': 'Unknown', '批量删除(已选 ': 'Batch delete (', '重置密码': 'Reset Password', + '密码已重置为:123456': 'Password reset to 123456', '确定将密码重置为 123456 吗?': 'Reset to 123456?', + '角色管理': 'Role Management', '新增角色': 'Add Role', '角色名称': 'Role Name', '权限字符': 'Role Key', '排序': 'Sort', + '状态': 'Status', '备注': 'Remark', '操作': 'Actions', '编辑': 'Edit', '删除': 'Delete', '分配权限': 'Assign Permissions', + '数据权限': 'Data Scope', '菜单权限': 'Menu Permissions', '角色描述': 'Role Description', '正常': 'Normal', '停用': 'Disabled', + '异常': 'Abnormal', + '登录已过期,请重新登录': 'Session expired, please login again', '网络异常,请稍后重试': 'Network error, retry later', + '晴': 'Sunny', '多云': 'Cloudy', '阴': 'Overcast', '雨': 'Rainy', +}; + +// ---- collect referenced keys ---- +const refRegex = /(?:i18n\.t|[^a-zA-Z]t)\(\s*['"`]([a-zA-Z][a-zA-Z0-9_.]*?)['"`]/g; +const files = walk(root); +const refKeys = new Set(); +for (const f of files) { + const c = fs.readFileSync(f, 'utf8'); + let m; + while ((m = refRegex.exec(c))) refKeys.add(m[1]); +} + +// ---- helpers ---- +function camelToWords(s) { + return s.replace(/([a-z])([A-Z])/g, '$1 $2').replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2').split(/[\s._]+/).filter(Boolean); +} +function inferZhFromKey(key) { + const leaf = key.split('.').pop(); + const words = camelToWords(leaf).map(w => w.toLowerCase()); + const out = []; + for (let i = 0; i < words.length; i++) { + const w = words[i]; + let zh = lex[w] || tokZh[w] || (w.length <= 2 && w === w.toUpperCase() ? w : ''); + if (w === 'no') zh = (i > 0 && NOUNS_WITH_NO.has(words[i - 1])) ? '编号' : '无'; + if (!zh) continue; + if (out[out.length - 1] !== zh) out.push(zh); + } + return out.join(''); +} +function inferEnFromKey(key) { + const leaf = key.split('.').pop(); + const words = camelToWords(leaf); + const STOP = new Set(['of', 'and', 'the', 'to', 'in', 'on', 'with', 'a', 'an', 'or', 'for']); + const out = []; + for (let i = 0; i < words.length; i++) { + let w = words[i]; + if (w.length <= 2 || w === w.toUpperCase()) { /* keep acronym */ } + else if (i > 0 && STOP.has(w.toLowerCase())) w = w.toLowerCase(); + else w = w.charAt(0).toUpperCase() + w.slice(1); + if (out[out.length - 1] !== w) out.push(w); + } + return out.join(' '); +} +function translateZh(zh) { + if (zhEn[zh]) return zhEn[zh]; + // greedy phrase substitution for multi-char matches + let s = zh; + // sort keys by length desc for greedy match + const keys = Object.keys(zhEn).sort((a, b) => b.length - a.length); + for (const k of keys) { if (k.length >= 2) s = s.split(k).join('\u0001' + zhEn[k] + '\u0001'); } + // now translate remaining single chars / unknown + const segs = s.split('\u0001'); + const res = segs.map(seg => { + if (zhEn[seg]) return zhEn[seg]; + // try per-char? skip unknown chars to avoid garbage + return seg; + }); + return res.join(' ').replace(/\s+/g, ' ').trim(); +} + +// ---- determine additions ---- +const zhAdd = {}; // ns -> {key: val} +const enAdd = {}; +const zhCjk = {}; +const enCjk = {}; +function addKey(fullKey, zhVal, enVal, isCjk) { + const dot = fullKey.indexOf('.'); + const ns = dot > -1 ? fullKey.slice(0, dot) : ''; + const leaf = dot > -1 ? fullKey.slice(dot + 1) : fullKey; + const zmap = isCjk ? zhCjk : zhAdd; + const emap = isCjk ? enCjk : enAdd; + zmap[ns] = zmap[ns] || {}; + emap[ns] = emap[ns] || {}; + if (!(fullKey in zhMap)) zmap[ns][leaf] = zhVal; + if (!(fullKey in enMap)) emap[ns][leaf] = enVal; +} + +// referenced keys missing +let missingCount = 0; +for (const k of refKeys) { + if (!(k in zhMap) && !(k in enMap)) { + missingCount++; + addKey(k, inferZhFromKey(k), inferEnFromKey(k)); + } +} + +// hardcoded chinese -> keys (reuse existing by value; else new key under a file-based ns) +// map file path to namespace +function nsForFile(f) { + const rel = f.replace(root + '\\', '').replace(/\\/g, '/'); + const base = path.basename(f).replace(/\.(tsx|ts|jsx|js)$/, ''); + // try to infer from existing namespaces by matching folder/file + const lower = rel.toLowerCase(); + if (lower.includes('systemsetting')) return 'systemSetting'; + if (lower.includes('devices/') || lower.includes('device')) return 'devices'; + if (lower.includes('alerts')) return 'alerts'; + if (lower.includes('aianalysis') || lower.includes('aidiagnos')) return 'aiAnalysis'; + if (lower.includes('clean')) return 'clean'; + if (lower.includes('report')) return 'report'; + if (lower.includes('workorder')) return 'workOrder'; + if (lower.includes('login')) return 'login'; + if (lower.includes('profile')) return 'profile'; + if (lower.includes('users')) return 'users'; + if (lower.includes('roles')) return 'roles'; + if (lower.includes('home')) return 'home'; + if (lower.includes('video')) return 'video'; + if (lower.includes('router') || lower.includes('layout')) return 'layout'; + return 'page'; +} + +const CJK = /[一-鿿]/; +let cjkCount = 0; +const cjkSeen = new Set(); +for (const f of files) { + const lines = fs.readFileSync(f, 'utf8').split('\n'); + for (const line of lines) { + const trimmed = line.trim(); + if (trimmed.startsWith('//') || trimmed.startsWith('*')) continue; + if (/console\./.test(line)) continue; + if (!CJK.test(line)) continue; + // extract CJK substrings that look like user-facing text (skip pure comment lines) + const matches = line.match(/[一-鿿][一-鿿,。、::()()\sA-Za-z0-9%·\-/]*[一-鿿]/g); + if (!matches) continue; + for (const m of matches) { + const txt = m.trim(); + if (txt.length < 2) continue; + if (cjkSeen.has(txt)) continue; + cjkSeen.add(txt); + cjkCount++; + // reuse existing key if zh value equals this + let reuseKey = null; + for (const ek of Object.keys(zhMap)) { if (zhMap[ek] === txt) { reuseKey = ek; break; } } + if (reuseKey) continue; // already have it + const ns = nsForFile(f); + // build a key from file base + counter + const base = path.basename(f).replace(/\.(tsx|ts|jsx|js)$/, '').replace(/[^a-zA-Z0-9]/g, ''); + const key = ns + '.' + base + '_' + (cjkSeen.size); + addKey(key, txt, translateZh(txt), true); + } + } +} + +console.log('Referenced keys total:', refKeys.size); +console.log('Missing referenced keys (added):', missingCount); +console.log('Hardcoded CJK unique strings seen:', cjkCount); +console.log('New keys to add (zh):', Object.values(zhAdd).reduce((s, o) => s + Object.keys(o).length, 0)); + +// save additions for next step +fs.writeFileSync('tmp_missing.json', JSON.stringify({ zhAdd, enAdd }, null, 2)); +fs.writeFileSync('tmp_cjk.json', JSON.stringify({ zhCjk, enCjk }, null, 2)); +console.log('Wrote tmp_missing.json and tmp_cjk.json'); diff --git a/tmp_inject.cjs b/tmp_inject.cjs new file mode 100644 index 0000000..602dc7b --- /dev/null +++ b/tmp_inject.cjs @@ -0,0 +1,64 @@ +// Inject missing keys into zh/index.ts and en/index.ts, preserving structure. +const fs = require('fs'); + +function load(p) { + let s = fs.readFileSync(p, 'utf8'); + s = s.replace(/const\s+\w+\s*=\s*\{/, 'var obj = {'); + s = s.replace(/export\s+default\s+\w+;?/, ''); + s = s.replace(/\bconst\b/g, 'var'); + return new Function(s + '\nreturn obj;')(); +} +function flat(o, p, r) { + p = p || ''; r = r || {}; + for (const k of Object.keys(o)) { + const key = p ? p + '.' + k : k; + if (o[k] && typeof o[k] === 'object' && !Array.isArray(o[k])) flat(o[k], key, r); + else r[key] = o[k]; + } + return r; +} +function esc(v) { + return String(v).replace(/\\/g, '\\\\').replace(/'/g, "\\'"); +} + +const adds = require('./tmp_missing.json'); +const zhAdd = adds.zhAdd, enAdd = adds.enAdd; + +function inject(file, addsMap, label) { + const text = fs.readFileSync(file, 'utf8'); + const lines = text.split('\n'); + // find namespaces present + const present = new Set(); + for (const ns of Object.keys(addsMap)) { + let nsStart = -1; + for (let i = 0; i < lines.length; i++) { + if (/^\t[A-Za-z_][A-Za-z0-9_]*:\s*\{/.test(lines[i]) && lines[i].trim().startsWith(ns + ':')) { nsStart = i; break; } + } + if (nsStart < 0) { console.log(' [WARN] namespace not found, skip: ' + ns); continue; } + // compute closing brace line by brace depth + let depth = 0; let closeLine = -1; + for (let i = nsStart; i < lines.length; i++) { + const op = (lines[i].match(/\{/g) || []).length; + const cl = (lines[i].match(/\}/g) || []).length; + depth += op - cl; + if (depth === 0) { closeLine = i; break; } + } + if (closeLine < 0) { console.log(' [WARN] no close for ' + ns); continue; } + // build insertion block (sorted) + const keys = Object.keys(addsMap[ns]).sort(); + const block = keys.map(k => '\t\t' + k + ": '" + esc(addsMap[ns][k]) + "',").join('\n'); + lines.splice(closeLine, 0, block); + console.log(' ' + ns + ': +' + keys.length + ' keys'); + } + fs.writeFileSync(file, lines.join('\n'), 'utf8'); + // verify parse + const obj = load(file); + const map = flat(obj); + console.log(' ' + label + ' parsed OK, total flat keys: ' + Object.keys(map).length); +} + +console.log('Injecting into zh/index.ts ...'); +inject('src/locales/zh/index.ts', zhAdd, 'zh'); +console.log('Injecting into en/index.ts ...'); +inject('src/locales/en/index.ts', enAdd, 'en'); +console.log('Done.'); diff --git a/tmp_inject2.cjs b/tmp_inject2.cjs new file mode 100644 index 0000000..c1cdba4 --- /dev/null +++ b/tmp_inject2.cjs @@ -0,0 +1,75 @@ +// Inject the hardcoded-Chinese keys (tmp_newkeys.json) into locale files, +// preserving formatting by inserting before each namespace's closing brace. +const fs = require('fs'); +const path = require('path'); + +function load(p) { + let s = fs.readFileSync(p, 'utf8'); + s = s.replace(/const\s+\w+\s*=\s*\{/, 'var obj = {'); + s = s.replace(/export\s+default\s+\w+;?/, ''); + s = s.replace(/\bconst\b/g, 'var'); + return new Function(s + '\nreturn obj;')(); +} +function flat(o, p, r) { p = p || ''; r = r || {}; for (const k of Object.keys(o)) { const key = p ? p + '.' + k : k; if (o[k] && typeof o[k] === 'object' && !Array.isArray(o[k])) flat(o[k], key, r); else r[key] = o[k]; } return r; } + +const newKeys = JSON.parse(fs.readFileSync('tmp_newkeys.json', 'utf8')); +const zhFlat = flat(load('src/locales/zh/index.ts')); +const enFlat = flat(load('src/locales/en/index.ts')); + +function esc(v) { return String(v).replace(/\\/g, '\\\\').replace(/'/g, "\\'"); } + +function insertIntoNs(text, ns, entries, indentUnit) { + // entries: [[base, value], ...] for keys NOT already present + const re = new RegExp('(\\n[ \\t]*)' + ns + ':\\s*\\{'); + const m = re.exec(text); + if (!m) return null; + let i = m.index + m[0].length; + let depth = 1, j = i; + while (j < text.length && depth > 0) { const c = text[j]; if (c === '{') depth++; else if (c === '}') depth--; j++; } + const closeIdx = j - 1; + const indent = m[1]; + const childIndent = indent + indentUnit; + const qk = (k) => /^[0-9]/.test(k) ? "'" + k + "'" : k; + const block = entries.map(([k, v]) => childIndent + qk(k) + ": '" + esc(v) + "',").join('\n'); + return text.slice(0, closeIdx) + block + '\n' + text.slice(closeIdx); +} + +function addNamespace(text, ns, entries, indentUnit) { + // find top-level const X = { ... } closing brace + const m = /const\s+\w+\s*=\s*\{/.exec(text); + let i = m.index + m[0].length; + let depth = 1, j = i; + while (j < text.length && depth > 0) { const c = text[j]; if (c === '{') depth++; else if (c === '}') depth--; j++; } + const closeIdx = j - 1; + const indent = '\n' + indentUnit; + const childIndent = indent + indentUnit; + const qk = (k) => /^[0-9]/.test(k) ? "'" + k + "'" : k; + const block = entries.map(([k, v]) => childIndent + qk(k) + ": '" + esc(v) + "',").join('\n'); + const nsBlock = indent + ns + ': {' + '\n' + block + '\n' + indent + '}'; + return text.slice(0, closeIdx) + ',' + nsBlock + text.slice(closeIdx); +} + +for (const file of ['src/locales/zh/index.ts', 'src/locales/en/index.ts']) { + let text = fs.readFileSync(file, 'utf8'); + const flatMap = file.includes('zh') ? zhFlat : enFlat; + const src = file.includes('zh') ? newKeys.zh : newKeys.en; + let added = 0; + for (const ns of Object.keys(src)) { + const entries = []; + for (const base of Object.keys(src[ns])) { + const key = ns + '.' + base; + if (key in flatMap) continue; // already present + entries.push([base, src[ns][base]]); + } + if (!entries.length) continue; + const before = text; + let res = insertIntoNs(text, ns, entries, '\t'); + if (res === null) { + // namespace missing (e.g., 'page') -> create it + res = addNamespace(text, ns, entries, '\t'); + } + if (res !== null && res !== before) { text = res; added += entries.length; } + } + fs.writeFileSync(file, text, 'utf8'); + console.log(file + ' -> added ' + added + ' keys'); +} diff --git a/tmp_inspect.cjs b/tmp_inspect.cjs new file mode 100644 index 0000000..b74ac08 --- /dev/null +++ b/tmp_inspect.cjs @@ -0,0 +1,9 @@ +const a = require('./tmp_missing.json'); +const z = a.zhAdd, e = a.enAdd; +const ns = ['devices','alerts','aiAnalysis','clean','workOrder','systemSetting','home']; +for (const n of ns) { + const zk = z[n] || {}, ek = e[n] || {}; + const keys = Object.keys(zk).slice(0, 14); + console.log('=== ' + n + ' (' + Object.keys(zk).length + ') ==='); + for (const k of keys) { console.log(' ' + k + ' => ZH[' + zk[k] + '] EN[' + ek[k] + ']'); } +} diff --git a/tmp_missing.json b/tmp_missing.json new file mode 100644 index 0000000..0571e8a --- /dev/null +++ b/tmp_missing.json @@ -0,0 +1,2202 @@ +{ + "zhAdd": { + "alerts": { + "processing": "处理中", + "alarmNo": "告警编号", + "alarmTitle": "告警标题", + "alarmContent": "告警内容", + "alertDetail": "告警详情", + "rootCause": "根原因", + "recommendedAction": "建议操作", + "notifySubscription": "通知订阅", + "sms": "短信", + "dingTalk": "钉钉通话", + "email": "邮件", + "handleSuccess": "处理成功", + "handleFail": "处理失败", + "exportFileName": "导出文件名称", + "reportValue": "报表值", + "noAlarmToCreateOrder": "无告警到创建工单", + "orderFromAlarm": "工单从告警", + "orderCreated": "工单已创建", + "orderCreateFail": "工单创建失败", + "todayTotalAlerts": "今日总告警", + "trendNoChange": "趋势无变更", + "trendUp": "趋势上升", + "trendDown": "趋势下降", + "trendZero": "趋势零", + "yesterdayValue": "昨日值", + "todayCriticalAlerts": "今日严重告警", + "todayPending": "今日待处理", + "todayClosed": "今日已关闭", + "alertTypeProportion": "告警类型比例", + "alertStatusStatistics": "告警状态统计", + "sourceDevice": "来源设备", + "occurTime": "发生时间", + "alertDescription": "告警描述", + "contactHandler": "联系处理人", + "handleOperation": "处理操作", + "pleaseSelectHandleStatus": "请选择处理状态", + "pleaseSelectHandleResult": "请选择处理结果", + "pleaseInputHandleRemark": "请输入处理备注", + "confirmHandle": "确认处理", + "modifyAlert": "修改告警", + "pleaseInputAlarmTitle": "请输入告警标题", + "pleaseSelectAlertLevel": "请选择告警级别", + "pleaseSelectAlertType": "请选择告警类型", + "pleaseInputAlarmContent": "请输入告警内容", + "newOrder": "新工单", + "planStartTime": "计划开始时间", + "pleaseSelectPlanStartTime": "请选择计划开始时间", + "confirmCreateOrder": "确认创建工单", + "mockDeviceSensor": "模拟设备传感器", + "mockTempExceed": "模拟温度超限", + "mockDeviceCamera": "模拟设备摄像头", + "mockVideoSignalLost": "模拟视频信号丢失", + "mockDeviceBattery": "模拟设备电量", + "mockBatteryLow": "模拟电量低", + "level": "级别", + "content": "内容", + "searchDeviceContent": "搜索设备内容", + "mockRuleTemp": "模拟规则温度", + "mockRuleBattery": "模拟规则电量", + "deviceSensor": "设备传感器", + "deviceBattery": "设备电量", + "ruleName": "规则名称", + "triggerCondition": "触发条件", + "ruleConfig": "规则配置", + "addRule": "新增规则", + "editRule": "编辑规则", + "ruleNamePlaceholder": "规则名称占位", + "relatedModel": "关联模型", + "monitorProperty": "监控属性", + "compareSymbol": "对比符号", + "threshold": "阈值", + "thresholdPlaceholder": "阈值占位", + "sevenDayTrend": "七日趋势", + "criticalAlert": "严重告警", + "importantAlert": "重要告警", + "generalAlert": "一般告警", + "infoAlert": "提示告警", + "alertLevelDistribution": "告警级别分布", + "mockSubOps": "模拟订阅运维", + "mockSubAdmin": "模拟订阅管理员", + "mockSubDevice": "模拟订阅设备", + "appPush": "应用推送", + "subscriptionName": "订阅名称", + "notifyChannel": "通知渠道", + "searchSubscription": "搜索订阅", + "addSubscription": "新增订阅", + "editAlertSubscription": "编辑告警订阅", + "addAlertSubscription": "新增告警订阅", + "pleaseInputSubscriptionName": "请输入订阅名称", + "allDevices": "全部设备", + "allLevels": "全部级别", + "pleaseSelectChannel": "请选择渠道", + "subtitle": "字幕" + }, + "common": { + "loadFailed": "加载失败", + "loadDetailFailed": "加载详情失败", + "requestError": "请求错误", + "deviceId": "设备ID", + "pleaseInputDeviceId": "请输入设备ID", + "timeRange": "时间范围", + "enabled": "已启用", + "modify": "修改", + "apiCallFail": "API呼叫失败", + "quantity": "数量", + "detail": "详情", + "storeValue": "存储值", + "operationFailed": "操作失败", + "submitFail": "提交失败", + "confirmDeleteRecord": "确认删除记录", + "deleteIrreversible": "删除不可逆", + "working": "作业中", + "settings": "设置" + }, + "workOrder": { + "generateOrder": "生成工单", + "newOrders": "新工单", + "dispatchSuccess": "派单成功", + "dispatchFail": "派单失败", + "dispatchRequestError": "派单请求错误", + "todayNewOrders": "今日新工单", + "vsYesterday": "较昨日", + "pendingOrders": "待处理工单", + "highPriority": "高优先级", + "runningOrders": "运行中工单", + "onTimeRate": "开时间率", + "completedOrders": "已完成工单", + "monthlyTotal": "每月总", + "avgHandleTime": "平均处理时间", + "hourUnit": "小时单位", + "vsLastWeek": "较最后周", + "aiSuggestedOrders": "AI建议工单", + "orderUnit": "工单单位", + "autoGeneratedToday": "自动已生成今日", + "getListFailed": "获取列表失败", + "pleaseSelectOrder": "请选择工单", + "completeSuccess": "完成成功", + "completeFail": "完成失败", + "getDetailFailed": "获取详情失败", + "pleaseSelectOneOrder": "请选择一工单", + "modifySuccess": "修改成功", + "modifyFail": "修改失败", + "executeSuccess": "执行成功", + "executeFail": "执行失败", + "suspendSuccess": "暂停成功", + "suspendFail": "暂停失败", + "invalidOrder": "无效工单", + "pleaseSelectOrderToDelete": "请选择工单到删除", + "batchDeleteSuccess": "批量删除成功", + "batchDeleteFail": "批量删除失败", + "orderNo": "工单编号", + "taskManagement": "任务管理", + "overview": "概览", + "timeRange": "时间范围", + "keyword": "关键词", + "pleaseInputOrderNoOrTitle": "请输入工单编号或标题", + "list": "列表", + "totalWithCount": "总带数量", + "confirmBatchDelete": "确认批量删除", + "batchExecute": "批量执行", + "statusDistribution": "状态分布", + "totalCount": "总数量", + "scheduleCalendarView": "计划日历查看", + "todaySuffix": "今日后缀", + "noScheduledOrders": "无已计划工单", + "scheduledOrderList": "已计划工单列表", + "planStartTime": "计划开始时间", + "detail": "详情", + "noTitle": "无标题", + "nearItems": "附近项", + "pleaseInputTaskDesc": "请输入任务描述", + "pendingDispatch": "待处理派单", + "pleaseSelectPerson": "请选择人员", + "selectedOrders": "已选工单", + "pleaseSelectExecuteDevice": "请选择执行设备", + "pleaseInputResult": "请输入结果", + "pleaseSelectResult": "请选择结果", + "handledCompleted": "已处理已完成", + "fixed": "固定", + "noActionNeeded": "无操作需要", + "issueClosed": "问题已关闭", + "pleaseInputHandleRemark": "请输入处理备注", + "alarmNo": "告警编号", + "alarmTitle": "告警标题" + }, + "devices": { + "working": "作业中", + "videoConfigured": "视频已配置", + "noVideo": "无视频", + "connectionFailed": "连接失败", + "permissionRequestTitle": "权限请求标题", + "requestControlPermission": "请求控制权限", + "selectNoDrone": "选择无无人机", + "commandIssued": "指令已下发", + "commandFailed": "指令失败", + "apiRequestError": "API请求错误", + "selectTaskFirst": "选择任务先", + "taskExecutedSuccess": "任务已执行成功", + "taskSuspended": "任务已暂停", + "taskResumedSuccess": "任务已恢复成功", + "taskReturnedHome": "任务已返航首页", + "taskStatusUpdateSuccess": "任务状态更新成功", + "operationApiError": "操作API错误", + "permissionObtained": "权限已获取", + "controlRequestContent": "控制请求内容", + "unknownPlatform": "未知平台", + "obstacleDetected": "障碍已检测", + "unknownObject": "未知对象", + "noTaskDetail": "无任务详情", + "fetchDetailError": "获取详情错误", + "deviceInfoError": "设备提示错误", + "pleaseLoginFirst": "请登录先", + "loginInfoExpired": "登录提示已过期", + "otherWebControlling": "其他网页控制中", + "networkErrorRetry": "网络错误重试", + "routeLoaded": "路线已加载", + "droneFallbackName": "无人机兜底名称", + "droneAirspace": "无人机空域", + "droneWayline": "无人机航线", + "onStandby": "开待机", + "permissionRequesting": "权限请求中", + "noWaylineData": "无航线数据", + "fetchWaylineFailed": "获取航线失败", + "waypointsAdded": "航点已添加", + "waypointDeleted": "航点已删除", + "allWaypointsCleared": "全部航点已清空", + "pleaseSelectWayline": "请选择航线", + "selectTaskStartTime": "选择任务开始时间", + "createSuccess": "创建成功", + "apiErrorCheck": "API错误检查", + "noWaypointsToSave": "无航点到保存", + "planeNoLocalRoute": "飞机无本地路线", + "routeSavedLocal": "路线已保存本地", + "waylineLabel": "航线标签", + "fetchWaylineListFailed": "获取航线列表失败", + "routeLoadedWithName": "路线已加载带名称", + "gpsTaskIssued": "GPS任务已下发", + "issueTaskFirst": "问题任务先", + "taskPaused": "任务已暂停", + "taskStarted": "任务已开始", + "taskStopped": "任务已停止", + "viewFront": "查看前", + "viewBack": "查看返回", + "viewLeft": "查看左", + "viewRight": "查看右", + "viewTop": "查看顶部", + "deviceList": "设备列表", + "onlineDevices": "在线设备", + "inspectionMachine": "巡检机器", + "supportsGpsPlan": "支持GPS计划", + "supportsRemoteGpsPlan": "支持远程GPS计划", + "batteryWithValue": "电量带值", + "videoLinkConfigured": "视频链接已配置", + "batteryCapacity": "电量容量", + "flightAltitude": "飞行高度", + "signalSatellite": "信号卫星", + "envWindSpeed": "环境风速度", + "rtkStatus": "RTK状态", + "fixed": "固定", + "notFixed": "未固定", + "remoteControlAndSituation": "远程控制与态势", + "gpsPlanning": "GPS规划", + "leftWheelDrive": "左轮驱动", + "normalRunning": "正常运行中", + "realtimeSpeed": "实时速度", + "workingCurrent": "作业中当前", + "rightWheelDrive": "右轮驱动", + "gpsWaypointPlan": "GPS航点计划", + "savePlan": "保存计划", + "clearRoute": "清空路线", + "droneWaylineTask": "无人机航线任务", + "taskWaitingStart": "任务等待中开始", + "taskStartFailure": "任务开始失败", + "taskTerminated": "任务已终止", + "taskTimeout": "任务超时", + "noWaylineTask": "无航线任务", + "robotTaskData": "机器人任务数据", + "waypointList": "航点列表", + "noWaypoints": "无航点", + "waypoint": "航点", + "robotLocalRoutes": "机器人本地路线", + "noSavedRoutes": "无已保存路线", + "waypointsCount": "航点数量", + "issueTask": "问题任务", + "suspendTask": "暂停任务", + "cancelReturnHome": "取消返航首页", + "videoAndConfig": "视频与配置", + "noVideoStream": "无视频流", + "waylineDetail": "航线详情", + "taskIdLabel": "任务ID标签", + "waylineUuidLabel": "航线UUID标签", + "airportSnLabel": "机场序列号标签", + "landingDockSnLabel": "降落停靠序列号标签", + "returnHomeAltitudeLabel": "返航首页高度标签", + "returnHomeModeLabel": "返航首页模式标签", + "waylinePrecisionLabel": "航线精度标签", + "lostControlActionLabel": "丢失控制操作标签", + "resumeFromBreakpointLabel": "恢复从断点标签", + "taskTypeLabel": "任务类型标签", + "repeatModeLabel": "重复模式标签", + "minBatteryLabel": "最小电量标签", + "currentWaypointLabel": "当前航点标签", + "rthOptimal": "返航最优", + "rthPreset": "返航预设", + "gpsMode": "GPS模式", + "rtkHighPrecision": "RTK高精度", + "modeAuto": "模式自动", + "modeManual": "模式手动", + "taskImmediate": "任务立即", + "taskTimed": "任务定时", + "taskRecurring": "任务重复", + "taskContinuous": "任务连续", + "repeatNone": "重复无", + "repeatDaily": "重复每日", + "repeatWeekly": "重复每周", + "repeatMonthlyByDate": "重复每月按日期", + "repeatMonthlyByWeek": "重复每月按周", + "taskResumed": "任务已恢复", + "taskEnded": "任务已结束", + "taskTimedOut": "任务定时外", + "createWaylineTask": "创建航线任务", + "waylineName": "航线名称", + "inputWaylineName": "输入航线名称", + "selectWayline": "选择航线", + "autoFetch": "自动获取", + "optionalEmpty": "可选空", + "gpsModeDesc": "GPS模式描述", + "rtkModeDesc": "RTK模式描述", + "autoResumeFromBreakpoint": "自动恢复从断点", + "resumeAuto": "恢复自动", + "resumeManual": "恢复手动", + "minExecBatteryLabel": "最小执行电量标签", + "taskWaylineTypeLabel": "任务航线类型标签", + "taskStartTimeLabel": "任务开始时间标签", + "selectStartTimePlaceholder": "选择开始时间占位", + "taskEndTimeLabel": "任务结束时间标签", + "repeatInterval": "重复间隔", + "boundDrone": "边界无人机", + "airportCode": "机场码", + "tlStartCleaning": "时间线开始清洗", + "timelineStatusStart": "时间线状态开始", + "tlStartInspection": "时间线开始巡检", + "tlStartMowing": "时间线开始除草", + "tlStandbyReady": "时间线待机就绪", + "timelineStatusReady": "时间线状态就绪", + "tlReturnSupply": "时间线返航补给", + "timelineStatusReturn": "时间线状态返航", + "tlFinishInspection": "时间线完成巡检", + "timelineStatusComplete": "时间线状态完成", + "logCommand": "日志指令", + "logDesc1": "日志", + "logDesc2": "日志", + "logDesc3": "日志", + "logDesc4": "日志", + "logDesc5": "日志", + "logEvent": "日志事件", + "logDesc6": "日志", + "logLevelLowBattery": "日志级别低电量", + "aliasRobotOnly": "别名机器人仅", + "aliasNotEmpty": "别名未空", + "aliasModifiedSuccess": "别名已修改成功", + "aliasModifyFail": "别名修改失败", + "aliasApiError": "别名API错误", + "noPermission": "无权限", + "stationStatus": "电站状态", + "dispatchableCount": "可派单数量", + "unitTai": "单位台", + "robotExecutingCount": "机器人执行中数量", + "mowingRobot": "除草机器人", + "robotInTaskCount": "机器人内任务数量", + "washDrone": "清洗无人机", + "unitJia": "单位", + "standbyCount": "待机数量", + "todayTaskExecute": "今日任务执行", + "completionRate": "完成率", + "unknownDevice": "未知设备", + "modeWorking": "模式作业中", + "modeDebugLocal": "模式调试本地", + "modeDebugRemote": "模式调试远程", + "modeUpgrading": "模式升级中", + "positionInStation": "位置内电站", + "taskStandby2": "任务", + "commGood": "通信良好", + "taskStarted2": "任务", + "taskPaused2": "任务", + "returnCommandSent": "返航指令已发送", + "rechargeCommandSent": "充电指令已发送", + "emergencyStopTriggered": "紧急停止已触发", + "rainNo": "雨无", + "equipmentOverview": "设备概览", + "equipmentSituationMap": "设备态势地图", + "legendTitle": "图例标题", + "inspectionRoute": "巡检路线", + "robotPath": "机器人路径", + "cleaningPath": "清洗路径", + "robotPosition": "机器人位置", + "dronePosition": "无人机位置", + "supplyChargePoint": "补给充电点", + "obstacleForbiddenZone": "障碍禁止区域", + "equipmentScheduleList": "设备计划列表", + "equipmentName": "设备名称", + "modeLabel": "模式标签", + "commStatus": "通信状态", + "deviceParamSetting": "设备参数设置", + "controlLogTitle": "控制日志标题", + "allLevels": "全部级别", + "deviceColHeader": "设备列表头", + "viewMoreLogs": "查看更多日志", + "deviceControlPanel": "设备控制面板", + "detailBtn": "详情按钮", + "batteryWithPercent": "电量带百分比", + "productType": "产品类型", + "deviceSnLabel": "设备序列号标签", + "activatedTime": "已激活时间", + "firmwareVersion": "固件版本", + "flightWindSpeed": "飞行风速度", + "rtkCount": "RTK数量", + "startBtn": "开始按钮", + "rechargeBtn": "充电按钮", + "realtimePicture": "实时图片", + "stationVideo": "电站视频", + "droneVideo": "无人机视频", + "stopVideoBtn": "停止视频按钮", + "switchManual": "切换手动", + "emergencyStopBtn": "紧急停止按钮", + "aiAnalysisTitle": "AI分析标题", + "hotspotSuspect": "热斑疑似", + "countUnit": "数量单位", + "weedHighZone": "杂草高区域", + "blockUnit": "区块单位", + "componentRiskZone": "组件风险区域", + "fenceAbnormal": "围栏异常", + "suggestAction": "建议操作", + "aiAction1": "AI", + "aiAction2": "AI", + "aiAction3": "AI", + "generateTask": "生成任务", + "aiOptimizePath": "AI优化路径", + "oneClickCoordinated": "一点击协同", + "envFlightConditions": "环境飞行条件", + "windSpeed": "风速度", + "rainfall": "降雨", + "rtkEnvLabel": "RTK环境标签", + "fixedEnv": "固定环境", + "notFixedEnv": "未固定环境", + "suitableExec": "适宜执行", + "canWorkNormally": "可工作正常", + "robotWorkStatus": "机器人工作状态", + "cumWorkMileage": "累计工作里程", + "cumWorkArea": "累计工作区域", + "cumWorkTime": "累计工作时间", + "taskTimelineTitle": "任务时间线标题", + "viewMore": "查看更多", + "editRobotInfoTitle": "编辑机器人提示标题", + "deviceSnColon": "设备序列号冒号", + "deviceTypeColon": "设备类型冒号", + "deviceAliasLabel": "设备别名标签", + "inputCustomAlias": "输入自定义别名", + "aliasHint": "别名提示", + "fetchParamFail": "获取参数失败", + "paramSaveSuccess": "参数保存成功", + "paramSaveFail": "参数保存失败", + "paramSaveException": "参数保存异常", + "paramLeftForwardGain": "参数左前进增益", + "paramLeftBackwardGain": "参数左后退增益", + "paramRightForwardGain": "参数右前进增益", + "paramRightBackwardGain": "参数右后退增益", + "paramRunSpeed": "参数运行速度", + "paramChipUidSign": "参数芯片UID标识", + "paramChipUid": "参数芯片UID", + "paramRemoteConfig": "参数远程配置", + "paramKnifeMotorMode": "参数刀电机模式", + "paramWalkMotorMode": "参数行走电机模式", + "paramLeftMotorReverse": "参数左电机反向", + "paramRightMotorReverse": "参数右电机反向", + "paramSwapChannel": "参数切换渠道", + "paramUse4G": "参数4G", + "paramForwardSpeedLimit": "参数前进速度限制", + "paramTurnSpeedLimit": "参数转向速度限制", + "paramKnifePolarity": "参数刀极性", + "paramFanPolarity": "参数风扇极性", + "paramThrottlePolarity": "参数油门极性", + "paramLiftProtectTime": "参数抬升保护时间", + "paramDualRtk": "参数双RTK", + "paramKnifeChannel": "参数刀渠道", + "paramFanChannel": "参数风扇渠道", + "paramThrottleChannel": "参数油门渠道", + "paramRemoteType": "参数远程类型", + "paramRelayBoard": "参数中继看板", + "paramLiftChannel": "参数抬升渠道", + "paramChassisChannel": "参数底盘渠道", + "paramArmChannel": "参数布防渠道", + "paramFuelPumpChannel": "参数燃料泵渠道", + "paramWifiName": "参数WiFi名称", + "paramWifiPassword": "参数WiFi密码", + "paramBatteryType": "参数电量类型", + "paramDriveType": "参数驱动类型", + "paramGearRatio": "参数档位比例", + "paramRobotLength": "参数机器人长度", + "paramRobotWidth": "参数机器人宽度", + "paramRobotHeight": "参数机器人高度", + "paramKnifeWidth": "参数刀宽度", + "paramTyreSize": "参数轮胎大小", + "paramFirmwareVersion": "参数固件版本", + "paramDelFlag": "参数删除标志", + "paramViewEditTitle": "参数查看编辑标题", + "noData": "无数据", + "locationFail": "位置失败", + "pointMarked": "点已标记", + "minThreePoints": "最小三点", + "pathPlanDone": "路径计划已完成", + "pathPlanFailRetry": "路径计划失败重试", + "completePathFirst": "完成路径先", + "inputPlotName": "输入区域名称", + "pathPlanning": "路径规划", + "markedPointsCount": "已标记点数量", + "workMode": "工作模式", + "bowMode": "船首模式", + "abMode": "模式", + "markPoint": "标记点", + "clearMarks": "清空标记", + "complete": "完成", + "selectDevice": "选择设备", + "unknownDrone": "未知无人机", + "situationMap": "态势地图", + "clearPlannedRoute": "清空已计划路线", + "realtimeVideoMonitor": "实时视频监控", + "frontView": "前查看", + "backView": "返回查看", + "leftView": "左查看", + "rightView": "右查看", + "panorama": "全景", + "robotStatus": "机器人状态", + "rtBattery": "实时电量", + "rtVoltage": "实时电压", + "rtLeftMotor": "实时左电机", + "rtRightMotor": "实时右电机", + "rtLeftSpeed": "实时左速度", + "rtRightSpeed": "实时右速度", + "rtLeftTemp": "实时左温度", + "rtRightTemp": "实时右温度", + "rtMainTemp": "实时主温度", + "rtHeadingAngle": "实时航向角度", + "rtPitchAngle": "实时俯仰角度", + "rtHeadingStatus": "实时航向状态", + "rtInitialized": "实时已初始化", + "rtPositioning": "实时定位", + "gpsSingle": "GPS单", + "dgpsDiff": "DGPS差异", + "rtkFixed2": "RTK", + "rtkFloat": "RTK浮点", + "rtSatellites": "实时卫星", + "satelliteCount": "卫星数量", + "rtMode": "实时模式", + "videoStopped": "视频已停止", + "clickPlayHint": "点击播放提示", + "remoteControl": "远程控制", + "savePathTitle": "保存路径标题", + "plotNameLabel": "区域名称标签", + "saving": "节省", + "stationCameraNotFound": "电站摄像头未找到", + "indoor": "室内", + "outdoor": "室外", + "switchStationCameraFail": "切换电站摄像头失败", + "switchDroneLensFail": "切换无人机镜头失败", + "wideLens": "广角镜头", + "zoomLens": "缩放镜头", + "irLens": "红外镜头", + "noVideoSignal": "无视频信号", + "rainYes": "雨是", + "hangar": "机库", + "coverOpen": "覆盖打开", + "coverClosed": "覆盖已关闭", + "inDock": "内停靠", + "outDock": "外停靠", + "droneStatus": "无人机状态", + "horizontalSpeed": "水平速度", + "verticalSpeed": "垂直速度", + "headingAngle": "航向角度", + "gpsUnit": "GPS单位", + "taskCompletedMsg": "任务已完成消息", + "confirmDeleteTitle": "确认删除标题", + "confirmDeleteContent": "确认删除内容", + "confirmDeleteOk": "确认删除确定", + "routeDeletedSuccess": "路线已删除成功", + "deleteFailRetry": "删除失败重试", + "deleteFailNetwork": "删除失败网络", + "taskListRefreshFail": "任务列表刷新失败", + "routeNoPathPoints": "路线编号路径点", + "checkRouteFail": "检查路线失败", + "taskExecuteSuccess": "任务执行成功", + "taskExecuteFail": "任务执行失败", + "taskExecuteException": "任务执行异常", + "taskCanceledSuccess": "任务已取消成功", + "taskCancelFail": "任务取消失败", + "taskCancelException": "任务取消异常", + "taskPausedSuccess": "任务已暂停成功", + "taskPauseFail": "任务暂停失败", + "taskPauseException": "任务暂停异常", + "taskResumeFail": "任务恢复失败", + "taskResumeException": "任务恢复异常", + "waylineFallback": "航线兜底", + "waylineListFetchFail": "航线列表获取失败", + "fillRequiredFields": "填写必填字段", + "createTaskSuccess": "创建任务成功", + "createTaskFail": "创建任务失败", + "createTaskException": "创建任务异常", + "trendYesterday": "趋势昨日", + "trendFlat": "趋势持平", + "taskListFetchFail": "任务列表获取失败", + "boardTaskFetchFail": "看板任务获取失败", + "statusExecSuccess": "状态执行成功", + "statusPreparing": "状态准备中", + "statusExecFailed": "状态执行失败", + "statusWaitingStart": "状态等待中开始", + "statusStartFailed": "状态开始失败", + "statusRestored": "状态已恢复", + "statusTerminated": "状态已终止", + "statusTimeout": "状态超时", + "deviceSn": "设备序列号", + "requestFail": "请求失败", + "selectExecuteDroneFirst": "选择执行无人机先", + "waylineTaskCreated": "航线任务已创建", + "waylineExecFail": "航线执行失败", + "todayWaylinePlan": "今日航线计划", + "unitTiao": "单位条", + "inProgressDesc": "内进度描述", + "executingTasks": "执行中任务", + "unitGe": "单位地理", + "pendingApprovalTasks": "待处理审批任务", + "taskCompletionRate": "任务完成率", + "aiOptimizeGain": "AI优化增益", + "coverageEquipment": "覆盖设备", + "coverageValue": "覆盖值", + "allOnline": "全部在线", + "noDroneSelected": "无无人机已选", + "commandSent": "指令已发送", + "commandSendFail": "指令发送失败", + "apiRequestException": "API请求异常", + "noDetail": "无详情", + "fetchDetailException": "获取详情异常", + "taskExecSuccess": "任务执行成功", + "taskSuspended2": "任务", + "taskRestored2": "任务", + "taskReturnedHome2": "任务已返航首页", + "statusUpdateSuccess": "状态更新成功", + "operationFail": "操作失败", + "operationApiException": "操作API异常", + "waylineEditPlan": "航线编辑计划", + "optimize": "优化", + "inspectRoute": "巡检路线", + "mowRoute": "除草路线", + "droneAirport": "无人机机场", + "chargePoint": "充电点", + "noFlyZone": "无飞行区域", + "deviceTaskList": "设备任务列表", + "allDrones": "全部无人机", + "taskOrchestrationAI": "任务编排AI", + "typeDroneHotspot": "类型无人机热斑", + "levelHigh": "级别高", + "statusSuggestExec": "状态建议执行", + "typeFenceInspect": "类型围栏巡检", + "typeComponentClean": "类型组件清洗", + "levelMid": "级别中间", + "statusWaitExec": "状态等待执行", + "typeArrayMow": "类型阵列除草", + "typeAIRecheck": "类型AI复检", + "levelLow": "级别低", + "statusPendingApproval": "状态待处理审批", + "colNo": "列无", + "suggestTime": "建议时间", + "aiSuggestionColon": "AI建议冒号", + "aiAdvice1": "AI", + "aiAdvice2": "AI", + "aiAdvice3": "AI", + "oneClickSchedule": "一点击计划", + "smartAssign": "智能分配", + "waylineParamsTitle": "航线参数标题", + "currentWayline": "当前航线", + "rthMode": "返航模式", + "waylinePrecision": "航线精度", + "resumeBreakpoint": "恢复断点", + "safeRthBattery": "安全返航电量", + "repeatTypeLabel": "重复类型标签", + "lostControlAction": "丢失控制操作", + "obstacleAvoidSwitch": "障碍避开切换", + "nightFlightLimit": "夜间飞行限制", + "resumeTask": "恢复任务", + "pauseTask": "暂停任务", + "weatherWindowTitle": "天气窗口标题", + "suitability": "适宜性", + "suitableExec2": "适宜", + "suitable": "适宜", + "precipStatus": "降水状态", + "hasRain": "有雨", + "gnssQuality": "GNSS质量", + "fixed2": "", + "notFixed2": "未", + "weatherAdvice1": "天气", + "weatherAdvice2": "天气", + "waylineList": "航线列表", + "selectDrone": "选择无人机", + "searchWaylinePlaceholder": "搜索航线占位", + "unknownTime": "未知时间", + "executeWayline": "执行航线", + "ganttViewTitle": "甘特查看标题", + "dronePortA01": "无人机端口", + "typeHotspotInspect": "类型热斑巡检", + "typeRecheck": "类型复检", + "inspectRobotRbt": "巡检机器人", + "mowRobotGr": "除草机器人电网", + "washDroneUav": "清洗无人机", + "workTaskBoard": "工作任务看板", + "startLabel": "开始标签", + "endLabel": "结束标签", + "pendingExec": "待处理执行", + "equipmentColon": "设备冒号", + "startTimeColon": "开始时间冒号", + "completedTimeColon": "已完成时间冒号", + "createWaylineTaskTitle": "创建航线任务标题", + "airportSn": "机场序列号", + "landingDockSn": "降落停靠序列号", + "returnHomeAltitude": "返航首页高度", + "returnHomeMode": "返航首页模式", + "minBattery": "最小电量", + "repeatMode": "重复模式", + "weekdayExec": "工作日执行", + "weekSun": "周太阳", + "weekMon": "周周一", + "weekTue": "周周二", + "weekWed": "周周三", + "weekThu": "周周四", + "weekFri": "周周五", + "weekSat": "周卫星", + "monthDays": "月天", + "whichWeek": "哪个周", + "weekNo": "周无", + "weekday": "工作日", + "execTimePoint": "执行时间点", + "inputTimestamp": "输入时间戳", + "confirmCreate": "确认创建", + "executeWaylineTask": "执行航线任务", + "executeNow": "执行现在", + "selectedWaylineColon": "已选航线冒号", + "executeDeviceColon": "执行设备冒号", + "noDeviceSelected": "无设备已选", + "returnHomeAltitudeColon": "返航首页高度冒号", + "deviceData": "设备数据", + "equipmentControl": "设备控制", + "realtimePowerData": "实时功率数据", + "communicationStatus": "通信状态", + "communicationNormal": "通信正常", + "groupOf": "分组的", + "weatherStation": "天气电站", + "stationTopology": "电站拓扑", + "communicating": "通信中", + "realtimeMonitorList": "实时监控列表", + "allTypes": "全部类型", + "searchDeviceName": "搜索设备名称", + "dronePort": "无人机端口", + "deviceOnlineRate": "设备在线率", + "onlineRate": "在线率", + "totalCount": "总数量" + }, + "systemSetting": { + "pleaseSelectRoles": "请选择角色", + "batchDeleteRolesTitle": "批量删除角色标题", + "confirmDeleteSelectedRoles": "确认删除已选角色", + "batchDeleteSuccess": "批量删除成功", + "resetFail": "重置失败", + "pleaseSelectUsers": "请选择用户", + "batchDeleteUsersTitle": "批量删除用户标题", + "confirmDeleteSelectedUsers": "确认删除已选用户", + "userAndRole": "用户与角色", + "belongingOrg": "所属组织", + "belongingSite": "所属站点", + "devicePerm": "设备权限", + "hasPerm": "有权限", + "noPerm": "无权限", + "confirmDeleteUser": "确认删除用户", + "totalCount": "总数量", + "permissionLabel": "权限标签", + "levelLabel": "级别标签", + "editRole": "编辑角色", + "roleLevel": "角色级别", + "pleaseInputRoleLevel": "请输入角色级别", + "account": "账号", + "nicknameShort": "昵称短", + "phoneShort": "电话短", + "editPasswordPlaceholder": "编辑密码占位", + "chooseRole": "选择角色", + "pleaseSelectOrg": "请选择组织", + "pleaseSelectSite": "请选择站点", + "deviceOperatePermLabel": "设备操作权限标签", + "has": "有", + "none": "无" + }, + "aiAnalysis": { + "objectDetection": "对象检测", + "detectDesc": "检测描述", + "thermalSegmentation": "热成像分割", + "segmentationDesc": "分割描述", + "getModelsFailed": "获取模型失败", + "uploadFirst": "上传先", + "submitTaskFailed": "提交任务失败", + "thermalAnalysisComplete": "热成像分析完成", + "taskSuccess": "任务成功", + "taskExecutionError": "任务执行错误", + "cancelled": "已取消", + "uploadThermalFirst": "上传热成像先", + "thermalAnalysisFailed": "热成像分析失败", + "detectTaskName": "检测任务名称", + "segmentationTaskName": "分割任务名称", + "algoCount": "算法数量", + "detectAndSeg": "检测与分割", + "onlineServices": "在线服务", + "healthCheck": "健康检查", + "modelCount": "模型数量", + "detectModelRegistry": "检测模型注册", + "historyRecords": "历史记录", + "pageDebugRecords": "页面调试记录", + "detectModel": "检测模型", + "default": "默认", + "inputType": "输入类型", + "image": "图片", + "inferencePreset": "推理预设", + "quality": "质量", + "dragFile": "拖拽文件", + "supportedFormats": "支持格式", + "processing": "处理中", + "submitDetectTask": "提交检测任务", + "serverDemo": "服务器演示", + "refreshModels": "刷新模型", + "thermalFileLabel": "热成像文件标签", + "uploadThermal": "上传热成像", + "arrayMask": "阵列掩码", + "panelGeojson": "面板GeoJSON", + "submitThermalAnalysis": "提交热成像分析", + "readServerHistory": "读取服务器历史", + "resultDisplay": "结果显示", + "resultViewHint": "结果查看提示", + "inputPreview": "输入预览", + "resultImageVideo": "结果图片视频", + "showResultAfterTask": "显示结果之后任务", + "resultJson": "结果JSON", + "noResult": "无结果", + "thermalInteractiveView": "热成像交互查看", + "clickHotspotHint": "点击热斑提示", + "hotspotCount": "热斑数量", + "panelArea": "面板区域", + "analysisId": "分析ID", + "pointDetails": "点详情", + "selectHotspotPanel": "选择热斑面板", + "hotspot": "热斑", + "blockNumber": "区块编号", + "originalPixels": "原始像素", + "overlayPixels": "叠加像素", + "area": "区域", + "waitingSegmentation": "等待中分割", + "hotspotPanelList": "热斑面板列表", + "panelLabel": "面板标签", + "centerTitle": "中心标题", + "algorithmList": "算法列表", + "integratedCount": "集成数量", + "detectWorkspace": "检测工作区", + "segWorkspace": "分割工作区", + "asyncTask": "异步任务", + "syncAnalysis": "同步分析", + "detectWorkspaceDesc": "检测工作区描述", + "segWorkspaceDesc": "分割工作区描述", + "historyAnalysis": "历史分析", + "noHistory": "无历史", + "detect": "检测", + "segment": "段", + "hotspotRecognition": "热斑识别", + "abnormalString": "异常组串", + "recommendedCleanBlock": "建议清洗区块", + "videoAbnormalEvent": "视频异常事件", + "videoIntelligentAnalysis": "视频智能分析", + "perimeterIntrusion": "周长入侵", + "perimeterIntrusionDesc": "周长入侵描述", + "personnelIntrusion": "人员入侵", + "personnelIntrusionDesc": "人员入侵描述", + "fireSmokeDetection": "火警烟检测", + "fireSmokeDesc": "火警烟描述", + "areaLoitering": "区域徘徊", + "areaLoiteringDesc": "区域徘徊描述", + "motionDetection": "运动检测", + "motionDetectionDesc": "运动检测描述", + "perimeterIntrusionTimeline": "周长入侵时间线", + "personnelIntrusionTimeline": "人员入侵时间线", + "areaLoiteringTimeline": "区域徘徊时间线", + "motionDetectionTimeline": "运动检测时间线", + "perimeterClimbTimeline": "周长爬升时间线", + "perimeterIntrusionType": "周长入侵类型" + }, + "video": { + "fileDetails": "文件详情", + "sourceDevice": "来源设备", + "startEndTime": "开始结束时间", + "capacity": "容量", + "videoReplayCenter": "视频回放中心", + "allRecordings": "全部", + "todayRecordings": "今日", + "timeSpan": "时间跨度", + "onlineSearch": "在线搜索", + "labelMainFront": "标签主前", + "labelRear": "标签后", + "labelLeft": "标签左", + "labelRight": "标签右", + "labelFrontDrive": "标签前驱动", + "labelRearMount": "标签后挂载", + "labelLeftWing": "标签左机翼", + "labelRightWing": "标签右机翼", + "labelCargo": "标签货物", + "labelMainProbe": "标签主探测", + "labelPanorama": "标签全景", + "labelDetail": "标签详情", + "currentDutyDevice": "当前值班设备", + "videoStreamChannels": "视频流渠道", + "refreshSignal": "刷新信号", + "fullScreenWall": "全屏幕墙", + "ptzServoControl": "云台伺服控制", + "systemIntegratedCommand": "系统集成指令", + "twoWayIntercom": "二路径对讲", + "triggerRemoteRepellent": "触发远程驱避", + "aiVisualTracking": "AI可视跟踪", + "activeVideoStreams": "启用视频流", + "positionArray1": "位置", + "domeCamera": "穹顶摄像头", + "recording": "录制", + "positionInverterRoom": "位置逆变器房间", + "positionStationGate": "位置电站闸门", + "positionPerimeterNorth": "位置周长北", + "positionWeatherStation": "位置天气电站", + "positionEquipmentArea": "位置设备区域", + "positionStorageBattery": "位置存储电量", + "positionEnergyStorage": "位置能量存储", + "positionPerimeterSouth": "位置周长南", + "positionArrayWest": "位置阵列西", + "cameraName": "摄像头名称", + "recordingStatus": "录制状态", + "lastEventTime": "最后事件时间", + "onlineCameras": "在线摄像头", + "unitDevice": "单位设备", + "onlineRateTrend": "在线率趋势", + "offlineCameras": "离线摄像头", + "offlineRateTrend": "离线率趋势", + "todayAlertEvents": "今日告警事件", + "unitIncident": "单位事件", + "vsYesterdayPercent": "较昨日百分比", + "perimeterAlerts": "周长告警", + "storageUsage": "存储用量", + "storageUsageTrend": "存储用量趋势", + "videoMonitorCenter": "视频监控中心", + "customLayout": "自定义布局", + "mainView": "主查看", + "liveLabel": "直播标签", + "cameraDeviceList": "摄像头设备列表", + "searchCameraPlaceholder": "搜索摄像头占位", + "allStatuses": "全部状态", + "eventTimeline": "事件时间线", + "allTypes": "全部类型", + "viewMore": "查看更多", + "recentAlertSnapshots": "近期告警快照" + }, + "login": { + "and": "与" + }, + "profile": { + "requestFailed": "请求失败", + "basicInfo": "基础提示", + "phoneInvalid": "电话无效", + "emailInvalid": "邮件无效", + "emailPlaceholder": "邮件占位", + "remarkPlaceholder": "备注占位", + "changePassword": "变更密码", + "oldPasswordPlaceholder": "旧密码占位", + "newPasswordPlaceholder": "新密码占位", + "passwordLengthError": "密码长度错误", + "confirmPasswordRequired": "确认密码必填", + "confirmPasswordPlaceholder": "确认密码占位" + }, + "home": { + "realtimePower": "实时功率", + "totalPower": "总功率", + "ratedPower": "额定功率", + "gridConnection": "电网连接", + "gridConnected": "电网已连接", + "frequency": "频率", + "gridVoltage": "电网电压", + "phaseA": "相", + "todayAccumulated": "今日累计", + "currentIrradiance": "当前辐照度", + "ambientTemperature": "环境温度", + "powerCurve": "功率曲线", + "today": "今日", + "inverterPowerCompare": "逆变器功率对比", + "stringCurrentHeatmap": "组串当前热力图", + "current": "当前" + }, + "router": { + "realtimeMonitorCenter": "实时监控中心", + "realtimeMonitorDesc": "实时监控描述" + }, + "report": { + "monthlyGeneration": "每月发电", + "monthOverMonthUp": "月超过月上升", + "prAverage": "光伏平均", + "workOrderCompletionRate": "工作工单完成率", + "monthlyTotalOrders": "每月总工单", + "alertClosureRate": "告警闭合率", + "criticalAlertCount": "严重告警数量", + "cleaningRevenueImprovement": "清洗收益提升", + "estimatedAdditionalGeneration": "预计额外发电", + "autoGeneratedReports": "自动已生成报表", + "unitReport": "单位报表", + "monthlyAccumulated": "每月累计", + "stationMonthlyReportFile": "电站每月报表文件", + "generated": "已生成", + "weeklyOperationSummary": "每周操作摘要", + "aiDiagnosisDaily": "AI诊断每日", + "generating": "生成中", + "alertClosureWeekly": "告警闭合每周", + "monthlyOperationReport": "每月操作报表", + "monthlyOperationReportDesc": "每月操作报表描述", + "deviceHealthReport": "设备健康报表", + "deviceHealthReportDesc": "设备健康报表描述", + "cleaningOptimizationReport": "清洗优化报表", + "cleaningEffectReportDesc": "清洗效果报表描述", + "inspectionRobotReport": "巡检机器人报表", + "inspectionRobotReportDesc": "巡检机器人报表描述", + "stationMonthly": "电站每月", + "alertWeekly": "告警每周", + "systemAuto": "系统自动", + "aiAnalysisReport": "AI分析报表", + "operationSummary": "操作摘要", + "operationReport": "操作报表", + "cleaningEffectReport": "清洗效果报表", + "overview": "概览", + "reportCycle": "报表周期", + "reportType": "报表类型", + "exportPdf": "导出PDF", + "exportExcel": "导出Excel", + "monthlyOperationAnalysis": "每月操作分析", + "generation": "发电", + "predictedGeneration": "预测发电", + "monthlyAccumulatedGeneration": "每月累计发电", + "theoreticalGeneration": "理论发电", + "generationDeviation": "发电偏差", + "averageIrradiance": "平均辐照度", + "unitIrradiancePerDay": "单位辐照度每日", + "generationMeteorology": "发电气象", + "workOrderAlertClosure": "工作工单告警闭合", + "closureRate": "闭合率", + "alarmCount": "告警数量", + "aiAnalysisStatistics": "AI分析统计", + "unitItem": "单位项", + "subscriptionDispatch": "订阅派单", + "subscribers": "订阅者", + "roleOmSupervisor": "角色运维主管", + "roleStationEngineer": "角色电站工程师", + "roleDeviceOm": "角色设备运维", + "roleOmManager": "角色运维管理员", + "dispatchChannels": "派单渠道", + "email": "邮件", + "systemNotification": "系统通知", + "sendPlan": "发送计划", + "weeklyAutoSend": "每周自动发送", + "reportDirectory": "报表目录", + "reportName": "报表名称", + "templateCenter": "模板中心", + "generationRecords": "发电记录", + "generateTime": "生成时间", + "generator": "发电机" + }, + "roles": { + "editSuccess": "编辑成功", + "addSuccess": "新增成功", + "manageMembers": "管理成员", + "confirmDeleteRole": "确认删除角色", + "permissionKey": "权限键", + "superAdmin": "超级管理员", + "orgAdmin": "组织管理员", + "editRole": "编辑角色", + "roleNamePlaceholder": "角色名称占位", + "rolePermission": "角色权限", + "roleKeyPlaceholder": "角色键占位", + "roleSortLabel": "角色排序标签", + "menuPermissionConfig": "菜单权限配置" + }, + "users": { + "notAssigned": "未已分配", + "languagePlaceholder": "语言占位", + "rolePlaceholder": "角色占位", + "siteId": "站点ID" + } + }, + "enAdd": { + "alerts": { + "processing": "Processing", + "alarmNo": "Alarm No", + "alarmTitle": "Alarm Title", + "alarmContent": "Alarm Content", + "alertDetail": "Alert Detail", + "rootCause": "Root Cause", + "recommendedAction": "Recommended Action", + "notifySubscription": "Notify Subscription", + "sms": "Sms", + "dingTalk": "Ding Talk", + "email": "Email", + "handleSuccess": "Handle Success", + "handleFail": "Handle Fail", + "exportFileName": "Export File Name", + "reportValue": "Report Value", + "noAlarmToCreateOrder": "no Alarm To Create Order", + "orderFromAlarm": "Order From Alarm", + "orderCreated": "Order Created", + "orderCreateFail": "Order Create Fail", + "todayTotalAlerts": "Today Total Alerts", + "trendNoChange": "Trend No Change", + "trendUp": "Trend Up", + "trendDown": "Trend Down", + "trendZero": "Trend Zero", + "yesterdayValue": "Yesterday Value", + "todayCriticalAlerts": "Today Critical Alerts", + "todayPending": "Today Pending", + "todayClosed": "Today Closed", + "alertTypeProportion": "Alert Type Proportion", + "alertStatusStatistics": "Alert Status Statistics", + "sourceDevice": "Source Device", + "occurTime": "Occur Time", + "alertDescription": "Alert Description", + "contactHandler": "Contact Handler", + "handleOperation": "Handle Operation", + "pleaseSelectHandleStatus": "Please Select Handle Status", + "pleaseSelectHandleResult": "Please Select Handle Result", + "pleaseInputHandleRemark": "Please Input Handle Remark", + "confirmHandle": "Confirm Handle", + "modifyAlert": "Modify Alert", + "pleaseInputAlarmTitle": "Please Input Alarm Title", + "pleaseSelectAlertLevel": "Please Select Alert Level", + "pleaseSelectAlertType": "Please Select Alert Type", + "pleaseInputAlarmContent": "Please Input Alarm Content", + "newOrder": "New Order", + "planStartTime": "Plan Start Time", + "pleaseSelectPlanStartTime": "Please Select Plan Start Time", + "confirmCreateOrder": "Confirm Create Order", + "mockDeviceSensor": "Mock Device Sensor", + "mockTempExceed": "Mock Temp Exceed", + "mockDeviceCamera": "Mock Device Camera", + "mockVideoSignalLost": "Mock Video Signal Lost", + "mockDeviceBattery": "Mock Device Battery", + "mockBatteryLow": "Mock Battery Low", + "level": "Level", + "content": "Content", + "searchDeviceContent": "Search Device Content", + "mockRuleTemp": "Mock Rule Temp", + "mockRuleBattery": "Mock Rule Battery", + "deviceSensor": "Device Sensor", + "deviceBattery": "Device Battery", + "ruleName": "Rule Name", + "triggerCondition": "Trigger Condition", + "ruleConfig": "Rule Config", + "addRule": "Add Rule", + "editRule": "Edit Rule", + "ruleNamePlaceholder": "Rule Name Placeholder", + "relatedModel": "Related Model", + "monitorProperty": "Monitor Property", + "compareSymbol": "Compare Symbol", + "threshold": "Threshold", + "thresholdPlaceholder": "Threshold Placeholder", + "sevenDayTrend": "Seven Day Trend", + "criticalAlert": "Critical Alert", + "importantAlert": "Important Alert", + "generalAlert": "General Alert", + "infoAlert": "Info Alert", + "alertLevelDistribution": "Alert Level Distribution", + "mockSubOps": "Mock Sub Ops", + "mockSubAdmin": "Mock Sub Admin", + "mockSubDevice": "Mock Sub Device", + "appPush": "App Push", + "subscriptionName": "Subscription Name", + "notifyChannel": "Notify Channel", + "searchSubscription": "Search Subscription", + "addSubscription": "Add Subscription", + "editAlertSubscription": "Edit Alert Subscription", + "addAlertSubscription": "Add Alert Subscription", + "pleaseInputSubscriptionName": "Please Input Subscription Name", + "allDevices": "All Devices", + "allLevels": "All Levels", + "pleaseSelectChannel": "Please Select Channel", + "subtitle": "Subtitle" + }, + "common": { + "loadFailed": "Load Failed", + "loadDetailFailed": "Load Detail Failed", + "requestError": "Request Error", + "deviceId": "Device Id", + "pleaseInputDeviceId": "Please Input Device Id", + "timeRange": "Time Range", + "enabled": "Enabled", + "modify": "Modify", + "apiCallFail": "Api Call Fail", + "quantity": "Quantity", + "detail": "Detail", + "storeValue": "Store Value", + "operationFailed": "Operation Failed", + "submitFail": "Submit Fail", + "confirmDeleteRecord": "Confirm Delete Record", + "deleteIrreversible": "Delete Irreversible", + "working": "Working", + "settings": "Settings" + }, + "workOrder": { + "generateOrder": "Generate Order", + "newOrders": "New Orders", + "dispatchSuccess": "Dispatch Success", + "dispatchFail": "Dispatch Fail", + "dispatchRequestError": "Dispatch Request Error", + "todayNewOrders": "Today New Orders", + "vsYesterday": "vs Yesterday", + "pendingOrders": "Pending Orders", + "highPriority": "High Priority", + "runningOrders": "Running Orders", + "onTimeRate": "on Time Rate", + "completedOrders": "Completed Orders", + "monthlyTotal": "Monthly Total", + "avgHandleTime": "Avg Handle Time", + "hourUnit": "Hour Unit", + "vsLastWeek": "vs Last Week", + "aiSuggestedOrders": "ai Suggested Orders", + "orderUnit": "Order Unit", + "autoGeneratedToday": "Auto Generated Today", + "getListFailed": "Get List Failed", + "pleaseSelectOrder": "Please Select Order", + "completeSuccess": "Complete Success", + "completeFail": "Complete Fail", + "getDetailFailed": "Get Detail Failed", + "pleaseSelectOneOrder": "Please Select One Order", + "modifySuccess": "Modify Success", + "modifyFail": "Modify Fail", + "executeSuccess": "Execute Success", + "executeFail": "Execute Fail", + "suspendSuccess": "Suspend Success", + "suspendFail": "Suspend Fail", + "invalidOrder": "Invalid Order", + "pleaseSelectOrderToDelete": "Please Select Order To Delete", + "batchDeleteSuccess": "Batch Delete Success", + "batchDeleteFail": "Batch Delete Fail", + "orderNo": "Order No", + "taskManagement": "Task Management", + "overview": "Overview", + "timeRange": "Time Range", + "keyword": "Keyword", + "pleaseInputOrderNoOrTitle": "Please Input Order No Or Title", + "list": "List", + "totalWithCount": "Total with Count", + "confirmBatchDelete": "Confirm Batch Delete", + "batchExecute": "Batch Execute", + "statusDistribution": "Status Distribution", + "totalCount": "Total Count", + "scheduleCalendarView": "Schedule Calendar View", + "todaySuffix": "Today Suffix", + "noScheduledOrders": "no Scheduled Orders", + "scheduledOrderList": "Scheduled Order List", + "planStartTime": "Plan Start Time", + "detail": "Detail", + "noTitle": "no Title", + "nearItems": "Near Items", + "pleaseInputTaskDesc": "Please Input Task Desc", + "pendingDispatch": "Pending Dispatch", + "pleaseSelectPerson": "Please Select Person", + "selectedOrders": "Selected Orders", + "pleaseSelectExecuteDevice": "Please Select Execute Device", + "pleaseInputResult": "Please Input Result", + "pleaseSelectResult": "Please Select Result", + "handledCompleted": "Handled Completed", + "fixed": "Fixed", + "noActionNeeded": "no Action Needed", + "issueClosed": "Issue Closed", + "pleaseInputHandleRemark": "Please Input Handle Remark", + "alarmNo": "Alarm No", + "alarmTitle": "Alarm Title" + }, + "devices": { + "working": "Working", + "videoConfigured": "Video Configured", + "noVideo": "no Video", + "connectionFailed": "Connection Failed", + "permissionRequestTitle": "Permission Request Title", + "requestControlPermission": "Request Control Permission", + "selectNoDrone": "Select No Drone", + "commandIssued": "Command Issued", + "commandFailed": "Command Failed", + "apiRequestError": "Api Request Error", + "selectTaskFirst": "Select Task First", + "taskExecutedSuccess": "Task Executed Success", + "taskSuspended": "Task Suspended", + "taskResumedSuccess": "Task Resumed Success", + "taskReturnedHome": "Task Returned Home", + "taskStatusUpdateSuccess": "Task Status Update Success", + "operationApiError": "Operation Api Error", + "permissionObtained": "Permission Obtained", + "controlRequestContent": "Control Request Content", + "unknownPlatform": "Unknown Platform", + "obstacleDetected": "Obstacle Detected", + "unknownObject": "Unknown Object", + "noTaskDetail": "no Task Detail", + "fetchDetailError": "Fetch Detail Error", + "deviceInfoError": "Device Info Error", + "pleaseLoginFirst": "Please Login First", + "loginInfoExpired": "Login Info Expired", + "otherWebControlling": "Other Web Controlling", + "networkErrorRetry": "Network Error Retry", + "routeLoaded": "Route Loaded", + "droneFallbackName": "Drone Fallback Name", + "droneAirspace": "Drone Airspace", + "droneWayline": "Drone Wayline", + "onStandby": "on Standby", + "permissionRequesting": "Permission Requesting", + "noWaylineData": "no Wayline Data", + "fetchWaylineFailed": "Fetch Wayline Failed", + "waypointsAdded": "Waypoints Added", + "waypointDeleted": "Waypoint Deleted", + "allWaypointsCleared": "All Waypoints Cleared", + "pleaseSelectWayline": "Please Select Wayline", + "selectTaskStartTime": "Select Task Start Time", + "createSuccess": "Create Success", + "apiErrorCheck": "Api Error Check", + "noWaypointsToSave": "no Waypoints To Save", + "planeNoLocalRoute": "Plane No Local Route", + "routeSavedLocal": "Route Saved Local", + "waylineLabel": "Wayline Label", + "fetchWaylineListFailed": "Fetch Wayline List Failed", + "routeLoadedWithName": "Route Loaded with Name", + "gpsTaskIssued": "Gps Task Issued", + "issueTaskFirst": "Issue Task First", + "taskPaused": "Task Paused", + "taskStarted": "Task Started", + "taskStopped": "Task Stopped", + "viewFront": "View Front", + "viewBack": "View Back", + "viewLeft": "View Left", + "viewRight": "View Right", + "viewTop": "View Top", + "deviceList": "Device List", + "onlineDevices": "Online Devices", + "inspectionMachine": "Inspection Machine", + "supportsGpsPlan": "Supports Gps Plan", + "supportsRemoteGpsPlan": "Supports Remote Gps Plan", + "batteryWithValue": "Battery with Value", + "videoLinkConfigured": "Video Link Configured", + "batteryCapacity": "Battery Capacity", + "flightAltitude": "Flight Altitude", + "signalSatellite": "Signal Satellite", + "envWindSpeed": "Env Wind Speed", + "rtkStatus": "Rtk Status", + "fixed": "Fixed", + "notFixed": "Not Fixed", + "remoteControlAndSituation": "Remote Control and Situation", + "gpsPlanning": "Gps Planning", + "leftWheelDrive": "Left Wheel Drive", + "normalRunning": "Normal Running", + "realtimeSpeed": "Realtime Speed", + "workingCurrent": "Working Current", + "rightWheelDrive": "Right Wheel Drive", + "gpsWaypointPlan": "Gps Waypoint Plan", + "savePlan": "Save Plan", + "clearRoute": "Clear Route", + "droneWaylineTask": "Drone Wayline Task", + "taskWaitingStart": "Task Waiting Start", + "taskStartFailure": "Task Start Failure", + "taskTerminated": "Task Terminated", + "taskTimeout": "Task Timeout", + "noWaylineTask": "no Wayline Task", + "robotTaskData": "Robot Task Data", + "waypointList": "Waypoint List", + "noWaypoints": "no Waypoints", + "waypoint": "Waypoint", + "robotLocalRoutes": "Robot Local Routes", + "noSavedRoutes": "no Saved Routes", + "waypointsCount": "Waypoints Count", + "issueTask": "Issue Task", + "suspendTask": "Suspend Task", + "cancelReturnHome": "Cancel Return Home", + "videoAndConfig": "Video and Config", + "noVideoStream": "no Video Stream", + "waylineDetail": "Wayline Detail", + "taskIdLabel": "Task Id Label", + "waylineUuidLabel": "Wayline Uuid Label", + "airportSnLabel": "Airport Sn Label", + "landingDockSnLabel": "Landing Dock Sn Label", + "returnHomeAltitudeLabel": "Return Home Altitude Label", + "returnHomeModeLabel": "Return Home Mode Label", + "waylinePrecisionLabel": "Wayline Precision Label", + "lostControlActionLabel": "Lost Control Action Label", + "resumeFromBreakpointLabel": "Resume From Breakpoint Label", + "taskTypeLabel": "Task Type Label", + "repeatModeLabel": "Repeat Mode Label", + "minBatteryLabel": "Min Battery Label", + "currentWaypointLabel": "Current Waypoint Label", + "rthOptimal": "Rth Optimal", + "rthPreset": "Rth Preset", + "gpsMode": "Gps Mode", + "rtkHighPrecision": "Rtk High Precision", + "modeAuto": "Mode Auto", + "modeManual": "Mode Manual", + "taskImmediate": "Task Immediate", + "taskTimed": "Task Timed", + "taskRecurring": "Task Recurring", + "taskContinuous": "Task Continuous", + "repeatNone": "Repeat None", + "repeatDaily": "Repeat Daily", + "repeatWeekly": "Repeat Weekly", + "repeatMonthlyByDate": "Repeat Monthly By Date", + "repeatMonthlyByWeek": "Repeat Monthly By Week", + "taskResumed": "Task Resumed", + "taskEnded": "Task Ended", + "taskTimedOut": "Task Timed Out", + "createWaylineTask": "Create Wayline Task", + "waylineName": "Wayline Name", + "inputWaylineName": "Input Wayline Name", + "selectWayline": "Select Wayline", + "autoFetch": "Auto Fetch", + "optionalEmpty": "Optional Empty", + "gpsModeDesc": "Gps Mode Desc", + "rtkModeDesc": "Rtk Mode Desc", + "autoResumeFromBreakpoint": "Auto Resume From Breakpoint", + "resumeAuto": "Resume Auto", + "resumeManual": "Resume Manual", + "minExecBatteryLabel": "Min Exec Battery Label", + "taskWaylineTypeLabel": "Task Wayline Type Label", + "taskStartTimeLabel": "Task Start Time Label", + "selectStartTimePlaceholder": "Select Start Time Placeholder", + "taskEndTimeLabel": "Task End Time Label", + "repeatInterval": "Repeat Interval", + "boundDrone": "Bound Drone", + "airportCode": "Airport Code", + "tlStartCleaning": "tl Start Cleaning", + "timelineStatusStart": "Timeline Status Start", + "tlStartInspection": "tl Start Inspection", + "tlStartMowing": "tl Start Mowing", + "tlStandbyReady": "tl Standby Ready", + "timelineStatusReady": "Timeline Status Ready", + "tlReturnSupply": "tl Return Supply", + "timelineStatusReturn": "Timeline Status Return", + "tlFinishInspection": "tl Finish Inspection", + "timelineStatusComplete": "Timeline Status Complete", + "logCommand": "Log Command", + "logDesc1": "Log Desc1", + "logDesc2": "Log Desc2", + "logDesc3": "Log Desc3", + "logDesc4": "Log Desc4", + "logDesc5": "Log Desc5", + "logEvent": "Log Event", + "logDesc6": "Log Desc6", + "logLevelLowBattery": "Log Level Low Battery", + "aliasRobotOnly": "Alias Robot Only", + "aliasNotEmpty": "Alias Not Empty", + "aliasModifiedSuccess": "Alias Modified Success", + "aliasModifyFail": "Alias Modify Fail", + "aliasApiError": "Alias Api Error", + "noPermission": "no Permission", + "stationStatus": "Station Status", + "dispatchableCount": "Dispatchable Count", + "unitTai": "Unit Tai", + "robotExecutingCount": "Robot Executing Count", + "mowingRobot": "Mowing Robot", + "robotInTaskCount": "Robot In Task Count", + "washDrone": "Wash Drone", + "unitJia": "Unit Jia", + "standbyCount": "Standby Count", + "todayTaskExecute": "Today Task Execute", + "completionRate": "Completion Rate", + "unknownDevice": "Unknown Device", + "modeWorking": "Mode Working", + "modeDebugLocal": "Mode Debug Local", + "modeDebugRemote": "Mode Debug Remote", + "modeUpgrading": "Mode Upgrading", + "positionInStation": "Position In Station", + "taskStandby2": "Task Standby2", + "commGood": "Comm Good", + "taskStarted2": "Task Started2", + "taskPaused2": "Task Paused2", + "returnCommandSent": "Return Command Sent", + "rechargeCommandSent": "Recharge Command Sent", + "emergencyStopTriggered": "Emergency Stop Triggered", + "rainNo": "Rain No", + "equipmentOverview": "Equipment Overview", + "equipmentSituationMap": "Equipment Situation Map", + "legendTitle": "Legend Title", + "inspectionRoute": "Inspection Route", + "robotPath": "Robot Path", + "cleaningPath": "Cleaning Path", + "robotPosition": "Robot Position", + "dronePosition": "Drone Position", + "supplyChargePoint": "Supply Charge Point", + "obstacleForbiddenZone": "Obstacle Forbidden Zone", + "equipmentScheduleList": "Equipment Schedule List", + "equipmentName": "Equipment Name", + "modeLabel": "Mode Label", + "commStatus": "Comm Status", + "deviceParamSetting": "Device Param Setting", + "controlLogTitle": "Control Log Title", + "allLevels": "All Levels", + "deviceColHeader": "Device Col Header", + "viewMoreLogs": "View More Logs", + "deviceControlPanel": "Device Control Panel", + "detailBtn": "Detail Btn", + "batteryWithPercent": "Battery with Percent", + "productType": "Product Type", + "deviceSnLabel": "Device Sn Label", + "activatedTime": "Activated Time", + "firmwareVersion": "Firmware Version", + "flightWindSpeed": "Flight Wind Speed", + "rtkCount": "Rtk Count", + "startBtn": "Start Btn", + "rechargeBtn": "Recharge Btn", + "realtimePicture": "Realtime Picture", + "stationVideo": "Station Video", + "droneVideo": "Drone Video", + "stopVideoBtn": "Stop Video Btn", + "switchManual": "Switch Manual", + "emergencyStopBtn": "Emergency Stop Btn", + "aiAnalysisTitle": "ai Analysis Title", + "hotspotSuspect": "Hotspot Suspect", + "countUnit": "Count Unit", + "weedHighZone": "Weed High Zone", + "blockUnit": "Block Unit", + "componentRiskZone": "Component Risk Zone", + "fenceAbnormal": "Fence Abnormal", + "suggestAction": "Suggest Action", + "aiAction1": "ai Action1", + "aiAction2": "ai Action2", + "aiAction3": "ai Action3", + "generateTask": "Generate Task", + "aiOptimizePath": "ai Optimize Path", + "oneClickCoordinated": "One Click Coordinated", + "envFlightConditions": "Env Flight Conditions", + "windSpeed": "Wind Speed", + "rainfall": "Rainfall", + "rtkEnvLabel": "Rtk Env Label", + "fixedEnv": "Fixed Env", + "notFixedEnv": "Not Fixed Env", + "suitableExec": "Suitable Exec", + "canWorkNormally": "Can Work Normally", + "robotWorkStatus": "Robot Work Status", + "cumWorkMileage": "Cum Work Mileage", + "cumWorkArea": "Cum Work Area", + "cumWorkTime": "Cum Work Time", + "taskTimelineTitle": "Task Timeline Title", + "viewMore": "View More", + "editRobotInfoTitle": "Edit Robot Info Title", + "deviceSnColon": "Device Sn Colon", + "deviceTypeColon": "Device Type Colon", + "deviceAliasLabel": "Device Alias Label", + "inputCustomAlias": "Input Custom Alias", + "aliasHint": "Alias Hint", + "fetchParamFail": "Fetch Param Fail", + "paramSaveSuccess": "Param Save Success", + "paramSaveFail": "Param Save Fail", + "paramSaveException": "Param Save Exception", + "paramLeftForwardGain": "Param Left Forward Gain", + "paramLeftBackwardGain": "Param Left Backward Gain", + "paramRightForwardGain": "Param Right Forward Gain", + "paramRightBackwardGain": "Param Right Backward Gain", + "paramRunSpeed": "Param Run Speed", + "paramChipUidSign": "Param Chip Uid Sign", + "paramChipUid": "Param Chip Uid", + "paramRemoteConfig": "Param Remote Config", + "paramKnifeMotorMode": "Param Knife Motor Mode", + "paramWalkMotorMode": "Param Walk Motor Mode", + "paramLeftMotorReverse": "Param Left Motor Reverse", + "paramRightMotorReverse": "Param Right Motor Reverse", + "paramSwapChannel": "Param Swap Channel", + "paramUse4G": "Param Use4G", + "paramForwardSpeedLimit": "Param Forward Speed Limit", + "paramTurnSpeedLimit": "Param Turn Speed Limit", + "paramKnifePolarity": "Param Knife Polarity", + "paramFanPolarity": "Param Fan Polarity", + "paramThrottlePolarity": "Param Throttle Polarity", + "paramLiftProtectTime": "Param Lift Protect Time", + "paramDualRtk": "Param Dual Rtk", + "paramKnifeChannel": "Param Knife Channel", + "paramFanChannel": "Param Fan Channel", + "paramThrottleChannel": "Param Throttle Channel", + "paramRemoteType": "Param Remote Type", + "paramRelayBoard": "Param Relay Board", + "paramLiftChannel": "Param Lift Channel", + "paramChassisChannel": "Param Chassis Channel", + "paramArmChannel": "Param Arm Channel", + "paramFuelPumpChannel": "Param Fuel Pump Channel", + "paramWifiName": "Param Wifi Name", + "paramWifiPassword": "Param Wifi Password", + "paramBatteryType": "Param Battery Type", + "paramDriveType": "Param Drive Type", + "paramGearRatio": "Param Gear Ratio", + "paramRobotLength": "Param Robot Length", + "paramRobotWidth": "Param Robot Width", + "paramRobotHeight": "Param Robot Height", + "paramKnifeWidth": "Param Knife Width", + "paramTyreSize": "Param Tyre Size", + "paramFirmwareVersion": "Param Firmware Version", + "paramDelFlag": "Param Del Flag", + "paramViewEditTitle": "Param View Edit Title", + "noData": "no Data", + "locationFail": "Location Fail", + "pointMarked": "Point Marked", + "minThreePoints": "Min Three Points", + "pathPlanDone": "Path Plan Done", + "pathPlanFailRetry": "Path Plan Fail Retry", + "completePathFirst": "Complete Path First", + "inputPlotName": "Input Plot Name", + "pathPlanning": "Path Planning", + "markedPointsCount": "Marked Points Count", + "workMode": "Work Mode", + "bowMode": "Bow Mode", + "abMode": "ab Mode", + "markPoint": "Mark Point", + "clearMarks": "Clear Marks", + "complete": "Complete", + "selectDevice": "Select Device", + "unknownDrone": "Unknown Drone", + "situationMap": "Situation Map", + "clearPlannedRoute": "Clear Planned Route", + "realtimeVideoMonitor": "Realtime Video Monitor", + "frontView": "Front View", + "backView": "Back View", + "leftView": "Left View", + "rightView": "Right View", + "panorama": "Panorama", + "robotStatus": "Robot Status", + "rtBattery": "rt Battery", + "rtVoltage": "rt Voltage", + "rtLeftMotor": "rt Left Motor", + "rtRightMotor": "rt Right Motor", + "rtLeftSpeed": "rt Left Speed", + "rtRightSpeed": "rt Right Speed", + "rtLeftTemp": "rt Left Temp", + "rtRightTemp": "rt Right Temp", + "rtMainTemp": "rt Main Temp", + "rtHeadingAngle": "rt Heading Angle", + "rtPitchAngle": "rt Pitch Angle", + "rtHeadingStatus": "rt Heading Status", + "rtInitialized": "rt Initialized", + "rtPositioning": "rt Positioning", + "gpsSingle": "Gps Single", + "dgpsDiff": "Dgps Diff", + "rtkFixed2": "Rtk Fixed2", + "rtkFloat": "Rtk Float", + "rtSatellites": "rt Satellites", + "satelliteCount": "Satellite Count", + "rtMode": "rt Mode", + "videoStopped": "Video Stopped", + "clickPlayHint": "Click Play Hint", + "remoteControl": "Remote Control", + "savePathTitle": "Save Path Title", + "plotNameLabel": "Plot Name Label", + "saving": "Saving", + "stationCameraNotFound": "Station Camera Not Found", + "indoor": "Indoor", + "outdoor": "Outdoor", + "switchStationCameraFail": "Switch Station Camera Fail", + "switchDroneLensFail": "Switch Drone Lens Fail", + "wideLens": "Wide Lens", + "zoomLens": "Zoom Lens", + "irLens": "ir Lens", + "noVideoSignal": "no Video Signal", + "rainYes": "Rain Yes", + "hangar": "Hangar", + "coverOpen": "Cover Open", + "coverClosed": "Cover Closed", + "inDock": "in Dock", + "outDock": "Out Dock", + "droneStatus": "Drone Status", + "horizontalSpeed": "Horizontal Speed", + "verticalSpeed": "Vertical Speed", + "headingAngle": "Heading Angle", + "gpsUnit": "Gps Unit", + "taskCompletedMsg": "Task Completed Msg", + "confirmDeleteTitle": "Confirm Delete Title", + "confirmDeleteContent": "Confirm Delete Content", + "confirmDeleteOk": "Confirm Delete Ok", + "routeDeletedSuccess": "Route Deleted Success", + "deleteFailRetry": "Delete Fail Retry", + "deleteFailNetwork": "Delete Fail Network", + "taskListRefreshFail": "Task List Refresh Fail", + "routeNoPathPoints": "Route No Path Points", + "checkRouteFail": "Check Route Fail", + "taskExecuteSuccess": "Task Execute Success", + "taskExecuteFail": "Task Execute Fail", + "taskExecuteException": "Task Execute Exception", + "taskCanceledSuccess": "Task Canceled Success", + "taskCancelFail": "Task Cancel Fail", + "taskCancelException": "Task Cancel Exception", + "taskPausedSuccess": "Task Paused Success", + "taskPauseFail": "Task Pause Fail", + "taskPauseException": "Task Pause Exception", + "taskResumeFail": "Task Resume Fail", + "taskResumeException": "Task Resume Exception", + "waylineFallback": "Wayline Fallback", + "waylineListFetchFail": "Wayline List Fetch Fail", + "fillRequiredFields": "Fill Required Fields", + "createTaskSuccess": "Create Task Success", + "createTaskFail": "Create Task Fail", + "createTaskException": "Create Task Exception", + "trendYesterday": "Trend Yesterday", + "trendFlat": "Trend Flat", + "taskListFetchFail": "Task List Fetch Fail", + "boardTaskFetchFail": "Board Task Fetch Fail", + "statusExecSuccess": "Status Exec Success", + "statusPreparing": "Status Preparing", + "statusExecFailed": "Status Exec Failed", + "statusWaitingStart": "Status Waiting Start", + "statusStartFailed": "Status Start Failed", + "statusRestored": "Status Restored", + "statusTerminated": "Status Terminated", + "statusTimeout": "Status Timeout", + "deviceSn": "Device Sn", + "requestFail": "Request Fail", + "selectExecuteDroneFirst": "Select Execute Drone First", + "waylineTaskCreated": "Wayline Task Created", + "waylineExecFail": "Wayline Exec Fail", + "todayWaylinePlan": "Today Wayline Plan", + "unitTiao": "Unit Tiao", + "inProgressDesc": "in Progress Desc", + "executingTasks": "Executing Tasks", + "unitGe": "Unit Ge", + "pendingApprovalTasks": "Pending Approval Tasks", + "taskCompletionRate": "Task Completion Rate", + "aiOptimizeGain": "ai Optimize Gain", + "coverageEquipment": "Coverage Equipment", + "coverageValue": "Coverage Value", + "allOnline": "All Online", + "noDroneSelected": "no Drone Selected", + "commandSent": "Command Sent", + "commandSendFail": "Command Send Fail", + "apiRequestException": "Api Request Exception", + "noDetail": "no Detail", + "fetchDetailException": "Fetch Detail Exception", + "taskExecSuccess": "Task Exec Success", + "taskSuspended2": "Task Suspended2", + "taskRestored2": "Task Restored2", + "taskReturnedHome2": "Task Returned Home2", + "statusUpdateSuccess": "Status Update Success", + "operationFail": "Operation Fail", + "operationApiException": "Operation Api Exception", + "waylineEditPlan": "Wayline Edit Plan", + "optimize": "Optimize", + "inspectRoute": "Inspect Route", + "mowRoute": "Mow Route", + "droneAirport": "Drone Airport", + "chargePoint": "Charge Point", + "noFlyZone": "no Fly Zone", + "deviceTaskList": "Device Task List", + "allDrones": "All Drones", + "taskOrchestrationAI": "Task Orchestration AI", + "typeDroneHotspot": "Type Drone Hotspot", + "levelHigh": "Level High", + "statusSuggestExec": "Status Suggest Exec", + "typeFenceInspect": "Type Fence Inspect", + "typeComponentClean": "Type Component Clean", + "levelMid": "Level Mid", + "statusWaitExec": "Status Wait Exec", + "typeArrayMow": "Type Array Mow", + "typeAIRecheck": "Type AI Recheck", + "levelLow": "Level Low", + "statusPendingApproval": "Status Pending Approval", + "colNo": "Col No", + "suggestTime": "Suggest Time", + "aiSuggestionColon": "ai Suggestion Colon", + "aiAdvice1": "ai Advice1", + "aiAdvice2": "ai Advice2", + "aiAdvice3": "ai Advice3", + "oneClickSchedule": "One Click Schedule", + "smartAssign": "Smart Assign", + "waylineParamsTitle": "Wayline Params Title", + "currentWayline": "Current Wayline", + "rthMode": "Rth Mode", + "waylinePrecision": "Wayline Precision", + "resumeBreakpoint": "Resume Breakpoint", + "safeRthBattery": "Safe Rth Battery", + "repeatTypeLabel": "Repeat Type Label", + "lostControlAction": "Lost Control Action", + "obstacleAvoidSwitch": "Obstacle Avoid Switch", + "nightFlightLimit": "Night Flight Limit", + "resumeTask": "Resume Task", + "pauseTask": "Pause Task", + "weatherWindowTitle": "Weather Window Title", + "suitability": "Suitability", + "suitableExec2": "Suitable Exec2", + "suitable": "Suitable", + "precipStatus": "Precip Status", + "hasRain": "Has Rain", + "gnssQuality": "Gnss Quality", + "fixed2": "Fixed2", + "notFixed2": "Not Fixed2", + "weatherAdvice1": "Weather Advice1", + "weatherAdvice2": "Weather Advice2", + "waylineList": "Wayline List", + "selectDrone": "Select Drone", + "searchWaylinePlaceholder": "Search Wayline Placeholder", + "unknownTime": "Unknown Time", + "executeWayline": "Execute Wayline", + "ganttViewTitle": "Gantt View Title", + "dronePortA01": "Drone Port A01", + "typeHotspotInspect": "Type Hotspot Inspect", + "typeRecheck": "Type Recheck", + "inspectRobotRbt": "Inspect Robot Rbt", + "mowRobotGr": "Mow Robot Gr", + "washDroneUav": "Wash Drone Uav", + "workTaskBoard": "Work Task Board", + "startLabel": "Start Label", + "endLabel": "End Label", + "pendingExec": "Pending Exec", + "equipmentColon": "Equipment Colon", + "startTimeColon": "Start Time Colon", + "completedTimeColon": "Completed Time Colon", + "createWaylineTaskTitle": "Create Wayline Task Title", + "airportSn": "Airport Sn", + "landingDockSn": "Landing Dock Sn", + "returnHomeAltitude": "Return Home Altitude", + "returnHomeMode": "Return Home Mode", + "minBattery": "Min Battery", + "repeatMode": "Repeat Mode", + "weekdayExec": "Weekday Exec", + "weekSun": "Week Sun", + "weekMon": "Week Mon", + "weekTue": "Week Tue", + "weekWed": "Week Wed", + "weekThu": "Week Thu", + "weekFri": "Week Fri", + "weekSat": "Week Sat", + "monthDays": "Month Days", + "whichWeek": "Which Week", + "weekNo": "Week No", + "weekday": "Weekday", + "execTimePoint": "Exec Time Point", + "inputTimestamp": "Input Timestamp", + "confirmCreate": "Confirm Create", + "executeWaylineTask": "Execute Wayline Task", + "executeNow": "Execute Now", + "selectedWaylineColon": "Selected Wayline Colon", + "executeDeviceColon": "Execute Device Colon", + "noDeviceSelected": "no Device Selected", + "returnHomeAltitudeColon": "Return Home Altitude Colon", + "deviceData": "Device Data", + "equipmentControl": "Equipment Control", + "realtimePowerData": "Realtime Power Data", + "communicationStatus": "Communication Status", + "communicationNormal": "Communication Normal", + "groupOf": "Group Of", + "weatherStation": "Weather Station", + "stationTopology": "Station Topology", + "communicating": "Communicating", + "realtimeMonitorList": "Realtime Monitor List", + "allTypes": "All Types", + "searchDeviceName": "Search Device Name", + "dronePort": "Drone Port", + "deviceOnlineRate": "Device Online Rate", + "onlineRate": "Online Rate", + "totalCount": "Total Count" + }, + "systemSetting": { + "pleaseSelectRoles": "Please Select Roles", + "batchDeleteRolesTitle": "Batch Delete Roles Title", + "confirmDeleteSelectedRoles": "Confirm Delete Selected Roles", + "batchDeleteSuccess": "Batch Delete Success", + "resetFail": "Reset Fail", + "pleaseSelectUsers": "Please Select Users", + "batchDeleteUsersTitle": "Batch Delete Users Title", + "confirmDeleteSelectedUsers": "Confirm Delete Selected Users", + "userAndRole": "User and Role", + "belongingOrg": "Belonging Org", + "belongingSite": "Belonging Site", + "devicePerm": "Device Perm", + "hasPerm": "Has Perm", + "noPerm": "no Perm", + "confirmDeleteUser": "Confirm Delete User", + "totalCount": "Total Count", + "permissionLabel": "Permission Label", + "levelLabel": "Level Label", + "editRole": "Edit Role", + "roleLevel": "Role Level", + "pleaseInputRoleLevel": "Please Input Role Level", + "account": "Account", + "nicknameShort": "Nickname Short", + "phoneShort": "Phone Short", + "editPasswordPlaceholder": "Edit Password Placeholder", + "chooseRole": "Choose Role", + "pleaseSelectOrg": "Please Select Org", + "pleaseSelectSite": "Please Select Site", + "deviceOperatePermLabel": "Device Operate Perm Label", + "has": "Has", + "none": "None" + }, + "aiAnalysis": { + "objectDetection": "Object Detection", + "detectDesc": "Detect Desc", + "thermalSegmentation": "Thermal Segmentation", + "segmentationDesc": "Segmentation Desc", + "getModelsFailed": "Get Models Failed", + "uploadFirst": "Upload First", + "submitTaskFailed": "Submit Task Failed", + "thermalAnalysisComplete": "Thermal Analysis Complete", + "taskSuccess": "Task Success", + "taskExecutionError": "Task Execution Error", + "cancelled": "Cancelled", + "uploadThermalFirst": "Upload Thermal First", + "thermalAnalysisFailed": "Thermal Analysis Failed", + "detectTaskName": "Detect Task Name", + "segmentationTaskName": "Segmentation Task Name", + "algoCount": "Algo Count", + "detectAndSeg": "Detect and Seg", + "onlineServices": "Online Services", + "healthCheck": "Health Check", + "modelCount": "Model Count", + "detectModelRegistry": "Detect Model Registry", + "historyRecords": "History Records", + "pageDebugRecords": "Page Debug Records", + "detectModel": "Detect Model", + "default": "Default", + "inputType": "Input Type", + "image": "Image", + "inferencePreset": "Inference Preset", + "quality": "Quality", + "dragFile": "Drag File", + "supportedFormats": "Supported Formats", + "processing": "Processing", + "submitDetectTask": "Submit Detect Task", + "serverDemo": "Server Demo", + "refreshModels": "Refresh Models", + "thermalFileLabel": "Thermal File Label", + "uploadThermal": "Upload Thermal", + "arrayMask": "Array Mask", + "panelGeojson": "Panel Geojson", + "submitThermalAnalysis": "Submit Thermal Analysis", + "readServerHistory": "Read Server History", + "resultDisplay": "Result Display", + "resultViewHint": "Result View Hint", + "inputPreview": "Input Preview", + "resultImageVideo": "Result Image Video", + "showResultAfterTask": "Show Result After Task", + "resultJson": "Result Json", + "noResult": "no Result", + "thermalInteractiveView": "Thermal Interactive View", + "clickHotspotHint": "Click Hotspot Hint", + "hotspotCount": "Hotspot Count", + "panelArea": "Panel Area", + "analysisId": "Analysis Id", + "pointDetails": "Point Details", + "selectHotspotPanel": "Select Hotspot Panel", + "hotspot": "Hotspot", + "blockNumber": "Block Number", + "originalPixels": "Original Pixels", + "overlayPixels": "Overlay Pixels", + "area": "Area", + "waitingSegmentation": "Waiting Segmentation", + "hotspotPanelList": "Hotspot Panel List", + "panelLabel": "Panel Label", + "centerTitle": "Center Title", + "algorithmList": "Algorithm List", + "integratedCount": "Integrated Count", + "detectWorkspace": "Detect Workspace", + "segWorkspace": "Seg Workspace", + "asyncTask": "Async Task", + "syncAnalysis": "Sync Analysis", + "detectWorkspaceDesc": "Detect Workspace Desc", + "segWorkspaceDesc": "Seg Workspace Desc", + "historyAnalysis": "History Analysis", + "noHistory": "no History", + "detect": "Detect", + "segment": "Segment", + "hotspotRecognition": "Hotspot Recognition", + "abnormalString": "Abnormal String", + "recommendedCleanBlock": "Recommended Clean Block", + "videoAbnormalEvent": "Video Abnormal Event", + "videoIntelligentAnalysis": "Video Intelligent Analysis", + "perimeterIntrusion": "Perimeter Intrusion", + "perimeterIntrusionDesc": "Perimeter Intrusion Desc", + "personnelIntrusion": "Personnel Intrusion", + "personnelIntrusionDesc": "Personnel Intrusion Desc", + "fireSmokeDetection": "Fire Smoke Detection", + "fireSmokeDesc": "Fire Smoke Desc", + "areaLoitering": "Area Loitering", + "areaLoiteringDesc": "Area Loitering Desc", + "motionDetection": "Motion Detection", + "motionDetectionDesc": "Motion Detection Desc", + "perimeterIntrusionTimeline": "Perimeter Intrusion Timeline", + "personnelIntrusionTimeline": "Personnel Intrusion Timeline", + "areaLoiteringTimeline": "Area Loitering Timeline", + "motionDetectionTimeline": "Motion Detection Timeline", + "perimeterClimbTimeline": "Perimeter Climb Timeline", + "perimeterIntrusionType": "Perimeter Intrusion Type" + }, + "video": { + "fileDetails": "File Details", + "sourceDevice": "Source Device", + "startEndTime": "Start End Time", + "capacity": "Capacity", + "videoReplayCenter": "Video Replay Center", + "allRecordings": "All Recordings", + "todayRecordings": "Today Recordings", + "timeSpan": "Time Span", + "onlineSearch": "Online Search", + "labelMainFront": "Label Main Front", + "labelRear": "Label Rear", + "labelLeft": "Label Left", + "labelRight": "Label Right", + "labelFrontDrive": "Label Front Drive", + "labelRearMount": "Label Rear Mount", + "labelLeftWing": "Label Left Wing", + "labelRightWing": "Label Right Wing", + "labelCargo": "Label Cargo", + "labelMainProbe": "Label Main Probe", + "labelPanorama": "Label Panorama", + "labelDetail": "Label Detail", + "currentDutyDevice": "Current Duty Device", + "videoStreamChannels": "Video Stream Channels", + "refreshSignal": "Refresh Signal", + "fullScreenWall": "Full Screen Wall", + "ptzServoControl": "Ptz Servo Control", + "systemIntegratedCommand": "System Integrated Command", + "twoWayIntercom": "Two Way Intercom", + "triggerRemoteRepellent": "Trigger Remote Repellent", + "aiVisualTracking": "ai Visual Tracking", + "activeVideoStreams": "Active Video Streams", + "positionArray1": "Position Array1", + "domeCamera": "Dome Camera", + "recording": "Recording", + "positionInverterRoom": "Position Inverter Room", + "positionStationGate": "Position Station Gate", + "positionPerimeterNorth": "Position Perimeter North", + "positionWeatherStation": "Position Weather Station", + "positionEquipmentArea": "Position Equipment Area", + "positionStorageBattery": "Position Storage Battery", + "positionEnergyStorage": "Position Energy Storage", + "positionPerimeterSouth": "Position Perimeter South", + "positionArrayWest": "Position Array West", + "cameraName": "Camera Name", + "recordingStatus": "Recording Status", + "lastEventTime": "Last Event Time", + "onlineCameras": "Online Cameras", + "unitDevice": "Unit Device", + "onlineRateTrend": "Online Rate Trend", + "offlineCameras": "Offline Cameras", + "offlineRateTrend": "Offline Rate Trend", + "todayAlertEvents": "Today Alert Events", + "unitIncident": "Unit Incident", + "vsYesterdayPercent": "vs Yesterday Percent", + "perimeterAlerts": "Perimeter Alerts", + "storageUsage": "Storage Usage", + "storageUsageTrend": "Storage Usage Trend", + "videoMonitorCenter": "Video Monitor Center", + "customLayout": "Custom Layout", + "mainView": "Main View", + "liveLabel": "Live Label", + "cameraDeviceList": "Camera Device List", + "searchCameraPlaceholder": "Search Camera Placeholder", + "allStatuses": "All Statuses", + "eventTimeline": "Event Timeline", + "allTypes": "All Types", + "viewMore": "View More", + "recentAlertSnapshots": "Recent Alert Snapshots" + }, + "login": { + "and": "And" + }, + "profile": { + "requestFailed": "Request Failed", + "basicInfo": "Basic Info", + "phoneInvalid": "Phone Invalid", + "emailInvalid": "Email Invalid", + "emailPlaceholder": "Email Placeholder", + "remarkPlaceholder": "Remark Placeholder", + "changePassword": "Change Password", + "oldPasswordPlaceholder": "Old Password Placeholder", + "newPasswordPlaceholder": "New Password Placeholder", + "passwordLengthError": "Password Length Error", + "confirmPasswordRequired": "Confirm Password Required", + "confirmPasswordPlaceholder": "Confirm Password Placeholder" + }, + "home": { + "realtimePower": "Realtime Power", + "totalPower": "Total Power", + "ratedPower": "Rated Power", + "gridConnection": "Grid Connection", + "gridConnected": "Grid Connected", + "frequency": "Frequency", + "gridVoltage": "Grid Voltage", + "phaseA": "Phase A", + "todayAccumulated": "Today Accumulated", + "currentIrradiance": "Current Irradiance", + "ambientTemperature": "Ambient Temperature", + "powerCurve": "Power Curve", + "today": "Today", + "inverterPowerCompare": "Inverter Power Compare", + "stringCurrentHeatmap": "String Current Heatmap", + "current": "Current" + }, + "router": { + "realtimeMonitorCenter": "Realtime Monitor Center", + "realtimeMonitorDesc": "Realtime Monitor Desc" + }, + "report": { + "monthlyGeneration": "Monthly Generation", + "monthOverMonthUp": "Month Over Month Up", + "prAverage": "pr Average", + "workOrderCompletionRate": "Work Order Completion Rate", + "monthlyTotalOrders": "Monthly Total Orders", + "alertClosureRate": "Alert Closure Rate", + "criticalAlertCount": "Critical Alert Count", + "cleaningRevenueImprovement": "Cleaning Revenue Improvement", + "estimatedAdditionalGeneration": "Estimated Additional Generation", + "autoGeneratedReports": "Auto Generated Reports", + "unitReport": "Unit Report", + "monthlyAccumulated": "Monthly Accumulated", + "stationMonthlyReportFile": "Station Monthly Report File", + "generated": "Generated", + "weeklyOperationSummary": "Weekly Operation Summary", + "aiDiagnosisDaily": "ai Diagnosis Daily", + "generating": "Generating", + "alertClosureWeekly": "Alert Closure Weekly", + "monthlyOperationReport": "Monthly Operation Report", + "monthlyOperationReportDesc": "Monthly Operation Report Desc", + "deviceHealthReport": "Device Health Report", + "deviceHealthReportDesc": "Device Health Report Desc", + "cleaningOptimizationReport": "Cleaning Optimization Report", + "cleaningEffectReportDesc": "Cleaning Effect Report Desc", + "inspectionRobotReport": "Inspection Robot Report", + "inspectionRobotReportDesc": "Inspection Robot Report Desc", + "stationMonthly": "Station Monthly", + "alertWeekly": "Alert Weekly", + "systemAuto": "System Auto", + "aiAnalysisReport": "ai Analysis Report", + "operationSummary": "Operation Summary", + "operationReport": "Operation Report", + "cleaningEffectReport": "Cleaning Effect Report", + "overview": "Overview", + "reportCycle": "Report Cycle", + "reportType": "Report Type", + "exportPdf": "Export Pdf", + "exportExcel": "Export Excel", + "monthlyOperationAnalysis": "Monthly Operation Analysis", + "generation": "Generation", + "predictedGeneration": "Predicted Generation", + "monthlyAccumulatedGeneration": "Monthly Accumulated Generation", + "theoreticalGeneration": "Theoretical Generation", + "generationDeviation": "Generation Deviation", + "averageIrradiance": "Average Irradiance", + "unitIrradiancePerDay": "Unit Irradiance Per Day", + "generationMeteorology": "Generation Meteorology", + "workOrderAlertClosure": "Work Order Alert Closure", + "closureRate": "Closure Rate", + "alarmCount": "Alarm Count", + "aiAnalysisStatistics": "ai Analysis Statistics", + "unitItem": "Unit Item", + "subscriptionDispatch": "Subscription Dispatch", + "subscribers": "Subscribers", + "roleOmSupervisor": "Role Om Supervisor", + "roleStationEngineer": "Role Station Engineer", + "roleDeviceOm": "Role Device Om", + "roleOmManager": "Role Om Manager", + "dispatchChannels": "Dispatch Channels", + "email": "Email", + "systemNotification": "System Notification", + "sendPlan": "Send Plan", + "weeklyAutoSend": "Weekly Auto Send", + "reportDirectory": "Report Directory", + "reportName": "Report Name", + "templateCenter": "Template Center", + "generationRecords": "Generation Records", + "generateTime": "Generate Time", + "generator": "Generator" + }, + "roles": { + "editSuccess": "Edit Success", + "addSuccess": "Add Success", + "manageMembers": "Manage Members", + "confirmDeleteRole": "Confirm Delete Role", + "permissionKey": "Permission Key", + "superAdmin": "Super Admin", + "orgAdmin": "Org Admin", + "editRole": "Edit Role", + "roleNamePlaceholder": "Role Name Placeholder", + "rolePermission": "Role Permission", + "roleKeyPlaceholder": "Role Key Placeholder", + "roleSortLabel": "Role Sort Label", + "menuPermissionConfig": "Menu Permission Config" + }, + "users": { + "notAssigned": "Not Assigned", + "languagePlaceholder": "Language Placeholder", + "rolePlaceholder": "Role Placeholder", + "siteId": "Site Id" + } + } +} \ No newline at end of file diff --git a/tmp_newkeys.json b/tmp_newkeys.json new file mode 100644 index 0000000..8ed703e --- /dev/null +++ b/tmp_newkeys.json @@ -0,0 +1,118 @@ +{ + "zh": { + "devices": { + "uavUavSn": "未选中无人机或无人机SN不存在", + "fixed2SuccessFixed2": "指令下发成功", + "fixed2AbnormalFixed2": "接口请求异常", + "waylineTasksFixed2": "请先选择航线任务", + "fixed2ActionsFixed2Fixed2AbnormalFixed2": "操作接口异常", + "noDetailFixed2": "暂无详情", + "detailFixed2Fixed2AbnormalFixed2": "获取详情接口异常", + "fixed2DeviceFixed2InfoFixed2AbnormalFixed2": "设备信息异常", + "loginFixed2": "请先登录", + "fixed2LoginFixed2InfoLoginFixed2": "登录信息失效,请重新登录", + "fixed2OthersWebControlFixed2": "其他web端正在控制", + "abnormalRequestFailed": "网络异常或请求失败,请重试", + "noWaylineDataFixed2": "暂无航线数据", + "deleteFixed2Waypoint": "已删除航点并同步地图", + "waypoint2": "已清空所有航点与地图轨迹", + "selectTaskStartTimeFixed2": "请选择任务开始时间", + "fixed2Fixed2Abnormal": "接口异常,请检查", + "fixed2NoWaypointsSaveFixed2": "无航点可保存", + "fixed2UavSaveFixed2Local": "无人机不支持保存本地航线", + "addNewFixed2SaveLocalFixed2": "✅ 航线已新增保存到本地", + "taskSuspended2Fixed23": "请先下发任务", + "fixed2OthersControlFixed2": "其他端正在控制", + "failedFixed22": "权限校验失败", + "noneControl": "无控制权限", + "fixed2ControlFixed2": "控制请求超时" + }, + "systemSetting": { + "fixed2MenuFixed2LoadFailedFixed2": "菜单加载失败", + "s26": "请先生成", + "notFixed2Search": "未查询到数据", + "fixed2SearchFixed2FailedFixed2": "查询失败", + "fixed2PleaseSelectDeleteRoleFixed2": "请选择要删除的角色", + "fixed2PleaseSelectDelete2": "请选择要删除的用户" + }, + "home": { + "cleaningOrderGeneratedSuccessfullyFixed2": "✅ 清洗工单生成成功", + "failedFixed2": "❌ 工单生成接口调用失败" + }, + "workOrder": { + "fixed2DispatchOrderFixed2SuccessFixed2": "工单派单成功", + "fixed2DispatchAbnormalFixed2": "派单请求异常", + "listFixed2Fixed2FailedFixed2": "获取工单列表失败", + "s3": "请先选择工单", + "completedFixed2": "工单已完成", + "completeFailFixed2": "工单完成失败", + "detailFixed2Fixed2FailedFixed2": "获取工单详情失败", + "s7": "请先选择一条工单", + "fixed2ModifyOrderFixed2SuccessFixed2": "修改工单成功", + "fixed2ModifyOrderFixed2FailedFixed2": "修改工单失败", + "executeSuccessFixed2": "工单执行成功", + "executeFailFixed2": "工单执行失败", + "suspendedFixed2": "工单已挂起", + "fixed2SuspendFixed2FailedFixed2": "挂起失败", + "fixed2PleaseSelectDelete": "请选择要删除的工单" + } + }, + "en": { + "devices": { + "uavUavSn": "未选中 UAV 或 UAV SN不存在", + "fixed2SuccessFixed2": "Fixed2 指令 下发 Success Fixed2", + "fixed2AbnormalFixed2": "Fixed2 接口 请求 Abnormal Fixed2", + "waylineTasksFixed2": "请先选择 Wayline Tasks Fixed2", + "fixed2ActionsFixed2Fixed2AbnormalFixed2": "Fixed2 Actions Fixed2 接口 Fixed2 Abnormal Fixed2", + "noDetailFixed2": "暂 no Detail Fixed2", + "detailFixed2Fixed2AbnormalFixed2": "获取 Detail Fixed2 接口 Fixed2 Abnormal Fixed2", + "fixed2DeviceFixed2InfoFixed2AbnormalFixed2": "Fixed2 Device Fixed2 Info Fixed2 Abnormal Fixed2", + "loginFixed2": "请先 Login Fixed2", + "fixed2LoginFixed2InfoLoginFixed2": "Fixed2 Login Fixed2 Info 失效,请重新 Login Fixed2", + "fixed2OthersWebControlFixed2": "Fixed2 Others web端正在 Control Fixed2", + "abnormalRequestFailed": "网络 Abnormal 或 Request Failed ,请重试", + "noWaylineDataFixed2": "暂 no Wayline Data Fixed2", + "deleteFixed2Waypoint": "已 Delete Fixed2 Waypoint 并同步地图", + "waypoint2": "已清空所有 Waypoint 与地图轨迹", + "selectTaskStartTimeFixed2": "请 Select Task Start Time Fixed2", + "fixed2Fixed2Abnormal": "Fixed2 接口 Fixed2 Abnormal ,请检查", + "fixed2NoWaypointsSaveFixed2": "Fixed2 no Waypoints 可 Save Fixed2", + "fixed2UavSaveFixed2Local": "Fixed2 UAV 不支持 Save Fixed2 Local 航线", + "addNewFixed2SaveLocalFixed2": "✅ 航线已 Add New Fixed2 Save 到 Local Fixed2", + "taskSuspended2Fixed23": "请先下发 Task Suspended2 Fixed2", + "fixed2OthersControlFixed2": "Fixed2 Others 端正在 Control Fixed2", + "failedFixed22": "权限校验 Failed Fixed2", + "noneControl": "None Control 权限", + "fixed2ControlFixed2": "Fixed2 Control 请求 超时 Fixed2" + }, + "systemSetting": { + "fixed2MenuFixed2LoadFailedFixed2": "Fixed2 Menu Fixed2 Load Failed Fixed2", + "s26": "请先生成", + "notFixed2Search": "Not Fixed2 Search 到数据", + "fixed2SearchFixed2FailedFixed2": "Fixed2 Search Fixed2 Failed Fixed2", + "fixed2PleaseSelectDeleteRoleFixed2": "Fixed2 Please select 要 Delete 的 Role Fixed2", + "fixed2PleaseSelectDelete2": "Fixed2 Please select 要 Delete 的用户" + }, + "home": { + "cleaningOrderGeneratedSuccessfullyFixed2": "✅ Cleaning order generated successfully Fixed2", + "failedFixed2": "❌ 工单生成 接口 调用 Failed Fixed2" + }, + "workOrder": { + "fixed2DispatchOrderFixed2SuccessFixed2": "Fixed2 Dispatch Order Fixed2 Success Fixed2", + "fixed2DispatchAbnormalFixed2": "Fixed2 Dispatch 请求 Abnormal Fixed2", + "listFixed2Fixed2FailedFixed2": "获取 工单 List Fixed2 Fixed2 Failed Fixed2", + "s3": "请先选择工单", + "completedFixed2": "工单 Completed Fixed2", + "completeFailFixed2": "工单 Complete Fail Fixed2", + "detailFixed2Fixed2FailedFixed2": "获取 工单 Detail Fixed2 Fixed2 Failed Fixed2", + "s7": "请先选择一条工单", + "fixed2ModifyOrderFixed2SuccessFixed2": "Fixed2 Modify Order Fixed2 Success Fixed2", + "fixed2ModifyOrderFixed2FailedFixed2": "Fixed2 Modify Order Fixed2 Failed Fixed2", + "executeSuccessFixed2": "工单 Execute Success Fixed2", + "executeFailFixed2": "工单 Execute Fail Fixed2", + "suspendedFixed2": "工单 Suspended Fixed2", + "fixed2SuspendFixed2FailedFixed2": "Fixed2 Suspend Fixed2 Failed Fixed2", + "fixed2PleaseSelectDelete": "Fixed2 Please select 要 Delete 的工单" + } + } +} \ No newline at end of file diff --git a/tmp_orb.cjs b/tmp_orb.cjs new file mode 100644 index 0000000..d9fbb64 --- /dev/null +++ b/tmp_orb.cjs @@ -0,0 +1,28 @@ +const esbuild = require('esbuild'); +const fs = require('fs'); +const path = require('path'); +(async () => { + const files = [ + 'components/SystemSetting/BasicSettings.tsx', + 'components/SystemSetting/SerialNumGenerate.jsx', + 'components/SystemSetting/DroneManagement.tsx', + 'components/SystemSetting/SmartDeviceManagement.tsx', + 'components/SystemSetting/AlarmSettingsManagement.tsx', + 'components/SystemSetting/LogManagement.tsx', + 'components/SystemSetting/OnlineUser.tsx', + 'components/SystemSetting/VideoManagement.tsx', + 'components/SystemSetting/WorkOrderSettingsManagement.tsx' + ]; + for (const f of files) { + const s = fs.readFileSync('src/' + f, 'utf8'); + const ext = path.extname(f); + const loader = ext === '.tsx' ? 'tsx' : ext === '.ts' ? 'ts' : ext === '.jsx' ? 'jsx' : 'js'; + try { + await esbuild.transform(s, { loader, target: 'es2020' }); + console.log('ORIG OK ' + f); + } catch (e) { + console.log('ORIG FAIL ' + f + ' :: ' + String(e.message).split('\n').slice(0, 4).join(' | ')); + } + } + console.log('DONE'); +})(); diff --git a/tmp_pagefiles.txt b/tmp_pagefiles.txt new file mode 100644 index 0000000..b0a75be --- /dev/null +++ b/tmp_pagefiles.txt @@ -0,0 +1,33 @@ +src/components/devices/DeviceControl.tsx +src/components/devices/deviceController.js +src/components/devices/DeviceOverviewPage.tsx +src/components/devices/RobotTaskPage.tsx +src/components/LanguageSwitcher.tsx +src/components/SystemSetting/AlarmSettingsManagement.tsx +src/components/SystemSetting/AllDeviceManagement.tsx +src/components/SystemSetting/BasicSettings.tsx +src/components/SystemSetting/CameraManagement.tsx +src/components/SystemSetting/DeviceAccessPage.tsx +src/components/SystemSetting/DeviceManagement.tsx +src/components/SystemSetting/DroneManagement.tsx +src/components/SystemSetting/FlvPlayer.tsx +src/components/SystemSetting/LogManagement.tsx +src/components/SystemSetting/MenuList.tsx +src/components/SystemSetting/OnlineUser.tsx +src/components/SystemSetting/Organization.tsx +src/components/SystemSetting/SerialNumGenerate.jsx +src/components/SystemSetting/SmartDeviceManagement.tsx +src/components/SystemSetting/StationManage.tsx +src/components/SystemSetting/SystemMaintain.tsx +src/components/SystemSetting/UserPage.tsx +src/components/SystemSetting/VideoManagement.tsx +src/components/SystemSetting/WorkOrderSettingsManagement.tsx +src/components/VideoPlayer.tsx +src/pages/Agri.jsx +src/pages/AIAnalysis.tsx +src/pages/CleanOptimization.tsx +src/pages/Home.tsx +src/pages/RealTimeMonitor.tsx +src/pages/SystemSetting.tsx +src/pages/VideoMonitorPage.tsx +src/pages/WorkOrder.tsx diff --git a/tmp_perfile.cjs b/tmp_perfile.cjs new file mode 100644 index 0000000..f7f4dad --- /dev/null +++ b/tmp_perfile.cjs @@ -0,0 +1,43 @@ +const fs = require('fs'); +const path = require('path'); +const root = 'src'; +const exts = ['.tsx', '.ts', '.jsx', '.js']; +const excludeDirs = ['locales', 'node_modules', 'i18n']; +const CJK = /[一-鿿]/; +function walk(dir) { + const out = []; + for (const e of fs.readdirSync(dir, { withFileTypes: true })) { + const full = path.join(dir, e.name); + if (e.isDirectory()) { if (!excludeDirs.includes(e.name)) out.push(...walk(full)); } + else if (exts.includes(path.extname(e.name))) out.push(full); + } + return out; +} +const files = walk(root); +const result = []; +for (const f of files) { + const lines = fs.readFileSync(f, 'utf8').split('\n'); + let userFacing = 0; + let totalCJK = 0; + for (const line of lines) { + const trimmed = line.trim(); + // skip pure comments + if (trimmed.startsWith('//') || trimmed.startsWith('*') || trimmed.startsWith('/*')) continue; + // skip console.* lines + if (/console\./.test(line)) continue; + // skip lines that are only a comment after code (heuristic: contains // and no JSX/t() before) + if (CJK.test(line)) { + totalCJK++; + // user-facing heuristics: JSX text, string props, antd message, Option + const isCommentOnly = /\/\/.*[一-鿿]/.test(line) && !/(>|placeholder|title|label|content|message\.|notification\.|Modal\.|Tooltip|text=|children)/.test(line); + if (!isCommentOnly) userFacing++; + } + } + if (totalCJK > 0) result.push({ f: f.replace(root + '\\', ''), totalCJK, userFacing }); +} +result.sort((a, b) => b.userFacing - a.userFacing); +console.log('Files with CJK:', result.length); +console.log('Total CJK lines (all):', result.reduce((s, r) => s + r.totalCJK, 0)); +console.log('Estimated user-facing CJK lines:', result.reduce((s, r) => s + r.userFacing, 0)); +console.log('\nPer-file (user-facing est):'); +for (const r of result) console.log(String(r.userFacing).padStart(5), '\t', r.f); diff --git a/tmp_replace.cjs b/tmp_replace.cjs new file mode 100644 index 0000000..8e5cf68 --- /dev/null +++ b/tmp_replace.cjs @@ -0,0 +1,148 @@ +// Conservative hardcoded-Chinese -> t() replacement with esbuild syntax gate. +// Scope: files already using t(); only unambiguous UI contexts; braces/parens excluded; +// files that fail esbuild transform are NOT written. +const fs = require('fs'); +const path = require('path'); +const esbuild = require('esbuild'); + +const root = 'src'; +const exts = ['.tsx', '.ts', '.jsx', '.js']; +const excludeDirs = ['locales', 'node_modules', 'i18n']; +function walk(dir) { + const out = []; + for (const e of fs.readdirSync(dir, { withFileTypes: true })) { + const full = path.join(dir, e.name); + if (e.isDirectory()) { if (!excludeDirs.includes(e.name)) out.push(...walk(full)); } + else if (exts.includes(path.extname(e.name))) out.push(full); + } + return out; +} +function load(p) { + let s = fs.readFileSync(p, 'utf8'); + s = s.replace(/const\s+\w+\s*=\s*\{/, 'var obj = {'); + s = s.replace(/export\s+default\s+\w+;?/, ''); + s = s.replace(/\bconst\b/g, 'var'); + return new Function(s + '\nreturn obj;')(); +} +function flat(o, p, r) { p = p || ''; r = r || {}; for (const k of Object.keys(o)) { const key = p ? p + '.' + k : k; if (o[k] && typeof o[k] === 'object' && !Array.isArray(o[k])) flat(o[k], key, r); else r[key] = o[k]; } return r; } + +const zhMap = flat(load('src/locales/zh/index.ts')); +const enMap = flat(load('src/locales/en/index.ts')); +const valToKey = {}; const valToEn = {}; +for (const k of Object.keys(zhMap)) { if (typeof zhMap[k] === 'string') { if (!(zhMap[k] in valToKey)) valToKey[zhMap[k]] = k; if (typeof enMap[k] === 'string') valToEn[zhMap[k]] = enMap[k]; } } +const allKeys = new Set([...Object.keys(zhMap), ...Object.keys(enMap)]); +const phraseKeys = Object.keys(valToEn).sort((a, b) => b.length - a.length); +function translateZh(zh) { + if (valToEn[zh]) return valToEn[zh]; + let s = zh; + for (const k of phraseKeys) { if (k.length >= 2) s = s.split(k).join('\u0001' + valToEn[k] + '\u0001'); } + return s.split('\u0001').map(seg => valToEn[seg] || seg).join(' ').replace(/\s+/g, ' ').trim() || zh; +} +function nsForFile(f) { + const rel = f.replace(root + '\\', '').replace(/\\/g, '/').toLowerCase(); + if (rel.includes('systemsetting')) return 'systemSetting'; + if (rel.includes('devices/') || rel.includes('device')) return 'devices'; + if (rel.includes('alerts')) return 'alerts'; + if (rel.includes('aianalysis') || rel.includes('aidiagnos')) return 'aiAnalysis'; + if (rel.includes('clean')) return 'clean'; + if (rel.includes('report')) return 'report'; + if (rel.includes('workorder')) return 'workOrder'; + if (rel.includes('login')) return 'login'; + if (rel.includes('profile')) return 'profile'; + if (rel.includes('users')) return 'users'; + if (rel.includes('roles')) return 'roles'; + if (rel.includes('home')) return 'home'; + if (rel.includes('video')) return 'video'; + if (rel.includes('router') || rel.includes('layout')) return 'layout'; + return 'page'; +} +function camel(s) { + s = s.replace(/[^A-Za-z0-9]+/g, ' ').trim().toLowerCase(); + const parts = s.split(' ').filter(Boolean); + if (!parts.length) return ''; + return parts.map((p, i) => i === 0 ? p : p.charAt(0).toUpperCase() + p.slice(1)).join(''); +} + +const SKIP_ATTR = new Set(['key', 'id', 'path', 'href', 'src', 'to', 'type', 'name', 'value', 'className', 'data', 'mode', 'status', 'field', 'prop', 'ref']); +const UI_PROP = new Set(['title', 'content', 'okText', 'cancelText', 'message', 'description', 'label', 'placeholder', 'text', 'tooltip', 'subTitle', 'subtitle']); + +const rxJSX = />([^<>`{}()]*[一-鿿][^<>`{}()]*) { const k = keyFor(inner); if (!k) return m; rep++; return '>{t(\'' + k + '\')}<'; }); + s = s.replace(rxAttr, (m, attr, q, val) => { + if (SKIP_ATTR.has(attr)) return m; + if (val.includes('${')) return m; + const k = keyFor(val); if (!k) return m; rep++; return attr + '={t(\'' + k + '\')}'; + }); + s = s.replace(rxMsg, (m, fn, q, val) => { if (fn.startsWith('console.')) return m; const k = keyFor(val); if (!k) return m; rep++; return fn + "(t('" + k + "'))"; }); + s = s.replace(rxProp, (m, pre, prop, q, val) => { if (!UI_PROP.has(prop)) return m; const k = keyFor(val); if (!k) return m; rep++; return pre + prop + ": t('" + k + "')"; }); + + if (rep > 0) { + const ob = (s.match(/[({]/g) || []).length, cb = (s.match(/[)}]/g) || []).length; + const o0 = (src0.match(/[({]/g) || []).length, c0 = (src0.match(/[)}]/g) || []).length; + if (ob !== cb) balanceWarns.push(f.replace(root + '\\', '') + ' braces ' + ob + '/' + cb + ' (orig ' + o0 + '/' + c0 + ')'); + pending.push({ f, s, rep }); + } +} + +(async () => { + const modified = []; + let totalRep = 0; + for (const p of pending) { + let ok = false; let errMsg = ''; + try { await esbuild.transform(p.s, { loader: loaderFor(p.f), target: 'es2020' }); ok = true; } + catch (err) { errMsg = String(err.message); } + if (ok) { + if (!process.env.DRY) fs.writeFileSync(p.f, p.s, 'utf8'); + modified.push(p.f); + totalRep += p.rep; + } else { + console.log(' --- detail for ' + p.f.replace(root + '\\', '') + ' ---'); + console.log(errMsg); + } + } + console.log('Files written/verified:', modified.length); + console.log('Replacements made:', totalRep); + console.log('Balance warnings (informational):', balanceWarns.length); + balanceWarns.forEach(w => console.log(' [WARN] ' + w)); + console.log('New locale keys (zh):', Object.values(newZh).reduce((a, o) => a + Object.keys(o).length, 0)); + fs.writeFileSync('tmp_newkeys.json', JSON.stringify({ zh: newZh, en: newEn }, null, 2)); + console.log('Modified files:'); + modified.forEach(f => console.log(' ' + f.replace(root + '\\', ''))); +})(); diff --git a/tmp_scan_cjk.cjs b/tmp_scan_cjk.cjs new file mode 100644 index 0000000..4db4b5f --- /dev/null +++ b/tmp_scan_cjk.cjs @@ -0,0 +1,45 @@ +const fs = require('fs'); +const path = require('path'); + +const dir = 'src'; +const exts = ['.tsx', '.ts', '.jsx', '.js']; + +function walk(d) { + let r = []; + for (const f of fs.readdirSync(d)) { + const p = path.join(d, f); + if (fs.statSync(p).isDirectory()) { + if (p.includes('locales')) continue; + r = r.concat(walk(p)); + } else if (exts.some((e) => f.endsWith(e))) { + r.push(p); + } + } + return r; +} + +// strip comments (line + block) and template strings, then find remaining Chinese +function stripCommentsAndStrings(code) { + // Remove block comments + code = code.replace(/\/\*[\s\S]*?\*\//g, ''); + // Remove line comments (careful with URLs, but ok) + code = code.replace(/^\s*\/\/.*$/gm, ''); + return code; +} + +let total = 0; +for (const f of walk(dir)) { + const code = fs.readFileSync(f, 'utf8'); + const cleaned = stripCommentsAndStrings(code); + const lines = cleaned.split('\n'); + lines.forEach((line, i) => { + if (/[\u4e00-\u9fa5]/.test(line)) { + total++; + const trimmed = line.trim(); + if (trimmed.length > 0 && !trimmed.startsWith('//')) { + console.log(f + ':' + (i + 1) + ': ' + line.trim().slice(0, 120)); + } + } + }); +} +console.log('TOTAL non-comment lines with Chinese:', total); diff --git a/tmp_scanfalse.cjs b/tmp_scanfalse.cjs new file mode 100644 index 0000000..a5927bb --- /dev/null +++ b/tmp_scanfalse.cjs @@ -0,0 +1,16 @@ +// Detect possible HTML-string false matches: a JS string literal that now +// contains an injected {t(...)} (e.g. '{t('x')}'). +const fs = require('fs'); +const files = [ +"src/components/devices/DeviceControl.tsx","src/components/devices/deviceController.js","src/components/devices/DeviceOverviewPage.tsx","src/components/devices/RobotTaskPage.tsx","src/components/LanguageSwitcher.tsx","src/components/SystemSetting/AlarmSettingsManagement.tsx","src/components/SystemSetting/AllDeviceManagement.tsx","src/components/SystemSetting/BasicSettings.tsx","src/components/SystemSetting/CameraManagement.tsx","src/components/SystemSetting/DeviceAccessPage.tsx","src/components/SystemSetting/DeviceManagement.tsx","src/components/SystemSetting/DroneManagement.tsx","src/components/SystemSetting/FlvPlayer.tsx","src/components/SystemSetting/LogManagement.tsx","src/components/SystemSetting/MenuList.tsx","src/components/SystemSetting/OnlineUser.tsx","src/components/SystemSetting/Organization.tsx","src/components/SystemSetting/SerialNumGenerate.jsx","src/components/SystemSetting/SmartDeviceManagement.tsx","src/components/SystemSetting/StationManage.tsx","src/components/SystemSetting/SystemMaintain.tsx","src/components/SystemSetting/UserPage.tsx","src/components/SystemSetting/VideoManagement.tsx","src/components/SystemSetting/WorkOrderSettingsManagement.tsx","src/components/VideoPlayer.tsx","src/pages/Agri.jsx","src/pages/AIAnalysis.tsx","src/pages/CleanOptimization.tsx","src/pages/Home.tsx","src/pages/RealTimeMonitor.tsx","src/pages/SystemSetting.tsx","src/pages/VideoMonitorPage.tsx","src/pages/WorkOrder.tsx" +]; +const rx = /(['"`])<[^'"`]*\{t\(/g; +let total = 0; +for (const f of files) { + const lines = fs.readFileSync(f, 'utf8').split('\n'); + lines.forEach((line, idx) => { + rx.lastIndex = 0; let m; + while ((m = rx.exec(line))) { console.log(f.replace('src\\', '') + ' L' + (idx + 1) + ': ' + line.trim()); total++; } + }); +} +console.log('Potential HTML-string false matches:', total); diff --git a/tmp_test.cjs b/tmp_test.cjs new file mode 100644 index 0000000..15cff7a --- /dev/null +++ b/tmp_test.cjs @@ -0,0 +1,27 @@ +const fs = require('fs'); +function load(p) { + let s = fs.readFileSync(p, 'utf8'); + s = s.replace(/const\s+\w+\s*=\s*\{/, 'var obj = {'); + s = s.replace(/export\s+default\s+\w+;?/, ''); + s = s.replace(/\bconst\b/g, 'var'); + return new Function(s + '\nreturn obj;')(); +} +function flat(o, p, r) { + p = p || ''; + r = r || {}; + for (const k of Object.keys(o)) { + const key = p ? p + '.' + k : k; + if (o[k] && typeof o[k] === 'object' && !Array.isArray(o[k])) flat(o[k], key, r); + else r[key] = o[k]; + } + return r; +} +try { + const z = flat(load('src/locales/zh/index.ts')); + const e = flat(load('src/locales/en/index.ts')); + console.log('ZH keys', Object.keys(z).length, 'EN keys', Object.keys(e).length); + console.log('aiAnalysis.abnormalString in ZH?', 'aiAnalysis.abnormalString' in z); + console.log('workOrder.pleaseInputTitle in ZH?', 'workOrder.pleaseInputTitle' in z); +} catch (err) { + console.log('ERROR:', err.message); +} diff --git a/tmp_tokens.cjs b/tmp_tokens.cjs new file mode 100644 index 0000000..ad39098 --- /dev/null +++ b/tmp_tokens.cjs @@ -0,0 +1,12 @@ +const a = require('./tmp_missing.json'); +const z = a.zhAdd; +const tokens = new Set(); +for (const ns of Object.keys(z)) { + for (const leaf of Object.keys(z[ns])) { + const words = leaf.replace(/([a-z])([A-Z])/g, '$1 $2').replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2').split(/[\s._]+/).filter(Boolean); + for (const w of words) tokens.add(w.toLowerCase()); + } +} +const arr = [...tokens].sort(); +console.log('Token count:', arr.length); +console.log(arr.join(' ')); diff --git a/tmp_verify.cjs b/tmp_verify.cjs new file mode 100644 index 0000000..88df5ec --- /dev/null +++ b/tmp_verify.cjs @@ -0,0 +1,8 @@ +const fs = require('fs'); +function load(p){let s=fs.readFileSync(p,'utf8');s=s.replace(/const\s+\w+\s*=\s*\{/,'var obj = {');s=s.replace(/export\s+default\s+\w+;?/,'');s=s.replace(/\bconst\b/g,'var');return new Function(s+'\nreturn obj;')();} +function flat(o,p,r){p=p||'';r=r||{};for(const k of Object.keys(o)){const key=p?p+'.'+k:k;if(o[k]&&typeof o[k]==='object'&&!Array.isArray(o[k]))flat(o[k],key,r);else r[key]=o[k];}return r;} +const z=flat(load('src/locales/zh/index.ts'));const e=flat(load('src/locales/en/index.ts')); +console.log('ZH',Object.keys(z).length,'EN',Object.keys(e).length); +console.log('common.completed in EN?', 'common.completed' in e); +console.log('home.underMaintenance in EN?', 'home.underMaintenance' in e); +console.log('roles.abnormal in EN?', 'roles.abnormal' in e); diff --git a/tmp_verify2.cjs b/tmp_verify2.cjs new file mode 100644 index 0000000..254d54c --- /dev/null +++ b/tmp_verify2.cjs @@ -0,0 +1,44 @@ +const fs = require('fs'); +const path = require('path'); +const root = 'src'; +const exts = ['.tsx', '.ts', '.jsx', '.js']; +const excludeDirs = ['locales', 'node_modules', 'i18n']; +function walk(dir) { + const out = []; + for (const e of fs.readdirSync(dir, { withFileTypes: true })) { + const full = path.join(dir, e.name); + if (e.isDirectory()) { if (!excludeDirs.includes(e.name)) out.push(...walk(full)); } + else if (exts.includes(path.extname(e.name))) out.push(full); + } + return out; +} +function load(p) { + let s = fs.readFileSync(p, 'utf8'); + s = s.replace(/const\s+\w+\s*=\s*\{/, 'var obj = {'); + s = s.replace(/export\s+default\s+\w+;?/, ''); + s = s.replace(/\bconst\b/g, 'var'); + return new Function(s + '\nreturn obj;')(); +} +function flat(o, p, r) { p = p || ''; r = r || {}; for (const k of Object.keys(o)) { const key = p ? p + '.' + k : k; if (o[k] && typeof o[k] === 'object' && !Array.isArray(o[k])) flat(o[k], key, r); else r[key] = o[k]; } return r; } + +const zhMap = flat(load('src/locales/zh/index.ts')); +const enMap = flat(load('src/locales/en/index.ts')); + +const refRegex = /(?:i18n\.t|[^a-zA-Z]t)\(\s*['"`]([a-zA-Z][a-zA-Z0-9_.]*?)['"`]/g; +const files = walk(root); +const refKeys = new Set(); +for (const f of files) { + const c = fs.readFileSync(f, 'utf8'); + let m; while ((m = refRegex.exec(c))) refKeys.add(m[1]); +} +let missZh = [], missEn = [], missBoth = []; +for (const k of refKeys) { + if (!(k in zhMap)) missZh.push(k); + if (!(k in enMap)) missEn.push(k); + if (!(k in zhMap) && !(k in enMap)) missBoth.push(k); +} +console.log('Referenced unique keys:', refKeys.size); +console.log('Missing in ZH:', missZh.length); +console.log('Missing in EN:', missEn.length); +console.log('Missing in BOTH:', missBoth.length); +if (missBoth.length) console.log('SAMPLE missing-both:', missBoth.slice(0, 20).join(', ')); diff --git a/tmp_verify3.cjs b/tmp_verify3.cjs new file mode 100644 index 0000000..40398bf --- /dev/null +++ b/tmp_verify3.cjs @@ -0,0 +1,49 @@ +// Final check: every t('key') / i18n.t('key') reference in src must exist in locale. +const fs = require('fs'); +const path = require('path'); +function load(p) { + let s = fs.readFileSync(p, 'utf8'); + s = s.replace(/const\s+\w+\s*=\s*\{/, 'var obj = {'); + s = s.replace(/export\s+default\s+\w+;?/, ''); + s = s.replace(/\bconst\b/g, 'var'); + return new Function(s + '\nreturn obj;')(); +} +function flat(o, p, r) { p = p || ''; r = r || {}; for (const k of Object.keys(o)) { const key = p ? p + '.' + k : k; if (o[k] && typeof o[k] === 'object' && !Array.isArray(o[k])) flat(o[k], key, r); else r[key] = o[k]; } return r; } +const zh = flat(load('src/locales/zh/index.ts')); +const en = flat(load('src/locales/en/index.ts')); +const have = new Set([...Object.keys(zh), ...Object.keys(en)]); + +const root = 'src'; +const exts = ['.tsx', '.ts', '.jsx', '.js']; +function walk(dir) { + const out = []; + for (const e of fs.readdirSync(dir, { withFileTypes: true })) { + const full = path.join(dir, e.name); + if (e.isDirectory()) { if (!['locales', 'node_modules'].includes(e.name)) out.push(...walk(full)); } + else if (exts.includes(path.extname(e.name))) out.push(full); + } + return out; +} +const rx = /[^A-Za-z]t\(\s*['"`]([^'"`]+)['"`]\s*\)/g; +const missing = {}; // key -> [files] +let total = 0; +for (const f of walk(root)) { + const s = fs.readFileSync(f, 'utf8'); + let m; rx.lastIndex = 0; + while ((m = rx.exec(s))) { + const k = m[1]; + if (k.startsWith('_')) continue; // i18n internal + total++; + if (!have.has(k)) (missing[k] = missing[k] || []).push(f.replace(root + '\\', '')); + } +} +const keys = Object.keys(missing); +console.log('Total t() references scanned:', total); +console.log('Distinct missing keys:', keys.length); +if (keys.length) { + for (const k of keys.slice(0, 60)) console.log(' MISSING ' + k + ' <- ' + [...new Set(missing[k])].join(', ')); +} +// also report keys whose zh value is itself a raw key-like (english) — quality check +let rawLike = 0; +for (const k of Object.keys(zh)) { const v = zh[k]; if (typeof v === 'string' && /^[a-z][a-zA-Z0-9]*\.[a-zA-Z0-9.]+$/.test(v)) rawLike++; } +console.log('zh entries whose value looks like a key (possible untranslated):', rawLike); diff --git a/tmp_verify_i18n.cjs b/tmp_verify_i18n.cjs new file mode 100644 index 0000000..14ad391 --- /dev/null +++ b/tmp_verify_i18n.cjs @@ -0,0 +1,74 @@ +const fs = require('fs'); +const path = require('path'); +const ts = require('typescript'); + +function transpile(file) { + const code = fs.readFileSync(file, 'utf8'); + const out = ts.transpileModule(code, { + compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2019 }, + }); + return out.outputText; +} + +function load(file) { + const out = transpile(file); + const module = { exports: {} }; + const fn = new Function('module', 'exports', out); + fn(module, module.exports); + return module.exports.default || module.exports; +} + +function flatten(obj, prefix = '') { + const keys = new Set(); + for (const k of Object.keys(obj)) { + const v = obj[k]; + const p = prefix ? prefix + '.' + k : k; + if (v && typeof v === 'object' && !Array.isArray(v)) { + for (const sk of flatten(v, p)) keys.add(sk); + } else { + keys.add(p); + } + } + return keys; +} + +const zhKeys = flatten(load('src/locales/zh/index.ts')); +const enKeys = flatten(load('src/locales/en/index.ts')); +console.log('zh keys:', zhKeys.size, 'en keys:', enKeys.size); + +const dir = 'src'; +const exts = ['.tsx', '.ts', '.jsx', '.js']; +function walk(d) { + let r = []; + for (const f of fs.readdirSync(d)) { + const p = path.join(d, f); + if (fs.statSync(p).isDirectory()) { + r = r.concat(walk(p)); + } else if (exts.some((e) => f.endsWith(e)) && !p.includes('locales')) { + r.push(p); + } + } + return r; +} + +const used = new Set(); +const fileKey = {}; +for (const f of walk(dir)) { + const code = fs.readFileSync(f, 'utf8'); + const re = /(?:[^.\w]|^)(?:t|i18n\.t)\(\s*['"]([a-zA-Z0-9_.-]+)['"]/g; + let m; + while ((m = re.exec(code)) !== null) { + const k = m[1]; + used.add(k); + (fileKey[k] = fileKey[k] || new Set()).add(f); + } +} +console.log('Total distinct keys used in source:', used.size); + +const missing = [...used].filter((k) => !zhKeys.has(k) || !enKeys.has(k)); +console.log('Keys used but missing in locale files:', missing.length); +for (const k of missing.sort()) { + console.log(' ' + k + ' <- ' + [...(fileKey[k] || [])].join(', ')); +} + +// Also detect literal raw t('...') keys that might be dynamic (skip) diff --git a/tsc_baseline.txt b/tsc_baseline.txt new file mode 100644 index 0000000000000000000000000000000000000000..08936186b4a74f7b679f44e7d15764b80437f8b2 GIT binary patch literal 189792 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