Browse Source

Merge branch 'refs/heads/professional2' into feature-mcp

kagg886 1 month ago
parent
commit
93ee1af053
100 changed files with 8409 additions and 2098 deletions
  1. 2 2
      .env.development
  2. 4 2
      .env.golocal
  3. 1 1
      .vscode/settings.json
  4. 113 95
      src/api/datahub/index.ts
  5. 1 1
      src/api/device/index.ts
  6. 2 2
      src/components/vue3cron/vue3cron.vue
  7. 56 2
      src/i18n/index.ts
  8. 186 0
      src/i18n/lang/en.ts
  9. 178 0
      src/i18n/lang/zh-cn.ts
  10. 186 0
      src/i18n/lang/zh-tw.ts
  11. 340 0
      src/i18n/pages/dateCenter/en.ts
  12. 340 0
      src/i18n/pages/dateCenter/zh-cn.ts
  13. 329 0
      src/i18n/pages/dateCenter/zh-tw.ts
  14. 28 0
      src/i18n/pages/formI18n/en.ts
  15. 36 0
      src/i18n/pages/formI18n/zh-cn.ts
  16. 36 0
      src/i18n/pages/formI18n/zh-tw.ts
  17. 222 0
      src/i18n/pages/iotCard/en.ts
  18. 222 0
      src/i18n/pages/iotCard/zh-cn.ts
  19. 222 0
      src/i18n/pages/iotCard/zh-tw.ts
  20. 384 0
      src/i18n/pages/iotmanagerI18n/en.ts
  21. 385 0
      src/i18n/pages/iotmanagerI18n/zh-cn.ts
  22. 384 0
      src/i18n/pages/iotmanagerI18n/zh-tw.ts
  23. 553 0
      src/i18n/pages/projects/en.ts
  24. 553 0
      src/i18n/pages/projects/zh-cn.ts
  25. 553 0
      src/i18n/pages/projects/zh-tw.ts
  26. 172 0
      src/i18n/pages/property/en.ts
  27. 172 0
      src/i18n/pages/property/zh-cn.ts
  28. 172 0
      src/i18n/pages/property/zh-tw.ts
  29. 39 0
      src/i18n/pages/tableI18n/en.ts
  30. 39 0
      src/i18n/pages/tableI18n/zh-cn.ts
  31. 39 0
      src/i18n/pages/tableI18n/zh-tw.ts
  32. 1 0
      src/layout/navBars/breadcrumb/setings.vue
  33. 12 0
      src/layout/navBars/breadcrumb/user.vue
  34. 3 1
      src/layout/navBars/tagsView/tagsView.vue
  35. 14 19
      src/layout/navMenu/subItem.vue
  36. 26 20
      src/layout/navMenu/vertical.vue
  37. 2 0
      src/main.ts
  38. 3 2
      src/store/modules/themeConfig.ts
  39. 4 0
      src/theme/dark.scss
  40. 1 1
      src/theme/element.scss
  41. 8 8
      src/utils/auth.ts
  42. 4 4
      src/utils/authDirective.ts
  43. 3 3
      src/utils/colDirective.ts
  44. 3 3
      src/utils/dataUiOptions.ts
  45. 19 6
      src/utils/origin.ts
  46. 1 1
      src/views/apihub/component/edit.vue
  47. 2 2
      src/views/apihub/plugin.vue
  48. 1 1
      src/views/designer/index.vue
  49. 32 19
      src/views/iot/alarm/list/index.vue
  50. 101 119
      src/views/iot/alarm/log/index.vue
  51. 23 12
      src/views/iot/cascade/deviceList.vue
  52. 29 17
      src/views/iot/cascade/index.vue
  53. 1 1
      src/views/iot/certificate/index.vue
  54. 1 1
      src/views/iot/configuration/list/index.vue
  55. 1 1
      src/views/iot/configuration/screen/index.vue
  56. 1 1
      src/views/iot/dataAnalysis/IndicatorAggregation/index.vue
  57. 1 1
      src/views/iot/dataAnalysis/exponentialTrend/index.vue
  58. 2 2
      src/views/iot/device-tree/tree/index.vue
  59. 169 0
      src/views/iot/device/category/component/addOrEdit.vue
  60. 0 175
      src/views/iot/device/category/component/edit.vue
  61. 76 83
      src/views/iot/device/category/index.vue
  62. 1 1
      src/views/iot/device/channel/index.vue
  63. 1 1
      src/views/iot/device/instance/component/chart.vue
  64. 41 46
      src/views/iot/device/instance/component/excel.vue
  65. 66 37
      src/views/iot/device/instance/detail.vue
  66. 120 46
      src/views/iot/device/instance/index.vue
  67. 48 35
      src/views/iot/device/product/component/dataParse.vue
  68. 21 10
      src/views/iot/device/product/component/deviceIn.vue
  69. 412 345
      src/views/iot/device/product/component/editAttr.vue
  70. 68 36
      src/views/iot/device/product/component/editEvent.vue
  71. 202 240
      src/views/iot/device/product/component/editFun.vue
  72. 54 31
      src/views/iot/device/product/component/editOption.vue
  73. 108 67
      src/views/iot/device/product/component/editPro.vue
  74. 145 74
      src/views/iot/device/product/component/editTab.vue
  75. 42 25
      src/views/iot/device/product/component/typeItem.vue
  76. 240 181
      src/views/iot/device/product/detail.vue
  77. 145 147
      src/views/iot/device/product/index.vue
  78. 1 1
      src/views/iot/device/template/index.vue
  79. 1 1
      src/views/iot/ice104/device/index.vue
  80. 1 1
      src/views/iot/ice104/template/index.vue
  81. 39 11
      src/views/iot/iotmanager/dashboard.vue
  82. 2 2
      src/views/iot/network/server/component/list.vue
  83. 1 1
      src/views/iot/network/server/index.vue
  84. 2 2
      src/views/iot/network/tunnel/component/list.vue
  85. 1 1
      src/views/iot/network/tunnel/index.vue
  86. 1 1
      src/views/iot/noticeservices/config/index.vue
  87. 1 1
      src/views/iot/noticeservices/config/setting.vue
  88. 1 1
      src/views/iot/noticeservices/log/index.vue
  89. 2 2
      src/views/iot/operate/remoteconf/index.vue
  90. 1 1
      src/views/iot/ota-update/data/index.vue
  91. 1 1
      src/views/iot/ota-update/module/index.vue
  92. 2 2
      src/views/iot/ota-update/update/component/batch.vue
  93. 1 1
      src/views/iot/ota-update/update/component/device.vue
  94. 3 3
      src/views/iot/ota-update/update/index.vue
  95. 33 32
      src/views/iot/projects/detail/device.vue
  96. 6 6
      src/views/iot/projects/detail/index.vue
  97. 8 8
      src/views/iot/projects/detail/info.vue
  98. 32 31
      src/views/iot/projects/detail/scene.vue
  99. 28 26
      src/views/iot/projects/detail/topo.vue
  100. 34 32
      src/views/iot/projects/detail/video.vue

+ 2 - 2
.env.development

@@ -5,7 +5,7 @@ VITE_NGINX_PROXY = ''
 VITE_SERVER_ORIGIN = 'http://127.0.0.1:8199'
 
 # 规则引擎服务地址
-VITE_RULE_SERVER_URL = 'http://127.0.0.1:9090'
+VITE_RULE_SERVER_URL = ':9090'
 
 # 流媒体服务地址
-VITE_MEDIA_SERVER_URL = 'http://127.0.0.1:8080'
+VITE_MEDIA_SERVER_URL = ':8080'

+ 4 - 2
.env.golocal

@@ -6,5 +6,7 @@ VITE_ROUTER_MODE = 'hash'
 
 VITE_SERVER_ORIGIN = ''
 
-VITE_RULE_SERVER_URL = 'http://127.0.0.1:9090'
-VITE_MEDIA_SERVER_URL = 'http://127.0.0.1:8080'
+VITE_MODBUS_API = ':8110'
+VITE_ICE104_API = ':8310'
+VITE_RULE_SERVER_URL = ':9090'
+VITE_MEDIA_SERVER_URL = ':8080'

+ 1 - 1
.vscode/settings.json

@@ -1,5 +1,5 @@
 {
-  "files.autoSave": "onFocusChange",
+  "files.autoSave": "off",
   "git.enableSmartCommit": true,
   "editor.tabSize": 2,
   "extensions.ignoreRecommendations": true,

+ 113 - 95
src/api/datahub/index.ts

@@ -10,106 +10,124 @@ import { get, post, del, put, file } from '/@/utils/request';
 
 export default {
 
-   common: {
-      getList: (params: object) => get('/source/search', params),
-      add: (data: object) => post('/source/api/add', data),
-      delete: (ids: number) => del('/source/del', { ids }),
-      edit: (data: object) => put('/source/api/edit', data),
-      detail: (sourceId: number) => get('/source/detail', { sourceId }),
-      deploy: (data: object) => post('/source/deploy', data),
-      undeploy: (data: object) => post('/source/undeploy', data),
-      api: (sourceId: number) => get('/source/api/get', { sourceId }),
-      devadd: (data: object) => post('/source/device/add', data),
-      devedit: (data: object) => put('/source/device/edit', data),
-      devapi: (sourceId: number) => get('/source/device/get', { sourceId }),
-      getdevList: (params: object) => get('/product/device/list', params),
-      getdata: (params: object) => get('/source/getdata', params),
-      getLists: (params: object) => get('/source/list', params),
-      copy: (params: object) => post('/source/copy', params),
+  common: {
+    getList: (params: object) => get('/source/search', params),
+    add: (data: object) => post('/source/api/add', data),
+    delete: (ids: number) => del('/source/del', { ids }),
+    edit: (data: object) => put('/source/api/edit', data),
+    detail: (sourceId: number) => get('/source/detail', { sourceId }),
+    deploy: (data: object) => post('/source/deploy', data),
+    undeploy: (data: object) => post('/source/undeploy', data),
+    api: (sourceId: number) => get('/source/api/get', { sourceId }),
+    devadd: (data: object) => post('/source/device/add', data),
+    devedit: (data: object) => put('/source/device/edit', data),
+    devapi: (sourceId: number) => get('/source/device/get', { sourceId }),
+    getdevList: (params: object) => get('/product/device/list', params),
+    getdata: (params: object) => get('/source/getdata', params),
+    getLists: (params: object) => get('/source/list', params),
+    copy: (params: object) => post('/source/copy', params),
 
-      dbadd: (data: object) => post('/source/db/add', data),
-      dbedit: (data: object) => put('/source/db/edit', data),
-      getfields: (sourceId: number) => get('/source/db/fields', { sourceId }),
+    dbadd: (data: object) => post('/source/db/add', data),
+    dbedit: (data: object) => put('/source/db/edit', data),
+    getfields: (sourceId: number) => get('/source/db/fields', { sourceId }),
 
-      devdb: (sourceId: number) => get('/source/db/get', { sourceId }),
+    devdb: (sourceId: number) => get('/source/db/get', { sourceId }),
 
-   },
+  },
 
-   node: {
-      getList: (params: object) => get('/source/node/list', params),
-      add: (data: object) => post('/source/node/add', data),
-      delete: (nodeId: number) => del('/source/node/del', { nodeId }),
-      edit: (data: object) => put('/source/node/edit', data),
-      getpropertyList: (params: object) => get('/product/tsl/property/all', params),
-   },
+  node: {
+    getList: (params: object) => get('/source/node/list', params),
+    add: (data: object) => post('/source/node/add', data),
+    delete: (nodeId: number) => del('/source/node/del', { nodeId }),
+    edit: (data: object) => put('/source/node/edit', data),
+    getpropertyList: (params: object) => get('/product/tsl/property/all', params),
+  },
 
-   template: {
-      getList: (params: object) => get('/source/template/search', params),
-      add: (data: object) => post('/source/template/add', data),
-      delete: (ids: number) => del('/source/template/del', { ids }),
-      edit: (data: object) => put('/source/template/edit', data),
-      detail: (id: string) => get('/source/template/detail', { id }),
-      allList: (params: object) => get('/source/template/list', params), // 获取所有已发布列表
-      getdata: (params: object) => get('/source/template/getdata', params),
-      getDictData: (params: object) => get('/common/dict/data/getDictData', params),
-      cityTree: (params: object) => get('/common/city/tree', params),
-      copy: (params: object) => post('/source/template/copy', params),
-      relation_check: (id: number) => get('/source/template/relation_check', { id }),
-      source_list: (id: number) => get('/source/template/source_list', { id }),
-      aggregate_from: (id: number) => get('/source/template/aggregate_from', { id }),
-      relation: (data: object) => post('/source/template/relation', data),
-      aggregate: (data: object) => post('/source/template/aggregate', data),
-   },
+  template: {
+    getList: (params: object) => get('/source/template/search', params),
+    add: (data: object) => post('/source/template/add', data),
+    delete: (ids: number) => del('/source/template/del', { ids }),
+    edit: (data: object) => put('/source/template/edit', data),
+    detail: (id: string) => get('/source/template/detail', { id }),
+    allList: (params: object) => get('/source/template/list', params), // 获取所有已发布列表
+    getdata: (params: object) => get('/source/template/getdata', params),
+    getDictData: (params: object) => get('/common/dict/data/getDictData', params),
+    cityTree: (params: object) => get('/common/city/tree', params),
+    copy: (params: object) => post('/source/template/copy', params),
+    relation_check: (id: number) => get('/source/template/relation_check', { id }),
+    source_list: (id: number) => get('/source/template/source_list', { id }),
+    aggregate_from: (id: number) => get('/source/template/aggregate_from', { id }),
+    relation: (data: object) => post('/source/template/relation', data),
+    aggregate: (data: object) => post('/source/template/aggregate', data),
+  },
 
-   tnode: {
-      getList: (params: object) => get('/source/template/node/list', params),
-      add: (data: object) => post('/source/template/node/add', data),
-      delete: (id: number) => del('/source/template/node/del', { id }),
-      edit: (data: object) => put('/source/template/node/edit', data),
-      deploy: (data: object) => post('/source/template/deploy', data),
-      undeploy: (data: object) => post('/source/template/undeploy', data),
-   },
+  tnode: {
+    getList: (params: object) => get('/source/template/node/list', params),
+    add: (data: object) => post('/source/template/node/add', data),
+    delete: (id: number) => del('/source/template/node/del', { id }),
+    edit: (data: object) => put('/source/template/node/edit', data),
+    deploy: (data: object) => post('/source/template/deploy', data),
+    undeploy: (data: object) => post('/source/template/undeploy', data),
+  },
 
-   weather: {
-      getCityWeatherList: () => get('/envirotronics/weather/cityWeatherList'),
-      getWhichCityWeather: (params: object) => get('/envirotronics/weather/getInfoById', params),
-      getTemperatureEchartById: (params: object) => get('/envirotronics/weather/getTemperatureEchartById', params),
-      getWindpowerEchartById: (params: object) => get('/envirotronics/weather/getWindpowerEchartById', params),
-      getCityWeatherHistory: (params: object) => get('/envirotronics/weather/GetCityWeatherHistory', params),
-      getCityWeatherHistoryExport: (params: object) => file('/envirotronics/weather/GetCityWeatherHistoryExport', params),
-   },
-   statistics: {
-      getStatisticsChartData: (params: object) => get('/statistics/bar/chart/data', params),
-      getStatisticsLineChartData: (params: object) => get('/statistics/broken/line/data', params),
-      getStatisticsTotalData: (params: object) => get('/statistics/city/data', params),
-      getStatisticsPieData: (params: object) => get('/statistics/tempering/ratio/data', params),
-      getStatisticsOverview: (params: object) => get('/statistics/overview', params),
-   },
-   iotManage: {
-      getOverviewData: () => get('/thing/overview'),
-      getAlarmList: (params: object) => get('/alarm/log/list', params),
-      getAlarmDetail: (id: number) => get('/alarm/log/detail', { id }),
-      getAlarmHandle: (data: object) => post('/alarm/log/handle', data),
-      // 设备消息总量本年统计
-      deviceDataTotalCount: (dateType: 'year' | 'month' | 'day') => get('/analysis/deviceDataTotalCount', { dateType }),
-      // 设备在线离线及总数统计
-      deviceOnlineOfflineCount: () => get('/analysis/deviceOnlineOfflineCount'),
-      // 本年度每月设备消息量统计 
-      deviceDataCount: (dateType: 'year' | 'month') => get('/analysis/deviceDataCount', { dateType }),
-      // 按年度每月设备告警数统计
-      deviceAlertCountByYearMonth: (year = '2023') => get('/analysis/deviceAlertCountByYearMonth', { year }),
-      // 按告警级别统计
-      deviceAlarmLevelCount: (dateType: 'year' | 'month' | 'day', date: string) => get('/analysis/deviceAlarmLevelCount', { dateType, date }),
-      // 产品数量统计
-      productCount: () => get('/analysis/productCount'),
-   },
-   // 计算指标管理
-   calculationIndicator: {
-      getList: (params: object) => get('/compute/list', params),
-      add: (data: object) => post('/compute/add', data),
-      delete: (id: number) => del('/compute/del', { id }),
-      edit: (data: object) => put('/compute/edit', data),
-      deploy: (data: object) => put('/compute/publish', data),
-      checkDeploy: (params: object) => get('/compute/checkComputeIndexDeploy', params)
-   },
+  weather: {
+    getCityWeatherList: () => get('/envirotronics/weather/cityWeatherList'),
+    getWhichCityWeather: (params: object) => get('/envirotronics/weather/getInfoById', params),
+    getTemperatureEchartById: (params: object) => get('/envirotronics/weather/getTemperatureEchartById', params),
+    getWindpowerEchartById: (params: object) => get('/envirotronics/weather/getWindpowerEchartById', params),
+    getCityWeatherHistory: (params: object) => get('/envirotronics/weather/GetCityWeatherHistory', params),
+    getCityWeatherHistoryExport: (params: object) => file('/envirotronics/weather/GetCityWeatherHistoryExport', params),
+  },
+  statistics: {
+    getStatisticsChartData: (params: object) => get('/statistics/bar/chart/data', params),
+    getStatisticsLineChartData: (params: object) => get('/statistics/broken/line/data', params),
+    getStatisticsTotalData: (params: object) => get('/statistics/city/data', params),
+    getStatisticsPieData: (params: object) => get('/statistics/tempering/ratio/data', params),
+    getStatisticsOverview: (params: object) => get('/statistics/overview', params),
+  },
+  iotManage: {
+    getOverviewData: () => get('/thing/overview'),
+    getAlarmList: (params: object) => get('/alarm/log/list', params),
+    getAlarmDetail: (id: number) => get('/alarm/log/detail', { id }),
+    getAlarmHandle: (data: object) => post('/alarm/log/handle', data),
+    // 设备消息总量本年统计
+    deviceDataTotalCount: (dateType: 'year' | 'month' | 'day') => get('/analysis/deviceDataTotalCount', { dateType }),
+    // 设备在线离线及总数统计
+    deviceOnlineOfflineCount: () => get('/analysis/deviceOnlineOfflineCount'),
+    // 本年度每月设备消息量统计 
+    deviceDataCount: (dateType: 'year' | 'month') => get('/analysis/deviceDataCount', { dateType }),
+    // 按年度每月设备告警数统计
+    deviceAlertCountByYearMonth: (year = '2023') => get('/analysis/deviceAlertCountByYearMonth', { year }),
+    // 按告警级别统计
+    deviceAlarmLevelCount: (dateType: 'year' | 'month' | 'day', date: string) => get('/analysis/deviceAlarmLevelCount', { dateType, date }),
+    // 产品数量统计
+    productCount: () => get('/analysis/productCount'),
+  },
+  // 计算指标管理
+  calculationIndicator: {
+    getList: (params: object) => get('/compute/list', params),
+    add: (data: object) => post('/compute/add', data),
+    delete: (id: number) => del('/compute/del', { id }),
+    edit: (data: object) => put('/compute/edit', data),
+    deploy: (data: object) => put('/compute/publish', data),
+    checkDeploy: (params: object) => get('/compute/checkComputeIndexDeploy', params)
+  },
+  tags: {
+    getTree: (data: object) => get('/tag/tree', data),
+    add: (data: object) => post('/tag/add', data),
+    edit: (data: object) => put('/tag/edit', data),
+    detail: (id: number) => get('/tag/detail', { id }),
+    del: (id: number) => del('/tag/del', { id }),
+  },
+  indicator: {
+    getList: (params: object) => get('/indicator/list', params),
+    data: (params: object) => get('/indicator/data', params),
+    getData: (params: object) => get('/indicator/getData', params),
+    detail: (code: string) => get('/indicator/detail', { code }),
+    add: (data: object) => post('/indicator/add', data),
+    del: (code: string) => del('/indicator/del', { code }),
+    edit: (data: object) => put('/indicator/edit', data),
+    publish: (code: string) => post('/indicator/publish', { code }),
+    unpublish: (code: string) => post('/indicator/unpublish', { code }),
+  }
 }

+ 1 - 1
src/api/device/index.ts

@@ -114,7 +114,7 @@ export default {
     setDeviceStatus: (data: object) => post('/product/device/setDeviceStatus', data),
     import: (data: object) => post('/product/device/import', data),
     export: (data: object) => file('/product/device/export', data),
-    
+    syncStatus: () => get('/product/device/syncStatus'),
   },
   dev_asset: {
     all: (params: object) => get('/asset/asset/all', params),

+ 2 - 2
src/components/vue3cron/vue3cron.vue

@@ -1,7 +1,7 @@
 
 <template>
   <div class="vue3-cron-div">
-    <el-button class="language" type="text">
+    <el-button class="language" text type="primary">
     </el-button>
     <el-tabs type="border-card">
       <el-tab-pane>
@@ -252,7 +252,7 @@
     <div class="bottom">
       <div class="value" style="margin: 10px;">
         <span> cron预览: </span>
-        <el-tag type="primary">
+        <el-tag>
           {{ state.cron }}
         </el-tag> <span>{秒数} {分钟} {小时} {日期} {月份} {?}</span>
       </div>

+ 56 - 2
src/i18n/index.ts

@@ -1,3 +1,11 @@
+/*
+ * @Author: vera_min vera_min@163.com
+ * @Date: 2025-08-02 12:21:54
+ * @LastEditors: vera_min vera_min@163.com
+ * @LastEditTime: 2025-08-06 01:54:53
+ * @FilePath: /sagoo-admin-ui/src/i18n/index.ts
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ */
 import { createI18n } from 'vue-i18n';
 import zhcnLocale from 'element-plus/lib/locale/lang/zh-cn';
 import enLocale from 'element-plus/lib/locale/lang/en';
@@ -14,6 +22,30 @@ import pagesLoginZhtw from '/@/i18n/pages/login/zh-tw';
 import pagesFormI18nZhcn from '/@/i18n/pages/formI18n/zh-cn';
 import pagesFormI18nEn from '/@/i18n/pages/formI18n/en';
 import pagesFormI18nZhtw from '/@/i18n/pages/formI18n/zh-tw';
+import pagesTable18nZhcn from './pages/tableI18n/zh-cn';
+import pagesTable18nEn from './pages/tableI18n/en';
+import pagesTable18nZhtw from './pages/tableI18n/zh-tw';
+
+import pagesIotmanagerZhcn from './pages/iotmanagerI18n/zh-cn';
+import pagesIotmanagerEn from './pages/iotmanagerI18n/en';
+import pagesIotmanagerZhtw from './pages/iotmanagerI18n/zh-tw';
+
+import pagesIotcardZhcn from './pages/iotCard/zh-cn';
+import pagesIotcardEn from './pages/iotCard/en';
+import pagesIotcardZhtw from './pages/iotCard/zh-tw';
+
+import pagesProjectsZhcn from './pages/projects/zh-cn';
+import pagesProjectsEn from './pages/projects/en';
+import pagesProjectsZhtw from './pages/projects/zh-tw';
+
+import pagesPropertyZhcn from './pages/property/zh-cn';
+import pagesPropertyEn from './pages/property/en';
+import pagesPropertyZhtw from './pages/property/zh-tw';
+
+import pagesDateCenterZhcn from './pages/dateCenter/zh-cn';
+import pagesDateCenterEn from './pages/dateCenter/en';
+import pagesDateCenterZhtw from './pages/dateCenter/zh-tw';
+
 
 // 定义语言国际化内容
 /**
@@ -28,7 +60,13 @@ const messages = {
 			...nextZhcn,
 			...pagesLoginZhcn,
 			...pagesFormI18nZhcn,
-		},
+			...pagesTable18nZhcn,
+			...pagesIotmanagerZhcn,
+			iotCard: pagesIotcardZhcn,
+			projects: pagesProjectsZhcn,
+			property: pagesPropertyZhcn,
+			dateCenter: pagesDateCenterZhcn
+		}
 	},
 	[enLocale.name]: {
 		...enLocale,
@@ -36,7 +74,13 @@ const messages = {
 			...nextEn,
 			...pagesLoginEn,
 			...pagesFormI18nEn,
-		},
+			...pagesTable18nEn,
+			...pagesIotmanagerEn,
+			iotCard: pagesIotcardEn,
+			projects: pagesProjectsEn,
+			property: pagesPropertyEn,
+			dateCenter: pagesDateCenterEn
+		},	
 	},
 	[zhtwLocale.name]: {
 		...zhtwLocale,
@@ -44,6 +88,12 @@ const messages = {
 			...nextZhtw,
 			...pagesLoginZhtw,
 			...pagesFormI18nZhtw,
+			...pagesTable18nZhtw,
+			...pagesIotmanagerZhtw,
+			iotCard: pagesIotcardZhtw,
+			projects: pagesProjectsZhtw,
+			property: pagesPropertyZhtw,
+			dateCenter: pagesDateCenterZhtw
 		},
 	},
 };
@@ -53,4 +103,8 @@ export const i18n = createI18n({
 	locale: store.state.themeConfig.themeConfig.globalI18n,
 	fallbackLocale: zhcnLocale.name,
 	messages,
+	silentTranslationWarn: true,
+	missingWarn: false,
+	silentFallbackWarn: true,
+	fallbackWarn: false
 });

+ 186 - 0
src/i18n/lang/en.ts

@@ -107,6 +107,11 @@ export default {
 		logOutExit: 'Exiting',
 		logOutSuccess: 'Exit successfully!',
 	},
+	login: {
+		title: 'Login',
+		serverVersion: 'Server Version',
+		frontendVersion: 'Frontend Version',
+	},
 	tagsView: {
 		refresh: 'refresh',
 		close: 'close',
@@ -178,4 +183,185 @@ export default {
 		copyTextSuccess: 'Copy succeeded!',
 		copyTextError: 'Copy failed!',
 	},
+	menuModule: {
+		runtimeMonitoring: {
+			title: "Runtime Monitoring",
+			weatherMonitoring: "Weather Monitoring",
+			realTimeMonitoring: "Real-time Monitoring",
+			weatherHistory: "Weather History",
+		},	
+		iotmanager: {
+			title: "IoT Management",
+			dashboard: "IoT Overview",
+			deviceManager: "Device",
+			productClass: "Product Category",
+			product: "Product",
+			productDetail: "Product Details",
+			device: "Device",
+			device1: "Device",
+			deviceDetail: "Device Details",
+			deviceDetail1: "Device Details",
+			configuration: "Configuration",
+			configurationList: "Configuration List",
+			screen: "Dashboard Design",
+			ruleEngine: "Rule Engine",
+			ruleInstance: "Rule Orchestration",
+			gridComponent: "Grid Component",
+			serverManager: "Server",
+			serverDetail: "Service Details",
+			editServer: "Edit Service",
+			newServer: "Add Service",
+			channelManagement: "Channel Management",
+			channelManagement1: "Channel Management",
+			editChannel: "Edit Channel",
+			newChannel: "Add Channel",
+			channelDetail: "Channel Details",
+			dataCollection: "Data Collection",
+			templateManagement: "Template",
+			dataAnalysis: "Data Analysis",
+			indicatorTrend: "Indicator Trend",
+			indicatorAggregation: "Indicator Aggregation",
+			certificateManagement: "Certificate",
+			certificateList: "Certificate List",
+			alarmCenter: "Alarm Center",
+			alarmStatistics: "Alarm Statistics",
+			alarmLog: "Alarm Log",
+			alarmConfiguration: "Alarm Configuration",
+			ice104: "IEC104",
+			template: "Template",
+			templateDetail: "Template Details",
+			sceneLinkage: "Scene Linkage",
+			sceneList: "Scene List",
+			sceneDetail: "Scene Details",
+			sceneRecord: "Scene Record",
+			sceneRecordDetail: "Record Details",
+			operationManagement: "Operation",
+			remoteConfiguration: "Remote Configuration",
+			otaUpgrade: "OTA Upgrade",
+			dataAnalysis1: "Data Analysis",
+			moduleManagement: "Module",
+			upgradePackageManagement: "Upgrade Package",
+			upgradePackageDetail: "Details",
+			notificationService: "Notification Service",
+			notificationLog: "Notification Log",
+			notificationConfiguration: "Notification Configuration",
+			notificationConfigurationManagement: "Notification Configuration Management",
+			cascadeManagement: "Cascade",
+		},
+		iotCard: {
+			title: "IoT Card",
+			homePage: "Home Page",
+			iotCardManagement: "IoT Card",
+			iotCardDetail: "IoT Card Details",
+			platformAccess: "Platform Access",
+		},
+		projectManagement: {
+			title: "Project Management",
+			projectOverview: "Project Overview",
+			projectList: "Project List",
+			projectDetail: "Project Details",
+			filterTemplate: "Filter Template",
+			deviceTree: "Device Tree",
+		},
+		assetManagement: {
+			title: "Asset",
+			deviceArchiveManagement: "Device Profile",
+			deviceArchiveProperty: "Device Archive Property",
+			assetRelationship: "Asset Relationship",
+			deviceMap: "Device Map",
+		},
+		dataCenter: {
+			title: "Data Center",
+			dataModelDetail: "Data Model Details",
+			dataSourceDetail: "Data Source Details",
+			indexManagement: "Index Management",
+			dataModeling: "Data Modeling",
+			dataSourceManagement: "Data Source Management",
+			tags: "Tags",
+			indicator: "Indicator",
+		},
+		developmentTools: {
+			title: "Development Tools",
+			codeGeneration: "Code Generation",
+		},
+		videoMonitoring: {
+			title: "Video Monitoring",
+			gbDevice: "GB Device",
+			deviceManagement: "Device Management",
+			deviceScreen: "Device Screen",
+			deviceGroup: "Device Group",
+			videoManagement: "Video Management",
+			alarmQuery: "Alarm Query",
+			videoPlayback: "Video Playback",
+			screenPlayback: "Screen Playback",
+			streamList: "Stream List",
+			streamProxy: "Stream Proxy",
+			streamPush: "Stream Push",
+		},
+		systemManagement: {
+			title: "System",
+			regionManagement: "Region",
+			organizationManagement: "Organization",
+			positionManagement: "Position",
+			roleManagement: "Role",
+			userManagement: "User",
+			systemMonitoring: "System Monitoring",
+			apiDocument: "API Document",
+			serviceMonitoring: "Service Monitoring",
+			cacheMonitoring: "Cache Monitoring",
+			systemNotification: "System Notification",
+			onlineUser: "Online User",
+			loginLog: "Login Log",
+			auditLog: "Audit Log",
+			taskLog: "Task Log",
+			systemLog: "System Log",
+			blacklist: "Blacklist",
+			configuration: "System Configuration",
+			configurationManagement: "Configuration",
+			pluginManagement: "Plugin",
+			applicationManagement: "Application",
+			menuManagement: "Menu",
+			parameterManagement: "Parameter",
+			dictionaryManagement: "Dictionary",
+			dictionaryDataManagement: "Dictionary Data",
+			scheduledTask: "Scheduled Task",
+			apiManagement: "API",
+			cityManagement: "City",
+			remoteUpgrade: "Remote Upgrade",
+			authorizationConfiguration: "Authorization Configuration",
+			tenantManagement: "Tenant",
+			// experimentalFunction: "Experimental Function",
+			// functionDevelopment: "Function Development",
+			// pageDevelopment: "Page Development",
+			// Designer: "Designer",
+			// apiDevelopment: "API Development",
+			// apiDefinition: "API Definition",
+			// dataSourceManagement: "Data Source Management",
+			// clientManagement: "Client Management",
+			// policyManagement: "Policy Management",
+			// policyControl: "Policy Control",
+		},
+		experimentalFunction: {
+			title: "Experimental Function",
+			dataAssistant: "Data Assistant",
+			managementPage: "Management Page",
+			energyAnalysis: "Energy Analysis",
+		},
+		functionDevelopment: {
+			title: "Function Development",
+			pageDevelopment: "Page Development",
+			Designer: "Designer",
+			apiDevelopment: "API Development",
+			apiDefinition: "API Definition",
+
+			dataSourceManagement: "Data Source",
+			clientManagement: "Client",
+			pluginManagement: "Plugin",
+
+		},
+		policyManagement: {
+			title: "Policy",
+			policyControl: "Policy Control",
+		}
+	}
 };

+ 178 - 0
src/i18n/lang/zh-cn.ts

@@ -108,6 +108,11 @@ export default {
 		logOutExit: '退出中',
 		logOutSuccess: '安全退出成功!',
 	},
+	login: {
+		title: '登录',
+		serverVersion: '服务端版本',
+		frontendVersion: '前端版本',
+	},
 	tagsView: {
 		refresh: '刷新',
 		close: '关闭',
@@ -179,4 +184,177 @@ export default {
 		copyTextSuccess: '复制成功!',
 		copyTextError: '复制失败!',
 	},
+	menuModule: {
+		runtimeMonitoring: {
+			title: "运行监测",
+			weatherMonitoring: "天气监测",
+			realTimeMonitoring: "实时监测",
+			weatherHistory: "天气历史",
+		},
+		iotmanager: {
+			title: "物联管理",
+			dashboard: "物联概览",
+			deviceManager: "设备管理",
+			productClass: "产品分类",
+			product: "产品",
+			productDetail: "产品详情",
+			device: "设备",
+			device1: "设备",
+			deviceDetail: "设备详情",
+			deviceDetail1: "设备详情",
+			configuration: "组态管理",
+			configurationList: "组态列表",
+			screen: "大屏设计",
+			ruleEngine: "规则引擎",
+			ruleInstance: "规则编排",
+			gridComponent: "网格组件",
+			serverManager: "服务器管理",
+			serverDetail: "服务详情",
+			editServer: "编辑服务",
+			newServer: "新增服务",
+			channelManagement: "通道管理",
+			channelManagement1: "通道管理",
+			editChannel: "编辑通道",
+			newChannel: "新增通道",
+			channelDetail: "通道详情",
+			dataCollection: "数据采集",
+			templateManagement: "模板管理",
+			dataAnalysis: "数据分析",
+			indicatorTrend: "指标趋势",
+			indicatorAggregation: "指标聚合",
+			certificateManagement: "证书管理",
+			certificateList: "证书列表",
+			alarmCenter: "告警中心",
+			alarmStatistics: "告警统计",
+			alarmLog: "告警日志",
+			alarmConfiguration: "告警配置",
+			ice104: "ice104",
+			template: "模版",
+			templateDetail: "模版详情",
+			sceneLinkage: "场景联动",
+			sceneList: "场景列表",
+			sceneDetail: "场景详情",
+			sceneRecord: "场景记录",
+			sceneRecordDetail: "记录详情",
+			operationManagement: "运维管理",
+			remoteConfiguration: "远程配置",
+			otaUpgrade: "OTA升级",
+			dataAnalysis1: "数据分析",
+			moduleManagement: "模块管理",
+			upgradePackageManagement: "升级包管理",
+			upgradePackageDetail: "详情",
+			notificationService: "通知服务",
+			notificationLog: "通知日志",
+			notificationConfiguration: "通知配置",
+			notificationConfigurationManagement: "通知配置管理",
+			cascadeManagement: "级联管理",
+		},
+		iotCard: {
+			title: "物联卡",
+			homePage: "首页",
+			iotCardManagement: "物联卡管理",
+			iotCardDetail: "物联网卡详情",
+			platformAccess: "平台接入",
+		},
+		projectManagement: {
+			title: "项目管理",
+			projectOverview: "项目概览",
+			projectList: "项目列表",
+			projectDetail: "项目详情",
+			filterTemplate: "过滤模板",
+			deviceTree: "设备树",
+
+		},
+		assetManagement: {
+			title: "资产管理",
+			deviceArchiveManagement: "设备档案管理",
+			deviceArchiveProperty: "设备档案属性",
+			assetRelationship: "资产关系",
+			deviceMap: "设备地图",
+		},
+		dataCenter: {
+			title: "数据中心",
+			dataModelDetail: "数据模型详情",
+			dataSourceDetail: "数据源详情",
+			indexManagement: "指数管理",
+			dataModeling: "数据建模",
+			dataSourceManagement: "数据源管理",
+			tags: "标签管理",
+			indicator: "指标管理",
+		},
+		developmentTools: {
+			title: "开发工具",
+			codeGeneration: "代码生成",
+		},
+		videoMonitoring: {
+			title: "视频监控",
+			gbDevice: "国标设备",
+			deviceManagement: "设备管理",
+			deviceScreen: "设备分屏",
+			deviceGroup: "设备分组",
+			videoManagement: "录像管理",
+			alarmQuery: "报警查询",
+			videoPlayback: "录像回放",
+			screenPlayback: "分屏播放",
+			streamList: "推流列表",
+			streamProxy: "拉流代理",
+			streamPush: "推流代理",
+
+		},
+		systemManagement: {
+			title: "系统管理",
+			regionManagement: "区域管理",
+			organizationManagement: "组织管理",
+			positionManagement: "岗位管理",
+			roleManagement: "角色管理",
+			userManagement: "用户管理",
+			systemMonitoring: "系统监控",
+			apiDocument: "api文档",
+			serviceMonitoring: "服务监测",
+			cacheMonitoring: "缓存监测",
+			systemNotification: "系统通知",
+			onlineUser: "在线用户",
+			loginLog: "登录日志",
+			auditLog: "操作日志",
+			taskLog: "定时任务日志",
+			systemLog: "系统日志",
+			blacklist: "访问黑名单",
+			configuration: "系统配置",
+			configurationManagement: "配置管理",
+			pluginManagement: "插件管理",
+			applicationManagement: "应用管理",
+			menuManagement: "菜单管理",
+			parameterManagement: "参数管理",
+			dictionaryManagement: "字典管理",
+			dictionaryDataManagement: "字典数据管理",
+			scheduledTask: "定时任务",
+			apiManagement: "接口管理",
+			cityManagement: "城市管理",
+			remoteUpgrade: "远程升级",
+			authorizationConfiguration: "授权配置",
+			tenantManagement: "租户管理"
+		},
+		experimentalFunction: {
+			title: "实验功能",
+			dataAssistant: "数据助手",
+			managementPage: "管理页面",
+			energyAnalysis: "能耗分析",
+		},
+		functionDevelopment: {
+			title: "功能开发",
+			pageDevelopment: "页面开发",
+			Designer: "Designer",
+			apiDevelopment: "API开发",
+			apiDefinition: "API定义",
+
+			dataSourceManagement: "数据源管理",
+			clientManagement: "客户端管理",
+			pluginManagement: "插件管理",
+
+		},
+		policyManagement: {
+			title: "策略管理",
+			policyControl: "策略控制",
+		}
+	}
 };

+ 186 - 0
src/i18n/lang/zh-tw.ts

@@ -1,3 +1,11 @@
+/*
+ * @Author: vera_min vera_min@163.com
+ * @Date: 2025-08-02 12:21:54
+ * @LastEditors: vera_min vera_min@163.com
+ * @LastEditTime: 2025-08-05 13:00:24
+ * @FilePath: /sagoo-admin-ui/src/i18n/lang/zh-tw.ts
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ */
 // 定义内容
 export default {
 	router: {
@@ -107,6 +115,11 @@ export default {
 		logOutExit: '退出中',
 		logOutSuccess: '安全退出成功!',
 	},
+	login: {
+		title: '登入',
+		serverVersion: '伺服器版本',
+		frontendVersion: '前端版本',
+	},
 	tagsView: {
 		refresh: '重繪',
 		close: '關閉',
@@ -178,4 +191,177 @@ export default {
 		copyTextSuccess: '複製成功!',
 		copyTextError: '複製失敗!',
 	},
+	menuModule: {
+		runtimeMonitoring: {
+			title: "運行監測",
+			weatherMonitoring: "天氣監測",
+			realTimeMonitoring: "實時監測",
+			weatherHistory: "天氣歷史",
+		},	
+		iotmanager: {
+			title: "物聯管理",
+			dashboard: "物聯概覽",
+			deviceManager: "設備管理",
+			productClass: "產品分類",
+			product: "產品",
+			productDetail: "產品詳情",
+			device: "設備",
+			device1: "設備",
+			deviceDetail: "設備詳情",
+			deviceDetail1: "設備詳情",
+			configuration: "組態管理",
+			configurationList: "組態列表",
+			screen: "大屏設計",
+			ruleEngine: "規則引擎",
+			ruleInstance: "規則編排",
+			gridComponent: "網格組件",
+			serverManager: "服務器管理",
+			serverDetail: "服務詳情",
+			editServer: "編輯服務",
+			newServer: "新增服務",
+			channelManagement: "通道管理",
+			channelManagement1: "通道管理",
+			editChannel: "編輯通道",
+			newChannel: "新增通道",
+			channelDetail: "通道詳情",
+			dataCollection: "數據採集",
+			templateManagement: "模板管理",
+			dataAnalysis: "數據分析",
+			indicatorTrend: "指標趨勢",
+			indicatorAggregation: "指標聚合",
+			certificateManagement: "證書管理",
+			certificateList: "證書列表",
+			alarmCenter: "告警中心",
+			alarmStatistics: "告警統計",
+			alarmLog: "告警日誌",
+			alarmConfiguration: "告警配置",
+			ice104: "ice104",
+			template: "模版",
+			templateDetail: "模版詳情",
+			sceneLinkage: "場景聯動",
+			sceneList: "場景列表",
+			sceneDetail: "場景詳情",
+			sceneRecord: "場景記錄",
+			sceneRecordDetail: "記錄詳情",
+			operationManagement: "運維管理",
+			remoteConfiguration: "遠程配置",
+			otaUpgrade: "OTA升級",
+			dataAnalysis1: "數據分析",
+			moduleManagement: "模塊管理",
+			upgradePackageManagement: "升級包管理",
+			upgradePackageDetail: "詳情",
+			notificationService: "通知服務",
+			notificationLog: "通知日誌",
+			notificationConfiguration: "通知配置",
+			notificationConfigurationManagement: "通知配置管理",
+			cascadeManagement: "級聯管理",
+		},
+		iotCard: {
+			title: "物聯卡",
+			homePage: "首頁",
+			iotCardManagement: "物聯卡管理",
+			iotCardDetail: "物聯網卡詳情",
+			platformAccess: "平台接入",
+		},
+		projectManagement: {
+			title: "項目管理",
+			projectOverview: "項目概覽",
+			projectList: "項目列表",
+			projectDetail: "項目詳情",
+			filterTemplate: "過濾模板",
+			deviceTree: "設備樹",
+
+		},
+		assetManagement: {
+			title: "資產管理",
+			deviceArchiveManagement: "設備檔案管理",
+			deviceArchiveProperty: "設備檔案屬性",
+			assetRelationship: "資產關係",
+			deviceMap: "設備地圖",
+		},
+		dataCenter: {
+			title: "數據中心",
+			dataModelDetail: "數據模型詳情",
+			dataSourceDetail: "數據源詳情",
+			indexManagement: "指數管理",
+			dataModeling: "數據建模",
+			dataSourceManagement: "數據源管理",
+			tags: "標籤管理",
+			indicator: "指標管理",
+		},
+		developmentTools: {
+			title: "開發工具",
+			codeGeneration: "代碼生成",
+		},
+		videoMonitoring: {
+			title: "視頻監控",
+			gbDevice: "國標設備",
+			deviceManagement: "設備管理",
+			deviceScreen: "設備分屏",
+			deviceGroup: "設備分組",
+			videoManagement: "錄像管理",
+			alarmQuery: "報警查詢",
+			videoPlayback: "錄像回放",
+			screenPlayback: "分屏播放",
+			streamList: "推流列表",
+			streamProxy: "拉流代理",
+			streamPush: "推流代理",
+
+		},
+		systemManagement: {
+			title: "系統管理",
+			regionManagement: "區域管理",
+			organizationManagement: "組織管理",
+			positionManagement: "崗位管理",
+			roleManagement: "角色管理",
+			userManagement: "用戶管理",
+			systemMonitoring: "系統監控",
+			apiDocument: "api文檔",
+			serviceMonitoring: "服務監測",
+			cacheMonitoring: "緩存監測",
+			systemNotification: "系統通知",
+			onlineUser: "在線用戶",
+			loginLog: "登錄日誌",
+			auditLog: "操作日誌",
+			taskLog: "定時任務日誌",
+			systemLog: "系統日誌",
+			blacklist: "訪問黑名單",
+			configuration: "系統配置",
+			configurationManagement: "配置管理",
+			pluginManagement: "插件管理",
+			applicationManagement: "應用管理",
+			menuManagement: "菜單管理",
+			parameterManagement: "參數管理",
+			dictionaryManagement: "字典管理",
+			dictionaryDataManagement: "字典數據管理",
+			scheduledTask: "定時任務",
+			apiManagement: "接口管理",
+			cityManagement: "城市管理",
+			remoteUpgrade: "遠程升級",
+			authorizationConfiguration: "授權配置",
+			tenantManagement: "租戶管理",
+		},
+		experimentalFunction: {
+			title: "實驗功能",
+			dataAssistant: "數據助手",
+			managementPage: "管理頁面",
+			energyAnalysis: "能耗分析",
+		},
+		functionDevelopment: {
+			title: "功能開發",
+			pageDevelopment: "頁面開發",
+			Designer: "Designer",
+			apiDevelopment: "API開發",
+			apiDefinition: "API定義",
+
+			dataSourceManagement: "數據源管理",
+			clientManagement: "客戶端管理",
+			pluginManagement: "插件管理",
+
+		},
+		policyManagement: {
+			title: "政策管理",
+			policyControl: "政策控制",
+		}
+	}
 };

+ 340 - 0
src/i18n/pages/dateCenter/en.ts

@@ -0,0 +1,340 @@
+export default {
+  tabs: {
+    info: 'Source Info',
+    nodes: 'Data Nodes',
+    viewData: 'View Data',
+    baseInfo: 'Basic Info',
+    ruleExpr: 'Rule Expression',
+    reqParams: 'Request Params',
+    sourceConfig: 'Source Config',
+    indicator: {
+      dialogTitleAdd: 'Add Calculation Indicator Model',
+      dialogTitleEdit: 'Edit Calculation Indicator Model',
+      labels: {
+        key: 'Indicator Key',
+        name: 'Indicator Name',
+        description: 'Description',
+        formula: 'Formula',
+        dataType: 'Data Type',
+        accuracy: 'Accuracy',
+        operators: 'Operators',
+        deviceAttr: 'Device Attribute',
+        currentFormula: 'Current Formula',
+        editFormula: 'Edit',
+      },
+      placeholders: {
+        inputKey: 'Enter indicator key',
+        inputName: 'Enter indicator name',
+        inputDescription: 'Enter description',
+        inputFormula: 'Enter formula',
+        selectDataType: 'Select data type',
+        inputAccuracy: 'Enter accuracy',
+      },
+      types: {
+        int: 'int (integer)',
+        long: 'long (long integer)',
+        float: 'float (single precision)',
+        double: 'double (double precision)',
+      },
+    },
+  },
+  labels: {
+    sourceNameTitle: 'Source Name:',
+    sourceStatus: 'Status',
+    sourceKey: 'Source Key',
+    sourceName: 'Source Name',
+    sourceDesc: 'Source Description',
+    sourceFrom: 'Source From',
+    deviceKey: 'Device Key',
+    productKey: 'Product Key',
+    dbType: 'Source From',
+    host: 'Host',
+    port: 'Port',
+    user: 'Username',
+    password: 'Password',
+    dbName: 'Database Name',
+    tableName: 'Table Name',
+    pk: 'Primary Key',
+    num: 'Batch Size',
+    cronExpression: 'Cron Expression',
+    expression: 'Expression',
+    param: 'Param',
+    method: 'Method',
+    url: 'URL',
+    interval: 'Update Interval',
+    paramType: 'Param Type',
+    paramTitle: 'Param Title',
+    paramKey: 'Param Key',
+    paramValue: 'Param Value',
+  },
+  columns: {
+    id: 'ID',
+    key: 'Key',
+    name: 'Name',
+    dataType: 'Data Type',
+    value: 'Value Items',
+    createdAt: 'Created At',
+    action: 'Actions',
+    sourceId: 'ID',
+    from: 'Source Type',
+  },
+  actions: {
+    publish: 'Publish',
+    disable: 'Disable',
+    add: 'Add',
+    edit: 'Edit',
+    delete: 'Delete',
+    detail: 'Detail',
+    records: 'Records',
+    search: 'Search',
+    createSource: 'New Source',
+    batchDelete: 'Batch Delete',
+    copy: 'Copy',
+  },
+  status: {
+    published: 'Published',
+    unpublished: 'Unpublished',
+    all: 'All',
+  },
+  options: {
+    all: 'All',
+    api: 'API Import',
+    db: 'Database',
+    file: 'File',
+    device: 'Device',
+    yes: 'Yes',
+    no: 'No',
+  },
+  messages: {
+    opSuccess: 'Operation succeeded',
+    deleteConfirmSelected: 'Are you sure to delete selected data?',
+    deleteNodeConfirm: 'This will permanently delete node: "{name}". Continue?',
+    pleaseSelectToDelete: 'Please select data to delete.',
+    tip: 'Tip',
+    confirm: 'Confirm',
+    cancel: 'Cancel',
+    deleteSuccess: 'Deleted successfully',
+    copyConfirm: 'Confirm to copy this data?',
+    copySuccess: 'Copied successfully',
+    batchDeleteConfirm: 'Confirm to batch delete these data?',
+    deleteRowConfirm: 'This will delete: "{name}". Continue?',
+  },
+  placeholders: {
+    input: 'Please enter',
+    sourceType: 'Source Type',
+  },
+  tags: {
+    labels: {
+      entity: 'Tag',
+      name: 'Tag Name',
+      code: 'English Code',
+      parent: 'Parent Tag',
+      color: 'Tag Color',
+      description: 'Description',
+    },
+    columns: {
+      name: 'Tag Name',
+      code: 'English Code',
+      color: 'Color',
+      description: 'Description',
+      createdAt: 'Created At',
+      action: 'Actions',
+    },
+    actions: {
+      search: 'Search',
+      createTag: 'New Tag',
+      add: 'Add',
+      edit: 'Edit',
+      delete: 'Delete',
+      cancel: 'Cancel',
+      confirm: 'Confirm',
+    },
+    options: {
+      colors: {
+        red: 'Red',
+        green: 'Green',
+        blue: 'Blue',
+        yellow: 'Yellow',
+        purple: 'Purple',
+        pink: 'Pink',
+        indigo: 'Indigo',
+        gray: 'Gray',
+        orange: 'Orange',
+        cyan: 'Cyan',
+      },
+    },
+    placeholders: {
+      searchNameOrCode: 'Search tag name or English code',
+      inputName: 'Please enter tag name',
+      inputCode: 'Please enter English code',
+      selectParent: 'Please select parent tag',
+      selectColor: 'Please select tag color',
+      inputDesc: 'Please enter description',
+    },
+    messages: {
+      tip: 'Tip',
+      deleteRowConfirm: 'This will permanently delete the tag: "{name}". Continue?',
+      deleteSuccess: 'Deleted successfully',
+      addSuccess: 'Added successfully',
+      editSuccess: 'Edited successfully',
+      required: ' is required',
+    },
+  },
+  modeling: {
+    labels: {
+      modelKey: 'Model Key',
+      tableName: 'Table Name',
+      modelName: 'Model Name',
+      modelType: 'Model Type',
+      desc: 'Description',
+      fieldTitle: 'Field Title',
+      fieldName: 'Field Name',
+      dataSourceName: 'Data Source Name',
+      defaultValue: 'Default Value',
+      remark: 'Remark',
+      indicatorKey: 'Indicator Key',
+      indicatorName: 'Indicator Name',
+      formula: 'Formula',
+      dataType: 'Data Type',
+      second: 'Second',
+      minute: 'Minute',
+      hour: 'Hour',
+      day: 'Day',
+      custom: 'Custom',
+      method: 'Method',
+      isPk: 'Primary Key',
+      isSorting: 'Sortable',
+      sortOrder: 'Sort Order',
+      orderDesc: 'Descending',
+      orderAsc: 'Ascending',
+    },
+    methods: {
+      default: 'Default',
+      max: 'Max',
+      min: 'Min',
+      avg: 'Average',
+      sum: 'Sum',
+    },
+    types: {
+      int: 'int (integer)',
+      long: 'long (long integer)',
+      float: 'float (single precision)',
+      double: 'double (double precision)',
+      string: 'string',
+      boolean: 'boolean',
+      date: 'date',
+    },
+    fieldNode: {
+      dialogTitleAdd: 'Add Field Node',
+      dialogTitleEdit: 'Edit Field Node',
+    },
+    columns: {
+      id: 'ID',
+      name: 'Model Name',
+      type: 'Type',
+      typeName: 'Type',
+      desc: 'Description',
+      status: 'Status',
+      createdAt: 'Created At',
+      action: 'Actions',
+    },
+    placeholders: {
+      input: 'Please enter',
+      select: 'Please select',
+      inputFieldTitle: 'Please enter field title',
+      inputFieldName: 'Please enter field name',
+      inputIndicatorKey: 'Please enter indicator key',
+    },
+    actions: {
+      search: 'Search',
+      reset: 'Reset',
+      createModel: 'New Model',
+      delete: 'Delete',
+      edit: 'Edit',
+      copy: 'Copy',
+      fieldManage: 'Field Manage',
+      dataRecord: 'Data Records',
+      aggregateSetting: 'Aggregation Settings',
+      calcIndicators: 'Calculation Indicators',
+      addFieldNode: 'Add Field Node',
+      addCalcIndicator: 'New Calculation Indicator Model',
+      publish: 'Publish',
+      unpublish: 'Unpublish',
+      confirm: 'Confirm',
+      cancel: 'Cancel',
+      set: 'Set',
+    },
+    status: {
+      published: 'Published',
+      unpublished: 'Unpublished',
+    },
+    messages: {
+      tip: 'Tips',
+      selectToDelete: 'Please select the data to delete.',
+      confirmBatchDelete: 'Are you sure you want to delete the selected data?',
+      deleteModelConfirm: 'This will permanently delete the model: "{name}". Continue?',
+      deleteNodeConfirm: 'This will permanently delete the node: "{name}". Continue?',
+      deleteSuccess: 'Deleted successfully',
+      copyConfirm: 'Confirm to copy?',
+      copySuccess: 'Copied successfully',
+      operateSuccess: 'Operation succeeded',
+      confirmPublish: 'Confirm to {status}?',
+      fieldRequired: '{field} is required',
+      lettersOnly: 'Letters only',
+    },
+    aggregate: {
+      dialogTitle: 'Set Aggregation',
+      labels: {
+        groupNode: 'Group Node',
+        timeWindowNode: 'Time Window Node',
+        duration: 'Duration',
+        unit: 'Time Unit',
+      },
+      placeholders: {
+        selectGroupNode: 'Select group node',
+        selectTimeWindowNode: 'Select time window node',
+        inputDuration: 'Enter duration',
+        selectUnit: 'Select unit',
+      },
+      units: {
+        1: 'Second',
+        2: 'Minute',
+        3: 'Hour',
+        4: 'Day',
+      },
+    },
+    units: {
+      1: 'Second',
+      2: 'Minute',
+      3: 'Hour',
+      4: 'Day',
+    },
+    indicator: {
+      key: 'Indicator Key',
+      name: 'Indicator Name',
+      formula: 'Formula',
+    },
+    relation: {
+      dialogTitle: 'Set Main Source and Join Fields',
+      labels: {
+        source: 'Data Source',
+        sourceNode: 'Source Node',
+      },
+      placeholders: {
+        selectSource: 'Please select data source',
+        selectSourceNode: 'Please select source node',
+      },
+    },
+    edit: {
+      dialogTitleAdd: 'Add Model',
+      dialogTitleEdit: 'Edit Model',
+      labels: {
+        scheduleRequest: 'Scheduled Request',
+        busiTypes: 'Unit Types',
+      },
+      cron: {
+        selectTitle: 'Select Cron Rule',
+      },
+    },
+  },
+};

+ 340 - 0
src/i18n/pages/dateCenter/zh-cn.ts

@@ -0,0 +1,340 @@
+export default {
+  tabs: {
+    info: '数据源信息',
+    nodes: '数据节点',
+    viewData: '查看数据',
+    baseInfo: '基本信息',
+    ruleExpr: '规则表达式',
+    reqParams: '请求参数',
+    sourceConfig: '数据源配置',
+    indicator: {
+      dialogTitleAdd: '新增计算指标模型',
+      dialogTitleEdit: '修改计算指标模型',
+      labels: {
+        key: '指标标识',
+        name: '指标名称',
+        formula: '计算公式',
+        dataType: '数据类型',
+        description: '指标说明',
+        accuracy: '精度',
+        operators: '运算符号',
+        deviceAttr: '设备属性',
+        currentFormula: '当前公式',
+        editFormula: '编辑',
+      },
+      placeholders: {
+        inputKey: '请输入指标标识',
+        inputName: '请输入指标名称',
+        inputDescription: '请输入指标说明',
+        inputFormula: '请输入计算公式',
+        selectDataType: '请选择数据类型',
+        inputAccuracy: '请输入精度',
+      },
+      types: {
+        int: 'int(整数型)',
+        long: 'long(长整数型)',
+        float: 'float(单精度)',
+        double: 'double(双精度)',
+      },
+    },
+  },
+  labels: {
+    sourceNameTitle: '数据源名称:',
+    sourceStatus: '状态',
+    sourceKey: '数据源标识',
+    sourceName: '数据源名称',
+    sourceDesc: '数据源描述',
+    sourceFrom: '数据来源',
+    deviceKey: '设备key',
+    productKey: '产品key',
+    dbType: '数据来源',
+    host: '主机地址',
+    port: '端口号',
+    user: '用户名',
+    password: '密码',
+    dbName: '数据库名称',
+    tableName: '表名称',
+    pk: '主键字段',
+    num: '每次获取数量',
+    cronExpression: '任务表达式',
+    expression: '表达式',
+    param: '参数',
+    method: '请求方法',
+    url: '请求地址',
+    interval: '更新时间',
+    paramType: '参数类型',
+    paramTitle: '参数标题',
+    paramKey: '参数名',
+    paramValue: '参数值',
+  },
+  columns: {
+    id: 'ID',
+    key: '数据标识',
+    name: '数据名称',
+    dataType: '数据类型',
+    value: '数据取值项',
+    createdAt: '创建时间',
+    action: '操作',
+    sourceId: 'ID',
+    from: '数据源类型',
+  },
+  actions: {
+    publish: '发布',
+    disable: '停用',
+    add: '添加',
+    edit: '修改',
+    delete: '删除',
+    detail: '详情',
+    records: '数据记录',
+    search: '查询',
+    createSource: '新增数据源',
+    batchDelete: '批量删除',
+    copy: '复制',
+  },
+  status: {
+    published: '已发布',
+    unpublished: '未发布',
+    all: '全部',
+  },
+  options: {
+    all: '全部',
+    api: 'api导入',
+    db: '数据库',
+    file: '文件',
+    device: '设备',
+    yes: '是',
+    no: '否',
+  },
+  messages: {
+    opSuccess: '操作成功',
+    deleteConfirmSelected: '你确定要删除所选数据?',
+    deleteNodeConfirm: '此操作将永久删除数据节点:“{name}”,是否继续?',
+    pleaseSelectToDelete: '请选择要删除的数据。',
+    tip: '提示',
+    confirm: '确认',
+    cancel: '取消',
+    deleteSuccess: '删除成功',
+    copyConfirm: '确定要复制该数据吗?',
+    copySuccess: '复制成功',
+    batchDeleteConfirm: '是否确认要批量删除这些数据吗?',
+    deleteRowConfirm: '此操作将删除:“{name}”,是否继续?',
+  },
+  placeholders: {
+    input: '请输入',
+    sourceType: '数据源类型',
+  },
+  tags: {
+    labels: {
+      entity: '标签',
+      name: '标签名称',
+      code: '英文标识',
+      parent: '父级标签',
+      color: '标签颜色',
+      description: '描述',
+    },
+    columns: {
+      name: '标签名称',
+      code: '英文标识',
+      color: '颜色',
+      description: '描述',
+      createdAt: '创建时间',
+      action: '操作',
+    },
+    actions: {
+      search: '查询',
+      createTag: '新增标签',
+      add: '添加',
+      edit: '修改',
+      delete: '删除',
+      cancel: '取消',
+      confirm: '确认',
+    },
+    options: {
+      colors: {
+        red: '红色',
+        green: '绿色',
+        blue: '蓝色',
+        yellow: '黄色',
+        purple: '紫色',
+        pink: '粉色',
+        indigo: '靛蓝',
+        gray: '灰色',
+        orange: '橙色',
+        cyan: '青色',
+      },
+    },
+    placeholders: {
+      searchNameOrCode: '搜索标签名称或英文标识',
+      inputName: '请输入标签名称',
+      inputCode: '请输入英文标识',
+      selectParent: '请选择父级标签',
+      selectColor: '请选择标签颜色',
+      inputDesc: '请输入描述',
+    },
+    messages: {
+      tip: '提示',
+      deleteRowConfirm: '此操作将永久删除标签:“{name}”,是否继续?',
+      deleteSuccess: '删除成功',
+      addSuccess: '添加成功',
+      editSuccess: '修改成功',
+      required: '不能为空',
+    },
+  },
+  modeling: {
+    labels: {
+      modelKey: '模型标识',
+      tableName: '模型表名',
+      modelName: '模型名称',
+      modelType: '模型类型',
+      desc: '描述',
+      fieldTitle: '字段标题',
+      fieldName: '字段名称',
+      dataSourceName: '数据源名称',
+      defaultValue: '默认值',
+      remark: '备注说明',
+      indicatorKey: '指标标识',
+      indicatorName: '指标名称',
+      formula: '计算公式',
+      dataType: '数据类型',
+      second: '秒',
+      minute: '分',
+      hour: '时',
+      day: '天',
+      custom: '自定义',
+      method: '取值方法',
+      isPk: '是否为主键',
+      isSorting: '是否排序',
+      sortOrder: '排序方式',
+      orderDesc: '降序',
+      orderAsc: '升序',
+    },
+    methods: {
+      default: '默认',
+      max: '最大值',
+      min: '最小值',
+      avg: '平均值',
+      sum: '求和',
+    },
+    types: {
+      int: 'int(整数型)',
+      long: 'long(长整数型)',
+      float: 'float(单精度)',
+      double: 'double(双精度)',
+      string: 'string(字符串)',
+      boolean: 'boolean(布尔)',
+      date: 'date(日期时间)',
+    },
+    fieldNode: {
+      dialogTitleAdd: '新增字段节点',
+      dialogTitleEdit: '修改字段节点',
+    },
+    columns: {
+      id: 'ID',
+      name: '模型名称',
+      type: '类型',
+      typeName: '类型',
+      desc: '描述',
+      status: '状态',
+      createdAt: '创建时间',
+      action: '操作',
+    },
+    placeholders: {
+      input: '请输入',
+      select: '请选择',
+      inputFieldTitle: '请输入字段标题',
+      inputFieldName: '请输入字段名称',
+      inputIndicatorKey: '请输入指标标识',
+    },
+    actions: {
+      search: '查询',
+      reset: '重置',
+      createModel: '新增模型',
+      delete: '删除',
+      edit: '修改',
+      copy: '复制',
+      fieldManage: '字段管理',
+      dataRecord: '数据记录',
+      aggregateSetting: '聚合设置',
+      calcIndicators: '计算指标',
+      addFieldNode: '新增字段节点',
+      addCalcIndicator: '新增计算指标模型',
+      publish: '发布',
+      unpublish: '取消发布',
+      confirm: '确认',
+      cancel: '取消',
+      set: '设置',
+    },
+    status: {
+      published: '已发布',
+      unpublished: '未发布',
+    },
+    messages: {
+      tip: '提示',
+      selectToDelete: '请选择要删除的数据。',
+      confirmBatchDelete: '你确定要删除所选数据?',
+      deleteModelConfirm: '此操作将永久删除模型:“{name}”,是否继续?',
+      deleteNodeConfirm: '此操作将永久删除数据节点:“{name}”,是否继续?',
+      deleteSuccess: '删除成功',
+      copyConfirm: '确定要复制该数据吗?',
+      copySuccess: '复制成功',
+      operateSuccess: '操作成功',
+      confirmPublish: '是否{status}?',
+      fieldRequired: '{field}不能为空',
+      lettersOnly: '只能输入英文字母',
+    },
+    aggregate: {
+      dialogTitle: '设置聚合',
+      labels: {
+        groupNode: '分组节点',
+        timeWindowNode: '时间窗口节点',
+        duration: '时间值',
+        unit: '时间单位',
+      },
+      placeholders: {
+        selectGroupNode: '请选择分组节点',
+        selectTimeWindowNode: '请选择时间窗口节点',
+        inputDuration: '请输入时间值',
+        selectUnit: '请选择时间单位',
+      },
+      units: {
+        1: '秒',
+        2: '分',
+        3: '时',
+        4: '天',
+      },
+    },
+    units: {
+        1: '秒',
+        2: '分',
+        3: '时',
+        4: '天',
+      },
+    indicator: {
+      key: '指标标识',
+      name: '指标名称',
+      formula: '计算公式',
+    },
+    relation: {
+      dialogTitle: '设置主源、关联字段',
+      labels: {
+        source: '数据源',
+        sourceNode: '数据源节点',
+      },
+      placeholders: {
+        selectSource: '请选择数据源',
+        selectSourceNode: '请选择数据源节点',
+      },
+    },
+    edit: {
+      dialogTitleAdd: '添加模型',
+      dialogTitleEdit: '修改模型',
+      labels: {
+        scheduleRequest: '定时请求',
+        busiTypes: '单元类型',
+      },
+      cron: {
+        selectTitle: '选择 Cron 规则',
+      },
+    },
+  },
+};

+ 329 - 0
src/i18n/pages/dateCenter/zh-tw.ts

@@ -0,0 +1,329 @@
+export default {
+  tabs: {
+    info: '資料源資訊',
+    nodes: '資料節點',
+    viewData: '查看資料',
+    baseInfo: '基本資訊',
+    ruleExpr: '規則表示式',
+    reqParams: '請求參數',
+    sourceConfig: '資料源配置',
+    indicator: {
+      dialogTitleAdd: '新增計算指標模型',
+      dialogTitleEdit: '修改計算指標模型',
+      labels: {
+        key: '指標標識',
+        name: '指標名稱',
+        formula: '計算公式',
+        dataType: '資料型別',
+        description: '指標說明',
+        accuracy: '精度',
+        operators: '運算符號',
+        deviceAttr: '設備屬性',
+        currentFormula: '當前公式',
+        editFormula: '編輯',
+      },
+      placeholders: {
+        inputKey: '請輸入指標標識',
+        inputName: '請輸入指標名稱',
+        inputDescription: '請輸入指標說明',
+        inputFormula: '請輸入計算公式',
+        selectDataType: '請選擇資料型別',
+        inputAccuracy: '請輸入精度',
+      },
+      types: {
+        int: 'int(整數型)',
+        long: 'long(長整數型)',
+        float: 'float(單精度)',
+        double: 'double(雙精度)',
+      },
+    },
+  },
+  labels: {
+    sourceNameTitle: '資料源名稱:',
+    sourceStatus: '狀態',
+    sourceKey: '資料源標識',
+    sourceName: '資料源名稱',
+    sourceDesc: '資料源描述',
+    sourceFrom: '資料來源',
+    deviceKey: '設備 key',
+    productKey: '產品 key',
+    dbType: '資料來源',
+    host: '主機地址',
+    port: '埠號',
+    user: '使用者名',
+    password: '密碼',
+    dbName: '資料庫名稱',
+    tableName: '表名稱',
+    pk: '主鍵欄位',
+    num: '每次獲取數量',
+    cronExpression: '排程表示式',
+    expression: '表示式',
+    param: '參數',
+    method: '請求方法',
+    url: '請求地址',
+    interval: '更新間隔',
+    paramType: '參數類型',
+    paramTitle: '參數標題',
+    paramKey: '參數名',
+    paramValue: '參數值',
+  },
+  columns: {
+    id: 'ID',
+    key: '資料標識',
+    name: '資料名稱',
+    dataType: '資料型別',
+    value: '取值項',
+    createdAt: '建立時間',
+    action: '操作',
+    sourceId: 'ID',
+    from: '資料源類型',
+  },
+  actions: {
+    publish: '發布',
+    disable: '停用',
+    add: '新增',
+    edit: '修改',
+    delete: '刪除',
+    detail: '詳情',
+    records: '資料記錄',
+    search: '查詢',
+    createSource: '新增資料源',
+    batchDelete: '批次刪除',
+    copy: '複製',
+  },
+  status: {
+    published: '已發布',
+    unpublished: '未發布',
+    all: '全部',
+  },
+  options: {
+    all: '全部',
+    api: 'API 導入',
+    db: '資料庫',
+    file: '檔案',
+    device: '設備',
+    yes: '是',
+    no: '否',
+  },
+  messages: {
+    opSuccess: '操作成功',
+    deleteConfirmSelected: '確定要刪除所選資料嗎?',
+    deleteNodeConfirm: '此操作將永久刪除資料節點:「{name}」,是否繼續?',
+    pleaseSelectToDelete: '請選擇要刪除的資料。',
+    tip: '提示',
+    confirm: '確認',
+    cancel: '取消',
+    deleteSuccess: '刪除成功',
+    copyConfirm: '確定要複製該資料嗎?',
+    copySuccess: '複製成功',
+    batchDeleteConfirm: '是否確認要批次刪除這些資料?',
+    deleteRowConfirm: '此操作將刪除:「{name}」,是否繼續?',
+  },
+  placeholders: {
+    input: '請輸入',
+    sourceType: '數據源類型',
+  },
+  tags: {
+    labels: {
+      entity: '標籤',
+      name: '標籤名稱',
+      code: '英文標識',
+      parent: '父級標籤',
+      color: '標籤顏色',
+      description: '描述',
+    },
+    columns: {
+      name: '標籤名稱',
+      code: '英文標識',
+      color: '顏色',
+      description: '描述',
+      createdAt: '創建時間',
+      action: '操作',
+    },
+    actions: {
+      search: '查詢',
+      createTag: '新增標籤',
+      add: '添加',
+      edit: '修改',
+      delete: '刪除',
+      cancel: '取消',
+      confirm: '確認',
+    },
+    options: {
+      colors: {
+        red: '紅色',
+        green: '綠色',
+        blue: '藍色',
+        yellow: '黃色',
+        purple: '紫色',
+        pink: '粉色',
+        indigo: '靛藍',
+        gray: '灰色',
+        orange: '橙色',
+        cyan: '青色',
+      },
+    },
+    placeholders: {
+      searchNameOrCode: '搜索標籤名稱或英文標識',
+      inputName: '請輸入標籤名稱',
+      inputCode: '請輸入英文標識',
+      selectParent: '請選擇父級標籤',
+      selectColor: '請選擇標籤顏色',
+      inputDesc: '請輸入描述',
+    },
+    messages: {
+      tip: '提示',
+      deleteRowConfirm: '此操作將永久刪除標籤:「{name}」,是否繼續?',
+      deleteSuccess: '刪除成功',
+      addSuccess: '添加成功',
+      editSuccess: '修改成功',
+      required: '不能為空',
+    },
+  },
+  modeling: {
+    labels: {
+      modelKey: '模型標識',
+      tableName: '模型表名',
+      modelName: '模型名稱',
+      modelType: '模型類型',
+      desc: '描述',
+      fieldTitle: '欄位標題',
+      fieldName: '欄位名稱',
+      dataSourceName: '資料源名稱',
+      defaultValue: '預設值',
+      remark: '備註說明',
+      indicatorKey: '指標標識',
+      indicatorName: '指標名稱',
+      formula: '計算公式',
+      dataType: '資料型別',
+      second: '秒',
+      minute: '分',
+      hour: '時',
+      day: '天',
+      custom: '自定義',
+      method: '取值方法',
+      isPk: '是否為主鍵',
+      isSorting: '是否排序',
+      sortOrder: '排序方式',
+      orderDesc: '降序',
+      orderAsc: '升序',
+    },
+    methods: {
+      default: '預設',
+      max: '最大值',
+      min: '最小值',
+      avg: '平均值',
+      sum: '求和',
+    },
+    types: {
+      int: 'int(整數型)',
+      long: 'long(長整數型)',
+      float: 'float(單精度)',
+      double: 'double(雙精度)',
+      string: 'string(字串)',
+      boolean: 'boolean(布林)',
+      date: 'date(日期時間)',
+    },
+    fieldNode: {
+      dialogTitleAdd: '新增欄位節點',
+      dialogTitleEdit: '修改欄位節點',
+    },
+    columns: {
+      id: 'ID',
+      name: '模型名稱',
+      type: '類型',
+      typeName: '類型',
+      desc: '描述',
+      status: '狀態',
+      createdAt: '建立時間',
+      action: '操作',
+    },
+    placeholders: {
+      input: '請輸入',
+      select: '請選擇',
+      inputFieldTitle: '請輸入欄位標題',
+      inputFieldName: '請輸入欄位名稱',
+      inputIndicatorKey: '請輸入指標標識',
+    },
+    actions: {
+      search: '查詢',
+      reset: '重置',
+      createModel: '新增模型',
+      delete: '刪除',
+      edit: '修改',
+      copy: '複製',
+      fieldManage: '欄位管理',
+      dataRecord: '資料記錄',
+      aggregateSetting: '聚合設定',
+      calcIndicators: '計算指標',
+      addFieldNode: '新增欄位節點',
+      addCalcIndicator: '新增計算指標模型',
+      publish: '發布',
+      unpublish: '取消發布',
+      confirm: '確認',
+      cancel: '取消',
+      set: '設定',
+    },
+    status: {
+      published: '已發布',
+      unpublished: '未發布',
+    },
+    messages: {
+      tip: '提示',
+      selectToDelete: '請選擇要刪除的資料。',
+      confirmBatchDelete: '你確定要刪除所選資料?',
+      deleteModelConfirm: '此操作將永久刪除模型:「{name}」,是否繼續?',
+      deleteNodeConfirm: '此操作將永久刪除資料節點:「{name}」,是否繼續?',
+      deleteSuccess: '刪除成功',
+      copyConfirm: '確定要複製該資料嗎?',
+      copySuccess: '複製成功',
+      operateSuccess: '操作成功',
+      confirmPublish: '是否{status}?',
+      fieldRequired: '{field}不能為空',
+      lettersOnly: '只能輸入英文字母',
+    },
+    aggregate: {
+      dialogTitle: '設定聚合',
+      labels: {
+        groupNode: '分組節點',
+        timeWindowNode: '時間視窗節點',
+        duration: '時間值',
+        unit: '時間單位',
+      },
+      placeholders: {
+        selectGroupNode: '請選擇分組節點',
+        selectTimeWindowNode: '請選擇時間視窗節點',
+        inputDuration: '請輸入時間值',
+        selectUnit: '請選擇單位',
+      },
+      units: {
+        1: '秒',
+        2: '分',
+        3: '時',
+        4: '天',
+      },
+    },
+    relation: {
+      dialogTitle: '設定主源、關聯欄位',
+      labels: {
+        source: '資料源',
+        sourceNode: '資料源節點',
+      },
+      placeholders: {
+        selectSource: '請選擇資料源',
+        selectSourceNode: '請選擇資料源節點',
+      },
+    },
+    edit: {
+      dialogTitleAdd: '新增模型',
+      dialogTitleEdit: '修改模型',
+      labels: {
+        scheduleRequest: '定時請求',
+        busiTypes: '單元類型',
+      },
+      cron: {
+        selectTitle: '選擇 Cron 規則',
+      },
+    },
+  },
+};

+ 28 - 0
src/i18n/pages/formI18n/en.ts

@@ -4,10 +4,38 @@ export default {
 		name: 'name',
 		email: 'email',
 		autograph: 'autograph',
+		createdTime: "Created Time",
+		alarmStatus: "Alarm Status",
+		categoryName: "Category Name",
+		sort: "Sort",
+		parentCategory: "Parent Category",
+		categoryKey: "Category Key",
+		desc: "Description"
 	},
 	formI18nPlaceholder: {
 		name: 'Please enter your name',
 		email: 'Please enter the users Department',
 		autograph: 'Please enter the login account name',
+		startDate: "The start date",
+		endDate: "The end date",
+		alarmStatus: "Alarm Status",
+		categoryName: "Please enter a category Name",
+		parentCategory: "Please select a category",
+		categoryKey: "Please enter a category key",
+		desc: "Please enter a description"
 	},
+	formI18nOption: {
+		unprocessed: "Unprocessed",
+		processed: "Processed",
+		ignored: "Ignored"
+	},
+	formI18nButton: {
+		query: "Query",
+		reset: "Reset",
+		addCategory: "Add Category",
+		edit: "Edit",
+		add: "Add",
+		cancel: "Cancel",
+		delete: "Delete"
+	}
 };

+ 36 - 0
src/i18n/pages/formI18n/zh-cn.ts

@@ -1,13 +1,49 @@
+/*
+ * @Author: vera_min vera_min@163.com
+ * @Date: 2025-08-02 12:21:54
+ * @LastEditors: vera_min vera_min@163.com
+ * @LastEditTime: 2025-08-06 18:04:35
+ * @FilePath: /sagoo-admin-ui/src/i18n/pages/formI18n/zh-cn.ts
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ */
 // 定义内容
 export default {
 	formI18nLabel: {
 		name: '姓名',
 		email: '用户归属组织',
 		autograph: '登陆账户名',
+		createdTime: "创建时间",
+		alarmStatus: "告警状态",
+		categoryName: "分类名称",
+		sort: "排序",
+		parentCategory: "上级分类",
+		categoryKey: "分类标识",
+		desc: "描述"
 	},
 	formI18nPlaceholder: {
 		name: '请输入姓名',
 		email: '请输入用户归属组织',
 		autograph: '请输入登陆账户名',
+		startDate: "开始日期",
+		endDate: "结束日期",
+		alarmStatus: "告警状态",
+		categoryName: "请输入分类名称",
+		parentCategory: "请选择分类",
+		categoryKey: "请输入分类标识",
+		desc: "请输入描述",
 	},
+	formI18nOption: {
+		unprocessed: "未处理",
+		processed: "已处理",
+		ignored: "已忽略"
+	},
+	formI18nButton: {
+		query: "查询",
+		reset: "重置",
+		addCategory: "新增分类",
+		edit: "修 改",
+		add: "添 加",
+		cancel: "取 消",
+		delete: "删除"
+	}
 };

+ 36 - 0
src/i18n/pages/formI18n/zh-tw.ts

@@ -1,13 +1,49 @@
+/*
+ * @Author: vera_min vera_min@163.com
+ * @Date: 2025-08-02 12:21:54
+ * @LastEditors: vera_min vera_min@163.com
+ * @LastEditTime: 2025-08-06 18:05:10
+ * @FilePath: /sagoo-admin-ui/src/i18n/pages/formI18n/zh-tw.ts
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ */
 // 定义内容
 export default {
 	formI18nLabel: {
 		name: '姓名',
 		email: '用戶歸屬部門',
 		autograph: '登入帳戶名',
+		createdTime: "創建時間",
+		alarmStatus: "告警狀態",
+		categoryName: "分類名稱",
+		sort: "排序",
+		parentCategory: "上級分類",
+		categoryKey: "分類標識",
+		desc: "描述"
 	},
 	formI18nPlaceholder: {
 		name: '請輸入姓名',
 		email: '請輸入用戶歸屬部門',
 		autograph: '請輸入登入帳戶名',
+		startDate: "開始日期",
+		endDate: "結束日期",
+		alarmStatus: "告警狀態",
+		categoryName: "請輸入分類名稱",
+		parentCategory: "請選擇分類",
+		categoryKey: "請輸入分類標識",
+		desc: "請輸入描述"
 	},
+	formI18nOption: {
+		unprocessed: "未處理",
+		processed: "已處理",
+		ignored: "已忽略"
+	},
+	formI18nButton: {
+		query: "查詢",
+		reset: "重置",
+		addCategory: "新增分類",
+		edit: "修 改",
+		add: "添 加",
+		cancel: "取 消",
+		delete: "刪除"
+	}
 };

+ 222 - 0
src/i18n/pages/iotCard/en.ts

@@ -0,0 +1,222 @@
+export default {
+  dashboard: {
+    title: 'Data Statistics',
+    carrier: {
+      placeholder: 'Please select',
+      telecom: 'Telecom',
+      unicom: 'Unicom',
+      mobile: 'Mobile'
+    },
+    statistics: {
+      yesterdayFlow: 'Yesterday Flow Consumption',
+      monthFlow: 'This Month Flow Consumption',
+      yearFlow: 'This Year Flow Consumption'
+    },
+    flowChart: {
+      title: 'Flow Statistics',
+      buttons: {
+        yesterday: 'Yesterday',
+        week: 'This Week',
+        month: 'This Month',
+        year: 'This Year'
+      },
+      datePicker: {
+        rangeSeparator: 'to',
+        startPlaceholder: 'Start Time',
+        endPlaceholder: 'End Time'
+      }
+    },
+    top10: {
+      title: 'Top 10 Flow Usage',
+      datePicker: {
+        rangeSeparator: 'to',
+        startPlaceholder: 'Start Date',
+        endPlaceholder: 'End Date'
+      }
+    },
+    charts: {
+      tooltip: {
+        traffic: 'Traffic'
+      },
+      series: {
+        traffic: 'Traffic'
+      }
+    }
+  },
+  index: {
+    search: {
+      placeholder: 'Enter ICCID or Card Number',
+      query: 'Query',
+      reset: 'Reset'
+    },
+    table: {
+      columns: {
+        cardNumber: 'Card Number',
+        iccid: 'ICCID',
+        bindDevice: 'Bind Device',
+        platform: 'Platform',
+        operator: 'Operator',
+        type: 'Type',
+        totalFlow: 'Total Flow',
+        usedFlow: 'Used Flow',
+        remainFlow: 'Remain Flow',
+        activationDate: 'Activation Date',
+        updateTime: 'Update Time',
+        status: 'Status',
+        actions: 'Actions'
+      },
+      actions: {
+        detail: 'Detail',
+        delete: 'Delete'
+      }
+    },
+    operators: {
+      telecom: 'China Telecom',
+      unicom: 'China Unicom',
+      mobile: 'China Mobile'
+    },
+    types: {
+      monthly: 'Monthly',
+      quarterly: 'Quarterly',
+      yearly: 'Yearly',
+      other: 'Other'
+    },
+    status: {
+      activatable: 'Activatable',
+      testActivated: 'Test Activated',
+      testDeactivated: 'Test Deactivated',
+      inUse: 'In Use',
+      suspended: 'Suspended',
+      operatorManaged: 'Operator Managed'
+    },
+    messages: {
+      deleteConfirm: 'This operation will delete the card number: "{cardNumber}", continue?',
+      tip: 'Tip',
+      confirm: 'Confirm',
+      cancel: 'Cancel',
+      deleteSuccess: 'Delete Success'
+    }
+  },
+  detail: {
+    basicInfo: {
+      title: 'Basic Information',
+      cardNumber: 'Card Number',
+      iccid: 'ICCID',
+      bindDevice: 'Bind Device',
+      platformType: 'Platform Type',
+      platformName: 'Platform Name',
+      operator: 'Operator',
+      type: 'Type',
+      activationDate: 'Activation Date',
+      updateTime: 'Update Time',
+      totalFlow: 'Total Flow',
+      usedFlow: 'Used Flow',
+      remainFlow: 'Remain Flow',
+      status: 'Status',
+      description: 'Description'
+    },
+    flowChart: {
+      title: 'Flow Statistics',
+      buttons: {
+        yesterday: 'Yesterday',
+        week: 'Last Week',
+        month: 'Last Month',
+        year: 'Last Year'
+      },
+      datePicker: {
+        rangeSeparator: 'to',
+        startPlaceholder: 'Start Time',
+        endPlaceholder: 'End Time'
+      }
+    },
+    dataStatistics: {
+      title: 'Data Statistics',
+      yesterdayFlow: 'Yesterday Flow Consumption',
+      monthFlow: 'Current Month Flow Consumption',
+      yearFlow: 'Current Year Flow Consumption'
+    }
+  },
+  platformManage: {
+    search: {
+      placeholder: 'Enter keyword to search',
+      query: 'Query',
+      reset: 'Reset',
+      add: 'Add'
+    },
+    table: {
+      columns: {
+        name: 'Name',
+        platformType: 'Platform Type',
+        appId: 'App ID',
+        status: 'Status',
+        description: 'Description',
+        actions: 'Actions'
+      },
+      actions: {
+        edit: 'Edit',
+        delete: 'Delete'
+      }
+    },
+    status: {
+      disabled: 'Disabled',
+      enabled: 'Enabled'
+    },
+    messages: {
+      deleteConfirm: 'This operation will delete the card number: "{cardNumber}", continue?',
+      tip: 'Tip',
+      confirm: 'Confirm',
+      cancel: 'Cancel',
+      deleteSuccess: 'Delete Success'
+    },
+    addOrEdit: {
+      title: {
+        add: 'Add',
+        edit: 'Edit'
+      },
+      form: {
+        platformType: 'Platform Type',
+        name: 'Name',
+        appId: 'App ID',
+        secretKey: 'Secret Key',
+        userId: 'User ID',
+        interfaceUrl: 'Interface URL',
+        status: 'Status',
+        description: 'Description'
+      },
+      placeholders: {
+        select: 'Please select',
+        name: 'Please enter name',
+        appId: 'Please enter App ID',
+        secretKey: 'Please enter secret key',
+        userId: 'Please enter user ID',
+        interfaceUrl: 'Please enter interface URL',
+        description: 'Please enter description'
+      },
+      operators: {
+        telecom: 'China Telecom',
+        unicom: 'China Unicom',
+        mobile: 'China Mobile'
+      },
+      switch: {
+        enabled: 'On',
+        disabled: 'Off'
+      },
+      buttons: {
+        cancel: 'Cancel',
+        confirm: 'Confirm'
+      },
+      validation: {
+        platformType: 'Please select platform type',
+        name: 'Please enter name',
+        userId: 'Please enter user ID',
+        secretKey: 'Please enter secret key',
+        appId: 'Please enter App ID',
+        interfaceUrl: 'Please enter interface URL'
+      },
+      messages: {
+        editSuccess: 'Edit Success',
+        addSuccess: 'Add Success'
+      }
+    }
+  }
+};

+ 222 - 0
src/i18n/pages/iotCard/zh-cn.ts

@@ -0,0 +1,222 @@
+export default {
+	dashboard: {
+		title: '数据统计',
+		carrier: {
+			placeholder: '请选择',
+			telecom: '电信',
+			unicom: '联通',
+			mobile: '移动'
+		},
+		statistics: {
+			yesterdayFlow: '昨日流量消耗',
+			monthFlow: '当月流量消耗',
+			yearFlow: '本年流量消耗'
+		},
+		flowChart: {
+			title: '流量统计',
+			buttons: {
+				yesterday: '昨日',
+				week: '近一周',
+				month: '近一月',
+				year: '近一年'
+			},
+			datePicker: {
+				rangeSeparator: '至',
+				startPlaceholder: '开始时间',
+				endPlaceholder: '结束时间'
+			}
+		},
+		top10: {
+			title: '流量使用TOP10',
+			datePicker: {
+				rangeSeparator: '至',
+				startPlaceholder: '开始日期',
+				endPlaceholder: '结束日期'
+			}
+		},
+		charts: {
+			tooltip: {
+				traffic: '流量'
+			},
+			series: {
+				traffic: '流量'
+			}
+		}
+	},
+	index: {
+		search: {
+			placeholder: '请输入ICCID或卡号',
+			query: '查询',
+			reset: '重置'
+		},
+		table: {
+			columns: {
+				cardNumber: '卡号',
+				iccid: 'ICCID',
+				bindDevice: '绑定设备',
+				platform: '平台对接',
+				operator: '运营商',
+				type: '类型',
+				totalFlow: '总流量',
+				usedFlow: '使用流量',
+				remainFlow: '剩余流量',
+				activationDate: '激活日期',
+				updateTime: '更新时间',
+				status: '状态',
+				actions: '操作'
+			},
+			actions: {
+				detail: '详情',
+				delete: '删除'
+			}
+		},
+		operators: {
+			telecom: '电信',
+			unicom: '联通',
+			mobile: '移动'
+		},
+		types: {
+			monthly: '月卡',
+			quarterly: '季卡',
+			yearly: '年卡',
+			other: '其他'
+		},
+		status: {
+			activatable: '可激活',
+			testActivated: '测试激活',
+			testDeactivated: '测试去激活',
+			inUse: '在用',
+			suspended: '停机',
+			operatorManaged: '运营商管理状态'
+		},
+		messages: {
+			deleteConfirm: '此操作将卡号为:“{cardNumber}”进行删除,是否继续?',
+			tip: '提示',
+			confirm: '确认',
+			cancel: '取消',
+			deleteSuccess: '删除成功'
+		}
+	},
+	detail: {
+		basicInfo: {
+			title: '基本信息',
+			cardNumber: '卡号',
+			iccid: 'ICCID',
+			bindDevice: '绑定设备',
+			platformType: '平台类型',
+			platformName: '平台名称',
+			operator: '运营商',
+			type: '类型',
+			activationDate: '激活日期',
+			updateTime: '更新时间',
+			totalFlow: '总流量',
+			usedFlow: '使用流量',
+			remainFlow: '剩余流量',
+			status: '状态',
+			description: '说明'
+		},
+		flowChart: {
+			title: '流量统计',
+			buttons: {
+				yesterday: '昨日',
+				week: '近一周',
+				month: '近一月',
+				year: '近一年'
+			},
+			datePicker: {
+				rangeSeparator: '至',
+				startPlaceholder: '开始时间',
+				endPlaceholder: '结束时间'
+			}
+		},
+		dataStatistics: {
+			title: '数据统计',
+			yesterdayFlow: '昨日流量消耗',
+			monthFlow: '当月流量消耗',
+			yearFlow: '本年流量消耗'
+		}
+	},
+	platformManage: {
+		search: {
+			placeholder: '请输入关键字搜索',
+			query: '查询',
+			reset: '重置',
+			add: '新增'
+		},
+		table: {
+			columns: {
+				name: '名称',
+				platformType: '平台类型',
+				appId: 'App ID',
+				status: '状态',
+				description: '说明',
+				actions: '操作'
+			},
+			actions: {
+				edit: '编辑',
+				delete: '删除'
+			}
+		},
+		status: {
+			disabled: '禁用',
+			enabled: '开启'
+		},
+		messages: {
+			deleteConfirm: '此操作将卡号为:“{cardNumber}”,是否继续?',
+			tip: '提示',
+			confirm: '确认',
+			cancel: '取消',
+			deleteSuccess: '删除成功'
+		},
+		addOrEdit: {
+			title: {
+				add: '新增',
+				edit: '编辑'
+			},
+			form: {
+				platformType: '平台类型',
+				name: '名称',
+				appId: 'App ID',
+				secretKey: 'secretKey',
+				userId: '用户id',
+				interfaceUrl: '接口地址',
+				status: '状态',
+				description: '说明'
+			},
+			placeholders: {
+				select: '请选择',
+				name: '请输入名称',
+				appId: '请输入App ID',
+				secretKey: '请输入secretKey',
+				userId: '请输入用户id',
+				interfaceUrl: '请输入接口地址',
+				description: '请输入说明'
+			},
+			operators: {
+				telecom: '电信',
+				unicom: '联通',
+				mobile: '移动'
+			},
+			switch: {
+				enabled: '启',
+				disabled: '禁'
+			},
+			buttons: {
+				cancel: '取 消',
+				confirm: '确定'
+			},
+			validation: {
+				platformType: '请选择平台类型',
+				name: '请输入名称',
+				userId: '请输入用户id',
+				secretKey: '请输入secretKey',
+				appId: '请输入App ID',
+				interfaceUrl: '请输入接口地址'
+			},
+			messages: {
+				editSuccess: '编辑成功',
+				addSuccess: '新增成功'
+			}
+		}
+	}
+};

+ 222 - 0
src/i18n/pages/iotCard/zh-tw.ts

@@ -0,0 +1,222 @@
+export default {
+	dashboard: {
+		title: '數據統計',
+		carrier: {
+			placeholder: '請選擇',
+			telecom: '電信',
+			unicom: '聯通',
+			mobile: '移動'
+		},
+		statistics: {
+			yesterdayFlow: '昨日流量消耗',
+			monthFlow: '當月流量消耗',
+			yearFlow: '本年流量消耗'
+		},
+		flowChart: {
+			title: '流量統計',
+			buttons: {
+				yesterday: '昨日',
+				week: '近一週',
+				month: '近一月',
+				year: '近一年'
+			},
+			datePicker: {
+				rangeSeparator: '至',
+				startPlaceholder: '開始時間',
+				endPlaceholder: '結束時間'
+			}
+		},
+		top10: {
+			title: '流量使用TOP10',
+			datePicker: {
+				rangeSeparator: '至',
+				startPlaceholder: '開始日期',
+				endPlaceholder: '結束日期'
+			}
+		},
+		charts: {
+			tooltip: {
+				traffic: '流量'
+			},
+			series: {
+				traffic: '流量'
+			}
+		}
+	},
+	index: {
+		search: {
+			placeholder: '請輸入ICCID或卡號',
+			query: '查詢',
+			reset: '重置'
+		},
+		table: {
+			columns: {
+				cardNumber: '卡號',
+				iccid: 'ICCID',
+				bindDevice: '綁定設備',
+				platform: '平台對接',
+				operator: '運營商',
+				type: '類型',
+				totalFlow: '總流量',
+				usedFlow: '使用流量',
+				remainFlow: '剩餘流量',
+				activationDate: '激活日期',
+				updateTime: '更新時間',
+				status: '狀態',
+				actions: '操作'
+			},
+			actions: {
+				detail: '詳情',
+				delete: '刪除'
+			}
+		},
+		operators: {
+			telecom: '電信',
+			unicom: '聯通',
+			mobile: '移動'
+		},
+		types: {
+			monthly: '月卡',
+			quarterly: '季卡',
+			yearly: '年卡',
+			other: '其他'
+		},
+		status: {
+			activatable: '可激活',
+			testActivated: '測試激活',
+			testDeactivated: '測試去激活',
+			inUse: '在用',
+			suspended: '停機',
+			operatorManaged: '運營商管理狀態'
+		},
+		messages: {
+			deleteConfirm: '此操作將卡號為:「{cardNumber}」進行刪除,是否繼續?',
+			tip: '提示',
+			confirm: '確認',
+			cancel: '取消',
+			deleteSuccess: '刪除成功'
+		}
+	},
+	detail: {
+		basicInfo: {
+			title: '基本信息',
+			cardNumber: '卡號',
+			iccid: 'ICCID',
+			bindDevice: '綁定設備',
+			platformType: '平台類型',
+			platformName: '平台名稱',
+			operator: '運營商',
+			type: '類型',
+			activationDate: '激活日期',
+			updateTime: '更新時間',
+			totalFlow: '總流量',
+			usedFlow: '使用流量',
+			remainFlow: '剩餘流量',
+			status: '狀態',
+			description: '說明'
+		},
+		flowChart: {
+			title: '流量統計',
+			buttons: {
+				yesterday: '昨日',
+				week: '近一週',
+				month: '近一月',
+				year: '近一年'
+			},
+			datePicker: {
+				rangeSeparator: '至',
+				startPlaceholder: '開始時間',
+				endPlaceholder: '結束時間'
+			}
+		},
+		dataStatistics: {
+			title: '數據統計',
+			yesterdayFlow: '昨日流量消耗',
+			monthFlow: '當月流量消耗',
+			yearFlow: '本年流量消耗'
+		}
+	},
+	platformManage: {
+		search: {
+			placeholder: '請輸入關鍵字搜索',
+			query: '查詢',
+			reset: '重置',
+			add: '新增'
+		},
+		table: {
+			columns: {
+				name: '名稱',
+				platformType: '平台類型',
+				appId: 'App ID',
+				status: '狀態',
+				description: '說明',
+				actions: '操作'
+			},
+			actions: {
+				edit: '編輯',
+				delete: '刪除'
+			}
+		},
+		status: {
+			disabled: '禁用',
+			enabled: '開啟'
+		},
+		messages: {
+			deleteConfirm: '此操作將卡號為:「{cardNumber}」,是否繼續?',
+			tip: '提示',
+			confirm: '確認',
+			cancel: '取消',
+			deleteSuccess: '刪除成功'
+		},
+		addOrEdit: {
+			title: {
+				add: '新增',
+				edit: '編輯'
+			},
+			form: {
+				platformType: '平台類型',
+				name: '名稱',
+				appId: 'App ID',
+				secretKey: 'secretKey',
+				userId: '用戶id',
+				interfaceUrl: '接口地址',
+				status: '狀態',
+				description: '說明'
+			},
+			placeholders: {
+				select: '請選擇',
+				name: '請輸入名稱',
+				appId: '請輸入App ID',
+				secretKey: '請輸入secretKey',
+				userId: '請輸入用戶id',
+				interfaceUrl: '請輸入接口地址',
+				description: '請輸入說明'
+			},
+			operators: {
+				telecom: '電信',
+				unicom: '聯通',
+				mobile: '移動'
+			},
+			switch: {
+				enabled: '啟',
+				disabled: '禁'
+			},
+			buttons: {
+				cancel: '取 消',
+				confirm: '確定'
+			},
+			validation: {
+				platformType: '請選擇平台類型',
+				name: '請輸入名稱',
+				userId: '請輸入用戶id',
+				secretKey: '請輸入secretKey',
+				appId: '請輸入App ID',
+				interfaceUrl: '請輸入接口地址'
+			},
+			messages: {
+				editSuccess: '編輯成功',
+				addSuccess: '新增成功'
+			}
+		}
+	}
+};

+ 384 - 0
src/i18n/pages/iotmanagerI18n/en.ts

@@ -0,0 +1,384 @@
+/*
+ * @Author: vera_min vera_min@163.com
+ * @Date: 2025-08-05 12:42:31
+ * @LastEditors: vera_min vera_min@163.com
+ * @LastEditTime: 2025-08-08 23:30:52
+ * @FilePath: /sagoo-admin-ui/src/i18n/pages/iotmanagerI18n/en.ts
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ */
+// 定义内容
+export default {
+	dashboard: {
+		"产品": "Product",
+		"在线设备": "Online Device",
+		"设备消息": "Device Message",
+		"设备警告": "Device Warning",
+		"启用": "Enable",
+		"停用": "Disable",
+		"本月": "This Month",
+		"今日": "Today",
+		warningType: "Warning Type",
+		messageCount: "Message Count",
+		warningCount: "Warning Count",
+		"超紧急": "Super Urgent",
+		"紧急": "Urgent",
+		"严重": "Serious",
+		"一般": "General",
+		"提醒": "Remind",
+		total: "Total"
+	},
+	alarmList: {
+		title: "Alarm Messages",
+		moreInfo: "More Info"
+	},
+	device: {
+		product: "Product",
+		baseType: "Basic Type",
+		extensionType: "Extension Type",
+		enable: "Enable",
+		disable: "Disable",
+		fullScreen: "Full Screen",
+		exitFullScreen: "Exit Full Screen",
+		tip1: "Please enter input parameters as a string. If it's an object string, it will be automatically converted to an object during execution",
+		tip2: "Execution results are displayed here",
+		tip3: "Save Script",
+		tip4: "Debug",
+		tip5: "Below are pre-built empty methods. Please do not modify the function names, just write the function content directly inside",
+		tip6: "This is device function call response data parsing",
+		tip7: "Write data processing code here",
+		tip8: "This is device function call send data parsing",
+		tip9: "Write data processing code here",
+		tip10: "Please enter executable script first",
+		runStatus: "Run Status",
+		deviceInfo: "Device Info",
+		thingModel: "Thing Model",
+		deviceFunction: "Device Function",
+		logManagement: "Log Management",
+		topicList: "Topic List",
+		deviceArchive: "Device Profile",
+		deviceStatus: "Device Status",
+		dataTime: "Data Time",
+		linkProtocol: "Link Protocol",
+		firmwareVersion: "Firmware Version",
+		registryTime: "Registration Time",
+		address: "Address",
+		deviceIdentifier: "Device Identifier",
+		deviceTimeout: "Device Timeout",
+		unitSecond: "s",
+		update: "Update",
+		productDetail: {
+			productInfo: "Product Information",
+			// Switch Status
+			enable: "ON",
+			disable: "OFF",
+			productIdentifier: "Product Identifier",
+			productCategory: "Product Category",
+			deviceType: "Device Type",
+			productImage: "Product Image",
+			loadFailed: "Load Failed",
+			messageProtocol: "Message Protocol",
+			// 物模型相关
+			thingModel: "Thing Model",
+			propertyDefinition: "Property Definition",
+			propertyIdentifier: "Property Identifier",
+			propertyName: "Property Name",
+			dataType: "Data Type",
+			precision: "Precision",
+			unit: "Unit",
+			readOnly: "Read Only",
+			readonly: "Read Only",
+			readWrite: "Read/Write",
+			remark: "Remark",
+			functionDefinition: "Function Definition",
+			functionIdentifier: "Function Identifier",
+			eventDefinition: "Event Definition",
+			eventIdentifier: "Event Identifier",
+			name: "Name",
+			eventLevel: "Event Level",
+			normal: "Normal",
+			warning: "Warning",
+			urgent: "Urgent",
+			tagDefinition: "Tag Definition",
+			// 导入导出
+			importModel: "Import Thing Model",
+			exportModel: "Export Thing Model",
+			deviceAccess: "Device Access",
+			dataParsing: "Data Parsing"
+		},
+		dialogI18n: {
+			addPro: "Add Product",
+			editPro: "Edit Product",
+			addPropertyDefinition: "Add Property Definition",
+			editPropertyDefinition: "Edit Property Definition",
+			editFunctionDefinition: "Edit Function Definition",
+			addFunctionDefinition: "Add Function Definition",
+			editOption: "Edit Option",
+			addOption: "Add Option",
+			editEventDefinition: "Edit Event Definition",
+			addEventDefinition: "Add Event Definition",
+			editTagDefinition: "Edit Tag Definition",
+			addTagDefinition: "Add Tag Definition"
+		},
+		tableI18nColumn: {
+			categoryName: "Category Name",
+			desc: "Description",
+			sort: "Sort",
+			key: "Key",
+			name: "Name",
+			category: "Category",
+			transportProtocol: "Transport Protocol",
+			deviceType: "Device Type",
+			status: "Status",
+			deviceName: "Device Name",
+			deviceType2: "Device Type",
+			lastOnlineTime: "Last Online Time",
+			desc2: "Description"
+		},
+		tableI18nAlarmType: {
+		},
+		tableI18nStatus: {
+			enabled: "Enabled",
+			disabled: "Disabled"
+		},
+		tableI18nConfirm: {
+			deleteCategoryMessage: "This operation will permanently delete the category: {name}, continue?",
+			deleteProductMessage: "This operation will permanently delete the product: {name}, continue?"
+		},
+		tableI18nAction: {
+			addCategory: "Add Category",
+			editCategory: "Edit Category",
+			deviceManagement: "Device Management",
+			addProductCategory: "Add Product Category",
+		},
+		formI18nLabel: {
+			keyword: "Keyword",
+			type: "Type",
+			status: "Status",
+			productKey: "Product Key",
+			productName: "Product Name",
+			productImage: "Product Image",
+			productCategory: "Product Category",
+			messageProtocol: "Message Protocol",
+			transportProtocol: "Transport Protocol",
+			authType: "Auth Type",
+			authUser: "Auth User",
+			authPasswd: "Auth Passwd",
+			accessToken: "Aceess Token",
+			certificateId: "Certificate Id",
+			deviceType: "Device Type",
+			desc: "Product Description",
+			propertyIdentifier: "Property Identifier",
+			propertyName: "Property Name",
+			dataType: "Data Type",
+			precision: "Precision",
+			unit: "Unit",
+			maxLength: "Max Length",
+			timeFormat: "Time Format",
+			booleanValue: "Boolean Value",
+			enumItems: "Enum Items",
+			jsonObject: "JSON Object",
+			parameterIdentifier: "Parameter Identifier",
+			parameterName: "Parameter Name",
+			elementType: "Element Type",
+			isReadOnly: "Is Read Only",
+			propertyDescription: "Description",
+			functionKey: "Function Key",
+			functionName: "Function Name",
+			functionInput: "Function Input",
+			functionOutput: "Function Output",
+			functionDescription: "Description",
+			parameterDescription: "Description",
+			eventKey: "Event Key",
+			eventName: "Event Name",
+			eventType: "Event Level",
+			eventDescription: "Description",
+			valueRange: "Value Range",
+			objectProperty: "Object Property",
+			tagKey: "Tag Key",
+			tagName: "Tag Name",
+			tagDescription: "Tag Description",
+			authDescription: "Authentication Description",
+			linkInfo: "Connection Information",
+			authConfig: "Authentication Configuration",
+			contactAdmin: "Please contact administrator",
+			productBind: "Associated Product",
+			deptIds: "Affiliated Organization",
+			deviceTag: "Device Tag"
+		},
+		formI18nPlaceholder: {
+			keyword: "Enter name or identifier",
+			type: "Type",
+			status: "Status",
+			productKey: "Enter product identifier",
+			productName: "Enter product name",
+			productCategory: "Select category",
+			messageProtocol: "Select message protocol",
+			transportProtocol: "Select transport protocol",
+			authUser: "Enter username",
+			authPasswd: "Enter password",
+			accessToken: "Enter Aceess Token",
+			certificateId: "Select certificate",
+			desc: "Enter product description",
+			propertyIdentifier: "Enter property identifier",
+			propertyName: "Enter property name",
+			selectDataType: "Select data type",
+			inputPrecision: "Enter precision",
+			inputUnit: "Enter unit",
+			inputMaxLength: "Enter max length",
+			inputTimeFormat: "Enter time format",
+			inputTrueText: "Enter true text",
+			inputBooleanValue: "Enter boolean value",
+			inputFalseText: "Enter false text",
+			inputEnumText: "Enter enum text",
+			inputEnumValue: "Enter enum value",
+			selectElementType: "Select element type",
+			inputDescription: "Enter property description",
+			functionKey: "Enter function key",
+			functionName: "Enter function name",
+			functionDescription: "Enter function description",
+			parameterIdentifier: "Enter parameter identifier",
+			parameterName: "Enter parameter name",
+			parameterDescription: "Enter parameter description",
+			eventKey: "Enter event key",
+			eventName: "Enter event name",
+			eventDescription: "Enter event description",
+			valueRangeMin: "Minimum value",
+			valueRangeMax: "Maximum value",
+			propertyIdentifier1: "Property identifier",
+			propertyName1: "Property name",
+			tagKey: "Tag Key",
+			tagName: "Tag Name",
+			tagDescription: "Tag Description",
+			inputParameter: "Please enter parameters",
+			productBind: "Select product",
+			deptIds: "Select affiliated organization",
+			deviceTag: "Enter tag"
+		},
+		formI18nOption: {
+			device: "Device",
+			gateway: "Gateway",
+			subDevice: "Sub Device",
+			on: "Enabled",
+			off: "Disabled",
+			readonly: "Read Only",
+			readWrite: "Read/Write",
+			info: "Information",
+			warning: "Warning",
+			fault: "Fault",
+			online: "Online",
+			offline: "Offline",
+		},
+		formI18nButton: {
+			addProduct: "Add Product",
+			addParameter: "Add Parameter",
+			collapse: "Collapse",
+			showMoreFilter: "More Filter",
+			batchEnable: "Batch Enable",
+			batchDisable: "Batch Disable",
+			importDevice: "Import Device",
+			exportDevice: "Export Device",
+			batchCheckStatus: "Batch Check Status"
+		},
+		rules: {
+			productName: "Product name cannot be empty",
+			productNameMax32: "Product name cannot exceed 32 characters",
+			productNameValidator: "Product name cannot contain spaces",
+			productKey: "Product identifier cannot be empty",
+			productKeyValidator: "Product identifier cannot contain spaces",
+			messageProtocol: "Message protocol cannot be empty",
+			transportProtocol: "Access method cannot be empty",
+			categoryId: "Product category cannot be empty",
+			deviceType: "Device type cannot be empty",
+			// 属性定义相关验证规则
+			propertyName: "Property name cannot be empty",
+			propertyNameMax32: "Property name cannot exceed 32 characters",
+			propertyNameValidator: "Property name cannot contain spaces",
+			propertyKey: "Property identifier cannot be empty",
+			accessMode: "Please select access mode",
+			dataType: "Please select data type",
+			functionName: "Function name cannot be empty",
+			functionNameMax32: "Function name cannot exceed 32 characters",
+			functionNameValidator: "Function name cannot contain spaces",
+			functionKey: "Function identifier cannot be empty",
+			functionType: "Please select data type",
+			parameterName: "Parameter name cannot be empty",
+			parameterNameMax32: "Parameter name cannot exceed 32 characters",
+			parameterNameValidator: "Parameter name cannot contain spaces",
+			parameterKey: "Parameter identifier cannot be empty",
+			eventName: "Event name cannot be empty",
+			eventNameMax32: "Event name cannot exceed 32 characters",
+			eventNameValidator: "Event name cannot contain spaces",
+			eventKey: "Event identifier cannot be empty",
+			eventType: "Please select event type",
+			tagKeyName: "Tag name cannot be empty",
+			tagKeyMax32: "Tag name cannot exceed 32 characters",
+			tagKeyValidator: "Tag name cannot contain spaces",
+			tagKey: "Tag identifier cannot be empty",
+			tagAccessMode: "Please select access mode",
+			tagType: "Please select data type"
+		}
+	},
+	alarm: {
+		tableI18nColumn: {
+			id: "ID",
+			alarmType: "Alarm Type",
+			alarmLevel: "Rule Level",
+			ruleName: "Rule Name",
+			productKey: "Product Key",
+			deviceKey: "Device Key",
+			alarmStatus: "Alarm Status",
+			alarmTime: "Alarm Time",
+		},
+		tableI18nAlarmType: {
+			ruleAlarm: "Rule Alarm",
+			deviceSelfAlarm: "Device Self Alarm",
+			ruleAlarmUpgrade: "Rule Alarm Upgrade"
+		},
+		tableI18nStatus: {
+			unprocessed: "Unprocessed",
+			processed: "Processed",
+			ignored: "Ignored"
+		}
+	},
+	// 级联管理
+	cascade: {
+		dialogI18n: {
+			deviceList: " - Device List",
+		},
+		tableI18nColumn: {
+			subPlatform: "Sub Platform",
+			address: "Address",
+			device: "Device",
+			status: "Status",
+			lastUpdateTime: "Last Update Time",
+			operation: "Operation",
+			identifier: "Identifier",
+			deviceName: "Device Name",
+			deviceType: "Device Type",
+			belongProduct: "Product",
+			lastOnlineTime: "Last Online Time"
+		},
+		tableI18nStatus: {
+			online: "Online",
+			offline: "Offline",
+			disabled: "Disabled"
+		},
+		tableI18nConfirm: {
+		},
+		tableI18nAction: {
+			viewDevice: "View Device",
+			detail: "Detail",
+			sync: "Sync"
+		},
+		formI18nLabel: {
+			subPlatformTotal: "Sub Platform",
+			deviceTotal: "Total Devices"
+		},
+		formI18nPlaceholder: {
+		},
+		formI18nOption: {
+		},
+		formI18nButton: {
+		}
+	},
+};

+ 385 - 0
src/i18n/pages/iotmanagerI18n/zh-cn.ts

@@ -0,0 +1,385 @@
+/*
+ * @Author: vera_min vera_min@163.com
+ * @Date: 2025-08-05 12:42:31
+ * @LastEditors: vera_min vera_min@163.com
+ * @LastEditTime: 2025-08-09 16:07:54
+ * @FilePath: /sagoo-admin-ui/src/i18n/pages/iotmanagerI18n/zh-cn.ts
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ */
+// 定义内容
+export default {
+	dashboard: {
+		"产品": "产品",
+		"在线设备": "在线设备",
+		"设备消息": "设备消息",
+		"设备警告": "设备警告",
+		"启用": "启用",
+		"停用": "停用",
+		"本月": "本月",
+		"今日": "今日",
+		warningType: "预警类型",
+		messageCount: "消息量",
+		warningCount: "预警量",
+		"超紧急": "超紧急",
+		"紧急": "紧急",
+		"严重": "严重",
+		"一般": "一般",
+		"提醒": "提醒",
+		total: "总计"
+	},
+	alarmList: {
+		title: "告警消息",
+		moreInfo: "更多信息"
+	},
+	device: {
+		product: "产品",
+		baseType: "基础类型",
+		extensionType: "扩展类型",
+		enable: "启用",
+		disable: "停用",
+		fullScreen: "全屏",
+		exitFullScreen: "退出全屏",
+		tip1: "请输入入参,以字符串的方式,如果是对象字符串会在执行时自动转换为对象再执行",
+		tip2: "此处显示执行结果",
+		tip3: "保存脚本",
+		tip4: "调试",
+		tip5: "下面是预制的空的方法,请不要修改函数名称,直接在内部编写函数即可",
+		tip6: "此处是设备功能调用应答数据解析",
+		tip7: "此处编写对数据的处理",
+		tip8: "此处是设备功能调用发送数据解析",
+		tip9: "此处编写对数据的处理",
+		tip10: "请先输入可执行脚本",
+		runStatus: "运行状态",
+		deviceInfo: "设备信息",
+		thingModel: "物模型",
+		deviceFunction: "设备功能",
+		logManagement: "日志管理",
+		topicList: "Topic列表",
+		deviceArchive: "设备档案",
+		deviceStatus: "设备状态",
+		dataTime: "数据时间",
+		linkProtocol: "链接协议",
+		firmwareVersion: "固件版本",
+		registryTime: "注册时间",
+		address: "详细地址",
+		deviceIdentifier: "设备标识",
+		deviceTimeout: "设备超时时间",
+		unitSecond: "秒",
+		update: "更新",
+		productDetail: {
+			productInfo: "产品信息",
+			// 开关状态
+			enable: "启用",
+			disable: "停用",
+			productIdentifier: "产品标识",
+			productCategory: "产品分类",
+			deviceType: "设备类型",
+			productImage: "产品图片",
+			loadFailed: "加载失败",
+			messageProtocol: "消息协议",
+			// 物模型相关
+			thingModel: "物模型",
+			propertyDefinition: "属性定义",
+			propertyIdentifier: "属性标识",
+			propertyName: "属性名称",
+			dataType: "数据类型",
+			precision: "精度",
+			unit: "单位",
+			readOnly: "是否只读",
+			readonly: "只读",
+			readWrite: "读写",
+			remark: "说明",
+			functionDefinition: "功能定义",
+			functionIdentifier: "功能标识",
+			eventDefinition: "事件定义",
+			eventIdentifier: "事件标识",
+			name: "名称",
+			eventLevel: "事件级别",
+			normal: "普通",
+			warning: "警告",
+			urgent: "紧急",
+			tagDefinition: "标签定义",
+			// 导入导出
+			importModel: "导入物模型",
+			exportModel: "导出物模型",
+			deviceAccess: "设备接入",
+			dataParsing: "数据解析"
+		},
+		dialogI18n: {
+			addPro: "新增产品",
+			editPro: "编辑产品",
+			addPropertyDefinition: "新增属性定义",
+			editPropertyDefinition: "修改属性定义",
+			editFunctionDefinition: "修改功能定义",
+			addFunctionDefinition: "新增功能定义",
+			editOption: "编辑参数",
+			addOption: "新增参数",	
+			editEventDefinition: "编辑事件定义",
+			addEventDefinition: "新增事件定义",
+			editTagDefinition: "编辑标签定义",
+			addTagDefinition: "新增标签定义"
+		},
+		tableI18nColumn: {
+			categoryName: "分类名称",
+			desc: "描述",
+			sort: "排序",
+			key: "标识",
+			name: "名称",
+			category: "分类",
+			transportProtocol: "接入方式",
+			deviceType: "类型",
+			status: "状态",
+			deviceName: "设备名称",
+			deviceType2: "设备类型",
+			lastOnlineTime: "最后上线时间",
+			desc2: "说明"
+		},
+		tableI18nAlarmType: {
+		},
+		tableI18nStatus: {
+			enabled: "已启用",
+			disabled: "未启用"
+		},
+		tableI18nConfirm: {
+			deleteCatrgoryMessage: "此操作将永久删除分类:{name}, 是否继续?",
+			deleteProductMessage: "此操作将永久删除产品:{name},是否继续?"
+		},
+		tableI18nAction: {
+			addCategory: "新增分类",
+			editCategory: "编辑分类",
+			deviceManagement: "设备管理",
+			addProductCategory: "新增产品分类",
+		},
+		formI18nLabel: {
+			keyword: "关键字",
+			type: "类型",
+			status: "状态",
+			productKey: "产品标识",
+			productName: "产品名称",
+			productImage: "产品图片",
+			productCategory: "产品分类",
+			messageProtocol: "消息协议",
+			transportProtocol: "接入方式",
+			authType: "认证方式",
+			authUser: "用户名",
+			authPasswd: "密码",
+			accessToken: "Aceess Token",
+			certificateId: "认证证书",
+			deviceType: "设备类型",
+			desc: "产品描述",
+			propertyIdentifier: "属性定义标识",
+			propertyName: "属性定义名称",
+			dataType: "数据类型",
+			precision: "精度",
+			unit: "单位",
+			maxLength: "最大长度",
+			timeFormat: "时间格式",
+			booleanValue: "布尔值",
+			enumItems: "枚举项",
+			jsonObject: "JSON对象",
+			parameterIdentifier: "参数标识",
+			parameterName: "参数名称",
+			elementType: "元素类型",
+			isReadOnly: "是否只读",
+			propertyDescription: "属性定义描述",
+			functionKey: "功能定义标识",
+			functionName: "功能定义名称",
+			functionInput: "输入参数",
+			functionOutput: "输出参数",
+			functionDescription: "功能定义描述",
+			parameterDescription: "参数描述",
+			eventKey: "事件定义标识",
+			eventName: "事件定义名称",
+			eventType: "事件类型",
+			eventDescription: "事件定义描述",
+			valueRange: "取值范围",
+			objectProperty: "对象属性",
+			tagKey: "标签定义标识",
+			tagName: "标签定义名称",
+			tagDescription: "标签定义描述",
+			authDescription: "认证说明",
+			linkInfo: "链接信息",
+			authConfig: "认证配置",
+			contactAdmin: "请联系管理员",
+			productBind: "所属产品",
+			deptIds: "所属组织",
+			deviceTag: "设备标签"
+		},
+		formI18nPlaceholder: {
+			keyword: "输入名称或标识",
+			type: "类型",
+			status: "启用状态",
+			productKey: "请输入产品标识",
+			productName: "请输入产品名称",
+			productCategory: "请选择分类",
+			messageProtocol: "请选择消息协议",
+			transportProtocol: "请选择接入方式",
+			authUser: "请输入用户名",
+			authPasswd: "请输入密码",
+			accessToken: "请输入Aceess Token",
+			certificateId: "请选择认证证书",
+			desc: "请输入产品描述",
+			propertyIdentifier: "请输入属性定义标识",
+			propertyName: "请输入属性定义名称",
+			selectDataType: "请选择数据类型",
+			inputPrecision: "请输入精度",
+			inputUnit: "请输入单位",
+			inputMaxLength: "请输入最大长度",
+			inputTimeFormat: "请输入时间格式",
+			inputTrueText: "请输入true时显示的文字",
+			inputBooleanValue: "请输入布尔值",
+			inputFalseText: "请输入false时显示的文字",
+			inputEnumText: "请输入枚举文本",
+			inputEnumValue: "请输入枚举值",
+			selectElementType: "请选择元素类型",
+			inputDescription: "请输入属性定义描述",
+			functionKey: "请输入功能定义标识",
+			functionName: "请输入功能定义名称",
+			functionDescription: "请输入功能定义描述",
+			parameterIdentifier: "请输入参数标识",
+			parameterName: "请输入参数名称",
+			parameterDescription: "请输入参数描述", 
+			eventKey: "请输入事件定义标识",
+			eventName: "请输入事件定义名称",
+			eventDescription: "请输入事件定义描述",
+			valueRangeMin: "最小值",
+			valueRangeMax: "最大值",
+			propertyIdentifier1: "属性标识",
+			propertyName1: "属性名称",
+			tagKey: "请输入标签定义标识",
+			tagName: "请输入标签定义名称",
+			tagDescription: "请输入标签定义描述",
+			inputParameter: "请输入参数",
+			productBind: "选择产品",
+			deptIds: "请选择所属组织",
+			deviceTag: "请输入标签"
+		},
+		formI18nOption: {
+			device: "设备",
+			gateway: "网关",
+			subDevice: "子设备",
+			on: "已启用",
+			off: "未启用",
+			readonly: "只读",
+			readWrite: "读写",
+			info: "信息",
+			warning: "告警",
+			fault: "故障",
+			online: "在线",
+			offline: "离线",
+		},
+		formI18nButton: {
+			addProduct: "新增产品",
+			addParameter: "新增参数",
+			collapse: "收起",
+			showMoreFilter: "更多筛选",
+			batchEnable: "批量启用",
+			batchDisable: "批量禁用",
+			importDevice: "导入设备",
+			exportDevice: "导出设备",
+			batchCheckStatus: "批量检测状态"
+		},
+		rules: {
+			productName: "产品名称不能为空",
+			productNameMax32: "产品名称不能超过32个字符",
+			productNameValidator: "产品名称不能包含空格",
+			productKey: "产品标识不能为空",
+			productKeyValidator: "产品标识不能包含空格",
+			messageProtocol: "消息协议不能为空",
+			transportProtocol: "接入方式不能为空",
+			categoryId: "产品分类不能为空",
+			deviceType: "设备类型不能为空",
+			// 属性定义相关验证规则
+			propertyName: "属性定义名称不能为空",
+			propertyNameMax32: "属性定义名称不能超过32个字符",
+			propertyNameValidator: "属性定义名称不能包含空格",
+			propertyKey: "属性定义标识不能为空",
+			accessMode: "请选择是否只读",
+			dataType: "请选择数据类型",
+			functionName: "功能定义名称不能为空",
+			functionNameMax32: "功能定义名称不能超过32个字符",
+			functionNameValidator: "功能定义名称不能包含空格",
+			functionKey: "功能定义标识不能为空",
+			functionType: "请选择数据类型",
+			parameterName: "参数名称不能为空",
+			parameterNameMax32: "参数名称不能超过32个字符",
+			parameterNameValidator: "参数名称不能包含空格",
+			parameterKey: "参数标识不能为空",
+			eventName: "事件定义名称不能为空",
+			eventNameMax32: "事件定义名称不能超过32个字符",
+			eventNameValidator: "事件定义名称不能包含空格", 
+			eventKey: "事件定义标识不能为空",
+			eventType: "请选择数据类型",
+			tagKeyName: "标签定义名称不能为空",
+			tagKeyMax32: "标签定义名称不能超过32个字符",
+			tagKeyValidator: "标签定义名称不能包含空格",
+			tagKey: "标签定义标识不能为空",
+			tagAccessMode: "请选择是否只读",
+			tagType: "请选择数据类型"
+		}
+	},
+	alarm: {
+		tableI18nColumn: {
+			id: "ID",
+			alarmType: "告警类型",
+			alarmLevel: "规则级别",
+			ruleName: "规则名称",
+			productKey: "产品标识",
+			deviceKey: "设备标识",
+			alarmStatus: "告警状态",
+			alarmTime: "告警时间",
+		},
+		tableI18nAlarmType: {
+			ruleAlarm: "规则告警",
+			deviceSelfAlarm: "设备自主告警",
+			ruleAlarmUpgrade: "规侧告警升级"
+		},
+		tableI18nStatus: {
+			unprocessed: "未处理",
+			processed: "已处理",
+			ignored: "已忽略"
+		}
+	},
+	// 级联管理
+	cascade: {
+		dialogI18n: {
+			deviceList: "-设备列表",
+		},
+		tableI18nColumn: {
+			subPlatform: "子平台",
+			address: "地址",
+			device: "设备",
+			status: "状态",
+			lastUpdateTime: "最后更新时间",
+			operation: "操作",
+			identifier: "标识",
+			deviceName: "设备名称",
+			deviceType: "设备类型",
+			belongProduct: "所属产品",
+			lastOnlineTime: "最后上线时间"
+			
+		},
+		tableI18nStatus: {
+			online: "在线",
+			offline: "离线",
+			disabled: "未启用"
+		},
+		tableI18nConfirm: {
+		},
+		tableI18nAction: {
+			viewDevice: "查看设备",
+			detail: "详情",
+			sync: "同步"
+		},
+		formI18nLabel: {
+			subPlatformTotal: "子平台",
+			deviceTotal: "设备总数"
+		},
+		formI18nPlaceholder: {
+		},
+		formI18nOption: {
+		},
+		formI18nButton: {
+		}
+	},
+};

+ 384 - 0
src/i18n/pages/iotmanagerI18n/zh-tw.ts

@@ -0,0 +1,384 @@
+/*
+ * @Author: vera_min vera_min@163.com
+ * @Date: 2025-08-05 12:42:31
+ * @LastEditors: vera_min vera_min@163.com
+ * @LastEditTime: 2025-08-08 23:14:56
+ * @FilePath: /sagoo-admin-ui/src/i18n/pages/iotmanager/zh-tw.ts
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ */
+// 定义内容
+export default {
+	dashboard: {
+		"产品": "產品",
+		"在线设备": "在線設備",
+		"设备消息": "設備訊息",
+		"设备警告": "設備警告",
+		"启用": "啟用",
+		"停用": "停用",
+		"本月": "本月",
+		"今日": "今日",
+		warningType: "預警類型",
+		messageCount: "訊息量",
+		warningCount: "預警量",
+		"超紧急": "超緊急",
+		"緊急": "緊急",
+		"嚴重": "嚴重",
+		"一般": "一般",
+		"提醒": "提醒",
+		total: "總計"
+	},
+	alarmList: {
+		title: "告警訊息",
+		moreInfo: "更多資訊"
+	},
+	device: {
+		product: "產品",
+		baseType: "基礎類型",
+		extensionType: "擴展類型",
+		enable: "啟用",
+		disable: "停用",
+		fullScreen: "全屏",
+		exitFullScreen: "退出全屏",
+		tip1: "請輸入入參,以字符串的方式,如果是對象字符串會在執行時自動轉換為對象再執行",
+		tip2: "此處顯示執行結果",
+		tip3: "保存腳本",
+		tip4: "調試",
+		tip5: "下面是預製的空的方法,請不要修改函數名稱,直接在內部編寫函數即可",
+		tip6: "此處是設備功能調用應答數據解析",
+		tip7: "此處編寫對數據的處理",
+		tip8: "此處是設備功能調用發送數據解析",
+		tip9: "此處編寫對數據的處理",
+		tip10: "請先輸入可執行腳本",
+		runStatus: "運行狀態",
+		deviceInfo: "設備信息",
+		thingModel: "物模型",
+		deviceFunction: "設備功能",
+		logManagement: "日誌管理",
+		topicList: "Topic列表",
+		deviceArchive: "設備檔案",
+		deviceStatus: "設備狀態",
+		dataTime: "資料時間",
+		linkProtocol: "鏈接協議",
+		firmwareVersion: "固件版本",
+		registryTime: "註冊時間",
+		address: "詳細地址",
+		deviceIdentifier: "設備標識",
+		deviceTimeout: "設備超時時間",
+		unitSecond: "秒",
+		update: "更新",
+		productDetail: {
+			productInfo: "產品資訊",
+			// 開關狀態
+			enable: "啟用",
+			disable: "停用",
+			productIdentifier: "產品標識",
+			productCategory: "產品分類",
+			deviceType: "設備類型",
+			productImage: "產品圖片",
+			loadFailed: "載入失敗",
+			messageProtocol: "訊息協議",
+			// 物模型相關
+			thingModel: "物模型",
+			propertyDefinition: "屬性定義",
+			propertyIdentifier: "屬性標識",
+			propertyName: "屬性名稱",
+			dataType: "數據類型",
+			precision: "精度",
+			unit: "單位",
+			readOnly: "是否只讀",
+			readonly: "只讀",
+			readWrite: "讀寫",
+			remark: "說明",
+			functionDefinition: "功能定義",
+			functionIdentifier: "功能標識",
+			eventDefinition: "事件定義",
+			eventIdentifier: "事件標識",
+			name: "名稱",
+			eventLevel: "事件級別",
+			normal: "普通",
+			warning: "警告",
+			urgent: "緊急",
+			tagDefinition: "標籤定義",
+			// 導入導出
+			importModel: "導入物模型",
+			exportModel: "導出物模型",
+			deviceAccess: "設備接入",
+			dataParsing: "資料解析"
+		},
+		dialogI18n: {
+			addPro: "新增產品",
+			editPro: "編輯產品",
+			addPropertyDefinition: "新增屬性定義",
+			editPropertyDefinition: "編輯屬性定義",
+			editFunctionDefinition: "編輯功能定義",
+			addFunctionDefinition: "新增功能定義",
+			editOption: "編輯參數",
+			addOption: "新增參數",
+			editEventDefinition: "編輯事件定義",
+			addEventDefinition: "新增事件定義",
+			editTagDefinition: "編輯標籤定義",
+			addTagDefinition: "新增標籤定義"
+		},
+		tableI18nColumn: {
+			categoryName: "分類名稱",
+			desc: "描述",
+			sort: "排序",
+			key: "標識",
+			name: "名稱",
+			category: "分類",
+			transportProtocol: "接入方式",
+			deviceType: "類型",
+			status: "狀態",
+			deviceName: "設備名稱",
+			deviceType2: "設備類型",
+			lastOnlineTime: "最後上線時間",
+			desc2: "說明"
+		},
+		tableI18nAlarmType: {
+		},
+		tableI18nStatus: {
+			enabled: "已啟用",
+			disabled: "未啟用"
+		},
+		tableI18nConfirm: {
+			deleteCatrgoryMessage: "此操作將永久刪除分類:{name}, 是否繼續?",
+			deleteProductMessage: "此操作將永久刪除產品:{name},是否繼續?"
+		},
+		tableI18nAction: {
+			addCategory: "新增分類",
+			editCategory: "編輯分類",
+			deviceManagement: "設備管理",
+			addProductCategory: "新增產品分類",
+		},
+		formI18nLabel: {
+			keyword: "關鍵字",
+			type: "類型",
+			status: "狀態",
+			productKey: "產品標識",
+			productName: "產品名稱",
+			productImage: "產品圖片",
+			productCategory: "產品分類",
+			messageProtocol: "消息協議",
+			transportProtocol: "接入方式",
+			authType: "認證方式",
+			authUser: "用戶名",
+			authPasswd: "密碼",
+			accessToken: "Aceess Token",
+			certificateId: "認證證書",
+			deviceType: "設備類型",
+			desc: "產品描述",
+			propertyIdentifier: "屬性定義標識",
+			propertyName: "屬性定義名稱",
+			dataType: "數據類型",
+			precision: "精度",
+			unit: "單位",
+			maxLength: "最大長度",
+			timeFormat: "時間格式",
+			booleanValue: "布林值",
+			enumItems: "枚舉項",
+			jsonObject: "JSON物件",
+			parameterIdentifier: "參數標識",
+			parameterName: "參數名稱",
+			elementType: "元素類型",
+			isReadOnly: "是否唯讀",
+			propertyDescription: "屬性定義描述",
+			functionKey: "功能定義標識",
+			functionName: "功能定義名稱",
+			functionInput: "輸入參數",
+			functionOutput: "輸出參數",
+			functionDescription: "功能定義描述",
+			parameterDescription: "參數描述",
+			eventKey: "事件定義標識",
+			eventName: "事件定義名稱",
+			eventType: "事件類型",
+			eventDescription: "事件定義描述",
+			valueRange: "取值範圍",
+			objectProperty: "對象屬性",
+			tagKey: "標籤定義標識",
+			tagName: "標籤定義名稱",
+			tagDescription: "標籤定義描述",
+			authDescription: "認證說明",
+			linkInfo: "鏈接信息",
+			authConfig: "認證配置",
+			contactAdmin: "請聯繫管理員",
+			productBind: "所屬產品",
+			deptIds: "所屬組織",
+			deviceTag: "設備標籤"
+		},
+		formI18nPlaceholder: {
+			keyword: "輸入名稱或標識",
+			type: "類型",
+			status: "啟用狀態",
+			productKey: "請輸入產品標識",
+			productName: "請輸入產品名稱",
+			productCategory: "請選擇分類",
+			messageProtocol: "請選擇消息協議",
+			transportProtocol: "請選擇接入方式",
+			authUser: "請輸入用戶名",
+			authPasswd: "請輸入密碼",
+			accessToken: "請輸入Aceess Token",
+			certificateId: "請選擇認證證書",
+			desc: "請輸入產品描述",
+			propertyIdentifier: "請輸入屬性定義標識",
+			propertyName: "請輸入屬性定義名稱",
+			selectDataType: "請選擇數據類型",
+			inputPrecision: "請輸入精度",
+			inputUnit: "請輸入單位",
+			inputMaxLength: "請輸入最大長度",
+			inputTimeFormat: "請輸入時間格式",
+			inputTrueText: "請輸入true時顯示的文字",
+			inputBooleanValue: "請輸入布林值",
+			inputFalseText: "請輸入false時顯示的文字",
+			inputEnumText: "請輸入枚舉文本",
+			inputEnumValue: "請輸入枚舉值",
+			selectElementType: "請選擇元素類型",
+			inputDescription: "請輸入屬性定義描述",
+			functionKey: "請輸入功能定義標識",
+			functionName: "請輸入功能定義名稱",
+			functionDescription: "請輸入功能定義描述",
+			parameterIdentifier: "請輸入參數標識",
+			parameterName: "請輸入參數名稱",
+			parameterDescription: "請輸入參數描述",
+			eventKey: "請輸入事件定義標識",
+			eventName: "請輸入事件定義名稱",
+			eventDescription: "請輸入事件定義描述",
+			valueRangeMin: "最小值",
+			valueRangeMax: "最大值",
+			propertyIdentifier1: "屬性標識",
+			propertyName1: "屬性名稱",
+			tagKey: "請輸入標籤定義標識",
+			tagName: "請輸入標籤定義名稱",
+			tagDescription: "請輸入標籤定義描述",
+			inputParameter: "請輸入參數",
+			productBind: "選擇產品",
+			deptIds: "請選擇所属組織",
+			deviceTag: "請輸入標籤"
+		},
+		formI18nOption: {
+			device: "設備",
+			gateway: "網關",
+			subDevice: "子設備",
+			on: "已啟用",
+			off: "未啟用",
+			readonly: "唯讀",
+			readWrite: "讀寫",
+			info: "資訊",
+			warning: "告警",
+			fault: "故障",
+			online: "在線",
+			offline: "離線",
+		},
+		formI18nButton: {
+			addProduct: "新增產品",
+			addParameter: "新增參數",
+			collapse: "收起",
+			showMoreFilter: "更多篩選",
+			batchEnable: "批量啟用",
+			batchDisable: "批量禁用",
+			importDevice: "导入设备",
+			exportDevice: "导出设备",
+			batchCheckStatus: "批量檢查狀態"
+		},
+		rules: {
+			productName: "產品名稱不能為空",
+			productNameMax32: "產品名稱不能超過32個字符",
+			productNameValidator: "產品名稱不能包含空格",
+			productKey: "產品標識不能為空",
+			productKeyValidator: "產品標識不能包含空格",
+			messageProtocol: "消息協議不能為空",
+			transportProtocol: "接入方式不能為空",
+			categoryId: "產品分類不能為空",
+			deviceType: "設備類型不能為空",
+			// 屬性定義相關驗證規則
+			propertyName: "屬性定義名稱不能為空",
+			propertyNameMax32: "屬性定義名稱不能超過32個字符",
+			propertyNameValidator: "屬性定義名稱不能包含空格",
+			propertyKey: "屬性定義標識不能為空",
+			accessMode: "請選擇是否唯讀",
+			dataType: "請選擇資料類型",
+			functionName: "功能定義名稱不能為空",
+			functionNameMax32: "功能定義名稱不能超過32個字符",
+			functionNameValidator: "功能定義名稱不能包含空格",
+			functionKey: "功能定義標識不能為空",
+			functionType: "請選擇資料類型",
+			parameterName: "參數名稱不能為空",
+			parameterNameMax32: "參數名稱不能超過32個字符",
+			parameterNameValidator: "參數名稱不能包含空格",
+			parameterKey: "參數標識不能為空",
+			eventName: "事件定義名稱不能為空",
+			eventNameMax32: "事件定義名稱不能超過32個字符",
+			eventNameValidator: "事件定義名稱不能包含空格",
+			eventKey: "事件定義標識不能為空",
+			eventType: "請選擇事件類型",
+			tagKeyName: "標籤定義名稱不能為空",
+			tagKeyMax32: "標籤定義名稱不能超過32個字符",
+			tagKeyValidator: "標籤定義名稱不能包含空格",
+			tagKey: "標籤定義標識不能為空",
+			tagAccessMode: "請選擇是否只讀",
+			tagType: "請選擇資料類型"
+		}
+	},
+	alarm: {
+		tableI18nColumn: {
+			id: "ID",
+			alarmType: "告警類型",
+			alarmLevel: "規則級別",
+			ruleName: "規則名稱",
+			productKey: "產品標識",
+			deviceKey: "設備標識",
+			alarmStatus: "告警狀態",
+			alarmTime: "告警時間",
+		},
+		tableI18nAlarmType: {
+			ruleAlarm: "規則告警",
+			deviceSelfAlarm: "設備自主告警",
+			ruleAlarmUpgrade: "規側告警升級"
+		},
+		tableI18nStatus: {
+			unprocessed: "未處理",
+			processed: "已處理",
+			ignored: "已忽略"
+		}
+	},
+	// 级联管理
+	cascade: {
+		dialogI18n: {
+			deviceList: "-設備列表",
+		},
+		tableI18nColumn: {
+			subPlatform: "子平台",
+			address: "地址",
+			device: "設備",
+			status: "狀態",
+			lastUpdateTime: "最後更新時間",
+			operation: "操作",
+			identifier: "標識",
+			deviceName: "設備名稱",
+			deviceType: "設備類型",
+			belongProduct: "所屬產品",
+			lastOnlineTime: "最後上線時間"
+		},
+		tableI18nStatus: {
+			online: "在線",
+			offline: "離線",
+			disabled: "未啟用"
+		},
+		tableI18nConfirm: {
+		},
+		tableI18nAction: {
+			viewDevice: "查看設備",
+			detail: "詳情",
+			sync: "同步"
+		},
+		formI18nLabel: {
+			subPlatformTotal: "子平台",
+			deviceTotal: "設備總數"
+		},
+		formI18nPlaceholder: {
+		},
+		formI18nOption: {
+		},
+		formI18nButton: {
+		}
+	},
+};

+ 553 - 0
src/i18n/pages/projects/en.ts

@@ -0,0 +1,553 @@
+export default {
+  list: {
+    search: {
+      channelPlaceholder: 'Search channel merchant',
+      statusPlaceholder: 'Please select format',
+      query: 'Search',
+      reset: 'Reset'
+    },
+    addOrEdit: {
+      form: {
+        name: 'Project Name',
+        addressCode: 'Region',
+        addressDetail: 'Address Detail',
+        channelMerchants: 'Channel Merchant',
+        customName: 'Key Customer',
+        repairCompany: 'Maintenance Company',
+        repairMobile: 'Maintenance Phone'
+      },
+      placeholders: {
+        select: 'Please select',
+        name: 'Enter project name',
+        addressDetail: 'Enter address detail',
+        channelMerchants: 'Enter channel merchant',
+        customName: 'Enter customer name',
+        repairCompany: 'Enter maintenance company',
+        repairMobile: 'Enter maintenance phone'
+      },
+      buttons: {
+        cancel: 'Cancel',
+        confirm: 'Confirm'
+      },
+      validation: {
+        name: 'Please enter project name',
+        addressCode: 'Please select region',
+        addressDetail: 'Please enter address detail',
+        channelMerchants: 'Please enter channel merchant',
+        customName: 'Please enter customer name',
+        repairCompany: 'Please enter maintenance company',
+        repairMobile: 'Please enter maintenance phone'
+      },
+      messages: {
+        addSuccess: 'Added successfully',
+        editSuccess: 'Edited successfully'
+      }
+    },
+    status: {
+      all: 'All status',
+      enabled: 'Enabled',
+      disabled: 'Disabled'
+    },
+    actions: {
+      add: 'Add',
+      delete: 'Delete',
+      enable: 'Enable',
+      disable: 'Disable'
+    },
+    table: {
+      columns: {
+        projectStatus: 'Project Status',
+        projectName: 'Project Name',
+        address: 'Province/City/District/County',
+        addressDetail: 'Address Detail',
+        channelMerchants: 'Channel Merchant',
+        customName: 'Key Customer',
+        repairCompany: 'Maintenance Company',
+        updatedAt: 'Updated At',
+        actions: 'Actions'
+      },
+      actions: {
+        detail: 'Detail',
+        edit: 'Edit',
+        delete: 'Delete'
+      }
+    },
+    messages: {
+      operationSuccess: 'Operation succeeded',
+      deleteConfirmSingle: 'This will delete project: “{name}”. Continue?',
+      deleteConfirmMultiple: 'This will delete {count} projects',
+      tip: 'Prompt',
+      confirm: 'Confirm',
+      cancel: 'Cancel',
+      deleteSuccess: 'Deleted successfully'
+    }
+  },
+  editDialog: {
+    title: {
+      add: 'Add Project',
+      edit: 'Edit Project'
+    }
+  },
+  detail: {
+    tabs: {
+      overview: 'Overview',
+      devices: 'Gateways & Devices',
+      scene: 'Scene Linkage',
+      topo: 'Topology Apps',
+      video: 'Video Surveillance'
+    },
+    device: {
+      actions: {
+        add: 'Add Device',
+        cancel: 'Cancel',
+        confirm: 'Confirm'
+      },
+      table: {
+        columns: {
+          key: 'Key',
+          name: 'Device Name',
+          deviceType: 'Device Type',
+          productName: 'Product',
+          status: 'Status',
+          lastOnlineTime: 'Last Online Time',
+          desc: 'Description',
+          actions: 'Actions'
+        },
+        actions: {
+          detail: 'Device Details',
+          unbind: 'Unbind'
+        },
+        statusTags: {
+          offline: 'Offline',
+          online: 'Online',
+          inactive: 'Inactive'
+        }
+      },
+      dialog: {
+        title: 'Add Device'
+      },
+      form: {
+        labels: {
+          product: 'Product',
+          device: 'Device'
+        },
+        placeholders: {
+          product: 'Select product',
+          device: 'Select device'
+        }
+      },
+      messages: {
+        selectDeviceFirst: 'Please select a device first',
+        addSuccess: 'Added successfully',
+        deleteConfirm: 'Are you sure to delete?',
+        tip: 'Prompt',
+        confirm: 'Confirm',
+        cancel: 'Cancel',
+        unbindSuccess: 'Unbound successfully'
+      }
+    },
+    info: {
+      actions: {
+        edit: 'Edit'
+      },
+      descriptions: {
+        name: 'Project Name',
+        customName: 'Key Customer',
+        address: 'Region',
+        channelMerchants: 'Channel Merchant',
+        repairCompany: 'Maintenance Company',
+        repairMobile: 'Maintenance Phone',
+        addressDetail: 'Address Detail'
+      }
+    },
+    scene: {
+      actions: {
+        add: 'Add Scene',
+        cancel: 'Cancel',
+        confirm: 'Confirm'
+      },
+      table: {
+        columns: {
+          id: 'ID',
+          name: 'Scene Name',
+          sceneType: 'Trigger Type',
+          status: 'Status',
+          description: 'Description',
+          createdAt: 'Created At',
+          actions: 'Actions'
+        },
+        actions: {
+          detail: 'Scene Details',
+          unbind: 'Unbind'
+        },
+        statusTags: {
+          enabled: 'Enabled',
+          disabled: 'Disabled'
+        },
+        sceneTypeTags: {
+          device: 'Device Trigger',
+          manual: 'Manual Trigger',
+          timer: 'Timer Trigger'
+        }
+      },
+      dialog: {
+        title: 'Add Scene'
+      },
+      form: {
+        labels: {
+          scene: 'Scene'
+        },
+        placeholders: {
+          scene: 'Select scene'
+        }
+      },
+      messages: {
+        selectSceneFirst: 'Please select a scene first',
+        addSuccess: 'Added successfully',
+        unbindConfirm: 'Are you sure to unbind this scene?',
+        tip: 'Prompt',
+        confirm: 'Confirm',
+        cancel: 'Cancel',
+        unbindSuccess: 'Unbound successfully'
+      }
+    },
+    topo: {
+      actions: {
+        add: 'Add Topology',
+        cancel: 'Cancel',
+        confirm: 'Confirm'
+      },
+      table: {
+        columns: {
+          id: 'ID',
+          name: 'Topology Name',
+          createdAt: 'Created At',
+          updatedAt: 'Updated At',
+          actions: 'Actions'
+        },
+        actions: {
+          preview: 'Preview',
+          edit: 'Edit Topology',
+          unbind: 'Unbind'
+        }
+      },
+      dialog: {
+        title: 'Add Topology'
+      },
+      form: {
+        labels: {
+          topo: 'Topology'
+        },
+        placeholders: {
+          topo: 'Select topology'
+        }
+      },
+      messages: {
+        selectTopoFirst: 'Please select a topology first',
+        addSuccess: 'Added successfully',
+        unbindConfirm: 'Are you sure to unbind this topology?',
+        tip: 'Prompt',
+        confirm: 'Confirm',
+        cancel: 'Cancel',
+        unbindSuccess: 'Unbound successfully'
+      }
+    },
+    video: {
+      actions: {
+        add: 'Add Video',
+        cancel: 'Cancel',
+        confirm: 'Confirm',
+        openInNewWindow: 'Open in new window'
+      },
+      table: {
+        columns: {
+          deviceName: 'Device Name',
+          deviceId: 'Device ID',
+          channelName: 'Channel Name',
+          model: 'Model',
+          manufacturer: 'Manufacturer',
+          liveStatus: 'Status',
+          keepAliveTime: 'Last Heartbeat',
+          registerTime: 'Register Time',
+          updateAt: 'Updated At',
+          actions: 'Actions'
+        },
+        actions: {
+          view: 'View Video',
+          unbind: 'Unbind'
+        }
+      },
+      dialog: {
+        addTitle: 'Add Video',
+        previewTitle: 'Preview Video'
+      },
+      form: {
+        labels: {
+          video: 'Video'
+        },
+        placeholders: {
+          video: 'Select video'
+        }
+      },
+      messages: {
+        selectVideoFirst: 'Please select a video first',
+        addSuccess: 'Added successfully',
+        unbindConfirm: 'Are you sure to unbind this video?',
+        tip: 'Prompt',
+        confirm: 'Confirm',
+        cancel: 'Cancel',
+        unbindSuccess: 'Unbound successfully'
+      },
+      liveStatusTags: {
+        idle: 'Idle',
+        inviting: 'Inviting',
+        streaming: 'Streaming'
+      }
+    }
+  },
+  screen: {
+    video: {
+      selects: {
+        project: 'Select Project'
+      }
+    },
+    info: {
+      title: 'Project Info',
+      selects: {
+        placeholder: 'Please select'
+      },
+      labels: {
+        projectName: 'Project Name:',
+        projectAddress: 'Project Address:'
+      },
+      units: {
+        squareMeter: 'm²',
+        device: 'units'
+      },
+      cards: {
+        coverageArea: 'Coverage Area',
+        deviceCount: 'Device Count',
+        maintainPhone: 'Maintenance Phone'
+      },
+      empty: {
+        dash: '-'
+      }
+    },
+    baseinfo: {
+      title: 'Basic Info',
+      selects: {
+        placeholder: 'Please select'
+      }
+    },
+    lineChart: {
+      title: 'Device Status',
+      date: {
+        rangeSeparator: 'to',
+        startPlaceholder: 'Start Time',
+        endPlaceholder: 'End Time'
+      },
+      selects: {
+        properties: 'Select properties'
+      }
+    }
+  },
+  filter: {
+    index: {
+      search: {
+        namePlaceholder: 'Search template name',
+        deviceKeyPlaceholder: 'Search device key',
+        statusPlaceholder: 'Select forward format',
+        query: 'Query',
+        reset: 'Reset'
+      },
+      status: {
+        all: 'All',
+        enabled: 'Enabled',
+        disabled: 'Disabled'
+      },
+      actions: {
+        add: 'Add',
+        delete: 'Delete'
+      },
+      table: {
+        columns: {
+          name: 'Template Name',
+          code: 'Template Code',
+          updatedAt: 'Updated At',
+          status: 'Status',
+          actions: 'Actions'
+        },
+        actions: {
+          detail: 'Details',
+          edit: 'Edit',
+          delete: 'Delete',
+          viewBind: 'View & Bind Devices'
+        }
+      },
+      switch: {
+        active: 'On',
+        inactive: 'Off'
+      },
+      messages: {
+        deleteConfirmSingle: 'This will delete template: “{name}”. Continue?',
+        deleteConfirmMultiple: 'This will delete {count} templates',
+        tip: 'Tip',
+        warn: 'Warning',
+        confirm: 'Confirm',
+        cancel: 'Cancel',
+        deleteSuccess: 'Deleted successfully',
+        enableActionText: 'Enable',
+        disableActionText: 'Disable',
+        statusChangeConfirm: 'Confirm to {action} template: 【{name}】?',
+        statusChangeSuccess: '{action} succeeded'
+      }
+    },
+    edit: {
+      form: {
+        name: 'Template Name'
+      },
+      placeholders: {
+        name: 'Enter template name'
+      },
+      actions: {
+        cancel: 'Cancel',
+        confirm: 'Confirm'
+      },
+      rules: {
+        required: 'Required'
+      },
+      messages: {
+        success: 'Operation succeeded'
+      }
+    },
+    editDialog: {
+      title: {
+        add: 'Add Template',
+        edit: 'Edit Template'
+      }
+    },
+    detail: {
+      dialog: {
+        title: 'Template Details'
+      },
+      sections: {
+        templateInfo: 'Device Data Filter Template Info',
+        customAttr: 'Custom Attributes'
+      },
+      descriptions: {
+        templateName: 'Template Name',
+        templateCode: 'Template Code',
+        updatedAt: 'Updated At'
+      },
+      actions: {
+        addCustomAttr: 'Add Custom Attribute'
+      },
+      table: {
+        columns: {
+          name: 'Attribute Name',
+          key: 'Attribute Code',
+          unit: 'Unit',
+          updatedAt: 'Updated At',
+          isVisible: 'Visible',
+          actions: 'Actions'
+        },
+        actions: {
+          edit: 'Edit',
+          delete: 'Delete'
+        }
+      },
+      switch: {
+        active: 'On',
+        inactive: 'Off'
+      },
+      messages: {
+        warn: 'Warning',
+        tip: 'Prompt',
+        confirm: 'Confirm',
+        cancel: 'Cancel',
+        deleteSuccess: 'Deleted successfully',
+        deleteConfirmSingle: 'This will delete attribute: “{name}”. Continue?',
+        deleteConfirmMultiple: 'This will delete {count} attributes',
+        confirmSetVisible: 'Confirm to set attribute "{name}" as visible?',
+        confirmSetInvisible: 'Confirm to set attribute "{name}" as invisible?',
+        setVisibleSuccess: 'Set visible successfully',
+        setInvisibleSuccess: 'Set invisible successfully'
+      }
+    }
+    ,
+    bindDevice: {
+      dialog: {
+        title: 'Bind Device'
+      },
+      sections: {
+        templateInfo: 'Device Data Filter Template Info',
+        bindDevice: 'Bind Device'
+      },
+      descriptions: {
+        templateName: 'Template Name',
+        templateCode: 'Template Code',
+        updatedAt: 'Updated At'
+      },
+      actions: {
+        bindDevice: 'Bind Device'
+      },
+      table: {
+        columns: {
+          name: 'Device Name',
+          key: 'Device Key',
+          productName: 'Product',
+          actions: 'Actions'
+        },
+        actions: {
+          unbind: 'Unbind'
+        }
+      },
+      messages: {
+        unbindSuccess: 'Unbound successfully'
+      }
+    },
+    bindDeviceForm: {
+      dialog: {
+        title: 'Bind Device'
+      },
+      table: {
+        columns: {
+          name: 'Device Name',
+          key: 'Device Key',
+          productName: 'Product'
+        }
+      },
+      actions: {
+        cancel: 'Cancel',
+        bind: 'Bind Device'
+      },
+      messages: {
+        bindSuccess: 'Operation successful'
+      }
+    },
+    attrEdit: {
+      titleEdit: 'Edit Attribute',
+      titleAdd: 'Add Attribute',
+      labels: {
+        name: 'Attribute Name',
+        key: 'Attribute KEY',
+        unit: 'Unit'
+      },
+      placeholders: {
+        name: 'Enter attribute name',
+        key: 'Enter attribute code',
+        unit: 'Enter unit'
+      },
+      actions: {
+        cancel: 'Cancel',
+        confirm: 'Confirm'
+      },
+      rules: {
+        required: 'Required'
+      },
+      messages: {
+        success: 'Operation succeeded'
+      }
+    }
+  }
+};

+ 553 - 0
src/i18n/pages/projects/zh-cn.ts

@@ -0,0 +1,553 @@
+export default {
+  list: {
+    search: {
+      channelPlaceholder: '渠道商搜索',
+      statusPlaceholder: '请选择转发格式',
+      query: '查询',
+      reset: '重置'
+    },
+    addOrEdit: {
+      form: {
+        name: '项目名称',
+        addressCode: '地区',
+        addressDetail: '详细地址',
+        channelMerchants: '渠道商',
+        customName: '关键客户',
+        repairCompany: '维修公司',
+        repairMobile: '维修电话'
+      },
+      placeholders: {
+        select: '请选择',
+        name: '输入项目名称',
+        addressDetail: '输入详细地址',
+        channelMerchants: '输入渠道商',
+        customName: '输入客户名称',
+        repairCompany: '输入维修公司',
+        repairMobile: '输入维修电话'
+      },
+      buttons: {
+        cancel: '取消',
+        confirm: '确定'
+      },
+      validation: {
+        name: '请输入项目名称',
+        addressCode: '请选择地区',
+        addressDetail: '请输入详细地址',
+        channelMerchants: '请输入渠道商',
+        customName: '请输入客户名称',
+        repairCompany: '请输入维修公司',
+        repairMobile: '请输入维修电话'
+      },
+      messages: {
+        addSuccess: '新增成功',
+        editSuccess: '编辑成功'
+      }
+    },
+    status: {
+      all: '全部状态',
+      enabled: '正常',
+      disabled: '禁用'
+    },
+    actions: {
+      add: '新增',
+      delete: '删除',
+      enable: '启用',
+      disable: '禁用'
+    },
+    table: {
+      columns: {
+        projectStatus: '项目状态',
+        projectName: '项目名称',
+        address: '省/市/区/县',
+        addressDetail: '详细地址',
+        channelMerchants: '渠道商',
+        customName: '关键客户',
+        repairCompany: '维修公司',
+        updatedAt: '更新时间',
+        actions: '操作'
+      },
+      actions: {
+        detail: '详情',
+        edit: '编辑',
+        delete: '删除'
+      }
+    },
+    messages: {
+      operationSuccess: '操作成功',
+      deleteConfirmSingle: '此操作将项目:“{name}”,是否继续?',
+      deleteConfirmMultiple: '此操作将{count}个项目',
+      tip: '提示',
+      confirm: '确认',
+      cancel: '取消',
+      deleteSuccess: '删除成功'
+    }
+  },
+  editDialog: {
+    title: {
+      add: '新增项目',
+      edit: '编辑项目'
+    }
+  },
+  detail: {
+    tabs: {
+      overview: '项目概况',
+      devices: '网关和设备',
+      scene: '场景联动',
+      topo: '组态应用',
+      video: '视频监控'
+    },
+    device: {
+      actions: {
+        add: '添加设备',
+        cancel: '取消',
+        confirm: '确定'
+      },
+      table: {
+        columns: {
+          key: '标识',
+          name: '设备名称',
+          deviceType: '设备类型',
+          productName: '所属产品',
+          status: '状态',
+          lastOnlineTime: '最后上线时间',
+          desc: '说明',
+          actions: '操作'
+        },
+        actions: {
+          detail: '设备详情',
+          unbind: '解绑'
+        },
+        statusTags: {
+          offline: '离线',
+          online: '在线',
+          inactive: '未启用'
+        }
+      },
+      dialog: {
+        title: '添加设备'
+      },
+      form: {
+        labels: {
+          product: '产品',
+          device: '设备'
+        },
+        placeholders: {
+          product: '选择产品',
+          device: '选择设备'
+        }
+      },
+      messages: {
+        selectDeviceFirst: '请先选择设备',
+        addSuccess: '添加成功',
+        deleteConfirm: '确定要删除?',
+        tip: '提示',
+        confirm: '确认',
+        cancel: '取消',
+        unbindSuccess: '解绑成功'
+      }
+    },
+    info: {
+      actions: {
+        edit: '编辑'
+      },
+      descriptions: {
+        name: '项目名称',
+        customName: '关键客户',
+        address: '地区',
+        channelMerchants: '渠道商',
+        repairCompany: '维修公司',
+        repairMobile: '维修电话',
+        addressDetail: '详细地址'
+      }
+    },
+    scene: {
+      actions: {
+        add: '添加场景',
+        cancel: '取消',
+        confirm: '确定'
+      },
+      table: {
+        columns: {
+          id: 'ID',
+          name: '场景名称',
+          sceneType: '触发方式',
+          status: '状态',
+          description: '场景描述',
+          createdAt: '创建时间',
+          actions: '操作'
+        },
+        actions: {
+          detail: '场景详情',
+          unbind: '解绑'
+        },
+        statusTags: {
+          enabled: '启用',
+          disabled: '禁用'
+        },
+        sceneTypeTags: {
+          device: '设备触发',
+          manual: '手动触发',
+          timer: '定时触发'
+        }
+      },
+      dialog: {
+        title: '添加场景'
+      },
+      form: {
+        labels: {
+          scene: '场景'
+        },
+        placeholders: {
+          scene: '选择场景'
+        }
+      },
+      messages: {
+        selectSceneFirst: '请先选择场景',
+        addSuccess: '添加成功',
+        unbindConfirm: '确定要解绑该场景?',
+        tip: '提示',
+        confirm: '确认',
+        cancel: '取消',
+        unbindSuccess: '解绑成功'
+      }
+    },
+    topo: {
+      actions: {
+        add: '添加组态',
+        cancel: '取消',
+        confirm: '确定'
+      },
+      table: {
+        columns: {
+          id: 'ID',
+          name: '组态图名称',
+          createdAt: '创建时间',
+          updatedAt: '更新时间',
+          actions: '操作'
+        },
+        actions: {
+          preview: '预览',
+          edit: '编辑组态图',
+          unbind: '解绑'
+        }
+      },
+      dialog: {
+        title: '添加组态'
+      },
+      form: {
+        labels: {
+          topo: '组态'
+        },
+        placeholders: {
+          topo: '选择组态'
+        }
+      },
+      messages: {
+        selectTopoFirst: '请先选择组态',
+        addSuccess: '添加成功',
+        unbindConfirm: '确定要解绑该组态?',
+        tip: '提示',
+        confirm: '确认',
+        cancel: '取消',
+        unbindSuccess: '解绑成功'
+      }
+    },
+    video: {
+      actions: {
+        add: '添加监控',
+        cancel: '取消',
+        confirm: '确定',
+        openInNewWindow: '新窗口预览'
+      },
+      table: {
+        columns: {
+          deviceName: '设备名称',
+          deviceId: '设备ID',
+          channelName: '通道名称',
+          model: '型号',
+          manufacturer: '厂商',
+          liveStatus: '状态',
+          keepAliveTime: '最后心跳时间',
+          registerTime: '注册时间',
+          updateAt: '更新时间',
+          actions: '操作'
+        },
+        actions: {
+          view: '查看监控',
+          unbind: '解绑'
+        }
+      },
+      dialog: {
+        addTitle: '添加监控',
+        previewTitle: '预览监控'
+      },
+      form: {
+        labels: {
+          video: '监控'
+        },
+        placeholders: {
+          video: '选择监控'
+        }
+      },
+      messages: {
+        selectVideoFirst: '请先选择监控',
+        addSuccess: '添加成功',
+        unbindConfirm: '确定要解绑该监控?',
+        tip: '提示',
+        confirm: '确认',
+        cancel: '取消',
+        unbindSuccess: '解绑成功'
+      },
+      liveStatusTags: {
+        idle: '空闲',
+        inviting: '调用中',
+        streaming: '拉流中'
+      }
+    }
+  },
+  screen: {
+    video: {
+      selects: {
+        project: '选择项目'
+      }
+    },
+    info: {
+      title: '项目信息',
+      selects: {
+        placeholder: '请选择'
+      },
+      labels: {
+        projectName: '项目名称:',
+        projectAddress: '项目地址:'
+      },
+      units: {
+        squareMeter: 'm²',
+        device: '台'
+      },
+      cards: {
+        coverageArea: '覆盖面积',
+        deviceCount: '设备台数',
+        maintainPhone: '维保电话'
+      },
+      empty: {
+        dash: '-'
+      }
+    },
+    baseinfo: {
+      title: '基本信息',
+      selects: {
+        placeholder: '请选择'
+      }
+    },
+    lineChart: {
+      title: '设备状态',
+      date: {
+        rangeSeparator: '至',
+        startPlaceholder: '开始时间',
+        endPlaceholder: '结束时间'
+      },
+      selects: {
+        properties: '请选择'
+      }
+    }
+  },
+  filter: {
+    index: {
+      search: {
+        namePlaceholder: '模板名称搜索',
+        deviceKeyPlaceholder: '设备key搜索',
+        statusPlaceholder: '请选择转发格式',
+        query: '查询',
+        reset: '重置'
+      },
+      status: {
+        all: '全部状态',
+        enabled: '正常',
+        disabled: '禁用'
+      },
+      actions: {
+        add: '新增',
+        delete: '删除'
+      },
+      table: {
+        columns: {
+          name: '模板名称',
+          code: '模板编码',
+          updatedAt: '更新时间',
+          status: '状态',
+          actions: '操作'
+        },
+        actions: {
+          detail: '详情',
+          edit: '编辑',
+          delete: '删除',
+          viewBind: '查看及绑定设备'
+        }
+      },
+      switch: {
+        active: '启',
+        inactive: '禁'
+      },
+      messages: {
+        deleteConfirmSingle: '此操作将模板:“{name}”,是否继续?',
+        deleteConfirmMultiple: '此操作将{count}个模板',
+        tip: '提示',
+        warn: '警告',
+        confirm: '确认',
+        cancel: '取消',
+        deleteSuccess: '删除成功',
+        enableActionText: '启用',
+        disableActionText: '停用',
+        statusChangeConfirm: '确认要{action}模板:【{name}】吗?',
+        statusChangeSuccess: '{action}成功'
+      }
+    },
+    edit: {
+      form: {
+        name: '模板名称'
+      },
+      placeholders: {
+        name: '输入模板名称'
+      },
+      actions: {
+        cancel: '取消',
+        confirm: '确定'
+      },
+      rules: {
+        required: '不能为空'
+      },
+      messages: {
+        success: '操作成功'
+      }
+    },
+    editDialog: {
+      title: {
+        add: '新增模板',
+        edit: '编辑模板'
+      }
+    },
+    detail: {
+      dialog: {
+        title: '模板详情'
+      },
+      sections: {
+        templateInfo: '设备数据过滤器模板信息',
+        customAttr: '自定义属性'
+      },
+      descriptions: {
+        templateName: '模板名称',
+        templateCode: '模板编码',
+        updatedAt: '更新时间'
+      },
+      actions: {
+        addCustomAttr: '添加自定义属性'
+      },
+      table: {
+        columns: {
+          name: '属性名称',
+          key: '属性编码',
+          unit: '单位',
+          updatedAt: '更新时间',
+          isVisible: '是否可见',
+          actions: '操作'
+        },
+        actions: {
+          edit: '编辑',
+          delete: '删除'
+        }
+      },
+      switch: {
+        active: '启',
+        inactive: '禁'
+      },
+      messages: {
+        warn: '警告',
+        tip: '提示',
+        confirm: '确认',
+        cancel: '取消',
+        deleteSuccess: '删除成功',
+        deleteConfirmSingle: '此操作将属性:“{name}”,是否继续?',
+        deleteConfirmMultiple: '此操作将{count}个属性',
+        confirmSetVisible: '确认要设置可见属性:【{name}】吗?',
+        confirmSetInvisible: '确认要设置不可见属性:【{name}】吗?',
+        setVisibleSuccess: '设置可见成功',
+        setInvisibleSuccess: '设置不可见成功'
+      }
+    }
+    ,
+    bindDevice: {
+      dialog: {
+        title: '绑定设备'
+      },
+      sections: {
+        templateInfo: '设备数据过滤器模板信息',
+        bindDevice: '绑定设备'
+      },
+      descriptions: {
+        templateName: '模板名称',
+        templateCode: '模板编码',
+        updatedAt: '更新时间'
+      },
+      actions: {
+        bindDevice: '绑定设备'
+      },
+      table: {
+        columns: {
+          name: '设备名称',
+          key: '设备编码',
+          productName: '所属产品',
+          actions: '操作'
+        },
+        actions: {
+          unbind: '解绑'
+        }
+      },
+      messages: {
+        unbindSuccess: '解绑成功'
+      }
+    },
+    bindDeviceForm: {
+      dialog: {
+        title: '绑定设备'
+      },
+      table: {
+        columns: {
+          name: '设备名称',
+          key: '设备编码',
+          productName: '所属产品'
+        }
+      },
+      actions: {
+        cancel: '取消',
+        bind: '绑定设备'
+      },
+      messages: {
+        bindSuccess: '操作成功'
+      }
+    },
+    attrEdit: {
+      titleEdit: '编辑属性',
+      titleAdd: '新增属性',
+      labels: {
+        name: '属性名称',
+        key: '属性KEY',
+        unit: '单位'
+      },
+      placeholders: {
+        name: '输入属性名称',
+        key: '输入属性编码',
+        unit: '输入单位'
+      },
+      actions: {
+        cancel: '取消',
+        confirm: '确定'
+      },
+      rules: {
+        required: '不能为空'
+      },
+      messages: {
+        success: '操作成功'
+      }
+    }
+  }
+};

+ 553 - 0
src/i18n/pages/projects/zh-tw.ts

@@ -0,0 +1,553 @@
+export default {
+  list: {
+    search: {
+      channelPlaceholder: '渠道商搜尋',
+      statusPlaceholder: '請選擇轉發格式',
+      query: '查詢',
+      reset: '重置'
+    },
+    addOrEdit: {
+      form: {
+        name: '項目名稱',
+        addressCode: '地區',
+        addressDetail: '詳細地址',
+        channelMerchants: '渠道商',
+        customName: '關鍵客戶',
+        repairCompany: '維修公司',
+        repairMobile: '維修電話'
+      },
+      placeholders: {
+        select: '請選擇',
+        name: '輸入項目名稱',
+        addressDetail: '輸入詳細地址',
+        channelMerchants: '輸入渠道商',
+        customName: '輸入客戶名稱',
+        repairCompany: '輸入維修公司',
+        repairMobile: '輸入維修電話'
+      },
+      buttons: {
+        cancel: '取消',
+        confirm: '確定'
+      },
+      validation: {
+        name: '請輸入項目名稱',
+        addressCode: '請選擇地區',
+        addressDetail: '請輸入詳細地址',
+        channelMerchants: '請輸入渠道商',
+        customName: '請輸入客戶名稱',
+        repairCompany: '請輸入維修公司',
+        repairMobile: '請輸入維修電話'
+      },
+      messages: {
+        addSuccess: '新增成功',
+        editSuccess: '編輯成功'
+      }
+    },
+    status: {
+      all: '全部狀態',
+      enabled: '正常',
+      disabled: '禁用'
+    },
+    actions: {
+      add: '新增',
+      delete: '刪除',
+      enable: '啟用',
+      disable: '禁用'
+    },
+    table: {
+      columns: {
+        projectStatus: '項目狀態',
+        projectName: '項目名稱',
+        address: '省/市/區/縣',
+        addressDetail: '詳細地址',
+        channelMerchants: '渠道商',
+        customName: '關鍵客戶',
+        repairCompany: '維修公司',
+        updatedAt: '更新時間',
+        actions: '操作'
+      },
+      actions: {
+        detail: '詳情',
+        edit: '編輯',
+        delete: '刪除'
+      }
+    },
+    messages: {
+      operationSuccess: '操作成功',
+      deleteConfirmSingle: '此操作將項目:「{name}」,是否繼續?',
+      deleteConfirmMultiple: '此操作將{count}個項目',
+      tip: '提示',
+      confirm: '確認',
+      cancel: '取消',
+      deleteSuccess: '刪除成功'
+    }
+  },
+  editDialog: {
+    title: {
+      add: '新增項目',
+      edit: '編輯項目'
+    }
+  },
+  detail: {
+    tabs: {
+      overview: '項目概況',
+      devices: '網關與設備',
+      scene: '場景聯動',
+      topo: '組態應用',
+      video: '視頻監控'
+    },
+    device: {
+      actions: {
+        add: '添加設備',
+        cancel: '取消',
+        confirm: '確定'
+      },
+      table: {
+        columns: {
+          key: '標識',
+          name: '設備名稱',
+          deviceType: '設備類型',
+          productName: '所屬產品',
+          status: '狀態',
+          lastOnlineTime: '最後上線時間',
+          desc: '說明',
+          actions: '操作'
+        },
+        actions: {
+          detail: '設備詳情',
+          unbind: '解綁'
+        },
+        statusTags: {
+          offline: '離線',
+          online: '在線',
+          inactive: '未啟用'
+        }
+      },
+      dialog: {
+        title: '添加設備'
+      },
+      form: {
+        labels: {
+          product: '產品',
+          device: '設備'
+        },
+        placeholders: {
+          product: '選擇產品',
+          device: '選擇設備'
+        }
+      },
+      messages: {
+        selectDeviceFirst: '請先選擇設備',
+        addSuccess: '添加成功',
+        deleteConfirm: '確定要刪除?',
+        tip: '提示',
+        confirm: '確認',
+        cancel: '取消',
+        unbindSuccess: '解綁成功'
+      }
+    },
+    info: {
+      actions: {
+        edit: '編輯'
+      },
+      descriptions: {
+        name: '項目名稱',
+        customName: '關鍵客戶',
+        address: '地區',
+        channelMerchants: '渠道商',
+        repairCompany: '維修公司',
+        repairMobile: '維修電話',
+        addressDetail: '詳細地址'
+      }
+    },
+    scene: {
+      actions: {
+        add: '添加場景',
+        cancel: '取消',
+        confirm: '確定'
+      },
+      table: {
+        columns: {
+          id: 'ID',
+          name: '場景名稱',
+          sceneType: '觸發方式',
+          status: '狀態',
+          description: '場景描述',
+          createdAt: '創建時間',
+          actions: '操作'
+        },
+        actions: {
+          detail: '場景詳情',
+          unbind: '解綁'
+        },
+        statusTags: {
+          enabled: '啟用',
+          disabled: '禁用'
+        },
+        sceneTypeTags: {
+          device: '設備觸發',
+          manual: '手動觸發',
+          timer: '定時觸發'
+        }
+      },
+      dialog: {
+        title: '添加場景'
+      },
+      form: {
+        labels: {
+          scene: '場景'
+        },
+        placeholders: {
+          scene: '選擇場景'
+        }
+      },
+      messages: {
+        selectSceneFirst: '請先選擇場景',
+        addSuccess: '添加成功',
+        unbindConfirm: '確定要解綁該場景?',
+        tip: '提示',
+        confirm: '確認',
+        cancel: '取消',
+        unbindSuccess: '解綁成功'
+      }
+    },
+    topo: {
+      actions: {
+        add: '添加組態',
+        cancel: '取消',
+        confirm: '確定'
+      },
+      table: {
+        columns: {
+          id: 'ID',
+          name: '組態圖名稱',
+          createdAt: '創建時間',
+          updatedAt: '更新時間',
+          actions: '操作'
+        },
+        actions: {
+          preview: '預覽',
+          edit: '編輯組態圖',
+          unbind: '解綁'
+        }
+      },
+      dialog: {
+        title: '添加組態'
+      },
+      form: {
+        labels: {
+          topo: '組態'
+        },
+        placeholders: {
+          topo: '選擇組態'
+        }
+      },
+      messages: {
+        selectTopoFirst: '請先選擇組態',
+        addSuccess: '添加成功',
+        unbindConfirm: '確定要解綁該組態?',
+        tip: '提示',
+        confirm: '確認',
+        cancel: '取消',
+        unbindSuccess: '解綁成功'
+      }
+    },
+    video: {
+      actions: {
+        add: '添加監控',
+        cancel: '取消',
+        confirm: '確定',
+        openInNewWindow: '新窗口預覽'
+      },
+      table: {
+        columns: {
+          deviceName: '設備名稱',
+          deviceId: '設備ID',
+          channelName: '通道名稱',
+          model: '型號',
+          manufacturer: '廠商',
+          liveStatus: '狀態',
+          keepAliveTime: '最後心跳時間',
+          registerTime: '註冊時間',
+          updateAt: '更新時間',
+          actions: '操作'
+        },
+        actions: {
+          view: '查看監控',
+          unbind: '解綁'
+        }
+      },
+      dialog: {
+        addTitle: '添加監控',
+        previewTitle: '預覽監控'
+      },
+      form: {
+        labels: {
+          video: '監控'
+        },
+        placeholders: {
+          video: '選擇監控'
+        }
+      },
+      messages: {
+        selectVideoFirst: '請先選擇監控',
+        addSuccess: '添加成功',
+        unbindConfirm: '確定要解綁該監控?',
+        tip: '提示',
+        confirm: '確認',
+        cancel: '取消',
+        unbindSuccess: '解綁成功'
+      },
+      liveStatusTags: {
+        idle: '空閒',
+        inviting: '調用中',
+        streaming: '拉流中'
+      }
+    }
+  },
+  screen: {
+    video: {
+      selects: {
+        project: '選擇項目'
+      }
+    },
+    info: {
+      title: '項目信息',
+      selects: {
+        placeholder: '請選擇'
+      },
+      labels: {
+        projectName: '項目名稱:',
+        projectAddress: '項目地址:'
+      },
+      units: {
+        squareMeter: 'm²',
+        device: '台'
+      },
+      cards: {
+        coverageArea: '覆蓋面積',
+        deviceCount: '設備台數',
+        maintainPhone: '維保電話'
+      },
+      empty: {
+        dash: '-'
+      }
+    },
+    baseinfo: {
+      title: '基本信息',
+      selects: {
+        placeholder: '請選擇'
+      }
+    },
+    lineChart: {
+      title: '設備狀態',
+      date: {
+        rangeSeparator: '至',
+        startPlaceholder: '開始時間',
+        endPlaceholder: '結束時間'
+      },
+      selects: {
+        properties: '請選擇'
+      }
+    }
+  },
+  filter: {
+    index: {
+      search: {
+        namePlaceholder: '模板名稱搜尋',
+        deviceKeyPlaceholder: '設備 key 搜尋',
+        statusPlaceholder: '請選擇轉發格式',
+        query: '查詢',
+        reset: '重置'
+      },
+      status: {
+        all: '全部狀態',
+        enabled: '正常',
+        disabled: '禁用'
+      },
+      actions: {
+        add: '新增',
+        delete: '刪除'
+      },
+      table: {
+        columns: {
+          name: '模板名稱',
+          code: '模板編碼',
+          updatedAt: '更新時間',
+          status: '狀態',
+          actions: '操作'
+        },
+        actions: {
+          detail: '詳情',
+          edit: '編輯',
+          delete: '刪除',
+          viewBind: '查看及綁定設備'
+        }
+      },
+      switch: {
+        active: '啟',
+        inactive: '禁'
+      },
+      messages: {
+        deleteConfirmSingle: '此操作將刪除模板:「{name}」,是否繼續?',
+        deleteConfirmMultiple: '此操作將刪除 {count} 個模板',
+        tip: '提示',
+        warn: '警告',
+        confirm: '確認',
+        cancel: '取消',
+        deleteSuccess: '刪除成功',
+        enableActionText: '啟用',
+        disableActionText: '停用',
+        statusChangeConfirm: '確認要{action}模板:【{name}】嗎?',
+        statusChangeSuccess: '{action}成功'
+      }
+    },
+    edit: {
+      form: {
+        name: '模板名稱'
+      },
+      placeholders: {
+        name: '輸入模板名稱'
+      },
+      actions: {
+        cancel: '取消',
+        confirm: '確定'
+      },
+      rules: {
+        required: '不能為空'
+      },
+      messages: {
+        success: '操作成功'
+      }
+    },
+    editDialog: {
+      title: {
+        add: '新增模板',
+        edit: '編輯模板'
+      }
+    },
+    detail: {
+      dialog: {
+        title: '模板詳情'
+      },
+      sections: {
+        templateInfo: '設備數據過濾器模板資訊',
+        customAttr: '自定義屬性'
+      },
+      descriptions: {
+        templateName: '模板名稱',
+        templateCode: '模板編碼',
+        updatedAt: '更新時間'
+      },
+      actions: {
+        addCustomAttr: '添加自定義屬性'
+      },
+      table: {
+        columns: {
+          name: '屬性名稱',
+          key: '屬性編碼',
+          unit: '單位',
+          updatedAt: '更新時間',
+          isVisible: '是否可見',
+          actions: '操作'
+        },
+        actions: {
+          edit: '編輯',
+          delete: '刪除'
+        }
+      },
+      switch: {
+        active: '啟',
+        inactive: '禁'
+      },
+      messages: {
+        warn: '警告',
+        tip: '提示',
+        confirm: '確認',
+        cancel: '取消',
+        deleteSuccess: '刪除成功',
+        deleteConfirmSingle: '此操作將屬性:「{name}」,是否繼續?',
+        deleteConfirmMultiple: '此操作將刪除 {count} 個屬性',
+        confirmSetVisible: '確認要設置可見屬性:【{name}】嗎?',
+        confirmSetInvisible: '確認要設置不可見屬性:【{name}】嗎?',
+        setVisibleSuccess: '設置可見成功',
+        setInvisibleSuccess: '設置不可見成功'
+      }
+    }
+    ,
+    bindDevice: {
+      dialog: {
+        title: '綁定設備'
+      },
+      sections: {
+        templateInfo: '設備數據過濾器模板信息',
+        bindDevice: '綁定設備'
+      },
+      descriptions: {
+        templateName: '模板名稱',
+        templateCode: '模板編碼',
+        updatedAt: '更新時間'
+      },
+      actions: {
+        bindDevice: '綁定設備'
+      },
+      table: {
+        columns: {
+          name: '設備名稱',
+          key: '設備編碼',
+          productName: '所屬產品',
+          actions: '操作'
+        },
+        actions: {
+          unbind: '解綁'
+        }
+      },
+      messages: {
+        unbindSuccess: '解綁成功'
+      }
+    },
+    bindDeviceForm: {
+      dialog: {
+        title: '綁定設備'
+      },
+      table: {
+        columns: {
+          name: '設備名稱',
+          key: '設備編碼',
+          productName: '所屬產品'
+        }
+      },
+      actions: {
+        cancel: '取消',
+        bind: '綁定設備'
+      },
+      messages: {
+        bindSuccess: '操作成功'
+      }
+    },
+    attrEdit: {
+      titleEdit: '編輯屬性',
+      titleAdd: '新增屬性',
+      labels: {
+        name: '屬性名稱',
+        key: '屬性KEY',
+        unit: '單位'
+      },
+      placeholders: {
+        name: '輸入屬性名稱',
+        key: '輸入屬性編碼',
+        unit: '輸入單位'
+      },
+      actions: {
+        cancel: '取消',
+        confirm: '確定'
+      },
+      rules: {
+        required: '不能為空'
+      },
+      messages: {
+        success: '操作成功'
+      }
+    }
+  }
+};

+ 172 - 0
src/i18n/pages/property/en.ts

@@ -0,0 +1,172 @@
+export default {
+  attribute: {
+    dialog: {
+      editTitle: 'Edit Device Archive Attribute',
+      addTitle: 'Add Device Archive Attribute',
+    },
+    form: {
+      keyword: 'Keyword',
+      keywordPlaceholder: 'Please enter keyword',
+      productKey: 'Product',
+      productPlaceholder: 'Please select product',
+      fieldName: 'Field Name',
+      fieldNamePlaceholder: 'Please enter field name',
+      fieldTitle: 'Field Title',
+      fieldTitlePlaceholder: 'Please enter field title',
+      fieldType: 'Field Type',
+      typePlaceholder: 'Please select field type',
+      fieldDesc: 'Field Description',
+      fieldDescPlaceholder: 'Please enter field description',
+    },
+    actions: {
+      add: 'Add Attribute',
+      save: 'Save',
+    },
+    columns: {
+      fieldName: 'Field Name',
+      fieldTitle: 'Field Title',
+      fieldType: 'Field Type',
+      createdAt: 'Created At',
+    },
+    types: {
+      input: 'Input',
+      textarea: 'Textarea',
+      date: 'Date',
+      file: 'Upload Image',
+    },
+    valid: {
+      nameRequired: 'Field name is required',
+      titleRequired: 'Field title is required',
+      productRequired: 'Product is required',
+      typeRequired: 'Field type is required',
+    },
+    messages: {
+      tips: 'Tips',
+      confirm: 'Confirm',
+      cancel: 'Cancel',
+      deleteSuccess: 'Deleted successfully',
+      saveSuccess: 'Saved successfully',
+      batchDeleteConfirm: 'Are you sure to batch delete these data?',
+      deleteItemConfirm: 'Are you sure to delete the item with name: “{name}”?',
+    },
+  },
+  dossier: {
+    dialog: {
+      editTitle: 'Edit Device Archive',
+      addTitle: 'Add Device Archive',
+    },
+    form: {
+      keyword: 'Name',
+      keywordPlaceholder: 'Please enter name',
+      productKey: 'Select Product',
+      productPlaceholder: 'Please select product',
+      deviceKey: 'Select Device',
+      devicePlaceholder: 'Please select device',
+      deviceName: 'Device Name',
+      deviceNamePlaceholder: 'Please enter device name',
+      deviceNumber: 'Device Code',
+      deviceNumberPlaceholder: 'Please enter device code',
+      deptId: 'Department',
+      deptPlaceholder: 'Please select department',
+      deviceCategory: 'Device Type',
+      deviceCategoryPlaceholder: 'Please enter device type',
+      installTime: 'Install Time',
+      datePlaceholder: 'Please select date',
+      dividerCustom: 'Custom Attributes',
+      inputPlaceholder: 'Please enter {title}',
+    },
+    actions: {
+      save: 'Confirm',
+    },
+    columns: {
+      deviceName: 'Device Name',
+      deviceKey: 'Device KEY',
+      deviceNumber: 'Device Code',
+      deviceCategory: 'Device Type',
+      installTime: 'Install Time',
+    },
+    valid: {
+      productRequired: 'Product is required',
+      deviceNameRequired: 'Device name is required',
+      deviceRequired: 'Device is required',
+    },
+    messages: {
+      tips: 'Tips',
+      confirm: 'Confirm',
+      cancel: 'Cancel',
+      deleteSuccess: 'Deleted successfully',
+      saveSuccess: 'Operation successful',
+      batchDeleteConfirm: 'Are you sure to batch delete these data?',
+      deleteItemConfirm: 'Are you sure to delete the item with name: “{name}”?',
+    },
+  },
+  relationship: {
+    dialog: {
+      editTitle: 'Edit Asset Relationship',
+      addTitle: 'Add Asset Relationship',
+      filterTitle: 'Set Filter Conditions',
+    },
+    form: {
+      keyword: 'Name',
+      keywordPlaceholder: 'Please enter name',
+      chooseRole: 'Role',
+      chooseRolePlaceholder: 'Please select role',
+      selectedDevices: 'Selected Devices',
+    },
+    valid: {
+      roleRequired: 'Role is required',
+      deviceRequired: 'Device is required',
+      fieldRequiredTitle: '{title} is required',
+    },
+    actions: {
+      add: 'Add Asset Relationship',
+      unbind: 'Unbind',
+    },
+    columns: {
+      deviceName: 'Device Name',
+      deviceKey: 'Device KEY',
+      deviceNumber: 'Device Code',
+      deviceCategory: 'Device Type',
+      installTime: 'Install Time',
+      status: 'Status',
+    },
+    switch: {
+      enableShort: 'On',
+      disableShort: 'Off',
+    },
+    status: {
+      enable: 'Enable',
+      disable: 'Disable',
+    },
+    messages: {
+      tips: 'Tips',
+      warning: 'Warning',
+      confirm: 'Confirm',
+      cancel: 'Cancel',
+      statusChangeConfirm: 'Are you sure to {text}?',
+      statusChangeSuccess: '{text} successfully',
+      saveSuccess: 'Operation successful',
+      unbindConfirm: 'Are you sure to unbind?',
+      unbindSuccess: 'Unbound successfully',
+      batchUnbindConfirm: 'Are you sure to batch unbind these data?',
+    },
+  },
+  deviceMap: {
+    form: {
+      selectProductPlaceholder: 'Select Product',
+    },
+    messages: {
+      noCoordsWarning: 'There are no devices with coordinates for this product!',
+    },
+    popup: {
+      todayAlarmTotal: 'Total alarms today',
+      todayUnhandled: 'Unhandled alarms today',
+      todayHandled: 'Handled alarms today',
+      codeLabel: 'Code: ',
+      lastOnlineTimeLabel: 'Last online time: ',
+      addressLabel: 'Address: ',
+      deviceTypeGateway: 'Gateway',
+      deviceTypeDevice: 'Device',
+    },
+  },
+};

+ 172 - 0
src/i18n/pages/property/zh-cn.ts

@@ -0,0 +1,172 @@
+export default {
+  attribute: {
+    dialog: {
+      editTitle: '编辑设备档案属性',
+      addTitle: '新增设备档案属性',
+    },
+    form: {
+      keyword: '关键词',
+      keywordPlaceholder: '请输入关键词',
+      productKey: '所属产品',
+      productPlaceholder: '请选择产品',
+      fieldName: '字段名称',
+      fieldNamePlaceholder: '请输入字段名称',
+      fieldTitle: '字段标题',
+      fieldTitlePlaceholder: '请输入字段标题',
+      fieldType: '字段类型',
+      typePlaceholder: '请选择字段类型',
+      fieldDesc: '字段描述',
+      fieldDescPlaceholder: '请输入字段描述',
+    },
+    actions: {
+      add: '新增属性',
+      save: '保存',
+    },
+    columns: {
+      fieldName: '字段名',
+      fieldTitle: '字段标题',
+      fieldType: '字段类型',
+      createdAt: '创建时间',
+    },
+    types: {
+      input: '输入框',
+      textarea: '文本框',
+      date: '日期',
+      file: '上传图片',
+    },
+    valid: {
+      nameRequired: '字段名称不能为空',
+      titleRequired: '字段标题不能为空',
+      productRequired: '所属产品不能为空',
+      typeRequired: '字段类型不能为空',
+    },
+    messages: {
+      tips: '提示',
+      confirm: '确认',
+      cancel: '取消',
+      deleteSuccess: '删除成功',
+      saveSuccess: '保存成功',
+      batchDeleteConfirm: '是否确认要批量删除这些数据吗?',
+      deleteItemConfirm: '是否确认删除名称为:“{name}”的数据项?',
+    },
+  },
+  dossier: {
+    dialog: {
+      editTitle: '编辑设备档案',
+      addTitle: '新增设备档案',
+    },
+    form: {
+      keyword: '名称',
+      keywordPlaceholder: '请输入名称',
+      productKey: '选择产品',
+      productPlaceholder: '请选择产品',
+      deviceKey: '选择设备',
+      devicePlaceholder: '请选择设备',
+      deviceName: '设备名称',
+      deviceNamePlaceholder: '请输入设备名称',
+      deviceNumber: '设备编码',
+      deviceNumberPlaceholder: '请输入设备编码',
+      deptId: '所属部门',
+      deptPlaceholder: '请选择所属部门',
+      deviceCategory: '设备类型',
+      deviceCategoryPlaceholder: '请输入设备类型',
+      installTime: '安装时间',
+      datePlaceholder: '请选择时间',
+      dividerCustom: '自定义属性',
+      inputPlaceholder: '请输入{title}',
+    },
+    actions: {
+      save: '确定',
+    },
+    columns: {
+      deviceName: '设备名称',
+      deviceKey: '设备KEY',
+      deviceNumber: '设备编码',
+      deviceCategory: '设备类型',
+      installTime: '安装时间',
+    },
+    valid: {
+      productRequired: '所属产品不能为空',
+      deviceNameRequired: '设备名称不能为空',
+      deviceRequired: '设备不能为空',
+    },
+    messages: {
+      tips: '提示',
+      confirm: '确认',
+      cancel: '取消',
+      deleteSuccess: '删除成功',
+      saveSuccess: '操作成功',
+      batchDeleteConfirm: '是否确认要批量删除这些数据吗?',
+      deleteItemConfirm: '是否确认删除名称为:“{name}”的数据项?',
+    },
+  },
+  relationship: {
+    dialog: {
+      editTitle: '编辑资产关系',
+      addTitle: '新增资产关系',
+      filterTitle: '设置筛选条件',
+    },
+    form: {
+      keyword: '名称',
+      keywordPlaceholder: '请输入名称',
+      chooseRole: '选择角色',
+      chooseRolePlaceholder: '请选择角色',
+      selectedDevices: '已选设备',
+    },
+    valid: {
+      roleRequired: '角色不能为空',
+      deviceRequired: '设备不能为空',
+      fieldRequiredTitle: '{title}不能为空',
+    },
+    actions: {
+      add: '新增资产关系',
+      unbind: '解绑',
+    },
+    columns: {
+      deviceName: '设备名称',
+      deviceKey: '设备KEY',
+      deviceNumber: '设备编码',
+      deviceCategory: '设备类型',
+      installTime: '安装时间',
+      status: '状态',
+    },
+    switch: {
+      enableShort: '启',
+      disableShort: '禁',
+    },
+    status: {
+      enable: '启用',
+      disable: '停用',
+    },
+    messages: {
+      tips: '提示',
+      warning: '警告',
+      confirm: '确定',
+      cancel: '取消',
+      statusChangeConfirm: '确认要【{text}】吗?',
+      statusChangeSuccess: '{text}成功',
+      saveSuccess: '操作成功',
+      unbindConfirm: '是否确认解绑',
+      unbindSuccess: '解绑成功',
+      batchUnbindConfirm: '是否确认要批量解绑这些数据吗?',
+    },
+  },
+  deviceMap: {
+    form: {
+      selectProductPlaceholder: '选择产品',
+    },
+    messages: {
+      noCoordsWarning: '该产品不存在有坐标的设备!',
+    },
+    popup: {
+      todayAlarmTotal: '今日报警总数',
+      todayUnhandled: '今日未处理报警',
+      todayHandled: '今日已处理报警',
+      codeLabel: '编号:',
+      lastOnlineTimeLabel: '最后在线时间:',
+      addressLabel: '地址:',
+      deviceTypeGateway: '网关',
+      deviceTypeDevice: '设备',
+    },
+  },
+};

+ 172 - 0
src/i18n/pages/property/zh-tw.ts

@@ -0,0 +1,172 @@
+export default {
+  attribute: {
+    dialog: {
+      editTitle: '編輯設備檔案屬性',
+      addTitle: '新增設備檔案屬性',
+    },
+    form: {
+      keyword: '關鍵詞',
+      keywordPlaceholder: '請輸入關鍵詞',
+      productKey: '所屬產品',
+      productPlaceholder: '請選擇產品',
+      fieldName: '欄位名稱',
+      fieldNamePlaceholder: '請輸入欄位名稱',
+      fieldTitle: '欄位標題',
+      fieldTitlePlaceholder: '請輸入欄位標題',
+      fieldType: '欄位類型',
+      typePlaceholder: '請選擇欄位類型',
+      fieldDesc: '欄位描述',
+      fieldDescPlaceholder: '請輸入欄位描述',
+    },
+    actions: {
+      add: '新增屬性',
+      save: '保存',
+    },
+    columns: {
+      fieldName: '欄位名',
+      fieldTitle: '欄位標題',
+      fieldType: '欄位類型',
+      createdAt: '建立時間',
+    },
+    types: {
+      input: '輸入框',
+      textarea: '文字框',
+      date: '日期',
+      file: '上傳圖片',
+    },
+    valid: {
+      nameRequired: '欄位名稱不能為空',
+      titleRequired: '欄位標題不能為空',
+      productRequired: '所屬產品不能為空',
+      typeRequired: '欄位類型不能為空',
+    },
+    messages: {
+      tips: '提示',
+      confirm: '確認',
+      cancel: '取消',
+      deleteSuccess: '刪除成功',
+      saveSuccess: '保存成功',
+      batchDeleteConfirm: '是否確認要批量刪除這些資料?',
+      deleteItemConfirm: '是否確認刪除名稱為:「{name}」的資料項?',
+    },
+  },
+  dossier: {
+    dialog: {
+      editTitle: '編輯設備檔案',
+      addTitle: '新增設備檔案',
+    },
+    form: {
+      keyword: '名稱',
+      keywordPlaceholder: '請輸入名稱',
+      productKey: '選擇產品',
+      productPlaceholder: '請選擇產品',
+      deviceKey: '選擇設備',
+      devicePlaceholder: '請選擇設備',
+      deviceName: '設備名稱',
+      deviceNamePlaceholder: '請輸入設備名稱',
+      deviceNumber: '設備編碼',
+      deviceNumberPlaceholder: '請輸入設備編碼',
+      deptId: '所屬部門',
+      deptPlaceholder: '請選擇所屬部門',
+      deviceCategory: '設備類型',
+      deviceCategoryPlaceholder: '請輸入設備類型',
+      installTime: '安裝時間',
+      datePlaceholder: '請選擇時間',
+      dividerCustom: '自定義屬性',
+      inputPlaceholder: '請輸入{title}',
+    },
+    actions: {
+      save: '確定',
+    },
+    columns: {
+      deviceName: '設備名稱',
+      deviceKey: '設備KEY',
+      deviceNumber: '設備編碼',
+      deviceCategory: '設備類型',
+      installTime: '安裝時間',
+    },
+    valid: {
+      productRequired: '所屬產品不能為空',
+      deviceNameRequired: '設備名稱不能為空',
+      deviceRequired: '設備不能為空',
+    },
+    messages: {
+      tips: '提示',
+      confirm: '確認',
+      cancel: '取消',
+      deleteSuccess: '刪除成功',
+      saveSuccess: '操作成功',
+      batchDeleteConfirm: '是否確認要批量刪除這些資料?',
+      deleteItemConfirm: '是否確認刪除名稱為:「{name}」的資料項?',
+    },
+  },
+  relationship: {
+    dialog: {
+      editTitle: '編輯資產關係',
+      addTitle: '新增資產關係',
+      filterTitle: '設置篩選條件',
+    },
+    form: {
+      keyword: '名稱',
+      keywordPlaceholder: '請輸入名稱',
+      chooseRole: '選擇角色',
+      chooseRolePlaceholder: '請選擇角色',
+      selectedDevices: '已選設備',
+    },
+    valid: {
+      roleRequired: '角色不能為空',
+      deviceRequired: '設備不能為空',
+      fieldRequiredTitle: '{title}不能為空',
+    },
+    actions: {
+      add: '新增資產關係',
+      unbind: '解绑',
+    },
+    columns: {
+      deviceName: '設備名稱',
+      deviceKey: '設備KEY',
+      deviceNumber: '設備編碼',
+      deviceCategory: '設備類型',
+      installTime: '安裝時間',
+      status: '狀態',
+    },
+    switch: {
+      enableShort: '啟',
+      disableShort: '禁',
+    },
+    status: {
+      enable: '啟用',
+      disable: '停用',
+    },
+    messages: {
+      tips: '提示',
+      warning: '警告',
+      confirm: '確定',
+      cancel: '取消',
+      statusChangeConfirm: '確認要【{text}】嗎?',
+      statusChangeSuccess: '{text}成功',
+      saveSuccess: '操作成功',
+      unbindConfirm: '是否確認解绑',
+      unbindSuccess: '解绑成功',
+      batchUnbindConfirm: '是否確認要批量解绑這些資料嗎?',
+    },
+  },
+  deviceMap: {
+    form: {
+      selectProductPlaceholder: '選擇產品',
+    },
+    messages: {
+      noCoordsWarning: '該產品不存在有座標的設備!',
+    },
+    popup: {
+      todayAlarmTotal: '今日報警總數',
+      todayUnhandled: '今日未處理報警',
+      todayHandled: '今日已處理報警',
+      codeLabel: '編號:',
+      lastOnlineTimeLabel: '最後上線時間:',
+      addressLabel: '地址:',
+      deviceTypeGateway: '網關',
+      deviceTypeDevice: '設備',
+    },
+  },
+};

+ 39 - 0
src/i18n/pages/tableI18n/en.ts

@@ -0,0 +1,39 @@
+/*
+ * @Author: vera_min vera_min@163.com
+ * @Date: 2025-08-06 01:16:57
+ * @LastEditors: vera_min vera_min@163.com
+ * @LastEditTime: 2025-08-09 13:10:35
+ * @FilePath: /sagoo-admin-ui/src/i18n/pages/tableI18n/en.ts
+ * @Description: 表格国际化 - 英文
+ */
+// 定义内容
+export default {
+	tableI18nColumn: {
+		id: "ID",
+		index: "Index",
+		operation: "Operation",
+		des: "Description",
+	},
+	tableI18nConfirm: {
+		deleteTitle: "Confirm",
+		confirmText: "Delete",
+		cancelText: "Cancel",
+		deleteSuccess: "Deleted successfully",
+		addSuccess: "Created successfully",
+		addFailed: "Failed to create",
+		editSuccess: "Updated successfully",
+		deleteSelectedMessage: "Are you sure you want to delete the selected data?",
+		selectDataFirst: "Please select the data to delete first.",
+		saveSuccess: "Saved successfully"
+	},
+	tableI18nAction: {
+		detail: "Detail",
+		handle: "Handle",
+		add: "Add",
+		edit: "Edit",
+		delete: "Delete",
+		cancel: "Cancel",
+		query: "Query",
+		more: "More"
+	}
+};

+ 39 - 0
src/i18n/pages/tableI18n/zh-cn.ts

@@ -0,0 +1,39 @@
+/*
+ * @Author: vera_min vera_min@163.com
+ * @Date: 2025-08-06 01:16:57
+ * @LastEditors: vera_min vera_min@163.com
+ * @LastEditTime: 2025-08-08 22:30:49
+ * @FilePath: /sagoo-admin-ui/src/i18n/pages/tableI18n/zh-cn.ts
+ * @Description: 表格国际化 - 简体中文
+ */
+// 定义内容
+export default {
+	tableI18nColumn: {
+		id: "ID",
+		index: "序号",
+		operation: "操作",
+		des: "描述",
+	},
+	tableI18nConfirm: {
+		deleteTitle: "提示",
+		confirmText: "删除",
+		cancelText: "取消",
+		deleteSuccess: "删除成功",
+		addSuccess: "新增成功",
+		addFailed: "新增失败",
+		editSuccess: "编辑成功",
+		deleteSelectedMessage: "你确定要删除所选数据?",
+		selectDataFirst: "请选择要删除的数据。",
+		saveSuccess: "保存成功"
+	},
+	tableI18nAction: {
+		detail: "详情",
+		handle: "处理",
+		add: "新增",
+		edit: "编辑",
+		delete: "删除",
+		cancel: "取 消",
+		query: "查询",
+		more: "更多"
+	}
+};

+ 39 - 0
src/i18n/pages/tableI18n/zh-tw.ts

@@ -0,0 +1,39 @@
+/*
+ * @Author: vera_min vera_min@163.com
+ * @Date: 2025-08-06 01:16:57
+ * @LastEditors: vera_min vera_min@163.com
+ * @LastEditTime: 2025-08-08 22:31:05
+ * @FilePath: /sagoo-admin-ui/src/i18n/pages/tableI18n/zh-tw.ts
+ * @Description: 表格国际化 - 繁体中文
+ */
+// 定义内容
+export default {
+	tableI18nColumn: {
+		id: "ID",
+		index: "序號",
+		operation: "操作",
+		des: "描述",
+	},
+	tableI18nConfirm: {
+		deleteTitle: "提示",
+		confirmText: "刪除",
+		cancelText: "取消",
+		deleteSuccess: "刪除成功",
+		addSuccess: "新增成功",
+		addFailed: "新增失敗",
+		editSuccess: "編輯成功",
+		deleteSelectedMessage: "你確定要刪除所選數據?",
+		selectDataFirst: "請選擇要刪除的數據。",
+		saveSuccess: "保存成功"
+	},
+	tableI18nAction: {
+		detail: "詳情",
+		handle: "處理",
+		add: "新增",
+		edit: "編輯",
+		delete: "刪除",
+		cancel: "取 消",
+		query: "查詢",
+		more: "更多"
+	}
+};

+ 1 - 0
src/layout/navBars/breadcrumb/setings.vue

@@ -559,6 +559,7 @@ export default defineComponent({
 					// 开启水印
 					onWartermarkChange();
 					// 语言国际化
+					console.log("globalI18n:", Local.get('themeConfig').globalI18n)
 					if (Local.get('themeConfig')) proxy.$i18n.locale = Local.get('themeConfig').globalI18n;
 					// 初始化菜单样式等
 					initSetStyle();

+ 12 - 0
src/layout/navBars/breadcrumb/user.vue

@@ -24,6 +24,18 @@
 				</el-dropdown-menu>
 			</template>
 		</el-dropdown> -->
+    <el-dropdown :show-timeout="70" :hide-timeout="50" trigger="click" @command="onLanguageChange">
+			<div class="layout-navbars-breadcrumb-user-icon">
+				<i class="iconfont" :class="disabledI18n === 'en' ? 'icon-fuhao-yingwen' : 'icon-fuhao-zhongwen'" :title="$t('message.user.title1')"></i>
+			</div>
+			<template #dropdown>
+				<el-dropdown-menu>
+					<el-dropdown-item command="zh-cn" :disabled="disabledI18n === 'zh-cn'">简体中文</el-dropdown-item>
+					<el-dropdown-item command="en" :disabled="disabledI18n === 'en'">English</el-dropdown-item>
+					<el-dropdown-item command="zh-tw" :disabled="disabledI18n === 'zh-tw'">繁體中文</el-dropdown-item>
+				</el-dropdown-menu>
+			</template>
+		</el-dropdown>
     <div class="layout-navbars-breadcrumb-user-icon" @click="onSearchClick">
       <el-icon :title="$t('message.user.title2')">
         <ele-Search />

+ 3 - 1
src/layout/navBars/tagsView/tagsView.vue

@@ -18,7 +18,8 @@
 				>
 					<i class="iconfont icon-webicon318 layout-navbars-tagsview-ul-li-iconfont" v-if="isActive(v)"></i>
 					<SvgIcon :name="v.meta?.icon" v-if="!isActive(v) && getThemeConfig.isTagsviewIcon" class="pr5" />
-					<span>{{ v.meta?.title.indexOf('.')>0?$t(v.meta?.title):v.meta?.title }}</span>
+					<span>	{{ v.name.startsWith('message.') > 0 ? $t(v.name) : $t(v.meta?.title) }}</span>
+					<!-- <span>{{ v.meta?.title.indexOf('.')>0?$t(v.meta?.title):v.meta?.title }}</span> -->
 					<template v-if="isActive(v)">
 						<SvgIcon
 							name="ele-RefreshRight"
@@ -69,6 +70,7 @@ import { isObjectValueEqual } from '/@/utils/arrayOperation';
 import other from '/@/utils/other';
 import Contextmenu from '/@/layout/navBars/tagsView/contextmenu.vue';
 
+
 // 定义接口来定义对象的类型
 interface TagsViewState {
 	routeActive: string;

+ 14 - 19
src/layout/navMenu/subItem.vue

@@ -1,9 +1,17 @@
+<!--
+ * @Author: vera_min vera_min@163.com
+ * @Date: 2025-08-02 12:21:54
+ * @LastEditors: vera_min vera_min@163.com
+ * @LastEditTime: 2025-08-05 11:17:25
+ * @FilePath: /sagoo-admin-ui/src/layout/navMenu/subItem.vue
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+-->
 <template>
-	<div :class="{ newStyle }" v-for="val in chils" :key="val.path">
+	<template v-for="val in chils">
 		<el-sub-menu :index="val.path" :key="val.path" v-if="val.children && val.children.length > 0">
 			<template #title>
 				<SvgIcon :name="val.meta?.icon" />
-				<span>{{ val.meta?.title.indexOf('.') > 0 ? $t(val.meta?.title) : val.meta?.title }}</span>
+				<span>{{ val.name.startsWith('message.') > 0 ? $t(val.name) : $t(val.meta?.title) }}</span>
 			</template>
 			<sub-item :chil="val.children" />
 		</el-sub-menu>
@@ -11,17 +19,17 @@
 			<el-menu-item :index="val.path" :key="val.path">
 				<template v-if="!val.meta?.isLink || (val.meta?.isLink && val.meta.isIframe)">
 					<SvgIcon :name="val.meta?.icon" />
-					<span>{{ val.meta?.title.indexOf('.') > 0 ? $t(val.meta?.title) : val.meta?.title }}</span>
+					<span>{{ val.name.startsWith('message.') > 0 ? $t(val.name) : $t(val.meta?.title) }}</span>
 				</template>
 				<template v-else>
-					<a :href="val.meta?.linkUrl" @click.stop target="_blank" rel="opener" class="w100">
+					<a :href="val.meta?.linkUrl" target="_blank" rel="opener" class="w100">
 						<SvgIcon :name="val.meta?.icon" />
-						{{ val.meta?.title.indexOf('.') > 0 ? $t(val.meta?.title) : val.meta?.title }}
+						{{ val.name.startsWith('message.') > 0 ? $t(val.name) : $t(val.meta?.title) }}
 					</a>
 				</template>
 			</el-menu-item>
 		</template>
-	</div>
+	</template>
 </template>
 
 <script lang="ts">
@@ -33,10 +41,6 @@ export default defineComponent({
 			type: Array,
 			default: () => [],
 		},
-		newStyle: {
-			type: Boolean,
-			default: false,
-		},
 	},
 	setup(props) {
 		// 获取父级菜单数据
@@ -49,12 +53,3 @@ export default defineComponent({
 	},
 });
 </script>
-<style scoped lang="scss">
-.newStyle .el-menu-item {
-	&.is-active {
-		background: var(--next-menu-level2-bg) !important;
-		font-weight: 500;
-		border-right: 2px solid #2D6CE8;
-	}
-}
-</style>

+ 26 - 20
src/layout/navMenu/vertical.vue

@@ -1,23 +1,38 @@
+<!--
+ * @Author: vera_min vera_min@163.com
+ * @Date: 2025-08-02 12:21:54
+ * @LastEditors: vera_min vera_min@163.com
+ * @LastEditTime: 2025-08-05 11:15:25
+ * @FilePath: /sagoo-admin-ui/src/layout/navMenu/vertical.vue
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+-->
 <template>
 	<el-menu router :default-active="defaultActive" background-color="transparent" :collapse="isCollapse" :unique-opened="getThemeConfig.isUniqueOpened" :collapse-transition="false">
 		<template v-for="val in menuLists">
 			<el-sub-menu :index="val.path" v-if="val.children && val.children.length > 0" :key="val.path">
 				<template #title>
 					<SvgIcon :name="val.meta?.icon" />
-					<span>{{ val.meta?.title.indexOf('.') > 0 ? $t(val.meta?.title) : val.meta?.title }}</span>
+					<span>{{ val.name.startsWith('message.') > 0 ? $t(val.name) : $t(val.meta?.title) }}</span>
 				</template>
-				<SubItem newStyle :chil="val.children" />
+				<SubItem :chil="val.children" />
 			</el-sub-menu>
 			<template v-else>
-				<el-menu-item :index="val.path" :key="val.path">
-					<SvgIcon :name="val.meta?.icon" />
-					<template #title v-if="!val.meta?.isLink || (val.meta?.isLink && val.meta.isIframe)">
-						<span>{{ val.meta?.title.indexOf('.') > 0 ? $t(val.meta?.title) : val.meta?.title }}</span>
-					</template>
-					<template #title v-else>
-						<a :href="val.meta?.isLink" target="_blank" rel="opener" class="w100">{{ val.meta?.title.indexOf('.')>0?$t(val.meta?.title):val.meta?.title }}</a>
-					</template>
-				</el-menu-item>
+				<template v-if="!val.meta?.isLink || (val.meta?.isLink && val.meta.isIframe)">
+					<el-menu-item :index="val.path" :key="val.path">
+						<SvgIcon :name="val.meta?.icon" />
+						<template #title>
+							<span>{{ val.name.startsWith('message.') > 0 ? $t(val.name) : $t(val.meta?.title) }}</span>
+						</template>
+					</el-menu-item>
+				</template>
+				<template v-else>
+					<el-menu-item :key="val.path">
+						<SvgIcon :name="val.meta?.icon" />
+						<template #title>
+							<a :href="val.meta?.linkUrl" target="_blank" rel="opener" class="w100">{{ val.meta?.name?.startsWith('message.') > 0 ? $t(val.meta?.name) : $t(val.meta?.title) }}</a>
+						</template>
+					</el-menu-item>
+				</template>
 			</template>
 		</template>
 	</el-menu>
@@ -89,12 +104,3 @@ export default defineComponent({
 	},
 });
 </script>
-<style scoped lang="scss">
-.el-menu-item {
-	&.is-active {
-		background: var(--next-menu-level2-bg) !important;
-		font-weight: 500;
-		border-right: 2px solid #2D6CE8;
-	}
-}
-</style>

+ 2 - 0
src/main.ts

@@ -23,6 +23,8 @@ import copy from '/@/components/copy/index.vue'
 import JsonViewer from "vue3-json-viewer"
 
 const app = createApp(App);
+// 取消 vue warn
+app.config.warnHandler = function () { }
 
 directive(app);
 other.elSvg(app);

+ 3 - 2
src/store/modules/themeConfig.ts

@@ -1,5 +1,6 @@
 import { Module } from 'vuex';
 import { ThemeConfigState, RootStateTypes } from '/@/store/interface/index';
+import { Local } from '/@/utils/storage';
 
 /**
  * 2020.05.28 by lyt 优化
@@ -115,7 +116,7 @@ const themeConfigModule: Module<ThemeConfigState, RootStateTypes> = {
 			 * 中的 `initSetLayoutChange(设置布局切换,重置主题样式)` 方法
 			 */
 			// 布局切换:可选值"<defaults|classic|transverse|columns>",默认 defaults
-			layout: 'defaults',
+			layout: 'columns',
 
 			/**
 			 * 后端控制路由
@@ -131,7 +132,7 @@ const themeConfigModule: Module<ThemeConfigState, RootStateTypes> = {
 			// 网站副标题(登录页顶部文字)
 			globalViceTitle: 'IOT管理系统',
 			// 默认初始语言,可选值"<zh-cn|en|zh-tw>",默认 zh-cn
-			globalI18n: 'zh-cn',
+			globalI18n: Local.get('themeConfig')?.globalI18n || 'zh-cn',
 			// 默认全局组件大小,可选值"<large|'default'|small>",默认 'large'
 			globalComponentSize: 'default',
 		},

+ 4 - 0
src/theme/dark.scss

@@ -67,6 +67,10 @@
 		}
 	}
 
+	.el-table__header-wrapper tr th.el-table-fixed-column--right {
+		background-color: #343435 !important;
+	}
+
 	// 高亮时
 	.el-menu-item.is-active {
 		color: var(--next-color-menu-text-blue) !important;

+ 1 - 1
src/theme/element.scss

@@ -215,7 +215,7 @@
 }
 .el-dialog__body {
 	max-height: calc(90vh - 111px) !important;
-	min-height: 50vh !important;
+	min-height: 200px !important;
 	overflow-y: auto;
 	overflow-x: hidden;
 }

+ 8 - 8
src/utils/auth.ts

@@ -16,23 +16,23 @@ export function removeToken() {
 
 export function setSystemInfo(data: any) {
   localStorage.setItem('sysinfo', JSON.stringify(data));
-  // 使用的事base64加密的,解决之后的值  sysPasswordChangePeriod + "|" + isSecurityControlEnabled + "|" + isRsaEnabled+ "|" + _tag+ "|" + isSSOEnabled
+  // 使用的事base64加密的,解决之后的值  
   // 顺序是,密码变更周期,是否启动安全控制,是否启用rsa,中间你需要根据 | 切割一下
   // console.log(window.atob(data.target).split('|'))
-  const [sysPasswordChangePeriod, isSecurityControlEnabled, isRsaEnabled, _tag, isSSOEnabled, isEnterprise, btnEnabled, colEnabled, uploadFileWay] = window.atob(data.target).split('|')
+  const { passwordChangePeriod, isSecurityControlEnabled, isRsaEnabled, isSsoEnabled, isEnterprise, buttonSwitch, columnSwitch, uploadFileWay } = JSON.parse(window.atob(data.target))
   // isEnterprise 是否为企业版,0代表专业版  1代表企业版
   // 安全开关是否开启 按钮权限,列表权限,rsa权限在开启安全权限下才使用
-  console.log('安全开关是否开启', isSecurityControlEnabled)
-  console.log('密码是否加密', isRsaEnabled)
+  // console.log('安全开关是否开启', isSecurityControlEnabled)
+  // console.log('密码是否加密', isRsaEnabled)
   const enabled = Number(isSecurityControlEnabled)
   sessionStorage.setItem('isSecurityControlEnabled', enabled ? '1' : '');
   sessionStorage.setItem('isRsaEnabled', (enabled && Number(isRsaEnabled)) ? '1' : '');
-  sessionStorage.setItem('sysPasswordChangePeriod', sysPasswordChangePeriod);
-  sessionStorage.setItem('isSSOEnabled', Number(isSSOEnabled) ? '1' : '');
+  sessionStorage.setItem('sysPasswordChangePeriod', passwordChangePeriod);
+  sessionStorage.setItem('isSSOEnabled', Number(isSsoEnabled) ? '1' : '');
   sessionStorage.setItem('isEnterprise', Number(isEnterprise) ? '1' : '');
 
-  localStorage.setItem('btnNoAuth', (enabled && Number(btnEnabled)) ? '' : '1');
-  localStorage.setItem('colNoAuth', (enabled && Number(colEnabled)) ? '' : '1');
+  localStorage.setItem('btnNoAuth', (enabled && Number(buttonSwitch)) ? '' : '1');
+  localStorage.setItem('colNoAuth', (enabled && Number(columnSwitch)) ? '' : '1');
   localStorage.setItem('uploadFileWay', uploadFileWay || '0');
 }
 

+ 4 - 4
src/utils/authDirective.ts

@@ -18,7 +18,7 @@ export function authDirective(app: App) {
 			if (buttons.includes(allPermissions)) return
 
 			// 不显示该dom
-			if (!buttons.includes(binding.value)) el.parentNode.removeChild(el)
+			if (!buttons.includes(binding.value)) el.parentNode?.removeChild(el)
 			// 设置为disabled
 			// if (!buttons.includes(binding.value)) el.classList.add('v-disabled')
 		},
@@ -26,7 +26,7 @@ export function authDirective(app: App) {
 	app.directive('noauth', {
 		mounted(el, binding) {
 			const buttons = <string[]>router.currentRoute.value.meta.buttons
-			if (buttons.includes(binding.value)) el.parentNode.removeChild(el)
+			if (buttons.includes(binding.value)) el.parentNode?.removeChild(el)
 		},
 	});
 	// 多个权限验证,满足一个则显示(v-auths="[xxx,xxx]")
@@ -41,7 +41,7 @@ export function authDirective(app: App) {
 					if (val === v) flag = true;
 				});
 			});
-			if (!flag) el.parentNode.removeChild(el);
+			if (!flag) el.parentNode?.removeChild(el);
 			// if (!flag) el.classList.add('v-disabled')
 		},
 	});
@@ -51,7 +51,7 @@ export function authDirective(app: App) {
 			if (localStorage.btnNoAuth) return
 			const buttons = <string[]>router.currentRoute.value.meta.buttons
 			if (buttons.includes(allPermissions)) return
-			!smallInBig(buttons, binding.value) && el.parentNode.removeChild(el)
+			!smallInBig(buttons, binding.value) && el.parentNode?.removeChild(el)
 			// !smallInBig(buttons, binding.value) && el.classList.add('v-disabled')
 		},
 	});

+ 3 - 3
src/utils/colDirective.ts

@@ -16,7 +16,7 @@ export function colDirective(app: App) {
 			if (localStorage.colNoAuth) return
 			const columns = <string[]>router.currentRoute.value.meta.columns
 			if (columns.includes(allPermissions)) return
-			if (!columns.includes(binding.value)) el.parentNode.removeChild(el)
+			if (!columns.includes(binding.value)) el.parentNode?.removeChild(el)
 		},
 	});
 	// 多个权限验证,满足一个则显示(v-cols="[xxx,xxx]")
@@ -31,7 +31,7 @@ export function colDirective(app: App) {
 					if (val === v) flag = true;
 				});
 			});
-			if (!flag) el.parentNode.removeChild(el);
+			if (!flag) el.parentNode?.removeChild(el);
 		},
 	});
 	// 多个权限验证,全部满足则显示(v-col-all="[xxx,xxx]")
@@ -40,7 +40,7 @@ export function colDirective(app: App) {
 			if (localStorage.colNoAuth) return
 			const columns = <string[]>router.currentRoute.value.meta.columns
 			if (columns.includes(allPermissions)) return
-			!smallInBig(columns, binding.value) && el.parentNode.removeChild(el)
+			!smallInBig(columns, binding.value) && el.parentNode?.removeChild(el)
 		},
 	});
 }

+ 3 - 3
src/utils/dataUiOptions.ts

@@ -262,7 +262,7 @@ export function getLine2Data({ xAxis = [] as any[], datas = [] as number[][], le
       },
       "userOptions": {
         "show": false
-      }
+      },
     },
     "line": {
       "radius": 3,
@@ -297,7 +297,7 @@ export function getLine2Data({ xAxis = [] as any[], datas = [] as number[][], le
   return { config, dataset }
 }
 
-export function getPieData({ datas = [] as number[][], legend = [] as string[], types = [] as number[], width = 500, height = 1300, responsive = false }) {
+export function getPieData({ datas = [] as number[][], legend = [] as string[], types = [] as number[], width = 500, height = 1300, responsive = false, totalText = "总计" }) {
 
   const colorList = ['#4285F4', '#2ecc71', '#FBBB04', '#e67e22', '#FF0000'].reverse()
 
@@ -344,7 +344,7 @@ export function getPieData({ datas = [] as number[][], legend = [] as string[],
             "hollow": {
               "total": {
                 "fontSize": 18,
-                "text": "总计",
+                "text": totalText,
                 "value": {
                   "fontSize": 18,
                 }

+ 19 - 6
src/utils/origin.ts

@@ -1,5 +1,5 @@
 export default function getOrigin(urlStr: string = '', type: string = 'http') {
-  const origin = import.meta.env.VITE_SERVER_ORIGIN
+  const origin = import.meta.env.VITE_SERVER_ORIGIN 
   const nginxProxy = import.meta.env.VITE_NGINX_PROXY
   const suffixUrl = import.meta.env.VITE_API_SUFFIX_URL
   const url = nginxProxy + suffixUrl + urlStr
@@ -28,15 +28,18 @@ export default function getOrigin(urlStr: string = '', type: string = 'http') {
 
 // 如果 modbus服务  ice104协议网关服务 指数管理服务
 export function getOtherServersOrigin(urlStr: string = '') {
-  const origin = import.meta.env.VITE_SERVER_ORIGIN || window.location.origin
-  const nginxProxy = import.meta.env.VITE_NGINX_PROXY
-  return origin + nginxProxy + urlStr
+  if (urlStr?.startsWith(':')) {
+    return window.location.protocol + '//' + window.location.hostname + urlStr
+  } else {
+    const nginxProxy = import.meta.env.VITE_NGINX_PROXY
+    const origin = getOnlyPartOrigin(import.meta.env.VITE_SERVER_ORIGIN)
+    return origin + nginxProxy + urlStr
+  }
 }
 
 // 规则引擎
 export function getRuleServerOrigin(urlStr: string = '') {
-  const origin = import.meta.env.VITE_SERVER_ORIGIN || window.location.origin
-  return origin + urlStr
+  return getOnlyPartOrigin(import.meta.env.VITE_RULE_SERVER_URL) + urlStr
 }
 
 // 流媒体服务
@@ -45,6 +48,8 @@ export function getMediaOrigin(urlStr: string = '') {
   const VITE_MEDIA_SERVER_URL = import.meta.env.VITE_MEDIA_SERVER_URL
   if (VITE_MEDIA_SERVER_URL.startsWith('http')) {
     return VITE_MEDIA_SERVER_URL + urlStr
+  } else if (VITE_MEDIA_SERVER_URL.startsWith(':')) {
+    return getOnlyPartOrigin(VITE_MEDIA_SERVER_URL) + urlStr
   } else {
     return origin + VITE_MEDIA_SERVER_URL + urlStr
   }
@@ -54,4 +59,12 @@ export function getSSEOrigin(urlStr: string = '') {
   const origin = import.meta.env.VITE_SERVER_ORIGIN || window.location.origin
   const nginxProxy = import.meta.env.VITE_NGINX_PROXY
   return origin + nginxProxy + urlStr
+}
+
+export function getOnlyPartOrigin(baseUrl: string = '') {
+  // 兼容只有端口号不一样的情况
+  if (baseUrl?.startsWith(':')) {
+    return window.location.protocol + '//' + window.location.hostname + baseUrl
+  }
+  return baseUrl || window.location.origin
 }

+ 1 - 1
src/views/apihub/component/edit.vue

@@ -14,7 +14,7 @@
         <span class="dialog-title">{{ formData.id ? '编辑API' : '新增API' }}</span>
         <div class="dialog-tools">
           <el-button
-            type="text"
+            text type="primary"
             @click="toggleFullscreen"
           >
             <el-icon :size="20">

+ 2 - 2
src/views/apihub/plugin.vue

@@ -48,7 +48,7 @@
           <el-table-column prop="type" label="类型" v-col="'type'" width="120" align="center">
             <template #default="scope">
               <el-tag size="small" type="success" v-if="scope.row.type === 'Go'">Go</el-tag>
-              <el-tag size="small" type="primary" v-else-if="scope.row.type === 'JavaScript'">JavaScript</el-tag>
+              <el-tag size="small" v-else-if="scope.row.type === 'JavaScript'">JavaScript</el-tag>
               <el-tag size="small" type="warning" v-else-if="scope.row.type === 'LUA'">LUA</el-tag>
               <span v-else>{{ scope.row.type }}</span>
             </template>
@@ -121,7 +121,7 @@
         <el-descriptions-item label="插件名称">{{ detail.name }}</el-descriptions-item>
         <el-descriptions-item label="插件类型">
           <el-tag size="small" type="success" v-if="detail.type === 'Go'">Go</el-tag>
-          <el-tag size="small" type="primary" v-else-if="detail.type === 'JavaScript'">JavaScript</el-tag>
+          <el-tag size="small" v-else-if="detail.type === 'JavaScript'">JavaScript</el-tag>
           <el-tag size="small" type="warning" v-else-if="detail.type === 'LUA'">LUA</el-tag>
           <span v-else>{{ detail.type }}</span>
         </el-descriptions-item>

+ 1 - 1
src/views/designer/index.vue

@@ -1,6 +1,6 @@
 <template>
 	<div class="page">
-		<el-card shadow="nover">
+		<el-card shadow="never">
 			<div class="search">
 				<el-form inline>
 					<el-form-item>

+ 32 - 19
src/views/iot/alarm/list/index.vue

@@ -4,36 +4,49 @@
 			<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
 				<div class="home-card-item" style="height: auto">
 					<div class="home-card-item-title">
-						<span>告警消息</span>
-						<el-button size="small" text type="primary" @click="toMore()">更多信息</el-button>
+						<span>{{ $t('message.alarmList.title') }}</span>
+						<el-button size="small" text type="primary" @click="toMore()">{{ $t('message.alarmList.moreInfo') }}</el-button>
 					</div>
 					<el-table :data="tableData.data" style="width: 100%" v-loading="loading">
 						<el-table-column label="ID" align="center" prop="id" width="100" v-col="'ID'" />
-						<el-table-column label="告警类型" width="120" prop="type" align="center" show-overflow-tooltip v-col="'type'">
+						<!-- 告警类型 -->
+						<el-table-column :label="$t('message.alarm.tableI18nColumn.alarmType')" width="120" prop="type" align="center" show-overflow-tooltip v-col="'type'">
 							<template #default="scope">
-								<span v-if="scope.row.type == 1">规则告警</span>
-								<span v-else-if="scope.row.type == 2">设备自主告警</span>
-								<span v-else-if="scope.row.type == 3">规侧告警升级</span>
-								<span v-else>设备自主告警</span>
+								<!-- 规则告警 -->
+								<span v-if="scope.row.type == 1">{{ $t('message.alarm.tableI18nAlarmType.ruleAlarm') }}</span>
+								<!-- 设备自主告警 -->
+								<span v-else-if="scope.row.type == 2">{{ $t('message.alarm.tableI18nAlarmType.deviceSelfAlarm') }}</span>
+								<!--  规则侧告警升级 -->
+								<span v-else-if="scope.row.type == 3">{{ $t('message.alarm.tableI18nAlarmType.ruleAlarmUpgrade') }}</span>
+								<!-- 设备自主告警 -->
+								<span v-else>{{ $t('message.alarm.tableI18nAlarmType.deviceSelfAlarm') }}</span>
 							</template>
 						</el-table-column>
-						<el-table-column label="规则级别" width="120" align="center" prop="alarmLevel.name" show-overflow-tooltip v-col="'alarmLevel'" />
-						<el-table-column label="规则名称" prop="ruleName" show-overflow-tooltip v-col="'ruleName'" />
-						<el-table-column label="产品标识" prop="productKey" show-overflow-tooltip v-col="'productKey'" />
-						<el-table-column label="设备标识" prop="deviceKey" show-overflow-tooltip v-col="'deviceKey'" />
-
-						<el-table-column prop="status" label="告警状态" width="100" align="center" v-col="'status'">
+						<!-- 规则级别 -->
+						<el-table-column :label="$t('message.alarm.tableI18nColumn.alarmLevel')" width="120" align="center" prop="alarmLevel.name" show-overflow-tooltip v-col="'alarmLevel'" />
+						<!-- 规则名称 -->
+						<el-table-column :label="$t('message.alarm.tableI18nColumn.ruleName')" prop="ruleName" show-overflow-tooltip v-col="'ruleName'" />
+						<!-- 产品标识 -->
+						<el-table-column :label="$t('message.alarm.tableI18nColumn.productKey')" prop="productKey" show-overflow-tooltip v-col="'productKey'" />
+						<!-- 设备标识 -->
+						<el-table-column :label="$t('message.alarm.tableI18nColumn.deviceKey')" prop="deviceKey" show-overflow-tooltip v-col="'deviceKey'" />
+						<!-- 告警状态 -->
+						<el-table-column prop="status" :label="$t('message.alarm.tableI18nColumn.alarmStatus')" width="120" align="center" v-col="'status'">
 							<template #default="scope">
-								<el-tag type="success" size="small" v-if="scope.row.status">已处理</el-tag>
-								<el-tag type="info" size="small" v-else>未处理</el-tag>
+								<el-tag type="success" size="small" v-if="scope.row.status">{{ $t('message.alarm.tableI18nStatus.processed') }}</el-tag>
+								<el-tag type="info" size="small" v-else>{{ $t('message.alarm.tableI18nStatus.unprocessed') }}</el-tag>
 							</template>
 						</el-table-column>
-						<el-table-column prop="createdAt" label="告警时间" align="center" width="170" v-col="'createdAt'"></el-table-column>
-						<el-table-column label="操作" width="130" align="center" fixed="right" v-col="'handle'">
+						<!-- 告警时间 -->
+						<el-table-column prop="createdAt" :label="$t('message.alarm.tableI18nColumn.alarmTime')" align="center" width="170" v-col="'createdAt'"></el-table-column>
+						<!-- 操作 -->
+						<el-table-column :label="$t('message.tableI18nColumn.operation')" width="130" align="center" fixed="right" v-col="'handle'">
 							<template #default="scope">
-								<el-button v-auth="'detail'" size="small" text type="primary" @click="onOpenDetailDic(scope.row)">详情</el-button>
+								<!-- 详情 -->
+								<el-button v-auth="'detail'" size="small" text type="primary" @click="onOpenDetailDic(scope.row)">{{ $t('message.tableI18nAction.detail') }}</el-button>
+								<!-- 处理 -->
 								<el-button v-auth="'edit'" size="small" text type="warning" @click="onOpenEditDic(scope.row)" v-if="scope.row.status == 0"
-									>处理</el-button
+									>{{ $t('message.tableI18nAction.handle') }}</el-button
 								>
 							</template>
 						</el-table-column>

+ 101 - 119
src/views/iot/alarm/log/index.vue

@@ -1,59 +1,76 @@
 <template>
 	<div class="page">
-		<el-card shadow="nover">
+		<el-card shadow="never">
 			<el-form :model="tableData.param" ref="queryRef" inline label-width="68px">
-				<el-form-item label="创建时间" prop="dateRange">
-					<el-date-picker v-model="tableData.param.dateRange" style="width: 240px" value-format="YYYY-MM-DD" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker>
+				<!-- 创建时间 -->
+				<el-form-item :label="$t('message.formI18nLabel.createdTime')" prop="dateRange" :label-width="labelWidth">
+					<el-date-picker v-model="tableData.param.dateRange" style="width: 240px" value-format="YYYY-MM-DD" type="daterange" range-separator="-" :start-placeholder="$t('message.formI18nPlaceholder.startDate')" :end-placeholder="$t('message.formI18nPlaceholder.endDate')"></el-date-picker>
 				</el-form-item>
-				<el-form-item label="告警状态" prop="status" style="width: 200px;">
-					<el-select v-model="tableData.param.status" placeholder="告警状态" clearable style="width: 240px">
-						<el-option label="未处理" :value="0" />
-						<el-option label="已处理" :value="1" />
-						<el-option label="已忽略" :value="2" />
+				<!-- 告警状态 -->
+				<el-form-item :label="$t('message.formI18nLabel.alarmStatus')" prop="status"  :label-width="labelWidth">
+					<el-select v-model="tableData.param.status" :placeholder="$t('message.formI18nPlaceholder.alarmStatus')" clearable style="width: 180px">
+						<el-option :label="$t('message.formI18nOption.unprocessed')" :value="0" />
+						<el-option :label="$t('message.formI18nOption.ignored')" :value="2" />
 					</el-select>
 				</el-form-item>
 				<el-form-item>
+					<!-- 查询 -->
 					<el-button type="primary" class="ml10" @click="typeList">
 						<el-icon>
 							<ele-Search />
 						</el-icon>
-						查询
+						{{ $t('message.formI18nButton.query') }}
 					</el-button>
+					<!-- 重置 -->
 					<el-button @click="resetQuery(queryRef)">
 						<el-icon>
 							<ele-Refresh />
 						</el-icon>
-						重置
+						{{ $t('message.formI18nButton.reset') }}
 					</el-button>
 				</el-form-item>
 			</el-form>
 			<el-table :data="tableData.data" style="width: 100%" v-loading="tableData.loading" max-height="calc(100vh  - 255px)">
 				<el-table-column label="ID" align="center" prop="id" width="100" v-col="'ID'" />
-				<el-table-column label="告警类型" prop="type" width="120" show-overflow-tooltip v-col="'type'">
+				<!-- 告警类型 -->
+				<el-table-column :label="$t('message.alarm.tableI18nColumn.alarmType')" prop="type" width="120" show-overflow-tooltip v-col="'type'">
 					<template #default="scope">
-						<span v-if="scope.row.type == 1">规则告警</span>
-            <span v-else-if="scope.row.type == 2">设备自主告警</span>
-            <span v-else-if="scope.row.type == 3">规侧告警升级</span>
-            <span v-else>设备自主告警</span>
+						<!-- 规则告警 -->
+						<span v-if="scope.row.type == 1">{{ $t('message.alarm.tableI18nAlarmType.ruleAlarm') }}</span>
+            <!-- 设备自主告警 -->
+						<span v-else-if="scope.row.type == 2">{{ $t('message.alarm.tableI18nAlarmType.deviceSelfAlarm') }}</span>
+            <!--  规则侧告警升级 -->
+						<span v-else-if="scope.row.type == 3">{{ $t('message.alarm.tableI18nAlarmType.ruleAlarmUpgrade') }}</span>
+            <!-- 设备自主告警 -->
+						<span v-else>{{ $t('message.alarm.tableI18nAlarmType.deviceSelfAlarm') }}</span>
 					</template>
 				</el-table-column>
-				<el-table-column label="规则级别" prop="alarmLevel.name" width="100" align="center" show-overflow-tooltip v-col="'alarmLevel'"></el-table-column>
-				<el-table-column label="规则名称" prop="ruleName" show-overflow-tooltip v-col="'ruleName'" />
-				<el-table-column label="产品标识" prop="productKey" show-overflow-tooltip v-col="'productKey'" />
-				<el-table-column label="设备标识" prop="deviceKey" show-overflow-tooltip v-col="'deviceKey'" />
+				<!-- 规则级别 -->
+				<el-table-column :label="$t('message.alarm.tableI18nColumn.alarmLevel')" prop="alarmLevel.name" width="100" align="center" show-overflow-tooltip v-col="'alarmLevel'"></el-table-column>
+				<!-- 规则名称 -->
+				<el-table-column :label="$t('message.alarm.tableI18nColumn.ruleName')" prop="ruleName" show-overflow-tooltip v-col="'ruleName'" />
+				<!-- 产品标识 -->
+				<el-table-column :label="$t('message.alarm.tableI18nColumn.productKey')" prop="productKey" show-overflow-tooltip v-col="'productKey'" />
+				<!-- 设备标识 -->
+				<el-table-column :label="$t('message.alarm.tableI18nColumn.deviceKey')" prop="deviceKey" show-overflow-tooltip v-col="'deviceKey'" />
 
-				<el-table-column prop="status" label="告警状态" width="100" align="center" v-col="'status'">
+				<!-- 告警状态 -->
+				<el-table-column prop="status" :label="$t('message.alarm.tableI18nColumn.alarmStatus')" width="120" align="center" v-col="'status'">
 					<template #default="scope">
-						<el-tag type="danger" size="small" v-if="scope.row.status == 0">未处理</el-tag>
-						<el-tag type="success" size="small" v-if="scope.row.status == 1">已处理</el-tag>
-						<el-tag type="info" size="small" v-if="scope.row.status == 2">已忽略</el-tag>
+						<el-tag type="danger" size="small" v-if="scope.row.status == 0">{{ $t('message.alarm.tableI18nStatus.unprocessed') }}</el-tag>
+						<el-tag type="success" size="small" v-if="scope.row.status == 1">{{ $t('message.alarm.tableI18nStatus.processed') }}</el-tag>
+						<el-tag type="info" size="small" v-if="scope.row.status == 2">{{ $t('message.alarm.tableI18nStatus.ignored') }}</el-tag>
 					</template>
 				</el-table-column>
-				<el-table-column prop="createdAt" label="告警时间" align="center" width="160" v-col="'createdAt'"></el-table-column>
-				<el-table-column label="操作" width="140" align="center" fixed="right" v-col="'handle'">
+				<!-- 告警时间 -->
+				<el-table-column prop="createdAt" :label="$t('message.alarm.tableI18nColumn.alarmTime')" align="center" width="160" v-col="'createdAt'"></el-table-column>
+				<!-- 操作 -->
+				<el-table-column :label="$t('message.tableI18nColumn.operation')" width="140" align="center" fixed="right" v-col="'handle'">
 					<template #default="scope">
-						<el-button size="small" text type="primary" @click="onOpenDetailDic(scope.row)" v-auth="'detail'">详情</el-button>
-						<el-button size="small" text type="warning" @click="onOpenEditDic(scope.row)" v-if="scope.row.status == 0" v-auth="'edit'">处理</el-button>
+						<!-- 详情 -->
+						<el-button size="small" text type="primary" @click="onOpenDetailDic(scope.row)" v-auth="'detail'">{{ $t('message.tableI18nAction.detail') }}</el-button>
+						<!-- 处理 -->
+						<el-button size="small" text type="warning" @click="onOpenEditDic(scope.row)" v-if="scope.row.status == 0" v-auth="'edit'">{{ $t('message.tableI18nAction.handle') }}</el-button>
 					</template>
 				</el-table-column>
 			</el-table>
@@ -65,12 +82,16 @@
 	</div>
 </template>
 
-<script lang="ts">
-import { toRefs, reactive, onMounted, ref, defineComponent } from 'vue';
+<script lang="ts" setup>
+import { onMounted, ref, computed } from 'vue';
 import { FormInstance } from 'element-plus';
 import api from '/@/api/alarm';
 import EditDic from './component/edit.vue';
 import DetailDic from './component/detail.vue';
+import { useI18n } from 'vue-i18n';
+
+// 国际化
+const { locale } = useI18n();
 
 // 定义接口来定义对象的类型
 interface TableDataRow {
@@ -81,99 +102,60 @@ interface TableDataRow {
 	desc: string;
 	createBy: string;
 }
-interface TableDataState {
-	ids: number[];
-	tableData: {
-		data: Array<TableDataRow>;
-		total: number;
-		loading: boolean;
-		param: {
-			pageNum: number;
-			pageSize: number;
 
-			dateRange: string[];
-			status: string;
-		};
-	};
-}
+const addDicRef = ref();
+const editDicRef = ref();
+const detailRef = ref();
+const queryRef = ref();
+const ids = ref<number[]>([])
+const tableData = ref({
+	data: [],
+	total: 0,
+	loading: false,
+	param: {
+		pageNum: 1,
+		pageSize: 20,
+		status: '',
+		dateRange: [],
+	},
+})
 
-export default defineComponent({
-	name: 'log',
-	components: { EditDic, DetailDic },
 
-	setup() {
-		const addDicRef = ref();
-		const editDicRef = ref();
-		const detailRef = ref();
-		const queryRef = ref();
-		const state = reactive<TableDataState>({
-			ids: [],
-			tableData: {
-				data: [],
-				total: 0,
-				loading: false,
-				param: {
-					pageNum: 1,
-					pageSize: 20,
-					status: '',
-					dateRange: [],
-				},
-			},
-		});
-		// 初始化表格数据
-		const initTableData = () => {
-			typeList();
-		};
-		const typeList = () => {
-			state.tableData.loading = true;
-			api.log
-				.getList(state.tableData.param)
-				.then((res: any) => {
-					state.tableData.data = res.list;
-					state.tableData.total = res.Total;
-				})
-				.finally(() => (state.tableData.loading = false));
-		};
+// 动态设置 createdTime 标签宽度
+const labelWidth = computed(() => {
+    return locale.value === 'en' ? '100px' : '68px';
+});
+// 初始化表格数据
+const initTableData = () => {
+	typeList();
+};
+const typeList = () => {
+	tableData.value.loading = true;
+	api.log
+		.getList(tableData.value.param)
+		.then((res: any) => {
+			tableData.value.data = res.list;
+			tableData.value.total = res.Total;
+		})
+		.finally(() => (tableData.value.loading = false));
+};
 
-		//打开详情页
-		const onOpenDetailDic = (row: TableDataRow) => {
-			detailRef.value.openDialog(row);
-		};
-		// 打开新增产品弹窗
-		const onOpenAddDic = () => {
-			editDicRef.value.openDialog();
-		};
-		// 打开修改产品弹窗
-		const onOpenEditDic = (row: TableDataRow) => {
-			editDicRef.value.openDialog(row);
-		};
-		// 页面加载时
-		onMounted(() => {
-			initTableData();
-		});
-		/** 重置按钮操作 */
-		const resetQuery = (formEl: FormInstance | undefined) => {
-			if (!formEl) return;
-			formEl.resetFields();
-			typeList();
-		};
-		// 多选框选中数据
-		const handleSelectionChange = (selection: TableDataRow[]) => {
-			state.ids = selection.map((item) => item.id);
-		};
-		return {
-			addDicRef,
-			editDicRef,
-			queryRef,
-			detailRef,
-			onOpenDetailDic,
-			onOpenAddDic,
-			onOpenEditDic,
-			typeList,
-			resetQuery,
-			handleSelectionChange,
-			...toRefs(state),
-		};
-	},
+//打开详情页
+const onOpenDetailDic = (row: TableDataRow) => {
+	detailRef.value.openDialog(row);
+};
+// 打开修改产品弹窗
+const onOpenEditDic = (row: TableDataRow) => {
+	editDicRef.value.openDialog(row);
+};
+// 页面加载时
+onMounted(() => {
+	initTableData();
 });
+/** 重置按钮操作 */
+const resetQuery = (formEl: FormInstance | undefined) => {
+	if (!formEl) return;
+	formEl.resetFields();
+	typeList();
+};
 </script>

+ 23 - 12
src/views/iot/cascade/deviceList.vue

@@ -1,24 +1,35 @@
 <template>
-	<el-dialog v-model="visible" :title="name + '-设备列表'" width="1000px" destroy-on-close>
+	<!-- name + '-设备列表' -->
+	<el-dialog v-model="visible" :title="name + $t('message.cascade.dialogI18n.deviceList')" width="1000px" destroy-on-close>
 		<div class="device-list">
 			<el-table :data="tableData" style="width: 100%" v-loading="loading">
-				<el-table-column label="标识" prop="key" min-width="150" show-overflow-tooltip>
+				<!-- 标识 -->
+				<el-table-column :label="$t('message.cascade.tableI18nColumn.identifier')" prop="key" min-width="150" show-overflow-tooltip>
 					<template #default="{ row }">
 						<copy :text="row.key"></copy>
 					</template>
 				</el-table-column>
-				<el-table-column label="设备名称" prop="name" min-width="160" show-overflow-tooltip />
-				<el-table-column label="设备类型" prop="product.deviceType" min-width="100" align="center" show-overflow-tooltip />
-				<el-table-column label="所属产品" prop="productName" min-width="120" align="center" show-overflow-tooltip />
-				<el-table-column prop="status" label="状态" min-width="80" align="center">
+				<!-- 设备名称 -->
+				<el-table-column :label="$t('message.cascade.tableI18nColumn.deviceName')" prop="name" min-width="160" show-overflow-tooltip />
+				<!-- 设备类型 -->
+				<el-table-column :label="$t('message.cascade.tableI18nColumn.deviceType')" prop="product.deviceType" min-width="100" align="center" show-overflow-tooltip />
+				<!-- 所属产品 -->
+				<el-table-column :label="$t('message.cascade.tableI18nColumn.belongProduct')" prop="productName" min-width="120" align="center" show-overflow-tooltip />
+				<!-- 状态 -->
+				<el-table-column prop="status" :label="$t('message.cascade.tableI18nColumn.status')" min-width="80" align="center">
 					<template #default="scope">
-						<el-tag type="danger" size="small" v-if="scope.row.status == 1">离线</el-tag>
-						<el-tag type="success" size="small" v-if="scope.row.status == 2">在线</el-tag>
-						<el-tag type="info" size="small" v-if="scope.row.status == 0">未启用</el-tag>
+						<!-- 离线 -->
+						<el-tag type="danger" size="small" v-if="scope.row.status == 1">{{$t('message.cascade.tableI18nStatus.offline')}}</el-tag>
+						<!-- 在线 -->
+						<el-tag type="success" size="small" v-if="scope.row.status == 2">{{$t('message.cascade.tableI18nStatus.online')}}</el-tag>
+						<!-- 未启用 -->
+						<el-tag type="info" size="small" v-if="scope.row.status == 0">{{$t('message.cascade.tableI18nStatus.disabled')}}</el-tag>
 					</template>
 				</el-table-column>
-				<el-table-column prop="lastOnlineTime" label="最后上线时间" align="center" width="160"></el-table-column>
-				<el-table-column label="操作" width="100" align="center">
+				<!-- 最后上线时间 -->
+				<el-table-column prop="lastOnlineTime" :label="$t('message.cascade.tableI18nColumn.lastOnlineTime')" align="center" width="160"></el-table-column>
+				<!-- 操作 -->
+				<el-table-column :label="$t('message.tableI18nColumn.operation')" width="100" align="center">
 					<template #default="scope">
 						<router-link
 							:to="'/iotmanager/device/instance/' + scope.row.key"
@@ -26,7 +37,7 @@
 							style="font-size: 12px; color: #409eff"
 							v-auth="'detail'"
 						>
-							<span>详情</span>
+							<span>{{ $t('message.tableI18nAction.detail') }}</span>
 						</router-link>
 					</template>
 				</el-table-column>

+ 29 - 17
src/views/iot/cascade/index.vue

@@ -1,61 +1,73 @@
 <template>
   <div class="page-full">
     <div class="search flex-row mb-4 gap-4">
-      <el-card shadow="nover" class="home-card-top-part flex1">
+      <el-card shadow="never" class="home-card-top-part flex1">
+        <!-- 子平台 -->
         <div class="top">
-          <div class="label">子平台</div>
+          <div class="label">{{ $t("message.cascade.formI18nLabel.subPlatformTotal") }}</div>
           <span class="font30">{{ count.PlatformTotal }}</span>
         </div>
         <div class="divider"></div>
         <div class="card-bottom">
+          <!-- 在线 -->
           <div class="flex" style="gap: 10px">
             <img src="/@/assets/ok.svg" alt="" class="icon" />
-            <span class="info" :style="{ color: '#3cd357' }">在线</span>
+            <span class="info" :style="{ color: '#3cd357' }">{{ $t("message.cascade.tableI18nStatus.online") }}</span>
             <div class="num">{{ count.PlatformOnline }}</div>
           </div>
           <div class="split"></div>
+          <!-- 离线 -->
           <div class="flex" style="gap: 10px">
             <img src="/@/assets/stop.svg" alt="" class="icon" />
-            <span class="info" :style="{ color: '#FFBB73' }">离线</span>
+            <span class="info" :style="{ color: '#FFBB73' }">{{ $t("message.cascade.tableI18nStatus.offline") }}</span>
             <div class="num">{{ count.PlatformOffline }}</div>
           </div>
         </div>
       </el-card>
-      <el-card shadow="nover" class="home-card-top-part flex1">
+      <el-card shadow="never" class="home-card-top-part flex1">
+        <!-- 设备总数 -->
         <div class="top">
-          <div class="label">设备总数</div>
+          <div class="label">{{ $t("message.cascade.formI18nLabel.deviceTotal") }}</div>
           <span class="font30">{{ count.DeviceTotal }}</span>
         </div>
         <div class="divider"></div>
         <div class="card-bottom">
+          <!-- 在线 -->
           <div class="flex" style="gap: 10px">
             <img src="/@/assets/ok.svg" alt="" class="icon" />
-            <span class="info" :style="{ color: '#3cd357' }">在线</span>
+            <span class="info" :style="{ color: '#3cd357' }">{{ $t("message.cascade.tableI18nStatus.online") }}</span>
             <div class="num">{{ count.DeviceOnline }}</div>
           </div>
           <div class="split"></div>
+          <!-- 离线 -->
           <div class="flex" style="gap: 10px">
             <img src="/@/assets/stop.svg" alt="" class="icon" />
-            <span class="info" :style="{ color: '#FFBB73' }">离线</span>
+            <span class="info" :style="{ color: '#FFBB73' }">{{ $t("message.cascade.tableI18nStatus.offline") }}</span>
             <div class="num">{{ count.DeviceOffline }}</div>
           </div>
         </div>
       </el-card>
     </div>
-    <el-card shadow="nover" class="page-full-part">
+    <el-card shadow="never" class="page-full-part">
       <el-table :data="tableData" style="width: 100%" row-key="id" v-loading="loading">
-        <el-table-column type="index" label="序号" width="100" align="center"></el-table-column>
-        <el-table-column prop="name" label="子平台" show-overflow-tooltip v-col="'name'" align="center"></el-table-column>
-        <el-table-column prop="address" label="地址" v-col="'address'" align="center"></el-table-column>
-        <el-table-column prop="deviceOnline" label="设备" v-col="'deviceOnline'" align="center">
+        <!-- 序号 -->
+        <el-table-column type="index" :label="$t('message.tableI18nColumn.index')" width="100" align="center"></el-table-column>
+        <!-- 子平台 -->
+        <el-table-column prop="name" :label="$t('message.cascade.tableI18nColumn.subPlatform')" show-overflow-tooltip v-col="'name'" align="center"></el-table-column>
+        <!-- 地址 -->
+        <el-table-column prop="address" :label="$t('message.cascade.tableI18nColumn.address')" v-col="'address'" align="center"></el-table-column>
+        <!-- 设备 -->
+        <el-table-column prop="deviceOnline" :label="$t('message.cascade.tableI18nColumn.device')" v-col="'deviceOnline'" align="center">
           <template #default="{ row }">{{ row.deviceOnline }}/{{ row.deviceOnline + row.deviceOffline }} </template>
         </el-table-column>
-        <el-table-column prop="status" label="状态" v-col="'status'" :formatter="(_row: any, a: any) => (a ? '在线' : '离线')" align="center"></el-table-column>
-        <el-table-column prop="lastTime" label="最后更新时间" width="180" align="center" v-col="'lastTime'"></el-table-column>
-        <el-table-column label="操作" width="160" v-col="'handle'" align="center">
+        <el-table-column prop="status" :label="$t('message.cascade.tableI18nColumn.status')" v-col="'status'" :formatter="(_row: any, a: any) => (a ? '在线' : '离线')" align="center"></el-table-column>
+        <!-- 最后更新时间 -->
+        <el-table-column prop="lastTime" :label="$t('message.cascade.tableI18nColumn.lastUpdateTime')" width="180" align="center" v-col="'lastTime'"></el-table-column>
+        <!-- 操作 -->
+        <el-table-column :label="$t('message.tableI18nColumn.operation')" width="160" v-col="'handle'" align="center">
           <template #default="{ row }">
             <!-- <el-button size="small" text type="warning" v-auth="'sync'" :loading="row.loading" @click="sync(row)">同步</el-button> -->
-            <el-button size="small" text type="primary" v-auth="'detail'" @click="viewDeviceList(row)">查看设备</el-button>
+            <el-button v-auth="'detail'" size="small" text type="primary"  @click="viewDeviceList(row)">{{ $t('message.cascade.tableI18nAction.viewDevice') }}</el-button>
           </template>
         </el-table-column>
       </el-table>

+ 1 - 1
src/views/iot/certificate/index.vue

@@ -1,6 +1,6 @@
 <template>
 	<div class="page">
-		<el-card shadow="nover">
+		<el-card shadow="never">
 			<el-form :model="state.tableData.param" ref="queryRef" inline @submit.prevent @keyup.enter="queryList">
 				<el-form-item label="证书名称" prop="keyWord">
 					<el-input v-model="state.tableData.param.name" placeholder="请输入证书名称" clearable />

+ 1 - 1
src/views/iot/configuration/list/index.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="page">
-    <el-card shadow="nover">
+    <el-card shadow="never">
       <div class="search">
         <el-form inline>
           <el-form-item label="关键字">

+ 1 - 1
src/views/iot/configuration/screen/index.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="page">
-    <el-card shadow="nover">
+    <el-card shadow="never">
       <div class="search">
         <el-form inline>
           <el-form-item label="关键字">

+ 1 - 1
src/views/iot/dataAnalysis/IndicatorAggregation/index.vue

@@ -1,6 +1,6 @@
 <template>
 	<div class="page">
-		<el-card shadow="nover">
+		<el-card shadow="never">
 			<el-form inline>
 				<el-form-item label="选择产品:" prop="productKey">
 					<el-select v-model="params.productKey" filterable placeholder="请选择产品" @change="productChange">

+ 1 - 1
src/views/iot/dataAnalysis/exponentialTrend/index.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="page">
-    <el-card shadow="nover">
+    <el-card shadow="never">
       <el-form inline>
         <el-form-item label="选择产品:" prop="productKey">
           <el-select v-model="params.productKey" filterable placeholder="请选择产品" @change="productChange">

+ 2 - 2
src/views/iot/device-tree/tree/index.vue

@@ -2,7 +2,7 @@
   <div class="page page-full">
     <el-row :gutter="15" class="h-full">
       <el-col :span="6" class="h-full">
-        <el-card shadow="nover" class="h-full">
+        <el-card shadow="never" class="h-full">
           <el-scrollbar v-loading="treeLoading">
             <el-input :prefix-icon="Search" v-model="searchVal" placeholder="请输入设备树名称" clearable style="width: 100%;" />
             <el-button v-if="!treeLoading && !treeData.length" type="primary" v-auth="'add'" class="mt-2" @click="operateCmd('add', {})" style="width: 100%">新建节点</el-button>
@@ -51,7 +51,7 @@
         </el-card>
       </el-col>
       <el-col :span="18" class="h-full">
-        <el-card shadow="nover" class="h-full" v-if="treeDetail.name">
+        <el-card shadow="never" class="h-full" v-if="treeDetail.name">
           <el-form :model="ruleForm" ref="formRef" label-width="80px">
             <el-tabs v-model="tabName" @tab-click="onTabClick">
               <el-tab-pane label="设备树信息" name="1">

+ 169 - 0
src/views/iot/device/category/component/addOrEdit.vue

@@ -0,0 +1,169 @@
+<template>
+	<div>
+		<el-dialog :title="(ruleForm.id ? $t('message.device.tableI18nAction.editCategory') : $t('message.device.tableI18nAction.addCategory'))" v-model="isShowDialog" width="600px">
+			<el-form ref="formRef" :model="ruleForm" :rules="rules" label-width="90px">
+				<el-row :gutter="35">
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+						<!-- 上级分类 -->
+						<el-form-item :label="$t('message.formI18nLabel.parentCategory')" :label-width="currentLocale == 'en' ? '120px' : '90px'">
+							<el-cascader :options="deptData" :props="{ checkStrictly: true, emitPath: false, value: 'id', label: 'name' }" :placeholder="$t('message.formI18nPlaceholder.parentCategory')" clearable class="w100" v-model="ruleForm.parentId">
+								<template #default="{ node, data }">
+									<span>{{ data.name }}</span>
+									<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
+								</template>
+							</el-cascader>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="16" :lg="16" :xl="16">
+						<!-- 分类名称 -->
+						<el-form-item :label="$t('message.formI18nLabel.categoryName')" prop="name" :label-width="currentLocale == 'en' ? '120px' : '90px'">
+							<el-input v-model="ruleForm.name" :placeholder="$t('message.formI18nPlaceholder.categoryName')" clearable></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="8">
+						<!-- 排序 -->
+						<el-form-item :label="$t('message.formI18nLabel.sort')" prop="sort" label-width="40px">
+							<el-input-number v-model="ruleForm.sort" :min="0" :max="100" step="1" step-strictly="true" controls-position="right" :placeholder="$t('message.formI18nPlaceholder.sort')" class="w100" />
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+						<!-- 分类标识 -->
+						<el-form-item :label="$t('message.formI18nLabel.categoryKey')" prop="key" :label-width="currentLocale == 'en' ? '120px' : '90px'">
+							<el-input v-model="ruleForm.key" :placeholder="$t('message.formI18nPlaceholder.categoryKey')" clearable></el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+						<!-- 描述 -->
+						<el-form-item :label="$t('message.formI18nLabel.desc')" prop="desc" :label-width="currentLocale == 'en' ? '120px' : '90px'">
+							<el-input v-model="ruleForm.desc" type="textarea" :placeholder="$t('message.formI18nPlaceholder.desc')" maxlength="150"></el-input>
+						</el-form-item>
+					</el-col>
+				</el-row>
+			</el-form>
+			<template #footer>
+				<span class="dialog-footer">
+					<el-button @click="onCancel">{{ $t('message.tableI18nAction.cancel') }}</el-button>
+					<el-button type="primary" @click="onSubmit" :loading="loading">{{ ruleForm.id ? $t('message.tableI18nAction.edit') : $t('message.tableI18nAction.add') }}</el-button>
+				</span>
+			</template>
+		</el-dialog>
+	</div>
+</template>
+
+<script lang="ts" setup>
+import { reactive, toRefs, defineComponent, ref, unref, computed, defineEmits } from 'vue';
+import api from '/@/api/device';
+import { ElMessage } from 'element-plus';
+import { useI18n } from 'vue-i18n';
+
+// 国际化
+const { locale, t } = useI18n();
+
+const currentLocale = computed(() => locale.value);	
+
+
+const emit = defineEmits(['getCateList']);
+
+
+interface RuleFormState {
+	id?: number;
+	parentId: number;
+	name: string;
+	key: string;
+	desc: string;
+	sort: number;
+	children?: RuleFormState[];
+}
+
+const baseForm: RuleFormState = {
+	parentId: 0, // 上级分类
+	name: '', // 分类名称
+	key: '',
+	desc: '',
+	sort: 0
+};
+
+		const formRef = ref<HTMLElement | null>(null);
+		const loading = ref(false); // 添加loading状态'
+		const isShowDialog = ref(false);
+		const deptData = ref([]);// 分类数据
+		const orgData = ref([]);// 组织数据
+		// 使用computed自动响应语言变化
+		const rules = computed(() => ({
+				name: [{ required: true, message: t('message.formI18nPlaceholder.categoryName'), trigger: 'blur' }],
+				key: [{ required: true, message: t('message.formI18nPlaceholder.categoryKey'), trigger: 'blur' }],
+		}));
+		const ruleForm = ref({
+			...baseForm,
+		})
+
+		// // 打开弹窗
+		const openDialog = (row?: RuleFormState | number) => {
+			resetForm();
+			api.category.getList({ status: 1 }).then((res: any) => {
+				deptData.value = res.category || [];
+			});
+
+
+			if (row && typeof row === 'object') {
+				ruleForm.value = row;
+			} else if (row && typeof row === 'number') {
+				ruleForm.value.parentId = row;
+			}
+			isShowDialog.value = true;
+		};
+		// 关闭弹窗
+		const closeDialog = () => {
+			isShowDialog.value = false;
+		};
+		// 取消
+		const onCancel = () => {
+			closeDialog();
+		};
+		// 新增
+		const onSubmit = () => {
+			const formWrap = unref(formRef) as any;
+			if (!formWrap) return;
+			formWrap.validate(async (valid: boolean) => {
+				if (valid) {
+					// 禁用按钮
+					loading.value = true;
+					if (!ruleForm.value.parentId) {
+						ruleForm.value.parentId = 0;
+					}
+					if (!ruleForm.value.id) {
+						//添加
+						try {
+							// 等待提交完成
+							await api.category.add(ruleForm.value);
+							ElMessage.success(t('message.tableI18nConfirm.addSuccess'));
+							closeDialog(); // 关闭弹窗
+							emit('getCateList');
+						} catch (error) {
+							ElMessage.error(t('message.tableI18nConfirm.addFailed'));
+						}
+					} else {
+						//修改
+						try {
+							// 等待提交完成
+							await api.category.edit(ruleForm.value);
+							ElMessage.success(t('message.tableI18nConfirm.editSuccess'));
+							closeDialog(); // 关闭弹窗
+							emit('getCateList');
+						} catch (error) { }
+					}
+					// 启用按钮
+					loading.value = false;
+				} else {
+					// 表单验证失败时,启用按钮
+					loading.value = false;
+				}
+			});
+		};
+		const resetForm = () => {
+			ruleForm.value = {
+				...baseForm,
+			};
+		};
+defineExpose({ openDialog });
+</script>

+ 0 - 175
src/views/iot/device/category/component/edit.vue

@@ -1,175 +0,0 @@
-<template>
-	<div class="system-edit-dept-container">
-		<el-dialog :title="(ruleForm.id ? '修改' : '添加') + '分类'" v-model="isShowDialog" width="600px">
-			<el-form ref="formRef" :model="ruleForm" :rules="rules" label-width="90px">
-				<el-row :gutter="35">
-					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
-						<el-form-item label="上级分类">
-							<el-cascader :options="deptData" :props="{ checkStrictly: true, emitPath: false, value: 'id', label: 'name' }" placeholder="请选择分类" clearable class="w100" v-model="ruleForm.parentId">
-								<template #default="{ node, data }">
-									<span>{{ data.name }}</span>
-									<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
-								</template>
-							</el-cascader>
-						</el-form-item>
-					</el-col>
-					<el-col :xs="24" :sm="12" :md="16" :lg="16" :xl="16">
-						<el-form-item label="分类名称" prop="name">
-							<el-input v-model="ruleForm.name" placeholder="请输入分类名称" clearable></el-input>
-						</el-form-item>
-					</el-col>
-					<el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="8">
-						<el-form-item label="排序" prop="sort" label-width="40px">
-							<el-input-number v-model="ruleForm.sort" :min="0" controls-position="right" placeholder="请输入排序" class="w100" />
-						</el-form-item>
-					</el-col>
-					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
-						<el-form-item label="分类标识" prop="key">
-							<el-input v-model="ruleForm.key" placeholder="请输入分类名称" clearable></el-input>
-						</el-form-item>
-					</el-col>
-					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
-						<el-form-item label="描述" prop="desc">
-							<el-input v-model="ruleForm.desc" type="textarea" placeholder="请输入描述" maxlength="150"></el-input>
-						</el-form-item>
-					</el-col>
-				</el-row>
-			</el-form>
-			<template #footer>
-				<span class="dialog-footer">
-					<el-button @click="onCancel">取 消</el-button>
-					<el-button type="primary" @click="onSubmit" :loading="loading">{{ ruleForm.id ? '修 改' : '添 加' }}</el-button>
-				</span>
-			</template>
-		</el-dialog>
-	</div>
-</template>
-
-<script lang="ts">
-import { reactive, toRefs, defineComponent, ref, unref } from 'vue';
-import api from '/@/api/device';
-import { ElMessage } from 'element-plus';
-
-interface RuleFormState {
-	id?: number;
-	parentId: number;
-	name: string;
-	key: string;
-	desc: string;
-	sort: number;
-	children?: RuleFormState[];
-}
-interface DeptSate {
-	isShowDialog: boolean;
-	ruleForm: RuleFormState;
-	deptData: RuleFormState[];
-	orgData: any[];
-	rules: object;
-}
-
-const baseForm: RuleFormState = {
-	parentId: 0, // 上级分类
-	name: '', // 分类名称
-	key: '',
-	desc: '',
-	sort: 0
-};
-
-export default defineComponent({
-	name: 'deviceEditCate',
-	setup(prop, { emit }) {
-		const formRef = ref<HTMLElement | null>(null);
-		const loading = ref(false); // 添加loading状态
-		const state = reactive<DeptSate>({
-			isShowDialog: false,
-			ruleForm: {
-				...baseForm,
-			},
-			deptData: [], // 分类数据
-			orgData: [], // 组织数据
-			rules: {
-				name: [{ required: true, message: '分类名称不能为空', trigger: 'blur' }],
-				key: [{ required: true, message: '分类标识不能为空', trigger: 'blur' }],
-			},
-		});
-
-		// 打开弹窗
-		const openDialog = (row?: RuleFormState | number) => {
-			resetForm();
-			api.category.getList({ status: 1 }).then((res: any) => {
-				state.deptData = res.category || [];
-			});
-
-
-			if (row && typeof row === 'object') {
-				state.ruleForm = row;
-			} else if (row && typeof row === 'number') {
-				state.ruleForm.parentId = row;
-			}
-			state.isShowDialog = true;
-		};
-		// 关闭弹窗
-		const closeDialog = () => {
-			state.isShowDialog = false;
-		};
-		// 取消
-		const onCancel = () => {
-			closeDialog();
-		};
-		// 新增
-		const onSubmit = () => {
-			const formWrap = unref(formRef) as any;
-			if (!formWrap) return;
-			formWrap.validate(async (valid: boolean) => {
-				if (valid) {
-					// 禁用按钮
-					loading.value = true;
-					if (!state.ruleForm.parentId) {
-						state.ruleForm.parentId = 0;
-					}
-					if (!state.ruleForm.id) {
-						//添加
-						try {
-							// 等待提交完成
-							await api.category.add(state.ruleForm);
-							ElMessage.success('分类添加成功');
-							closeDialog(); // 关闭弹窗
-							emit('getCateList');
-						} catch (error) {
-							ElMessage.error('分类添加失败');
-						}
-					} else {
-						//修改
-						try {
-							// 等待提交完成
-							await api.category.edit(state.ruleForm);
-							ElMessage.success('分类修改成功');
-							closeDialog(); // 关闭弹窗
-							emit('getCateList');
-						} catch (error) { }
-					}
-					// 启用按钮
-					loading.value = false;
-				} else {
-					// 表单验证失败时,启用按钮
-					loading.value = false;
-				}
-			});
-		};
-		const resetForm = () => {
-			state.ruleForm = {
-				...baseForm,
-			};
-		};
-		return {
-			loading,
-			openDialog,
-			closeDialog,
-			onCancel,
-			onSubmit,
-			formRef,
-			...toRefs(state),
-		};
-	},
-});
-</script>

+ 76 - 83
src/views/iot/device/category/index.vue

@@ -1,47 +1,61 @@
 <template>
   <div class="page">
-    <el-card shadow="nover">
+    <el-card shadow="never">
       <el-form inline>
-        <el-form-item label="分类名称">
-          <el-input v-model="tableData.param.name" placeholder="请输入分类名称" @keyup.enter.native="getCateList" class="w-50" clearable />
+        <el-form-item :label="$t('message.formI18nLabel.categoryName')">
+          <el-input v-model="tableData.param.name" :placeholder="$t('message.formI18nPlaceholder.categoryName')" @keyup.enter.native="getCateList" class="w-50" clearable :style="{width: currentLang === 'en' ? '220px' : '150px' }" />
         </el-form-item>
         <el-form-item>
+          <!-- 查询 -->
           <el-button type="primary" class="ml10" @click="getCateList">
             <el-icon>
               <ele-Search />
             </el-icon>
-            查询
+            {{ $t('message.formI18nButton.query') }}
           </el-button>
+          <!-- 新增分类 -->
           <el-button type="primary" class="ml10" @click="onOpenAdd" v-auth="'add'">
             <el-icon>
               <ele-FolderAdd />
             </el-icon>
-            新增分类
+            {{ $t('message.formI18nButton.addCategory') }}
           </el-button>
         </el-form-item>
       </el-form>
       <el-table :data="tableData.data" style="width: 100%" row-key="id" default-expand-all :tree-props="{ children: 'children', hasChildren: 'hasChildren' }" v-loading="tableData.loading">
-        <el-table-column prop="name" label="分类名称" v-col="'name'" show-overflow-tooltip> </el-table-column>
-        <el-table-column prop="desc" label="描述" align="center" v-col="'desc'"></el-table-column>
-        <el-table-column prop="sort" v-col="'sort'" label="排序" align="center"></el-table-column>
-        <el-table-column label="操作" align="center" width="140" fixed="right">
+        <!-- 分类名称 -->
+        <el-table-column prop="name" :label="$t('message.device.tableI18nColumn.categoryName')" v-col="'name'" show-overflow-tooltip> </el-table-column>
+        <!-- 描述 -->
+        <el-table-column prop="desc" :label="$t('message.device.tableI18nColumn.desc')" align="center" v-col="'desc'"></el-table-column>
+        <!-- 排序 -->
+        <el-table-column prop="sort" v-col="'sort'" :label="$t('message.device.tableI18nColumn.sort')" align="center"></el-table-column>
+        <!-- 操作 -->
+        <el-table-column :label="$t('message.tableI18nColumn.operation')" align="center" width="140" fixed="right">
           <template #default="scope">
-            <el-button size="small" type="text" @click="onOpenAdd(scope.row)" v-auth="'add'">新增</el-button>
-            <el-button size="small" text type="warning" @click="onOpenEdit(scope.row)" v-auth="'edit'">修改</el-button>
-            <el-button size="small" text type="info" @click="onTabelRowDel(scope.row)" v-auth="'del'">删除</el-button>
+            <!-- 新增 -->
+            <el-button size="small" text type="primary" @click="onOpenAdd(scope.row)" v-auth="'add'">{{ $t('message.tableI18nAction.add') }}</el-button>
+            <!-- 修改 -->
+            <el-button size="small" text type="warning" @click="onOpenEdit(scope.row)" v-auth="'edit'">{{ $t('message.tableI18nAction.edit') }}</el-button>
+            <!-- 删除 -->
+            <el-button size="small" text type="info" @click="onTabelRowDel(scope.row)" v-auth="'del'">{{ $t('message.tableI18nAction.delete') }}</el-button>
           </template>
         </el-table-column>
       </el-table>
     </el-card>
-    <EditCate ref="editDeptRef" @getCateList="getCateList" />
+    <AddOrEditCate ref="addOrEditRef" @getCateList="getCateList" />
   </div>
 </template>
 
-<script lang="ts">
-import { ref, toRefs, reactive, onMounted, defineComponent } from 'vue';
+<script lang="ts" setup>
+import { ref, onMounted, computed, watch } from 'vue';
 import { ElMessageBox, ElMessage } from 'element-plus';
-import EditCate from './component/edit.vue';
+import AddOrEditCate from './component/addOrEdit.vue';
 import api from '/@/api/device';
+import { useI18n } from 'vue-i18n';
+
+const { t, locale } = useI18n();
+
+const currentLang = computed(() => locale.value);
 
 // 定义接口来定义对象的类型
 interface TableDataRow {
@@ -53,75 +67,54 @@ interface TableDataRow {
   leader: string;
   children?: TableDataRow[];
 }
-interface TableDataState {
-  tableData: {
-    data: Array<TableDataRow>;
-    loading: boolean;
-    param: {
-      name: string;
-      status: number;
-    };
-  };
-}
 
-export default defineComponent({
-  name: 'deviceCate',
-  components: { EditCate },
-  setup() {
-    const editDeptRef = ref();
-    const state = reactive<TableDataState>({
-      tableData: {
-        data: [],
-        loading: false,
-        param: {
-          name: '',
-          status: -1,
-        },
-      },
-    });
-    // 初始化表格数据
-    const initTableData = () => {
+const addOrEditRef = ref();
+const tableData = ref<any>({
+    data: [],
+    loading: false,
+    param: {
+      name: '',
+      status: -1,
+    },
+});
+// 初始化表格数据
+const initTableData = () => {
+  getCateList();
+};
+const getCateList = () => {
+  tableData.value.loading = true;
+  api.category.getList(tableData.value.param).then((res: any) => {
+    tableData.value.data = res.category;
+  }).finally(() => (tableData.value.loading = false));
+};
+// 打开新增菜单弹窗
+const onOpenAdd = (row?: TableDataRow) => {
+  addOrEditRef.value.openDialog(row?.id);
+};
+// 打开编辑菜单弹窗
+const onOpenEdit = (row: TableDataRow) => {
+  addOrEditRef.value.openDialog({ ...row });
+};
+// 删除当前行
+const onTabelRowDel = (row: TableDataRow) => {
+  ElMessageBox.confirm(
+    t('message.device.tableI18nConfirm.deleteCatrgoryMessage', { name: row.name }),
+    t('message.tableI18nConfirm.deleteTitle'),
+    {
+      confirmButtonText: t('message.tableI18nConfirm.confirmText'),
+      cancelButtonText: t('message.tableI18nConfirm.cancelText'),
+      type: 'warning',
+    }
+  ).then(() => {
+    api.category.del(row.id).then(() => {
+      // 删除成功
+      ElMessage.success(t('message.tableI18nConfirm.deleteSuccess'));
       getCateList();
-    };
-    const getCateList = () => {
-      state.tableData.loading = true;
-      api.category.getList(state.tableData.param).then((res: any) => {
-        state.tableData.data = res.category;
-      }).finally(() => (state.tableData.loading = false));
-    };
-    // 打开新增菜单弹窗
-    const onOpenAdd = (row?: TableDataRow) => {
-      editDeptRef.value.openDialog(row?.id);
-    };
-    // 打开编辑菜单弹窗
-    const onOpenEdit = (row: TableDataRow) => {
-      editDeptRef.value.openDialog({ ...row });
-    };
-    // 删除当前行
-    const onTabelRowDel = (row: TableDataRow) => {
-      ElMessageBox.confirm(`此操作将永久删除分类:${row.name}, 是否继续?`, '提示', {
-        confirmButtonText: '删除',
-        cancelButtonText: '取消',
-        type: 'warning',
-      }).then(() => {
-        api.category.del(row.id).then(() => {
-          ElMessage.success('删除成功');
-          getCateList();
-        });
-      });
-    };
-    // 页面加载时
-    onMounted(() => {
-      initTableData();
     });
-    return {
-      editDeptRef,
-      getCateList,
-      onOpenAdd,
-      onOpenEdit,
-      onTabelRowDel,
-      ...toRefs(state),
-    };
-  },
+  });
+};
+// 页面加载时
+onMounted(() => {
+  initTableData();
 });
 </script>

+ 1 - 1
src/views/iot/device/channel/index.vue

@@ -1,6 +1,6 @@
 <template>
 	<div class="page">
-		<el-card shadow="nover">
+		<el-card shadow="never">
 			<div class="search">
 				<el-form :model="params" inline ref="queryRef" @keyup.enter.native="getList(1)">
 					<el-form-item label="通道名称" prop="title">

+ 1 - 1
src/views/iot/device/instance/component/chart.vue

@@ -5,7 +5,7 @@
 				<div class="dialog-header">
 					<h4 :id="titleId" :class="titleClass">{{ data.name + `(${data.key})` }}</h4>
 					<div class="dialog-header-actions">
-						<el-button type="text" @click="toggleFullscreen" class="fullscreen-btn">
+						<el-button text type="primary" @click="toggleFullscreen" class="fullscreen-btn">
 							<i class="iconfont" :class="!isFullscreen ? 'icon-fullscreen' : 'icon-tuichuquanping'"></i>
 						</el-button>
 					</div>

+ 41 - 46
src/views/iot/device/instance/component/excel.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="system-edit-dic-container">
-    <el-dialog :title="(open_type === 'upload' ? '导入' : '导出') + '设备'" v-model="isShowDialog" width="769px">
+    <el-dialog :title="(open_type === 'upload' ? '导入' : '导出') + '设备'" v-model="isShowDialog" width="769px" destroy-on-close>
       <el-form :model="ruleForm" ref="formRef" :rules="rules" label-width="110px" v-if="isShowDialog">
         <el-form-item label="所属产品" prop="productKey">
           <el-select v-model="ruleForm.productKey" placeholder="请选择所属产品" class="w100">
@@ -27,8 +27,7 @@
       <template #footer>
         <span class="dialog-footer">
           <el-button @click="onCancel">取 消</el-button>
-          <el-button type="primary" @click="onSubmit" v-if="open_type !== 'upload'">{{ open_type === 'upload' ? '导入设备' : '导出设备'
-            }}</el-button>
+          <el-button type="primary" @click="onSubmit" v-if="open_type !== 'upload'">{{ open_type === "upload" ? "导入设备" : "导出设备" }}</el-button>
         </span>
       </template>
     </el-dialog>
@@ -36,11 +35,11 @@
 </template>
 
 <script lang="ts">
-import { reactive, toRefs, defineComponent, ref, unref, nextTick } from 'vue';
-import api from '/@/api/device';
+import { reactive, toRefs, defineComponent, ref, unref, nextTick } from "vue";
+import api from "/@/api/device";
 import { ElMessage, UploadProps } from "element-plus";
-import downloadFile from '/@/utils/download';
-import getOrigin from '/@/utils/origin';
+import downloadFile from "/@/utils/download";
+import getOrigin from "/@/utils/origin";
 import { getToken } from "/@/utils/auth";
 
 interface RuleFormState {
@@ -49,25 +48,23 @@ interface RuleFormState {
 }
 
 const form: RuleFormState = {
-  productKey: '',
-  path: '',
-}
+  productKey: "",
+  path: "",
+};
 
 interface DicState {
   productData: any[];
   isShowDialog: boolean;
   ruleForm: RuleFormState;
-  rules: {}
+  rules: {};
   open_type: string;
 }
 
-
 export default defineComponent({
   setup(prop, { emit }) {
-
     const uploadUrl: string = getOrigin("/product/device/import");
     const headers = {
-      Authorization: 'Bearer ' + getToken(),
+      Authorization: "Bearer " + getToken(),
     };
     const formRef = ref<HTMLElement | null>(null);
     const tagRef = ref<HTMLElement | null>(null);
@@ -75,38 +72,36 @@ export default defineComponent({
     const uploadRef = ref();
     const state = reactive<DicState>({
       isShowDialog: false,
-      open_type: '',
+      open_type: "",
       productData: [], // 分类数据
       ruleForm: {
-        ...form
+        ...form,
       },
       rules: {
-        productKey: [{ required: true, message: '所属产品不能为空', trigger: 'blur' }],
-      }
+        productKey: [{ required: true, message: "所属产品不能为空", trigger: "blur" }],
+      },
     });
 
-
-    const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile: any) => {
+    const beforeAvatarUpload: UploadProps["beforeUpload"] = (rawFile: any) => {
       if (!state.ruleForm.productKey) {
-        ElMessage.error('请先选择所属产品!');
+        ElMessage.error("请先选择所属产品!");
         return false;
       }
       if (rawFile.size / 1024 / 1024 > 2) {
-        ElMessage.error('文件不能超过2MB!');
+        ElMessage.error("文件不能超过2MB!");
         return false;
       }
-      uploading.value = true
+      uploading.value = true;
       return true;
     };
 
     const updateImg = (res: any, file: File) => {
       uploadRef.value.handleRemove(file);
-      uploading.value = false
+      uploading.value = false;
       if (res.code === 0) {
-        ElMessage.success('导入成功');
+        ElMessage.success("导入成功");
         closeDialog(); // 关闭弹窗
-        emit('typeList')
-
+        emit("typeList");
       } else {
         ElMessage.error(res.message);
       }
@@ -122,12 +117,15 @@ export default defineComponent({
     };
     const resetForm = () => {
       state.ruleForm = {
-        ...form
-      }
+        ...form,
+      };
     };
     // 关闭弹窗
-    const closeDialog = () => {
+    const closeDialog = (done?: () => void) => {
+      uploadRef.value?.abort();
       state.isShowDialog = false;
+      uploading.value = false;
+      done?.();
     };
     // 取消
     const onCancel = () => {
@@ -137,46 +135,44 @@ export default defineComponent({
     const getCurrentTime = () => {
       const date = new Date();
       const year = date.getFullYear().toString();
-      const month = (date.getMonth() + 1).toString().padStart(2, '0');
-      const day = date.getDate().toString().padStart(2, '0');
-      const hours = date.getHours().toString().padStart(2, '0');
-      const minutes = date.getMinutes().toString().padStart(2, '0');
+      const month = (date.getMonth() + 1).toString().padStart(2, "0");
+      const day = date.getDate().toString().padStart(2, "0");
+      const hours = date.getHours().toString().padStart(2, "0");
+      const minutes = date.getMinutes().toString().padStart(2, "0");
       return year + month + day + hours + minutes;
-    }
+    };
     const onSubmit = () => {
       const formWrap = unref(formRef) as any;
       if (!formWrap) return;
       formWrap.validate((valid: boolean) => {
         if (valid) {
-          if (state.open_type === 'upload') {
+          if (state.open_type === "upload") {
             api.device.import(state.ruleForm).then((res: any) => {
-              ElMessage.success('导入成功');
+              ElMessage.success("导入成功");
               closeDialog(); // 关闭弹窗
             });
           } else {
             const selectedProduct = state.productData.find((item) => item.key === state.ruleForm.productKey);
             if (selectedProduct) {
-              api.device.export({ productKey: state.ruleForm.productKey }).then((res: any) => downloadFile(res, selectedProduct.name + "-" + getCurrentTime() + ".xlsx"))
+              api.device.export({ productKey: state.ruleForm.productKey }).then((res: any) => downloadFile(res, selectedProduct.name + "-" + getCurrentTime() + ".xlsx"));
               closeDialog(); // 关闭弹窗
             }
-
-
           }
         }
       });
     };
     const down = () => {
-      const fileURL = '/deviceImportExample.xlsx';
+      const fileURL = "/deviceImportExample.xlsx";
       // 创建下载链接
-      const link = document.createElement('a');
+      const link = document.createElement("a");
       link.href = fileURL;
-      link.setAttribute('download', 'deviceImportExample.xlsx');
+      link.setAttribute("download", "deviceImportExample.xlsx");
 
       // 模拟点击下载链接进行下载
       document.body.appendChild(link);
       link.click();
       document.body.removeChild(link);
-    }
+    };
     return {
       uploadUrl,
       headers,
@@ -198,7 +194,6 @@ export default defineComponent({
 </script>
 <style scoped lang="scss">
 .el-form {
-
   ::v-deep(.el-input-group__prepend),
   ::v-deep(.el-input-group__append) {
     padding: 0 5px;
@@ -216,4 +211,4 @@ export default defineComponent({
     }
   }
 }
-</style>
+</style>

+ 66 - 37
src/views/iot/device/instance/detail.vue

@@ -2,34 +2,41 @@
   <div class="page bg page-full">
     <div class="content">
       <div class="cont_box" style="align-items: center">
-        <div class="title">设备:{{ detail.name }}</div>
-        <el-tag v-if="areaData.status === 0" type="info" style="margin-left: 20px">未启用</el-tag>
-        <el-tag v-else-if="areaData.status === 1" type="danger" style="margin-left: 20px">离线</el-tag>
-        <el-tag v-else-if="areaData.status === 2" type="success" style="margin-left: 20px">在线</el-tag>
+        <!-- 设备 -->
+        <div class="title">{{$t('message.device.formI18nOption.device')}}:{{ detail.name }}</div>
+        <!-- 未启用 -->
+        <el-tag v-if="areaData.status === 0" type="info" style="margin-left: 20px">{{ $t('message.device.formI18nOption.off') }}</el-tag>
+        <!-- 离线 -->
+        <el-tag v-else-if="areaData.status === 1" type="danger" style="margin-left: 20px">{{ $t('message.device.formI18nOption.offline') }}</el-tag>
+        <!-- 在线 -->
+        <el-tag v-else-if="areaData.status === 2" type="success" style="margin-left: 20px">{{ $t('message.device.formI18nOption.online') }}</el-tag>
       </div>
     </div>
 
     <div class="content-box page-full-part page-full">
       <el-tabs v-model="activeName" @tab-click="handleClick">
-        <el-tab-pane label="运行状态" name="3">
+        <!-- 运行状态 -->
+        <el-tab-pane :label="$t('message.device.runStatus')" name="3">
           <div style="display: flex; flex-wrap: wrap">
             <div class="ant-card">
               <div class="ant-card-body">
                 <div class="cardflex">
-                  <div>设备状态</div>
+                  <div>{{ $t('message.device.deviceStatus') }}</div>
                   <div @click="getrunData()" style="cursor: pointer">
                     <el-icon style="font-size: 18px">
                       <ele-Refresh />
                     </el-icon>
                   </div>
                 </div>
-
-                <div class="statusname" v-if="areaData.status == 0">未启用</div>
-                <div class="statusname" v-if="areaData.status == 1">离线</div>
-                <div class="statusname" v-if="areaData.status == 2">在线</div>
+                <!-- 未启用 -->
+                <div class="statusname" v-if="areaData.status == 0">{{ $t('message.device.formI18nOption.off') }}</div>
+                <!-- 离线 -->
+                <div class="statusname" v-if="areaData.status == 1">{{ $t('message.device.formI18nOption.offline') }}</div>
+                <!-- 在线 -->
+                <div class="statusname" v-if="areaData.status == 2">{{ $t('message.device.formI18nOption.online') }}</div>
                 <div class="cardflex comtest">
-                  <div>数据时间</div>
-                  <div>{{ areaData.lastOnlineTime || "未启用" }}</div>
+                  <div>{{$t('message.device.dataTime')}}</div>
+                  <div>{{ areaData.lastOnlineTime || $t('message.device.formI18nOption.off') }}</div>
                 </div>
               </div>
             </div>
@@ -69,33 +76,46 @@
             </div>
           </div>
         </el-tab-pane>
-        <el-tab-pane label="设备信息" name="1">
+        <!-- 设备信息 -->
+        <el-tab-pane :label="$t('message.device.deviceInfo')" name="1">
           <div class="pro-box">
-            <div class="protitle">设备信息</div>
+            <div class="protitle">{{$t('message.device.deviceInfo')}}</div>
             <div>
-              <el-button size="small" type="primary" v-auth="'edit'" @click="onOpenEditDic(detail)">编辑</el-button>
+              <el-button size="small" type="primary" v-auth="'edit'" @click="onOpenEditDic(detail)">{{ $t('message.tableI18nAction.edit') }}</el-button>
             </div>
           </div>
 
           <el-descriptions class="margin-top" :column="3" border>
-            <el-descriptions-item label="设备标识">
+            <!-- 设备标识 -->
+            <el-descriptions-item :label="$t('message.device.deviceIdentifier')">
               <copy :text="detail.key"></copy>
             </el-descriptions-item>
-            <el-descriptions-item label="设备名称">{{ detail.name }}</el-descriptions-item>
-            <el-descriptions-item label="消息协议">{{ prodetail.messageProtocol }}</el-descriptions-item>
-            <el-descriptions-item label="产品标识">
+            <!-- 设备名称 -->
+            <el-descriptions-item :label="$t('message.device.tableI18nColumn.deviceName')">{{ detail.name }}</el-descriptions-item>
+            <!-- 消息协议 -->
+            <el-descriptions-item :label="$t('message.device.formI18nLabel.messageProtocol')">{{ prodetail.messageProtocol }}</el-descriptions-item>
+            <!-- 产品标识 -->
+            <el-descriptions-item :label="$t('message.device.formI18nLabel.productKey')">
               <copy :text="prodetail.key"></copy>
             </el-descriptions-item>
-            <el-descriptions-item label="产品名称">
+            <!-- 产品名称 -->
+            <el-descriptions-item :label="$t('message.device.formI18nLabel.productName')">
               <router-link :to="'/iotmanager/device/product/detail/' + prodetail.key" class="link-type">{{ detail.productName }} </router-link>
             </el-descriptions-item>
-            <el-descriptions-item label="链接协议">{{ prodetail.transportProtocol }}</el-descriptions-item>
-            <el-descriptions-item label="设备类型">{{ prodetail.deviceType }}</el-descriptions-item>
-            <el-descriptions-item label="固件版本">{{ detail.version }}</el-descriptions-item>
-            <el-descriptions-item label="注册时间">{{ detail.registryTime }}</el-descriptions-item>
-            <el-descriptions-item label="最后上线时间">{{ detail.lastOnlineTime || "" }}</el-descriptions-item>
-            <el-descriptions-item label="详细地址">{{ detail.address }}</el-descriptions-item>
-            <el-descriptions-item label="说明">{{ detail.desc }}</el-descriptions-item>
+            <!-- 链接协议 -->
+            <el-descriptions-item :label="$t('message.device.linkProtocol')">{{ prodetail.transportProtocol }}</el-descriptions-item>
+            <!-- 设备类型 -->
+            <el-descriptions-item :label="$t('message.device.tableI18nColumn.deviceType2')">{{ prodetail.deviceType }}</el-descriptions-item>
+            <!-- 固件版本 -->
+            <el-descriptions-item :label="$t('message.device.firmwareVersion')">{{ detail.version }}</el-descriptions-item>
+            <!-- 注册时间 -->
+            <el-descriptions-item :label="$t('message.device.registryTime')">{{ detail.registryTime }}</el-descriptions-item>
+            <!-- 最后上线时间 -->
+            <el-descriptions-item :label="$t('message.device.tableI18nColumn.lastOnlineTime')">{{ detail.lastOnlineTime || "" }}</el-descriptions-item>
+            <!-- 详细地址 -->
+            <el-descriptions-item :label="$t('message.device.address')">{{ detail.address }}</el-descriptions-item>
+            <!-- 说明 -->
+            <el-descriptions-item :label="$t('message.device.tableI18nColumn.desc2')">{{ detail.desc }}</el-descriptions-item>
             <el-descriptions-item :label="item.name" v-for="(item, index) in detail.tags" :key="index">{{ item.value }}</el-descriptions-item>
             <!-- <el-descriptions-item label="标签">
             <div class="capsule-wrapper">
@@ -108,15 +128,20 @@
           </el-descriptions>
           <div class="flex" style="margin-top: 20px">
             <el-input type="number" style="width: 380px; margin-right: 20px" v-model.number="detail.onlineTimeout">
-              <template #prepend>设备超时时间</template>
-              <template #append>秒</template>
+              <!-- 设备超时时间 -->
+              <template #prepend>{{$t('message.device.deviceTimeout')}}</template>
+              <!-- 秒 -->
+              <template #append>{{ $t('message.device.unitSecond') }}</template>
             </el-input>
+            <!-- 更新 -->
             <el-button type="primary" @click="onlineTimeoutUpdate">
-              <el-icon style="font-size: 18px"><ele-Refresh /></el-icon>更新</el-button
-            >
+              <el-icon style="font-size: 18px"><ele-Refresh /></el-icon>
+              {{ $t('message.device.update') }}
+            </el-button>
           </div>
         </el-tab-pane>
-        <el-tab-pane label="物模型" name="2">
+        <!-- 物模型 -->
+        <el-tab-pane :label="$t('message.device.thingModel')" name="2">
           <el-tabs type="border-card" v-model="activetab" @tab-click="wuhandleClick">
             <el-tab-pane label="属性定义" name="attr">
               <div class="wu-title">
@@ -194,7 +219,7 @@
                 <el-table-column label="名称" prop="name" show-overflow-tooltip />
                 <el-table-column prop="level" label="事件级别" width="120" align="center">
                   <template #default="scope">
-                    <el-tag type="primary" size="small" v-if="scope.row.level == 0">普通</el-tag>
+                    <el-tag size="small" v-if="scope.row.level == 0">普通</el-tag>
                     <el-tag type="warning" size="small" v-if="scope.row.level == 1">警告</el-tag>
                     <el-tag type="danger" size="small" v-if="scope.row.level == 2">紧急</el-tag>
                   </template>
@@ -243,10 +268,12 @@
           </el-tabs>
           <pagination v-show="tableData.total > 0" :total="tableData.total" v-model:page="tableData.param.pageNum" v-model:limit="tableData.param.pageSize" @pagination="getList()" />
         </el-tab-pane>
-        <el-tab-pane label="设备功能" name="5">
+        <!-- 设备功能 -->
+        <el-tab-pane :label="$t('message.device.deviceFunction')" name="5">
           <functionCom :device-key="detail.key" :product-key="prodetail.key" v-if="detail.key && prodetail.key && activeName === '5'"></functionCom>
         </el-tab-pane>
-        <el-tab-pane label="日志管理" name="4">
+        <!-- 日志管理 -->
+        <el-tab-pane :label="$t('message.device.logManagement')" name="4">
           <div class="system-user-search mb15">
             <el-form :model="logtableData.param" ref="logqueryRef" inline label-width="68px">
               <el-form-item label="日志类型" prop="types">
@@ -287,7 +314,8 @@
 
           <pagination v-show="logtableData.total > 0" :total="logtableData.total" v-model:page="logtableData.param.pageNum" v-model:limit="logtableData.param.pageSize" @pagination="getlog" />
         </el-tab-pane>
-        <el-tab-pane label="Topic列表" name="topic">
+        <!-- Topic列表 -->
+        <el-tab-pane :label="$t('message.device.topicList')" name="topic">
           SagooMqtt协议 ,涉及的topic如下:
           <el-table style="width: 100%; margin-top: 20px" :data="topicData" border>
             <el-table-column label="描述" prop="info" width="250" />
@@ -339,7 +367,8 @@
             <pagination v-show="deviceTableData.total > 0" :total="deviceTableData.total" v-model:page="deviceTableData.param.pageNum" v-model:limit="deviceTableData.param.pageSize" @pagination="getDeviceTableData" />
           </div>
         </el-tab-pane>
-        <el-tab-pane label="设备档案" name="7" v-if="deviceAssetData">
+        <!-- 设备档案 -->
+        <el-tab-pane :label="$t('message.device.deviceArchive')" name="7" v-if="deviceAssetData">
           <el-form label-width="110px">
             <div class="pro-box">
               <div class="protitle">设备档案</div>

+ 120 - 46
src/views/iot/device/instance/index.vue

@@ -1,39 +1,54 @@
 <template>
   <div class="page">
-    <el-card shadow="nover" v-loading="batchLoading">
+    <el-card shadow="never" v-loading="batchLoading">
       <div class="system-user-search mb15">
         <el-form :model="tableData.param" ref="queryRef" inline class="search-form">
           <div class="search-conditions">
-            <el-form-item label="关键字" prop="keyWord">
-              <el-input v-model="tableData.param.keyWord" placeholder="输入名称或标识" clearable style="width: 200px" @keyup.enter.native="typeList" />
+            <!-- 关键字 -->
+            <el-form-item :label="$t('message.device.formI18nLabel.keyword')" prop="keyWord">
+              <!-- 输入名称或标识 -->
+              <el-input v-model="tableData.param.keyWord" :placeholder="$t('message.device.formI18nPlaceholder.keyword')" clearable style="width: 200px" @keyup.enter.native="typeList" />
             </el-form-item>
             <!--          <el-form-item label="标识" prop="key">-->
             <!--            <el-input v-model="tableData.param.key" placeholder="请输入设备标识" clearable style="width: 150px" @keyup.enter.native="typeList" />-->
             <!--          </el-form-item>-->
-            <el-form-item label="状态" prop="status">
-              <el-select v-model="tableData.param.status" placeholder="状态" clearable style="width: 80px">
-                <el-option label="在线" :value="2" />
-                <el-option label="离线" :value="1" />
-                <el-option label="未启用" :value="0" />
+            <!-- 状态 -->
+            <el-form-item :label="$t('message.device.formI18nLabel.status')" prop="status">
+              <!-- 状态 -->
+              <el-select v-model="tableData.param.status" :placeholder="$t('message.device.formI18nLabel.status')" clearable style="width: 88px">
+                <!-- 在线 -->
+                <el-option :label="$t('message.device.formI18nOption.online')" :value="2" />
+                <!-- 离线 -->
+                <el-option :label="$t('message.device.formI18nOption.offline')" :value="1" />
+                <!-- 未启用 -->
+                <el-option :label="$t('message.device.formI18nOption.off')" :value="0" />
               </el-select>
             </el-form-item>
 
             <template v-if="showMoreFilter">
-              <el-form-item label="所属产品" prop="productKey">
-                <el-select v-model="tableData.param.productKey" style="width: 120px" filterable clearable placeholder="选择产品">
+              <!-- 所属产品 -->
+              <el-form-item :label="$t('message.device.formI18nLabel.productBind')" prop="productKey">
+                <!-- 选择产品 -->
+                <el-select v-model="tableData.param.productKey" style="width: 120px" filterable clearable :placeholder="$t('message.device.formI18nLabel.productBind')">
                   <el-option v-for="item in productData" :key="item.key" :label="item.name" :value="item.key" value-key="id"> </el-option>
                 </el-select>
               </el-form-item>
 
-              <el-form-item label="设备类型" prop="deviceTypes">
-                <el-select v-model="tableData.param.deviceTypes" multiple style="width: 260px" clearable placeholder="设备类型">
-                  <el-option label="设备" value="设备" />
-                  <el-option label="网关" value="网关" />
-                  <el-option label="子设备" value="子设备" />
+              <!-- 设备类型 -->
+              <el-form-item :label="$t('message.device.formI18nLabel.deviceType')" prop="deviceTypes">
+                <el-select v-model="tableData.param.deviceTypes" multiple style="width: 260px" clearable :placeholder="$t('message.device.formI18nLabel.deviceType')">
+                  <!-- 设备 -->
+                  <el-option :label="$t('message.device.formI18nOption.device')" value="设备" />
+                  <!-- 网关 -->
+                  <el-option :label="$t('message.device.formI18nOption.gateway')" value="网关" />
+                  <!-- 子设备 -->
+                  <el-option :label="$t('message.device.formI18nOption.subDevice')" value="子设备" />
                 </el-select>
               </el-form-item>
 
-              <el-form-item label="所属组织" prop="deptIds">
+              <!-- 所属组织 -->
+              <el-form-item :label="$t('message.device.formI18nLabel.deptIds')" prop="deptIds">
+                <!-- 请选择所属组织 -->
                 <el-cascader
                   v-model="tableData.param.deptIds"
                   :options="deptOptions"
@@ -47,21 +62,23 @@
                   }"
                   style="width: 200px"
                   clearable
-                  placeholder="请选择所属组织"
+                  :placeholder="$t('message.device.formI18nPlaceholder.deptIds')"
                   collapse-tags
                   collapse-tags-tooltip
                   filterable
                 />
               </el-form-item>
 
-              <el-form-item label="设备标签" prop="tags">
+              <!-- 设备标签 -->
+              <el-form-item :label="$t('message.device.formI18nLabel.deviceTag')" prop="tags">
+                <!-- 请输入标签 -->
                 <el-select
                   v-model="tableData.param.tags"
                   multiple
                   filterable
                   allow-create
                   default-first-option
-                  placeholder="请输入标签"
+                  :placeholder="$t('message.device.formI18nPlaceholder.deviceTag')"
                   style="width: 260px"
                   clearable
                 >
@@ -77,7 +94,8 @@
           <div class="search-actions">
             <el-form-item>
               <el-button link type="primary" @click="showMoreFilter = !showMoreFilter">
-                {{ showMoreFilter ? '收起' : '更多筛选' }}
+                <!-- 收起/更多筛选 -->
+                {{ showMoreFilter ? $t('message.device.formI18nButton.collapse') : $t('message.device.formI18nButton.showMoreFilter') }}
                 <el-icon class="el-icon--right">
                   <component :is="showMoreFilter ? 'ArrowUp' : 'ArrowDown'" />
                 </el-icon>
@@ -85,11 +103,12 @@
             </el-form-item>
 
             <el-form-item>
+              <!-- 查询 -->
               <el-button type="primary" class="ml10" @click="typeList">
                 <el-icon>
                   <ele-Search />
                 </el-icon>
-                查询
+                {{ $t('message.tableI18nAction.query') }}
               </el-button>
               <!-- <el-button @click="resetQuery(queryRef)">
                 <el-icon>
@@ -97,56 +116,72 @@
                 </el-icon>
                 重置
               </el-button> -->
+              <!-- 新增 -->
               <el-button type="primary" class="ml10" @click="onOpenAddDic" v-auth="'add'">
                 <el-icon>
                   <ele-FolderAdd />
                 </el-icon>
-                新增
+                {{ $t('message.tableI18nAction.add') }}
               </el-button>
+              <!-- 删除 -->
               <el-button type="info" class="ml10" @click="onRowDel()" v-auth="'del'">
                 <el-icon>
                   <ele-Delete />
                 </el-icon>
-                删除
+                {{ $t('message.tableI18nAction.delete') }}
               </el-button>
 
               <el-dropdown>
+                <!-- 更多 -->
                 <el-button type="danger" class="ml10">
-                  更多
+                  {{ $t('message.tableI18nAction.more') }}
                   <el-icon class="el-icon--right"><arrow-down /></el-icon>
                 </el-button>
                 <template #dropdown>
                   <el-dropdown-menu>
                     <el-dropdown-item>
+                      <!-- 批量启用 -->
                       <el-button type="success" @click="setDeviceStatus1()">
                         <el-icon>
                           <ele-Open />
                         </el-icon>
-                        批量启用
+                        {{ $t('message.device.formI18nButton.batchEnable') }}
                       </el-button>
                     </el-dropdown-item>
                     <el-dropdown-item>
+                      <!-- 批量禁用 -->
                       <el-button type="warning" @click="setDeviceStatus0()">
                         <el-icon>
                           <ele-TurnOff />
                         </el-icon>
-                        批量禁用
+                        {{ $t('message.device.formI18nButton.batchDisable') }}
                       </el-button>
                     </el-dropdown-item>
                     <el-dropdown-item>
+                      <!-- 导入设备 -->
                       <el-button @click="onOpenexcelDic('upload')">
                         <el-icon>
                           <ele-Upload />
                         </el-icon>
-                        导入设备
+                        {{ $t('message.device.formI18nButton.importDevice') }}
                       </el-button>
                     </el-dropdown-item>
                     <el-dropdown-item>
+                      <!-- 导出设备 -->
                       <el-button @click="onOpenexcelDic('down')">
                         <el-icon>
                           <ele-Download />
                         </el-icon>
-                        导出设备
+                        {{ $t('message.device.formI18nButton.exportDevice') }}
+                      </el-button>
+                    </el-dropdown-item>
+                    <el-dropdown-item>
+                      <!-- 批量检测状态 -->
+                      <el-button @click="batchCheckStatus()">
+                        <el-icon>
+                          <ele-Refresh />
+                        </el-icon>
+                        {{ $t('message.device.formI18nButton.batchCheckStatus') }}
                       </el-button>
                     </el-dropdown-item>
                   </el-dropdown-menu>
@@ -158,33 +193,48 @@
       </div>
       <el-table :data="tableData.data" style="width: 100%" @selection-change="handleSelectionChange" v-loading="tableData.loading">
         <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="标识" prop="key" min-width="150" show-overflow-tooltip v-col="'key'">
+        <!-- 标识 -->
+        <el-table-column :label="$t('message.device.tableI18nColumn.key')" prop="key" min-width="150" show-overflow-tooltip v-col="'key'">
           <template #default="{ row }">
             <copy :text="row.key"></copy>
           </template>
         </el-table-column>
-        <el-table-column label="设备名称" prop="name" min-width="160" show-overflow-tooltip v-col="'name'" />
-        <el-table-column label="设备类型" prop="product.deviceType" min-width="100" align="center" show-overflow-tooltip v-col="'deviceType'" />
-        <el-table-column label="所属产品" prop="productName" min-width="120" align="center" show-overflow-tooltip v-col="'productName'" />
-        <el-table-column prop="status" label="状态" min-width="80" align="center" v-col="'status'">
+        <!-- 设备名称 -->
+        <el-table-column :label="$t('message.device.tableI18nColumn.deviceName')" prop="name" min-width="160" show-overflow-tooltip v-col="'name'" />
+        <!-- 设备类型 -->
+        <el-table-column :label="$t('message.device.tableI18nColumn.deviceType2')" prop="product.deviceType" min-width="120" align="center" show-overflow-tooltip v-col="'deviceType'" />
+        <!-- 所属产品 -->
+        <el-table-column :label="$t('message.device.formI18nLabel.productBind')" prop="productName" min-width="160" align="center" show-overflow-tooltip v-col="'productName'" />
+        <!-- 状态 -->
+        <el-table-column prop="status" :label="$t('message.device.tableI18nColumn.status')" min-width="90" align="center" v-col="'status'">
           <template #default="scope">
-            <el-tag type="danger" size="small" v-if="scope.row.status == 1">离线</el-tag>
-            <el-tag type="success" size="small" v-if="scope.row.status == 2">在线</el-tag>
-            <el-tag type="info" size="small" v-if="scope.row.status == 0">未启用</el-tag>
+            <!-- 离线 -->
+            <el-tag type="danger" size="small" v-if="scope.row.status == 1">{{ $t('message.device.formI18nOption.offline') }}</el-tag>
+            <!-- 在线 -->
+            <el-tag type="success" size="small" v-if="scope.row.status == 2">{{ $t('message.device.formI18nOption.online') }}</el-tag>
+            <!-- 未启用 -->
+            <el-tag type="info" size="small" v-if="scope.row.status == 0">{{ $t('message.device.formI18nOption.off') }}</el-tag>
           </template>
         </el-table-column>
-        <el-table-column prop="lastOnlineTime" label="最后上线时间" align="center" width="160" v-col="'lastOnlineTime'"></el-table-column>
-        <el-table-column prop="desc" label="说明" show-overflow-tooltip v-col="'desc'"></el-table-column>
-
-        <el-table-column label="操作" width="180" align="center" fixed="right">
+        <!-- 最后上线时间  -->
+        <el-table-column prop="lastOnlineTime" :label="$t('message.device.tableI18nColumn.lastOnlineTime')" align="center" width="160" v-col="'lastOnlineTime'"></el-table-column>
+        <!-- 说明 -->
+        <el-table-column prop="desc" :label="$t('message.device.tableI18nColumn.desc2')" min-width="110" show-overflow-tooltip v-col="'desc'"></el-table-column>
+        <!-- 操作 -->
+        <el-table-column :label="$t('message.tableI18nColumn.operation')" width="200" align="center" fixed="right">
           <template #default="scope">
+            <!-- 详情 -->
             <router-link :to="'/iotmanager/device/instance/' + scope.row.key" class="link-type" style="padding-right: 12px;font-size: 12px;color: #409eff;" v-auth="'detail'">
-              <span>详情</span>
+              <span>{{ $t('message.tableI18nAction.detail') }}</span>
             </router-link>
-            <el-button size="small" text type="warning" @click="onOpenEditDic(scope.row)" v-auth="'edit'">修改</el-button>
-            <el-button size="small" text type="success" @click="onActionStatus(scope.row)" v-if="scope.row.status == 0" v-auth="'status'">启用</el-button>
-            <el-button size="small" text type="primary" @click="onActionStatus(scope.row)" v-if="scope.row.status > 0" v-auth="'status'">停用</el-button>
-            <el-button size="small" text type="info" @click="onRowDel(scope.row)" v-auth="'del'">删除</el-button>
+            <!-- 修改 -->
+            <el-button size="small" text type="warning" @click="onOpenEditDic(scope.row)" v-auth="'edit'">{{ $t('message.tableI18nAction.edit') }}</el-button>
+            <!-- 启用 -->
+            <el-button size="small" text type="success" @click="onActionStatus(scope.row)" v-if="scope.row.status == 0" v-auth="'status'">{{ $t('message.device.enable') }}</el-button>
+            <!-- 停用 -->
+            <el-button size="small" text type="primary" @click="onActionStatus(scope.row)" v-if="scope.row.status > 0" v-auth="'status'">{{ $t('message.device.disable') }}</el-button>
+            <!-- 删除 -->
+            <el-button size="small" text type="info" @click="onRowDel(scope.row)" v-auth="'del'">{{ $t('message.tableI18nAction.delete') }}</el-button>
           </template>
         </el-table-column>
       </el-table>
@@ -306,6 +356,18 @@ export default defineComponent({
       excelDicRef.value.openDialog(type);
     };
 
+    const batchCheckStatus = () => {
+      const loading = ElMessage({
+        message: '同步中,请稍后...',
+        type: 'info',
+        duration: 0,
+      });
+      api.device.syncStatus().then(() => {
+        ElMessage.success('同步成功');
+        typeList();
+      }).finally(() => loading.close());
+    };
+
     //批量启用
     const setDeviceStatus1 = (row?: TableDataRow) => {
       let keys: string[] = [];
@@ -315,9 +377,11 @@ export default defineComponent({
         keys = state.keys;
       }
       if (keys.length === 0) {
+        // 请选择要操作的数据。
         ElMessage.error('请选择要操作的数据。');
         return;
       }
+      // 确认要批量启用这些设备吗?
       ElMessageBox.confirm("确认要批量启用这些设备吗?", '提示', {
         confirmButtonText: '确认',
         cancelButtonText: '取消',
@@ -326,6 +390,7 @@ export default defineComponent({
         .then(() => {
           batchLoading.value = true
           api.device.setDeviceStatus({ ids: keys, status: 1 }).then(() => {
+            // 启用成功
             ElMessage.success('启用成功');
             typeList();
           }).finally(() => batchLoading.value = false)
@@ -342,9 +407,11 @@ export default defineComponent({
         keys = state.keys;
       }
       if (keys.length === 0) {
+        // 请选择要操作的数据。
         ElMessage.error('请选择要操作的数据。');
         return;
       }
+      // 确认要批量禁用这些设备吗?
       ElMessageBox.confirm("确认要批量禁用这些设备吗?", '提示', {
         confirmButtonText: '确认',
         cancelButtonText: '取消',
@@ -353,6 +420,7 @@ export default defineComponent({
         .then(() => {
           batchLoading.value = true
           api.device.setDeviceStatus({ ids: keys, status: 0 }).then(() => {
+            // 禁用成功
             ElMessage.success('禁用成功');
             typeList();
           }).finally(() => batchLoading.value = false)
@@ -361,15 +429,18 @@ export default defineComponent({
     }
     // 删除产品
     const onRowDel = (row?: TableDataRow) => {
+      // 你确定要删除所选数据?
       let msg = '你确定要删除所选数据?';
       let keys: string[] = [];
       if (row) {
+        // 此操作将永久删除设备:"${row.name}",是否继续?
         msg = `此操作将永久删除设备:"${row.name}",是否继续?`;
         keys = [row.key];
       } else {
         keys = state.keys;
       }
       if (keys.length === 0) {
+        // 请选择要删除的数据。
         ElMessage.error('请选择要删除的数据。');
         return;
       }
@@ -380,6 +451,7 @@ export default defineComponent({
       })
         .then(() => {
           api.instance.del(keys).then(() => {
+            // 删除成功
             ElMessage.success('删除成功');
             typeList();
           });
@@ -408,11 +480,13 @@ export default defineComponent({
       if (item.status == 0) {
         api.instance.devdeploy(item.key).then((res: any) => {
           typeList();
+          // 操作成功
           ElMessage.success(res.message || '操作成功');
         });
       } else {
         api.instance.devundeploy(item.key).then((res: any) => {
           typeList();
+          // 操作成功
           ElMessage.success(res.message || '操作成功');
         });
       }
@@ -432,10 +506,10 @@ export default defineComponent({
     };
 
     return {
-
       addDicRef,
       excelDicRef,
       onOpenexcelDic,
+      batchCheckStatus,
       editDicRef,
       detailRef,
       queryRef,

+ 48 - 35
src/views/iot/device/product/component/dataParse.vue

@@ -1,79 +1,91 @@
 <template>
 	<div class="data-parse-wrapper" style="position: relative" ref="wrapperRef">
 		<div class="full-screen-btn">
-			<el-icon v-if="!isFullScreen" @click="toggleFullScreen" title="全屏"><FullScreen /></el-icon>
-			<el-icon v-else @click="toggleFullScreen" title="退出全屏"><Close /></el-icon>
+			<!-- fullScreen -->
+			<el-icon v-if="!isFullScreen" @click="toggleFullScreen" :title="$t('message.device.fullScreen')"><FullScreen /></el-icon>
+			<!-- exitFullScreen -->
+			<el-icon v-else @click="toggleFullScreen" :title="$t('message.device.exitFullScreen')"><Close /></el-icon>
 		</div>
 		<div class="flex" style="align-items: stretch">
-			<codeEditor class="params flex1" ref="mirrorRef" style="height: calc(100vh - 310px)" mode="javascript" :content="content"></codeEditor>
+			<codeEditor class="params flex1" ref="mirrorRef" style="height: calc(100vh - 310px)" mode="javascript" :content="script || emptyFunction"></codeEditor>
 			<div class="mock" style="width: 300px; margin-left: 20px">
 				<el-radio-group v-model="functionName">
 					<el-radio-button label="parse">parse</el-radio-button>
 					<el-radio-button label="send">send</el-radio-button>
 				</el-radio-group>
+				<!-- 请输入入参,以字符串的方式,如果是对象字符串会在执行时自动转换为对象再执行 -->
 				<el-input
 					class="input"
 					v-model="inputData"
 					type="textarea"
-					placeholder="请输入入参,以字符串的方式,如果是对象字符串会在执行时自动转换为对象再执行"
+					:placeholder="$t('message.device.tip1')"
 				></el-input>
-				<el-input class="input" v-model="outputData" type="textarea" readonly placeholder="此处显示执行结果"></el-input>
+				<!-- 此处显示执行结果 -->
+				<el-input class="input" v-model="outputData" type="textarea" readonly :placeholder="$t('message.device.tip2')"></el-input>
 			</div>
 		</div>
-		<el-button type="primary" style="margin-top: 20px" v-auth="'save'" @click="saveCode">保存脚本</el-button>
-		<el-button type="primary" style="margin-top: 20px" v-auth="'debug'" :loading="runing" @click="mock">调试</el-button>
+		<!-- 保存脚本 -->
+		<el-button type="primary" style="margin-top: 20px" v-auth="'save'" @click="saveCode">{{ $t('message.device.tip3') }}</el-button>
+		<!-- 调试 -->
+		<el-button type="primary" style="margin-top: 20px" v-auth="'debug'" :loading="runing" @click="mock">{{ $t('message.device.tip4') }}</el-button>
 	</div>
 </template>
 
 <script lang="ts" setup>
-import { onMounted, ref, onUnmounted } from 'vue'
+import { onMounted, ref, onUnmounted, computed } from 'vue'
 import codeEditor from '/@/components/codeEditor/index.vue'
 import { ElMessage } from 'element-plus'
 import api from '/@/api/device'
 import { useRoute } from 'vue-router'
 import { FullScreen, Close } from '@element-plus/icons-vue'
+import { useI18n } from 'vue-i18n'
+const { t } = useI18n()
+// tip5: "下面是预制的空的方法,请不要修改函数名称,直接在内部编写函数即可",
+// tip6: "此处是设备功能调用应答数据解析",
+// tip7: "此处编写对数据的处理",
+// tip8: "此处是设备功能调用发送数据解析",
+// tip9: "此处编写对数据的处理",
+// tip10: "请先输入可执行脚本",
+const emptyFunction = computed(() => 
+`
+	// ${t('message.device.tip5')}
 
-const emptyFunction = `
-// 下面是预制的空的方法,请不要修改函数名称,直接在内部编写函数即可
-
-// 此处是设备功能调用应答数据解析
-function parse (data) {
+	// ${t('message.device.tip6')}
+	function parse (data) {
 
-	// 此处编写对数据的处理
+		// ${t('message.device.tip7')}
 
-	return data
-}
+		return data
+	}
 
 
-// 此处是设备功能调用发送数据解析
-function send (data) {
+	// ${t('message.device.tip8')}
+	function send (data) {
 
-	// 此处编写对数据的处理
+		// ${t('message.device.tip9')}
 
-	return data
-}
+		return data
+	}
 `
+);
 
 const route = useRoute()
 
 const emit = defineEmits(['updateScript'])
 
-const props = defineProps({
+defineProps({
 	script: String,
 })
 
 const inputData = ref('')
 const outputData = ref('')
-const content = ref('')
 const functionName = ref('parse')
 const runing = ref(false)
 const mirrorRef = ref()
 const isFullScreen = ref(false)
-const wrapperRef = ref(null)
+const wrapperRef = ref()
 
 onMounted(() => {
-	content.value = props.script! || emptyFunction
-
 	// 添加全屏变化事件监听
 	document.addEventListener('fullscreenchange', handleFullscreenChange)
 	document.addEventListener('webkitfullscreenchange', handleFullscreenChange)
@@ -92,16 +104,16 @@ onUnmounted(() => {
 const toggleFullScreen = () => {
 	if (!isFullScreen.value) {
 		// 进入全屏
-		const element = wrapperRef.value as HTMLElement
+		const element = wrapperRef.value! as HTMLElement
 		if (element) {
 			if (element.requestFullscreen) {
 				element.requestFullscreen()
 			} else if ((element as any).webkitRequestFullscreen) {
 				// Safari
-				;(element as any).webkitRequestFullscreen()
+				(element as any).webkitRequestFullscreen()
 			} else if ((element as any).msRequestFullscreen) {
 				// IE11
-				;(element as any).msRequestFullscreen()
+				(element as any).msRequestFullscreen()
 			}
 		}
 	} else {
@@ -110,10 +122,10 @@ const toggleFullScreen = () => {
 			document.exitFullscreen()
 		} else if ((document as any).webkitExitFullscreen) {
 			// Safari
-			;(document as any).webkitExitFullscreen()
+			(document as any).webkitExitFullscreen()
 		} else if ((document as any).msExitFullscreen) {
 			// IE11
-			;(document as any).msExitFullscreen()
+			(document as any).msExitFullscreen()
 		}
 	}
 	isFullScreen.value = !isFullScreen.value
@@ -152,7 +164,7 @@ function toSave(data: string) {
 			scriptInfo: data,
 		})
 		.then(() => {
-			ElMessage.success('保存成功')
+			ElMessage.success(t('message.tableI18nConfirm.saveSuccess'))
 			emit('updateScript', data)
 		})
 }
@@ -179,13 +191,14 @@ async function mock() {
 	outputData.value = ''
 
 	await validate()
-
-	if (!inputData.value) return ElMessage.error('请输入参数')
+  // 请输入参数
+	if (!inputData.value) return ElMessage.error(t('message.device.formI18nPlaceholder.inputParameter'))
 
 	const funStr = mirrorRef.value.getValue()
 
 	if (funStr === '') {
-		return ElMessage.error('请先输入可执行脚本')
+		// 请先输入可执行脚本
+		return ElMessage.error(t('message.device.tip10'))
 	}
 
 	// 远程调试

+ 21 - 10
src/views/iot/device/product/component/deviceIn.vue

@@ -1,35 +1,46 @@
 <template>
-	<div class="title">接入方式</div>
+	<!-- 接入方式 -->
+	<div class="title">{{ $t('message.device.formI18nLabel.transportProtocol') }}</div>
 	<div class="text">{{ data.name }}</div>
-	<div class="title">消息协议</div>
+	<!-- 消息协议 -->
+	<div class="title">{{ $t('message.device.formI18nLabel.messageProtocol') }}</div>
 	<div class="text">{{ data.protocol }}</div>
-	<div class="title">认证说明</div>
+	<!-- 认证说明 -->
+	<div class="title">{{ $t('message.device.formI18nLabel.authDescription') }}</div>
 	<div class="text">{{ data.description }}</div>
-	<div class="title">链接信息</div>
+	<!-- 链接信息 -->
+	<div class="title">{{ $t('message.device.formI18nLabel.linkInfo') }}</div>
 	<div class="text">{{ data.link }}</div>
-	<div class="title">认证配置</div>
+	<!-- 认证配置 -->
+	<div class="title">{{ $t('message.device.formI18nLabel.authConfig') }}</div>
 
-	<template v-if="!isAdmin">请联系管理员</template>
+	<!-- 请联系管理员 -->
+	<template v-if="!isAdmin">{{ $t('message.device.formI18nLabel.contactAdmin') }}</template>
 	<template v-else-if="data.authType === 1 || data.authType === 2">
-		<el-form-item label="认证方式" prop="authType" label-width="80px" style="margin-bottom: 0;">
+		<!-- 认证方式 -->
+		<el-form-item :label="$t('message.device.formI18nLabel.authType')" prop="authType" label-width="80px" style="margin-bottom: 0;">
 			{{ data.authType === 1 ? 'Basic' : 'AccessToken' }}
 		</el-form-item>
 		<template v-if="data.authType === 1">
-			<el-form-item label="用户名" prop="authUser" label-width="80px" style="margin-bottom: 0;">
+			<!-- 用户名 -->
+			<el-form-item :label="$t('message.device.formI18nLabel.authUser')" prop="authUser" label-width="80px" style="margin-bottom: 0;">
 				{{ data.authUser }}
 			</el-form-item>
-			<el-form-item label="密码" prop="authPasswd" label-width="80px">
+			<!-- 密码 -->
+			<el-form-item :label="$t('message.device.formI18nLabel.authPasswd')" prop="authPasswd" label-width="80px">
 				{{ data.authPasswd }}
 			</el-form-item>
 		</template>
 		<template v-else>
+			<!-- Aceess Token -->
 			<el-form-item label="Aceess Token" prop="accessToken">
 				{{ data.accessToken }}
 			</el-form-item>
 		</template>
 	</template>
 	<template v-else-if="data.authType === 3">
-		<el-form-item label="认证证书" prop="certificateName">
+		<!-- 认证证书 -->
+		<el-form-item :label="$t('message.device.formI18nLabel.certificateId')" prop="certificateName">
 			{{ data.certificateName }}
 		</el-form-item>
 	</template>

+ 412 - 345
src/views/iot/device/product/component/editAttr.vue

@@ -1,58 +1,105 @@
 <template>
 	<div class="system-edit-dic-container">
-		<el-dialog :title="(ruleForm.id !== 0 ? '修改' : '添加') + '属性定义'" v-model="isShowDialog" width="769px">
-			<el-form :model="ruleForm" ref="formRef" :rules="rules" label-width="120px">
-				<el-form-item label="属性定义标识" prop="key">
-					<el-input v-model="ruleForm.key" placeholder="请输入属性定义标识" :disabled="ruleForm.id !== 0 ? true : false" />
+		<!-- 新增属性定义/编辑属性定义 -->
+		<el-dialog
+			:title="ruleForm.id !== 0 ? $t('message.device.dialogI18n.editPropertyDefinition') : $t('message.device.dialogI18n.addPropertyDefinition')"
+			v-model="isShowDialog"
+			width="769px"
+		>
+			<!-- 根据当前语言设置label-width -->
+			<el-form :model="ruleForm" ref="formRef" :rules="rules" :label-width="currentLocale == 'en' ? '150px' : '120px'">
+				<!-- 属性定义标识 -->
+				<el-form-item :label="$t('message.device.formI18nLabel.propertyIdentifier')" prop="key">
+					<!-- 请输入属性定义标识 -->
+					<el-input
+						v-model="ruleForm.key"
+						:placeholder="$t('message.device.formI18nPlaceholder.propertyIdentifier')"
+						:disabled="ruleForm.id !== 0 ? true : false"
+					/>
 				</el-form-item>
-				<el-form-item label="属性定义名称" prop="name">
-					<el-input v-model.trim="ruleForm.name" placeholder="请输入属性定义名称" />
+				<!-- 属性定义名称 -->
+				<el-form-item :label="$t('message.device.formI18nLabel.propertyName')" prop="name">
+					<!-- 请输入属性定义名称 -->
+					<el-input v-model.trim="ruleForm.name" :placeholder="$t('message.device.formI18nPlaceholder.propertyName')" />
 				</el-form-item>
 
-				<el-form-item label="数据类型" prop="type">
-					<el-select v-model="valueType.type" placeholder="请选择数据类型" @change="seletChange" :disabled="ruleForm.id !== 0 ? true : false">
-						<el-option-group v-for="group in typeData" :key="group.label" :label="group.label">
-							<el-option v-for="item in group.options" :key="item.type" :label="item.title" :value="item.type" />
-						</el-option-group>
-					</el-select>
+				<!-- 数据类型 -->
+				<el-form-item :label="$t('message.device.formI18nLabel.dataType')" prop="type">
+					<template v-if="typeData.length > 0">
+						<!-- 请选择数据类型 -->
+						<el-select
+							v-model="valueType.type"
+							:placeholder="$t('message.device.formI18nPlaceholder.selectDataType')"
+							@change="seletChange"
+							:disabled="ruleForm.id !== 0 ? true : false"
+						>
+							<el-option-group v-for="group in typeData" :key="group.label" :label="group.label">
+								<el-option v-for="item in group.options" :key="item.type" :label="item.title" :value="item.type" />
+							</el-option-group>
+						</el-select>
+					</template>
 				</el-form-item>
 
 				<!--根据数据类型输出不同表单-->
 
-				<el-form-item label="精度" prop="decimals" v-if="type == 'float' || type == 'double'">
-					<el-input v-model="valueType.decimals" placeholder="请输入精度" />
+				<!-- 精度 -->
+				<el-form-item :label="$t('message.device.formI18nLabel.precision')" prop="decimals" v-if="type == 'float' || type == 'double'">
+					<!-- 请输入精度 -->
+					<el-input v-model="valueType.decimals" :placeholder="$t('message.device.formI18nPlaceholder.inputPrecision')" />
 				</el-form-item>
 
-				<el-form-item label="单位" prop="unit" v-if="type == 'int' || type == 'long' || type == 'float' || type == 'double'">
-					<el-input v-model="valueType.unit" placeholder="请输入单位" />
+				<!-- 单位 -->
+				<el-form-item
+					:label="$t('message.device.formI18nLabel.unit')"
+					prop="unit"
+					v-if="type == 'int' || type == 'long' || type == 'float' || type == 'double'"
+				>
+					<!-- 请输入单位 -->
+					<el-input v-model="valueType.unit" :placeholder="$t('message.device.formI18nPlaceholder.inputUnit')" />
 				</el-form-item>
 
-				<el-form-item label="最大长度" prop="maxLength" v-if="type == 'string'">
-					<el-input v-model="valueType.maxLength" placeholder="请输入最大长度" />
+				<!-- 最大长度 -->
+				<el-form-item :label="$t('message.device.formI18nLabel.maxLength')" prop="maxLength" v-if="type == 'string'">
+					<!-- 请输入最大长度 -->
+					<el-input v-model="valueType.maxLength" :placeholder="$t('message.device.formI18nPlaceholder.inputMaxLength')" />
 				</el-form-item>
 
-				<el-form-item label="时间格式" prop="maxLength" v-if="type == 'date'">
-					<el-input v-model="valueType.maxLength" placeholder="请输入时间格式" />
+				<!-- 时间格式 -->
+				<el-form-item :label="$t('message.device.formI18nLabel.timeFormat')" prop="maxLength" v-if="type == 'date'">
+					<!-- 请输入时间格式 -->
+					<el-input v-model="valueType.maxLength" :placeholder="$t('message.device.formI18nPlaceholder.inputTimeFormat')" />
 				</el-form-item>
 
-				<el-form-item label="布尔值" prop="trueText" v-if="type == 'boolean'">
+				<!-- 布尔值 -->
+				<el-form-item :label="$t('message.device.formI18nLabel.booleanValue')" prop="trueText" v-if="type == 'boolean'">
 					<div class="input-box flex-row">
-						<el-input v-model="valueType.trueText" placeholder="请输入true时显示的文字" /><span style="margin: 0px 10px">~</span>
-						<el-input v-model="valueType.trueValue" placeholder="请输入布尔值" disabled />
+						<!-- 请输入true时显示的文字 -->
+						<el-input v-model="valueType.trueText" :placeholder="$t('message.device.formI18nPlaceholder.inputTrueText')" /><span
+							style="margin: 0px 10px"
+							>~</span
+						>
+						<!-- 请输入布尔值 -->
+						<el-input v-model="valueType.trueValue" :placeholder="$t('message.device.formI18nPlaceholder.inputBooleanValue')" disabled />
 					</div>
 
 					<div class="input-box flex-row">
-						<el-input v-model="valueType.falseText" placeholder="请输入false时显示的文字" /> <span style="margin: 0px 10px">~</span>
-						<el-input v-model="valueType.falseValue" placeholder="请输入布尔值" disabled />
+						<!-- 请输入false时显示的文字 -->
+						<el-input v-model="valueType.falseText" :placeholder="$t('message.device.formI18nPlaceholder.inputFalseText')" />
+						<span style="margin: 0px 10px">~</span>
+						<!-- 请输入布尔值 -->
+						<el-input v-model="valueType.falseValue" :placeholder="$t('message.device.formI18nPlaceholder.inputBooleanValue')" disabled />
 					</div>
 				</el-form-item>
 
-				<el-form-item label="枚举项" prop="maxLength" v-if="type == 'enum'">
+				<!-- 枚举项 -->
+				<el-form-item :label="$t('message.device.formI18nLabel.enumItems')" prop="maxLength" v-if="type == 'enum'">
 					<div class="input-box flex-row" v-for="(item, index) in enumdata" :key="index">
-						<el-input v-model="item.text" placeholder="请输入枚举文本" /><span style="margin: 0px 10px"><el-icon>
-								<Right />
-							</el-icon></span>
-						<el-input v-model="item.value" placeholder="请输入枚举值" />
+						<!-- 请输入枚举文本 -->
+						<el-input v-model="item.text" :placeholder="$t('message.device.formI18nPlaceholder.inputEnumText')" /><span style="margin: 0px 10px"
+							><el-icon> <Right /> </el-icon
+						></span>
+						<!-- 请输入枚举值 -->
+						<el-input v-model="item.value" :placeholder="$t('message.device.formI18nPlaceholder.inputEnumValue')" />
 						<div class="input-option">
 							<el-icon @click="addEnum" v-if="index == 0">
 								<Plus />
@@ -64,15 +111,20 @@
 					</div>
 				</el-form-item>
 
-				<el-form-item label="JSON对象" prop="maxLength" v-if="type == 'object'">
+				<el-form-item :label="$t('message.device.formI18nLabel.jsonObject')" prop="maxLength" v-if="type == 'object'">
 					<div v-for="(item, index) in jsondata" :key="index" class="jslist">
 						<div class="jsonlist">
-							<div>参数标识:{{ item.key }}</div>
-							<div>参数名称:{{ item.name }}</div>
-							<div>数据类型:{{ item.valueType.type }}</div>
+							<!-- 参数标识:{{ item.key }} -->
+							<div>{{ $t('message.device.formI18nLabel.parameterIdentifier') }}:{{ item.key }}</div>
+							<!-- 参数名称:{{ item.name }} -->
+							<div>{{ $t('message.device.formI18nLabel.parameterName') }}:{{ item.name }}</div>
+							<!-- 数据类型:{{ item.valueType.type }} -->
+							<div>{{ $t('message.device.formI18nLabel.dataType') }}:{{ item.valueType.type }}</div>
 							<div class="jsonoption">
-								<el-link type="primary" @click="editjson(index)">编辑</el-link>
-								<el-link type="primary" @click="deljson(index)">删除</el-link>
+								<!-- 编辑 -->
+								<el-link type="primary" @click="editjson(index)">{{ $t('message.tableI18nAction.edit') }}</el-link>
+								<!-- 删除 -->
+								<el-link type="primary" @click="deljson(index)">{{ $t('message.tableI18nAction.delete') }}</el-link>
 							</div>
 						</div>
 					</div>
@@ -82,54 +134,81 @@
 							<el-icon>
 								<Plus />
 							</el-icon>
-							<div>添加参数</div>
+							<!-- 添加参数 -->
+							<div>{{ $t('message.device.formI18nButton.addParameter') }}</div>
 						</div>
 					</div>
 				</el-form-item>
 
 				<div v-if="type == 'array'">
-					<el-form-item label="元素类型" prop="types">
-						<el-select v-model="elementType.type" placeholder="请选择元素类型" @change="seletChanges">
+					<!-- 元素类型 -->
+					<el-form-item :label="$t('message.device.formI18nLabel.elementType')" prop="types">
+						<!-- 请选择元素类型 -->
+						<el-select v-model="elementType.type" :placeholder="$t('message.device.formI18nPlaceholder.selectElementType')" @change="seletChanges">
 							<el-option-group v-for="group in typeData" :key="group.label" :label="group.label">
 								<el-option v-for="item in group.options" :key="item.type" :label="item.title" :value="item.type" :disabled="item.type == 'array'" />
 							</el-option-group>
 						</el-select>
 					</el-form-item>
 
-					<el-form-item label="精度" prop="decimals" v-if="types == 'float' || types == 'double'">
-						<el-input v-model="elementType.decimals" placeholder="请输入精度" />
+					<!-- 精度 -->
+					<el-form-item :label="$t('message.device.formI18nLabel.precision')" prop="decimals" v-if="types == 'float' || types == 'double'">
+						<!-- 请输入精度 -->
+						<el-input v-model="elementType.decimals" :placeholder="$t('message.device.formI18nPlaceholder.inputPrecision')" />
 					</el-form-item>
 
-					<el-form-item label="单位" prop="unit" v-if="types == 'int' || types == 'long' || types == 'float' || types == 'double'">
-						<el-input v-model="elementType.unit" placeholder="请输入单位" />
+					<!-- 单位 -->
+					<el-form-item
+						:label="$t('message.device.formI18nLabel.unit')"
+						prop="unit"
+						v-if="types == 'int' || types == 'long' || types == 'float' || types == 'double'"
+					>
+						<!-- 请输入单位 -->
+						<el-input v-model="elementType.unit" :placeholder="$t('message.device.formI18nPlaceholder.inputUnit')" />
 					</el-form-item>
 
-					<el-form-item label="最大长度" prop="maxLength" v-if="types == 'string'">
-						<el-input v-model="elementType.maxLength" placeholder="请输入最大长度" />
+					<!-- 最大长度 -->
+					<el-form-item :label="$t('message.device.formI18nLabel.maxLength')" prop="maxLength" v-if="types == 'string'">
+						<!-- 请输入最大长度 -->
+						<el-input v-model="elementType.maxLength" :placeholder="$t('message.device.formI18nPlaceholder.inputMaxLength')" />
 					</el-form-item>
 
-					<el-form-item label="时间格式" prop="maxLength" v-if="types == 'date'">
-						<el-input v-model="elementType.maxLength" placeholder="请输入时间格式" />
+					<!-- 时间格式 -->
+					<el-form-item :label="$t('message.device.formI18nLabel.timeFormat')" prop="maxLength" v-if="types == 'date'">
+						<!-- 请输入时间格式 -->
+						<el-input v-model="elementType.maxLength" :placeholder="$t('message.device.formI18nPlaceholder.inputTimeFormat')" />
 					</el-form-item>
 
-					<el-form-item label="布尔值" prop="maxLength" v-if="types == 'boolean'">
+					<!-- 布尔值 -->
+					<el-form-item :label="$t('message.device.formI18nLabel.booleanValue')" prop="maxLength" v-if="types == 'boolean'">
 						<div class="input-box flex-row">
-							<el-input v-model="elementType.trueText" placeholder="请输入true时显示的文字" /><span style="margin: 0px 10px">~</span>
-							<el-input v-model="elementType.trueValue" placeholder="请输入布尔值" disabled />
+							<!-- 请输入true时显示的文字 -->
+							<el-input v-model="elementType.trueText" :placeholder="$t('message.device.formI18nPlaceholder.inputTrueText')" /><span
+								style="margin: 0px 10px"
+								>~</span
+							>
+							<!-- 请输入布尔值 -->
+							<el-input v-model="elementType.trueValue" :placeholder="$t('message.device.formI18nPlaceholder.inputBooleanValue')" disabled />
 						</div>
 
 						<div class="input-box flex-row">
-							<el-input v-model="elementType.falseText" placeholder="请输入false时显示的文字" /> <span style="margin: 0px 10px">~</span>
-							<el-input v-model="elementType.falseValue" placeholder="请输入布尔值" disabled />
+							<!-- 请输入false时显示的文字 -->
+							<el-input v-model="elementType.falseText" :placeholder="$t('message.device.formI18nPlaceholder.inputFalseText')" />
+							<span style="margin: 0px 10px">~</span>
+							<!-- 请输入布尔值 -->
+							<el-input v-model="elementType.falseValue" :placeholder="$t('message.device.formI18nPlaceholder.inputBooleanValue')" disabled />
 						</div>
 					</el-form-item>
 
-					<el-form-item label="枚举项" prop="maxLength" v-if="types == 'enum'">
+					<!-- 枚举项 -->
+					<el-form-item :label="$t('message.device.formI18nLabel.enumItems')" prop="maxLength" v-if="types == 'enum'">
 						<div class="input-box flex-row" v-for="(item, index) in enumdata" :key="index">
-							<el-input v-model="item.text" placeholder="请输入枚举文本" /><span style="margin: 0px 10px"><el-icon>
-									<Right />
-								</el-icon></span>
-							<el-input v-model="item.value" placeholder="请输入枚举值" />
+							<!-- 请输入枚举文本 -->
+							<el-input v-model="item.text" :placeholder="$t('message.device.formI18nPlaceholder.inputEnumText')" /><span style="margin: 0px 10px"
+								><el-icon> <Right /> </el-icon
+							></span>
+							<!-- 请输入枚举值 -->
+							<el-input v-model="item.value" :placeholder="$t('message.device.formI18nPlaceholder.inputEnumValue')" />
 							<div class="input-option">
 								<el-icon @click="addEnum" v-if="index == 0">
 									<Plus />
@@ -142,16 +221,20 @@
 					</el-form-item>
 				</div>
 
-
-				<el-form-item label="JSON对象" prop="maxLength" v-if="types == 'object'">
+				<el-form-item :label="$t('message.device.formI18nLabel.jsonObject')" prop="maxLength" v-if="types == 'object'">
 					<div v-for="(item, index) in jsondata" :key="index" class="jslist">
 						<div class="jsonlist">
-							<div>参数标识:{{ item.key }}</div>
-							<div>参数名称:{{ item.name }}</div>
-							<div>数据类型:{{ item.valueType.type }}</div>
+							<!-- 参数标识:{{ item.key }} -->
+							<div>{{ $t('message.device.formI18nLabel.parameterIdentifier') }}:{{ item.key }}</div>
+							<!-- 参数名称:{{ item.name }} -->
+							<div>{{ $t('message.device.formI18nLabel.parameterName') }}:{{ item.name }}</div>
+							<!-- 数据类型:{{ item.valueType.type }} -->
+							<div>{{ $t('message.device.formI18nLabel.dataType') }}:{{ item.valueType.type }}</div>
 							<div class="jsonoption">
-								<el-link type="primary" @click="editjson(index)">编辑</el-link>
-								<el-link type="primary" @click="deljson(index)">删除</el-link>
+								<!-- 编辑 -->
+								<el-link type="primary" @click="editjson(index)">{{ $t('message.tableI18nAction.edit') }}</el-link>
+								<!-- 删除 -->
+								<el-link type="primary" @click="deljson(index)">{{ $t('message.tableI18nAction.delete') }}</el-link>
 							</div>
 						</div>
 					</div>
@@ -161,27 +244,36 @@
 							<el-icon>
 								<Plus />
 							</el-icon>
-							<div>添加参数</div>
+							<!-- 添加参数 -->
+							<div>{{ $t('message.tableI18nAction.addParameter') }}</div>
 						</div>
 					</div>
 				</el-form-item>
 
 				<!--根据数据类型输出不同表单-->
-
-				<el-form-item label="是否只读" prop="accessMode">
+				<!-- 是否只读 -->
+				<el-form-item :label="$t('message.device.formI18nLabel.isReadOnly')" prop="accessMode">
 					<el-radio-group v-model="ruleForm.accessMode">
-						<el-radio :label="1">只读</el-radio>
-						<el-radio :label="0">读写</el-radio>
+						<!-- 1:只读 -->
+						<el-radio :label="1">{{ $t('message.device.formI18nOption.readonly') }}</el-radio>
+						<!-- 0:读写 -->
+						<el-radio :label="0">{{ $t('message.device.formI18nOption.readWrite') }}</el-radio>
 					</el-radio-group>
 				</el-form-item>
-				<el-form-item label="属性定义描述" prop="desc">
-					<el-input v-model="ruleForm.desc" type="textarea" placeholder="请输入属性定义描述"></el-input>
+				<!-- 属性定义描述 -->
+				<el-form-item :label="$t('message.device.formI18nLabel.propertyDescription')" prop="desc">
+					<!-- 请输入属性定义描述 -->
+					<el-input v-model="ruleForm.desc" type="textarea" :placeholder="$t('message.device.formI18nPlaceholder.inputDescription')"></el-input>
 				</el-form-item>
 			</el-form>
 			<template #footer>
 				<span class="dialog-footer">
-					<el-button @click="onCancel">取 消</el-button>
-					<el-button type="primary" @click="onSubmit">{{ ruleForm.id !== 0 ? '修 改' : '添 加' }}</el-button>
+					<!-- 取消 -->
+					<el-button @click="onCancel">{{ $t('message.tableI18nAction.cancel') }}</el-button>
+					<!-- 编辑/添加 -->
+					<el-button type="primary" @click="onSubmit">{{
+						ruleForm.id !== 0 ? $t('message.tableI18nAction.edit') : $t('message.tableI18nAction.add')
+					}}</el-button>
 				</span>
 			</template>
 		</el-dialog>
@@ -189,32 +281,23 @@
 	</div>
 </template>
 
-<script lang="ts">
-import { reactive, toRefs, defineComponent, ref, unref } from 'vue';
-import api from '/@/api/device';
-import { Plus, Minus, Right } from '@element-plus/icons-vue';
-import EditOption from './editOption.vue';
-import { ElMessage } from 'element-plus';
-import { validateNoSpace } from '/@/utils/validator';
-
-interface RuleFormState {
-	id: number;
-	productKey: string;
-  deviceKey: string;
-	name: string;
-	dictType: string;
-	valueType: Object;
-	status: number;
-	desc: string;
-}
-interface DicState {
-	isShowDialog: boolean;
-	ruleForm: RuleFormState;
-	typeData: RuleFormState[];
-	rules: {};
-}
+<script lang="ts" setup>
+import { computed, ref, unref, toRaw } from 'vue'
+import api from '/@/api/device'
+import { Plus, Minus, Right } from '@element-plus/icons-vue'
+import EditOption from './editOption.vue'
+import { ElMessage } from 'element-plus'
+import { validateNoSpace } from '/@/utils/validator'
+import { useI18n } from 'vue-i18n'
+
+// 国际化
+const { locale, t } = useI18n()
 
-const valueType = {
+const currentLocale = computed(() => locale.value)
+
+const emit = defineEmits(['typeList'])
+
+const valueTypeModule = {
 	type: '',
 	maxLength: '',
 	trueText: '是',
@@ -223,268 +306,254 @@ const valueType = {
 	falseValue: false,
 }
 
-export default defineComponent({
-	name: 'deviceEditPro',
-	components: { Plus, Minus, Right, EditOption },
-	setup(prop, { emit }) {
-		const formRef = ref<HTMLElement | null>(null);
-		const editOptionRef = ref();
-		const state = reactive<DicState>({
-			isShowDialog: false,
-			typeData: [], //
-			type: '',
-			types: '',
-			productKey: '',
-			valueType: JSON.parse(JSON.stringify(valueType)),
-			elementType: JSON.parse(JSON.stringify(valueType)),
-			enumdata: [
-				{
-					text: '',
-					value: '',
-				},
-			],
-
-			jsondata: [],
-
-			ruleForm: {
-				id: 0,
-				productKey: '',
-        deviceKey: '',
-				name: '',
-				key: '',
-				transportProtocol: '',
-				accessMode: 1,
-				status: 1,
-				valueType: JSON.parse(JSON.stringify(valueType)),
-				desc: '',
-			},
-			rules: {
-				name: [{ required: true, message: '属性定义名称不能为空', trigger: 'blur' },
-				{ max: 32, message: '属性定义名称不能超过32个字符', trigger: 'blur' },
-				{ validator: validateNoSpace, message: '属性定义名称不能包含空格', trigger: 'blur' }
-				],
-				key: [{ required: true, message: '属性定义标识不能为空', trigger: 'blur' }],
-				accessMode: [{ required: true, message: '请选择是否只读', trigger: 'blur' }],
-				type: [{ required: true, message: '请选择数据类型', trigger: 'blur' }],
-			},
-		});
-
-		// 打开弹窗
-		const openDialog = (row: RuleFormState | null, productKey: string | null) => {
-      resetForm();
-
-			api.product.getDataType({ status: -1 }).then((res: any) => {
-				const datat = Object.values(res.dataType);
-				datat.forEach((item, index) => {
-					if (index == 0) {
-						datat[index]['label'] = '基础类型';
-						datat[index]['options'] = item;
-					} else {
-						datat[index]['label'] = '扩展类型';
-						datat[index]['options'] = item;
-					}
-				});
-				state.typeData = datat || [];
-			});
-			state.ruleForm = row;
-			if (row.valueType) {
-				state.ruleForm = row;
-
-				state.productKey = productKey;
-        state.deviceKey = row.deviceKey;
-        state.valueType = row.valueType;
-				state.ruleForm.valueType.type = row.valueType.type;
-				state.ruleForm.type = row.valueType.type;
-				state.type = row.valueType.type;
-				state.ruleForm.accessMode = row.accessMode;
-				if (row.valueType.elementType) {
-					state.elementType = row.valueType.elementType;
-					state.types = row.valueType.elementType.type;
-				}
-
-				if (row.type == 'enum') {
-					state.enumdata = row.valueType.elements;
-				}
-
-				if (row.type == 'object') {
-					state.jsondata = JSON.parse(JSON.stringify(row.valueType.properties));
-				}
-
-				if (row.type == 'array' && state.types == 'enum') {
-					state.enumdata = row.valueType.elementType.elements
-				}
-				if (row.type == 'array' && state.types == 'object') {
-					state.jsondata = JSON.parse(JSON.stringify(row.valueType.elementType.properties));
-				}
+const formRef = ref<HTMLElement | null>(null)
+const editOptionRef = ref()
+const rules = computed(() => ({
+	name: [
+		// 属性定义名称不能为空
+		{ required: true, message: t('message.device.rules.propertyName'), trigger: 'blur' },
+		// 属性定义名称不能超过32个字符
+		{ max: 32, message: t('message.device.rules.propertyNameMax32'), trigger: 'blur' },
+		// 属性定义名称不能包含空格
+		{ validator: validateNoSpace, message: t('message.device.rules.propertyNameValidator'), trigger: 'blur' },
+	],
+	// 属性定义标识不能为空
+	key: [{ required: true, message: t('message.device.rules.propertyKey'), trigger: 'blur' }],
+	// 请选择是否只读
+	accessMode: [{ required: true, message: t('message.device.rules.accessMode'), trigger: 'blur' }],
+	// 请选择数据类型
+	type: [{ required: true, message: t('message.device.rules.dataType'), trigger: 'blur' }],
+}))
+const ruleForm = ref<any>({
+	id: 0,
+	productKey: '',
+	deviceKey: '',
+	name: '',
+	key: '',
+	transportProtocol: '',
+	accessMode: 1,
+	status: 1,
+	valueType: JSON.parse(JSON.stringify(valueTypeModule)),
+	desc: '',
+})
+
+const jsondata = ref<any>([])
+const isShowDialog = ref(false)
+const elementType = ref(JSON.parse(JSON.stringify(valueTypeModule)))
+const valueType = ref(JSON.parse(JSON.stringify(valueTypeModule)))
+const typeData = ref([])
+const enumdata = ref([
+	{
+		text: '',
+		value: '',
+	},
+])
+const type = ref('')
+const types = ref('')
+const productKey = ref<any>('')
+
+// 打开弹窗
+const openDialog = (row: any, productKeys: string | null) => {
+	resetForm()
+
+	api.product.getDataType({ status: -1 }).then((res: any) => {
+		const datat: any = Object.values(res.dataType)
+		datat.forEach((item: any, index: any) => {
+			if (index == 0) {
+				datat[index]['label'] = t('message.device.baseType')
+				datat[index]['options'] = item
+			} else {
+				datat[index]['label'] = t('message.device.extensionType')
+				datat[index]['options'] = item
 			}
+		})
+		typeData.value = datat || []
+	})
+	ruleForm.value = row
+	if (row.valueType) {
+		ruleForm.value = row
+
+		productKey.value = productKeys
+		// deviceKey.value = row.deviceKey
+		valueType.value = row.valueType
+		ruleForm.value.valueType.type = row.valueType.type
+		ruleForm.value.type = row.valueType.type
+		type.value = row.valueType.type
+		ruleForm.value.accessMode = row.accessMode
+		if (row.valueType.elementType) {
+			elementType.value = row.valueType.elementType
+			types.value = row.valueType.elementType.type
+		}
 
+		if (row.type == 'enum') {
+			enumdata.value = row.valueType.elements
+		}
 
-			state.isShowDialog = true;
-		};
-		const resetForm = () => {
-			state.ruleForm = {
-				name: '',
-				key: '',
-				transportProtocol: '',
-				accessMode: 1,
-				status: 1,
-				valueType: JSON.parse(JSON.stringify(valueType)),
-				desc: '',
-
-			};
-			state.type = '';
-			state.types = '';
-			state.valueType = JSON.parse(JSON.stringify(valueType));
-			state.elementType = JSON.parse(JSON.stringify(valueType));
-			state.jsondata = [];
-			state.enumdata = [{
-				text: '',
-				value: '',
-			},];
-		};
-
-		const seletChange = (val) => {
-			state.type = val;
-			state.ruleForm.type = val;
-		};
-		const seletChanges = (val) => {
-			state.types = val;
-		};
-
-		const addEnum = () => {
-			state.enumdata.push({
-				text: '',
-				value: '',
-			});
-		};
-		const delEnum = (index) => {
-			state.enumdata.splice(index, 1);
-		};
-
-		const editjson = (index) => {
-
-			editOptionRef.value.openDialog(state.jsondata[index]);
+		if (row.type == 'object') {
+			jsondata.value = JSON.parse(JSON.stringify(row.valueType.properties))
+		}
 
+		if (row.type == 'array' && types.value == 'enum') {
+			enumdata.value = row.valueType.elementType.elements
 		}
-		const deljson = (index) => {
-			state.jsondata.splice(index, 1);
+		if (row.type == 'array' && types.value == 'object') {
+			jsondata.value = JSON.parse(JSON.stringify(row.valueType.elementType.properties))
 		}
+	}
 
-		const addJson = () => {
-			editOptionRef.value.openDialog({ productKey: '', id: 0 });
-		};
-		const getOptionData = (data) => {
-			state.jsondata.push(data);
-		};
-		// 关闭弹窗
-		const closeDialog = () => {
-			state.isShowDialog = false;
-		};
-		// 取消
-		const onCancel = () => {
-			closeDialog();
-		};
-		// 新增
-		const onSubmit = () => {
-			const formWrap = unref(formRef) as any;
-			if (!formWrap) return;
-			formWrap.validate((valid: boolean) => {
-				if (valid) {
-					if (state.ruleForm.id !== 0) {
-						//修改
-						if (state.type == 'enum') {
-							state.valueType.elements = state.enumdata;
-						}
+	isShowDialog.value = true
+}
+const resetForm = () => {
+	valueType.value = JSON.parse(JSON.stringify(valueTypeModule))
+	ruleForm.value = {
+		name: '',
+		key: '',
+		transportProtocol: '',
+		accessMode: 1,
+		status: 1,
+		valueType: JSON.parse(JSON.stringify(valueType.value)),
+		desc: '',
+	}
+	type.value = ''
+	types.value = ''
+	elementType.value = JSON.parse(JSON.stringify(valueType.value))
+	jsondata.value = []
+	enumdata.value = [
+		{
+			text: '',
+			value: '',
+		},
+	]
+}
 
-						if (state.type == 'object') {
-							state.valueType.properties = state.jsondata;
-						}
+const seletChange = (val: string) => {
+	type.value = val
+	ruleForm.value.type = val
+}
+const seletChanges = (val: string) => {
+	types.value = val
+}
 
-						if (state.type == 'array') {
-							state.valueType.elementType = state.elementType;
-							//如果是选中数组,并选择了枚举
-							if (state.types == 'enum') {
-								state.valueType.elementType = {
-									elements: state.enumdata,
-									type: 'enum'
-								}
-							}
-							//如果是选中数组,并选择了object
-							if (state.types == 'object') {
-								state.valueType.elementType = {
-									properties: state.jsondata,
-									type: 'object'
-								}
-							}
-						}
+const addEnum = () => {
+	enumdata.value.push({
+		text: '',
+		value: '',
+	})
+}
+const delEnum = (index: number) => {
+	enumdata.value.splice(index, 1)
+}
 
-						state.ruleForm.valueType = state.valueType;
-						state.ruleForm.productKey = state.productKey
-						api.model.propertyedit(state.ruleForm).then(() => {
-							ElMessage.success('属性定义类型修改成功');
-							closeDialog(); // 关闭弹窗
-							emit('typeList');
-						});
-					} else {
-						//添加
-						if (state.type == 'enum') {
-							state.valueType.elements = state.enumdata;
-						}
+const editjson = (index: number) => {
+	editOptionRef.value.openDialog(jsondata.value[index])
+}
+const deljson = (index: number) => {
+	jsondata.value.splice(index, 1)
+}
 
-						if (state.type == 'object') {
-							state.valueType.properties = state.jsondata;
-						}
+const addJson = () => {
+	editOptionRef.value.openDialog({ productKey: '', id: 0 })
+}
+const getOptionData = (data: any) => {
+	jsondata.value.push(data)
+}
+// 关闭弹窗
+const closeDialog = () => {
+	isShowDialog.value = false
+}
+// 取消
+const onCancel = () => {
+	closeDialog()
+}
+// 新增
+const onSubmit = () => {
+	const formWrap = unref(formRef) as any
+	if (!formWrap) return
+	formWrap.validate((valid: boolean) => {
+		if (valid) {
+			if (ruleForm.value.id !== 0) {
+				//修改
+				if (type.value == 'enum') {
+					valueType.value.elements = enumdata.value
+				}
+
+				if (type.value == 'object') {
+					valueType.value.properties = jsondata.value
+				}
 
-						if (state.type == 'array') {
-							state.valueType.elementType = state.elementType;
-							//如果是选中数组,并选择了枚举
-							if (state.types == 'enum') {
-								state.valueType.elementType = {
-									elements: state.enumdata,
-									type: 'enum'
-								}
-							}
-							//如果是选中数组,并选择了object
-							if (state.types == 'object') {
-								state.valueType.elementType = {
-									properties: state.jsondata,
-									type: 'object'
-								}
-							}
+				if (type.value == 'array') {
+					valueType.value.elementType = elementType.value
+					//如果是选中数组,并选择了枚举
+					if (types.value == 'enum') {
+						valueType.value.elementType = {
+							elements: enumdata.value,
+							type: 'enum',
 						}
+					}
+					//如果是选中数组,并选择了object
+					if (types.value == 'object') {
+						valueType.value.elementType = {
+							properties: jsondata.value,
+							type: 'object',
+						}
+					}
+				}
+
+				ruleForm.value.valueType = valueType.value
+				ruleForm.value.productKey = productKey.value
+				api.model.propertyedit(ruleForm.value).then(() => {
+					ElMessage.success(t('message.tableI18nConfirm.editSuccess'))
+					closeDialog() // 关闭弹窗
+					emit('typeList')
+				})
+			} else {
+				//添加
+				if (type.value == 'enum') {
+					valueType.value.elements = enumdata.value
+				}
+
+				if (type.value == 'object') {
+					valueType.value.properties = jsondata.value
+				}
 
-						state.ruleForm.valueType = state.valueType;
-						api.model.propertyadd(state.ruleForm).then(() => {
-							ElMessage.success('属性定义类型添加成功');
-							closeDialog(); // 关闭弹窗
-							emit('typeList');
-						});
+				if (type.value == 'array') {
+					valueType.value.elementType = elementType.value
+					//如果是选中数组,并选择了枚举
+					if (types.value == 'enum') {
+						valueType.value.elementType = {
+							elements: enumdata.value,
+							type: 'enum',
+						}
+					}
+					//如果是选中数组,并选择了object
+					if (types.value == 'object') {
+						valueType.value.elementType = {
+							properties: jsondata.value,
+							type: 'object',
+						}
 					}
 				}
-			});
-		};
-
-		return {
-			editOptionRef,
-			getOptionData,
-			openDialog,
-			deljson,
-			editjson,
-			addEnum,
-			delEnum,
-			addJson,
-			seletChange,
-			seletChanges,
-			closeDialog,
-			onCancel,
-			onSubmit,
-			formRef,
-			...toRefs(state),
-		};
-	},
-});
+				// 确保 ruleForm.value.valueType 不为空,否则初始化为空对象
+				if (!ruleForm.value.valueType) {
+					ruleForm.value.valueType = {}
+				}
+				Object.assign(ruleForm.value.valueType, valueType.value)
+
+				// 深拷贝确保响应式对象正确序列化
+				const submitData = JSON.parse(JSON.stringify(ruleForm.value))
+				api.model.propertyadd(submitData).then(() => {
+					ElMessage.success(t('message.tableI18nConfirm.addSuccess'))
+					closeDialog() // 关闭弹窗
+					emit('typeList')
+				})
+			}
+		}
+	})
+}
+
+defineExpose({ openDialog })
 </script>
-<style scoped>
+<style scoped lang="scss">
 .input-box {
 	display: flex;
 	flex-direction: row;
@@ -524,8 +593,6 @@ export default defineComponent({
 	justify-content: space-between;
 }
 
-.jsonoption {}
-
 .jsonoption a {
 	margin: 0px 10px;
 }

+ 68 - 36
src/views/iot/device/product/component/editEvent.vue

@@ -1,31 +1,46 @@
 <template>
 	<div class="system-edit-dic-container">
-		<el-dialog :title="(ruleForm.id !== 0 ? '修改' : '添加') + '事件定义'" v-model="isShowDialog" width="769px">
-			<el-form :model="ruleForm" ref="formRef" :rules="rules" label-width="120px">
-				<el-form-item label="事件定义标识" prop="key">
-					<el-input v-model="ruleForm.key" placeholder="请输入事件定义标识" :disabled="ruleForm.id !== 0 ? true : false" />
+		<!-- 编辑事件定义 / 新增事件定义 -->
+		<el-dialog :title="ruleForm.id !== 0 ? $t('message.device.dialogI18n.editEventDefinition') : $t('message.device.dialogI18n.addEventDefinition')" v-model="isShowDialog" width="769px">
+			<el-form :model="ruleForm" ref="formRef" :rules="rules" :label-width="currentLocale == 'en' ? '150px' : '120px'">
+				<!-- 事件定义标识 -->
+				<el-form-item :label="$t('message.device.formI18nLabel.eventKey')" prop="key">
+					<!-- 请输入事件定义标识 -->
+					<el-input v-model="ruleForm.key" :placeholder="$t('message.device.formI18nPlaceholder.eventKey')" :disabled="ruleForm.id !== 0 ? true : false" />
 				</el-form-item>
-				<el-form-item label="事件定义名称" prop="name">
-					<el-input v-model="ruleForm.name" placeholder="请输入事件定义名称" />
+				<!-- 事件定义名称 -->
+				<el-form-item :label="$t('message.device.formI18nLabel.eventName')" prop="name">
+					<!-- 请输入事件定义名称 -->
+					<el-input v-model="ruleForm.name" :placeholder="$t('message.device.formI18nPlaceholder.eventName')" />
 				</el-form-item>
 
-				<el-form-item label="事件类型" prop="level">
+				<!-- 事件类型 -->
+				<el-form-item :label="$t('message.device.formI18nLabel.eventType')" prop="level">
 					<el-radio-group v-model="ruleForm.level">
-						<el-radio :label="0">信息</el-radio>
-						<el-radio :label="1">告警</el-radio>
-						<el-radio :label="2">故障</el-radio>
+						<!-- 信息 -->
+						<el-radio :label="0">{{ $t('message.device.formI18nOption.info') }}</el-radio>
+						<!-- 告警 -->
+						<el-radio :label="1">{{ $t('message.device.formI18nOption.warning') }}</el-radio>
+						<!-- 故障 -->
+						<el-radio :label="2">{{ $t('message.device.formI18nOption.fault') }}</el-radio>
 					</el-radio-group>
 				</el-form-item>
 
-				<el-form-item label="输出参数" prop="maxLength">
+				<!-- 输出参数 -->
+				<el-form-item :label="$t('message.device.formI18nLabel.functionOutput')" prop="maxLength">
 					<div v-for="(item, index) in outputsdata" :key="index" class="jslist">
 						<div class="jsonlist">
-							<div>参数标识:{{ item.key }}</div>
-							<div>参数名称:{{ item.name }}</div>
-							<div>数据类型:{{ item.valueType.type }}</div>
+							<!-- 参数标识 -->
+							<div>{{ $t('message.device.formI18nLabel.parameterIdentifier') }}:{{ item.key }}</div>
+							<!-- 参数名称 -->
+							<div>{{ $t('message.device.formI18nLabel.parameterName') }}:{{ item.name }}</div>
+							<!-- 数据类型 -->
+							<div>{{ $t('message.device.formI18nLabel.dataType') }}:{{ item.valueType.type }}</div>
 							<div class="jsonoption">
-								<el-link type="primary" @click="editjson(index, 'fun')">编辑</el-link>
-								<el-link type="primary" @click="deljson(index, 'fun')">删除</el-link>
+								<!-- 编辑 -->
+								<el-link type="primary" @click="editjson(index, 'fun')">{{ $t('message.tableI18nAction.edit') }}</el-link>
+								<!-- 删除 -->
+								<el-link type="primary" @click="deljson(index, 'fun')">{{ $t('message.tableI18nAction.delete') }}</el-link>
 							</div>
 						</div>
 					</div>
@@ -35,19 +50,24 @@
 							<el-icon>
 								<Plus />
 							</el-icon>
-							<div>添加参数</div>
+							<!-- 添加参数 -->
+							<div>{{ $t('message.device.formI18nButton.addParameter') }}</div>
 						</div>
 					</div>
 				</el-form-item>
 
-				<el-form-item label="事件定义描述	" prop="desc">
-					<el-input v-model="ruleForm.desc" type="textarea" placeholder="请输入事件定义描述"></el-input>
+				<!-- 事件定义描述 -->
+				<el-form-item :label="$t('message.device.formI18nLabel.eventDescription')" prop="desc">
+					<!-- 请输入事件定义描述 -->
+					<el-input v-model="ruleForm.desc" type="textarea" :placeholder="$t('message.device.formI18nPlaceholder.eventDescription')"></el-input>
 				</el-form-item>
 			</el-form>
 			<template #footer>
 				<span class="dialog-footer">
-					<el-button @click="onCancel">取 消</el-button>
-					<el-button type="primary" @click="onSubmit">{{ ruleForm.id !== 0 ? '修 改' : '添 加' }}</el-button>
+					<!-- 取消 -->
+					<el-button @click="onCancel">{{ $t('message.tableI18nAction.cancel') }}</el-button>
+					<!-- 编辑 / 新增 -->
+					<el-button type="primary" @click="onSubmit">{{ ruleForm.id !== 0 ? $t('message.tableI18nAction.edit') : $t('message.tableI18nAction.add') }}</el-button>
 				</span>
 			</template>
 		</el-dialog>
@@ -56,12 +76,13 @@
 </template>
 
 <script lang="ts">
-import { reactive, toRefs, defineComponent, ref, unref } from 'vue';
+import { reactive, toRefs, defineComponent, ref, unref, computed } from 'vue';
 import api from '/@/api/device';
 import { Plus, Minus, Right } from '@element-plus/icons-vue';
 import EditOption from './editOption.vue';
 import { validateNoSpace } from '/@/utils/validator';
 import { ElMessage } from 'element-plus';
+import { useI18n } from 'vue-i18n';
 
 interface RuleFormState {
 	id?: number;
@@ -85,7 +106,7 @@ interface DicState {
 	enumdata: any[];
 	outputsdata: any[];
 	elementType: any;
-	rules: any;
+	// rules: any;
 }
 
 const form = {
@@ -105,6 +126,24 @@ export default defineComponent({
 	setup(prop, { emit }) {
 		const formRef = ref<HTMLElement | null>(null);
 		const editOptionRef = ref();
+		const { t, locale } = useI18n();
+		const currentLocale = computed(() => locale.value);	
+		const rules = computed(() => {
+			return {
+				name: [
+					// 事件定义名称不能为空
+					{ required: true, message: t('message.device.rules.eventName'), trigger: 'blur' },
+					// 事件定义名称不能超过32个字符
+					{ max: 32, message: t('message.device.rules.eventNameMax32'), trigger: 'blur' },
+					// 事件定义名称不能包含空格
+					{ validator: validateNoSpace, message: t('message.device.rules.eventNameValidator'), trigger: 'blur' }
+				],
+				// 事件定义标识不能为空
+				key: [{ required: true, message: t('message.device.rules.eventKey'), trigger: 'blur' }],
+				// 请选择数据类型
+				type: [{ required: true, message: t('message.device.rules.eventType'), trigger: 'blur' }],
+			}
+		});
 		const state = reactive<DicState>({
 			isShowDialog: false,
 			typeData: [], //
@@ -123,16 +162,7 @@ export default defineComponent({
 			],
 			jsondata: [],
 			outputsdata: [],
-			ruleForm: JSON.parse(JSON.stringify(form)),
-			rules: {
-				name: [
-					{ required: true, message: '事件定义名称不能为空', trigger: 'blur' },
-					{ max: 32, message: '事件定义名称不能超过32个字符', trigger: 'blur' },
-					{ validator: validateNoSpace, message: '事件定义名称不能包含空格', trigger: 'blur' }
-				],
-				key: [{ required: true, message: '事件定义标识不能为空', trigger: 'blur' }],
-				type: [{ required: true, message: '请选择数据类型', trigger: 'blur' }],
-			},
+			ruleForm: JSON.parse(JSON.stringify(form))
 		});
 
 		// 打开弹窗
@@ -143,9 +173,11 @@ export default defineComponent({
 				const datat: any[] = Object.values(res.dataType);
 				datat.forEach((item, index) => {
 					if (index == 0) {
+						// 基础类型
 						datat[index]['label'] = '基础类型';
 						datat[index]['options'] = item;
 					} else {
+						// 扩展类型
 						datat[index]['label'] = '扩展类型';
 						datat[index]['options'] = item;
 					}
@@ -236,6 +268,7 @@ export default defineComponent({
 					}
 
 					theApi(state.ruleForm).then(() => {
+						// 事件定义类型操作成功
 						ElMessage.success('事件定义类型操作成功');
 						closeDialog(); // 关闭弹窗
 						emit('typeList');
@@ -245,6 +278,8 @@ export default defineComponent({
 		};
 
 		return {
+			rules,
+			currentLocale,
 			editOptionRef,
 			getOptionData,
 			openDialog,
@@ -303,9 +338,6 @@ export default defineComponent({
 	flex-direction: row;
 	justify-content: space-between;
 }
-
-.jsonoption {}
-
 .jsonoption a {
 	margin: 0px 10px;
 }

+ 202 - 240
src/views/iot/device/product/component/editFun.vue

@@ -1,23 +1,34 @@
 <template>
 	<div class="system-edit-dic-container">
-		<el-dialog :title="(ruleForm.id !== 0 ? '修改' : '添加') + '功能定义'" v-model="isShowDialog" width="769px">
+		<!-- 新增功能定义/编辑功能定义 -->
+		<el-dialog :title="ruleForm.id !== 0 ? $t('message.device.dialogI18n.editFunctionDefinition') : $t('message.device.dialogI18n.addFunctionDefinition')" v-model="isShowDialog" width="769px">
 			<el-form :model="ruleForm" ref="formRef" :rules="rules" label-width="120px">
-				<el-form-item label="功能定义标识" prop="key">
-					<el-input v-model="ruleForm.key" placeholder="请输入功能定义标识" :disabled="ruleForm.id !== 0 ? true : false" />
+				<!-- 功能定义标识 -->
+				<el-form-item :label="$t('message.device.formI18nLabel.functionKey')" prop="key">
+					<!-- 请输入功能定义标识 -->
+					<el-input v-model="ruleForm.key" :placeholder="$t('message.device.formI18nPlaceholder.functionKey')" :disabled="ruleForm.id !== 0 ? true : false" />
 				</el-form-item>
-				<el-form-item label="功能定义名称" prop="name">
-					<el-input v-model="ruleForm.name" placeholder="请输入功能定义名称" />
+				<!-- 功能定义名称 -->
+				<el-form-item :label="$t('message.device.formI18nLabel.functionName')" prop="name">
+					<!-- 请输入功能定义名称 -->
+					<el-input v-model="ruleForm.name" :placeholder="$t('message.device.formI18nPlaceholder.functionName')" />
 				</el-form-item>
 
-				<el-form-item label="输入参数" prop="maxLength">
+				<!-- 输入参数 -->
+				<el-form-item :label="$t('message.device.formI18nLabel.functionInput')" prop="maxLength">
 					<div v-for="(item, index) in inputsdata" :key="index" class="jslist">
 						<div class="jsonlist">
-							<div>参数标识:{{ item.key }}</div>
-							<div>参数名称:{{ item.name }}</div>
-							<div>数据类型:{{ item.valueType.type }}</div>
+							<!-- 参数标识 -->
+							<div>{{ $t('message.device.formI18nLabel.parameterIdentifier') }}:{{ item.key }}</div>
+							<!-- 参数名称 -->
+							<div>{{ $t('message.device.formI18nLabel.parameterName') }}:{{ item.name }}</div>
+							<!-- 数据类型 -->
+							<div>{{ $t('message.device.formI18nLabel.dataType') }}:{{ item.valueType.type }}</div>
 							<div class="jsonoption">
-								<el-link type="primary" @click="editjson(index, 'fun')">编辑</el-link>
-								<el-link type="primary" @click="deljson(index, 'fun')">删除</el-link>
+								<!-- 编辑 -->
+								<el-link type="primary" @click="editjson(index, 'fun')">{{ $t('message.tableI18nAction.edit') }}</el-link>
+								<!-- 删除 -->
+								<el-link type="primary" @click="deljson(index, 'fun')">{{ $t('message.tableI18nAction.delete') }}</el-link>
 							</div>
 						</div>
 					</div>
@@ -27,20 +38,27 @@
 							<el-icon>
 								<Plus />
 							</el-icon>
-							<div>添加参数</div>
+							<!-- 添加参数 -->
+							<div>{{ $t('message.device.formI18nButton.addParameter') }}</div>
 						</div>
 					</div>
 				</el-form-item>
 
-				<el-form-item label="输出参数" prop="">
+				<!-- 输出参数 -->
+				<el-form-item :label="$t('message.device.formI18nLabel.functionOutput')" prop="">
 					<div v-for="(item, index) in outputsdata" :key="index" class="jslist">
 						<div class="jsonlist">
-							<div>参数标识:{{ item.key }}</div>
-							<div>参数名称:{{ item.name }}</div>
-							<div>数据类型:{{ item.valueType.type }}</div>
+							<!-- 参数标识 -->
+							<div>{{ $t('message.device.formI18nLabel.parameterIdentifier') }}:{{ item.key }}</div>
+							<!-- 参数名称 -->
+							<div>{{ $t('message.device.formI18nLabel.parameterName') }}:{{ item.name }}</div>
+							<!-- 数据类型 -->
+							<div>{{ $t('message.device.formI18nLabel.dataType') }}:{{ item.valueType.type }}</div>
 							<div class="jsonoption">
-								<el-link type="primary" @click="editjsonOut(index)">编辑</el-link>
-								<el-link type="primary" @click="deljsonOut(index, 'fun')">删除</el-link>
+								<!-- 编辑 -->
+								<el-link type="primary" @click="editjsonOut(index)">{{ $t('message.tableI18nAction.edit') }}</el-link>
+								<!-- 删除 -->
+								<el-link type="primary" @click="deljsonOut(index, 'fun')">{{ $t('message.tableI18nAction.delete') }}</el-link>
 							</div>
 						</div>
 					</div>
@@ -50,19 +68,23 @@
 							<el-icon>
 								<Plus />
 							</el-icon>
-							<div>添加参数</div>
+							<!-- 添加参数 -->
+							<div>{{ $t('message.device.formI18nButton.addParameter') }}</div>
 						</div>
 					</div>
 				</el-form-item>
-
-				<el-form-item label="功能定义描述	" prop="desc">
-					<el-input v-model="ruleForm.desc" type="textarea" placeholder="请输入功能定义描述"></el-input>
+				<!-- 功能定义描述 -->
+				<el-form-item :label="$t('message.device.formI18nLabel.functionDescription')" prop="desc">
+					<!-- 请输入功能定义描述 -->
+					<el-input v-model="ruleForm.desc" type="textarea" :placeholder="$t('message.device.formI18nPlaceholder.functionDescription')"></el-input>
 				</el-form-item>
 			</el-form>
 			<template #footer>
 				<span class="dialog-footer">
-					<el-button @click="onCancel">取 消</el-button>
-					<el-button type="primary" @click="onSubmit">{{ ruleForm.id !== 0 ? '修 改' : '添 加' }}</el-button>
+					<!-- 取消 -->
+					<el-button @click="onCancel">{{ $t('message.tableI18nAction.cancel') }}</el-button>
+					<!-- 编辑 / 新增 -->
+					<el-button type="primary" @click="onSubmit">{{ ruleForm.id !== 0 ? $t('message.tableI18nAction.edit') : $t('message.tableI18nAction.add') }}</el-button>
 				</span>
 			</template>
 		</el-dialog>
@@ -71,42 +93,29 @@
 	</div>
 </template>
 
-<script lang="ts">
-import { reactive, toRefs, defineComponent, ref, unref } from 'vue';
-import api from '/@/api/device';
-import { Plus, Minus, Right } from '@element-plus/icons-vue';
-import EditOption from './editOption.vue';
-import { validateNoSpace } from '/@/utils/validator';
+<script lang="ts" setup>
+import { reactive, toRefs, defineComponent, ref, unref, computed } from 'vue'
+import api from '/@/api/device'
+import { Plus, Minus, Right } from '@element-plus/icons-vue'
+import EditOption from './editOption.vue'
+import { validateNoSpace } from '/@/utils/validator'
+import { ElMessage } from 'element-plus'
+import { useI18n } from 'vue-i18n';
 
-import { ElMessage } from 'element-plus';
+const { locale, t } = useI18n();
 
 interface RuleFormState {
-	id?: number;
-	key: string;
-	productKey: string;
-	name: string;
-	type: string;
-	dictType: string;
-	valueType: Object;
-	inputs: any;
-	outputs: any;
-	status: number;
-	desc: string;
-}
-interface DicState {
-	isShowDialog: boolean;
-	productKey: string;
-	type: string;
-	types: string;
-	ruleForm: RuleFormState;
-	typeData: any[];
-	inputsdata: any[];
-	outputsdata: any[];
-	jsondata: any[];
-	elementType: any;
-	enumdata: any;
-	valueType: any;
-	rules: {};
+	id?: number
+	key: string
+	productKey: string
+	name: string
+	type: string
+	dictType: string
+	valueType: Object
+	inputs: any
+	outputs: any
+	status: number
+	desc: string
 }
 
 const form = {
@@ -125,199 +134,154 @@ const form = {
 	desc: '',
 }
 
-export default defineComponent({
-	name: 'deviceEditPro',
-	components: { Plus, Minus, Right, EditOption },
-	setup(prop, { emit }) {
-		const formRef = ref<HTMLElement | null>(null);
-		const editOptionRef = ref();
-		const editOptionOutRef = ref();
-		const state = reactive<DicState>({
-			isShowDialog: false,
-			typeData: [], //
-			type: '',
-			types: '',
-			productKey: '',
-			valueType: {
-				type: '',
-				maxLength: '',
-				trueText: '是',
-				trueValue: 'true',
-				falseText: '否',
-				falseValue: 'false',
-			},
-			elementType: {
-				type: '',
-				maxLength: '',
-			},
-			enumdata: [
-				{
-					text: '',
-					value: '',
-				},
-			],
-			jsondata: [],
-			inputsdata: [],
-			outputsdata: [],
-			ruleForm: JSON.parse(JSON.stringify(form)),
-			rules: {
-				name: [{ required: true, message: '功能定义名称不能为空', trigger: 'blur' },
-				{ max: 32, message: '功能定义名称不能超过32个字符', trigger: 'blur' },
-				{ validator: validateNoSpace, message: '功能定义名称不能包含空格', trigger: 'blur' }
-				],
-				key: [{ required: true, message: '功能定义标识不能为空', trigger: 'blur' }],
-				type: [{ required: true, message: '请选择数据类型', trigger: 'blur' }],
-			},
-		});
-
-		// 打开弹窗
-		const openDialog = (row: RuleFormState, productKey: string) => {
-			resetForm();
-			state.ruleForm = row;
-			state.productKey = productKey;
-			state.inputsdata = row.inputs || [];
-			state.outputsdata = row.outputs || [];
-			state.isShowDialog = true;
-
-		};
-		const resetForm = () => {
-			state.ruleForm = JSON.parse(JSON.stringify(form))
-			state.type = '';
-			state.types = '';
-			state.inputsdata = [];
-			state.outputsdata = [];
-			state.elementType = [];
-			state.valueType = {};
-		};
-
-		const seletChange = (val: string) => {
-			state.type = val;
-			state.ruleForm.type = val;
+const emit = defineEmits(['typeList'])
 
-		};
-		const seletChanges = (val: string) => {
-			state.types = val;
-		};
+const formRef = ref<HTMLElement | null>(null)
+const editOptionRef = ref()
+const editOptionOutRef = ref()
+const isShowDialog = ref(false)
+const type = ref('')
+const types = ref('')
+const productKey = ref('')
+// const valueType = ref<any>({
+// 	type: '',
+// 	maxLength: '',
+// 	trueText: '是',
+// 	trueValue: 'true',
+// 	falseText: '否',
+// 	falseValue: 'false',
+// })
+const elementType = ref({
+	type: '',
+	maxLength: '',
+})
+const jsondata = ref<any>([])
+const inputsdata = ref<any>([])
+const outputsdata = ref<any>([])
+const ruleForm = ref(JSON.parse(JSON.stringify(form)))
+const rules = computed(() => ({
+	name: [
+		// 功能定义名称不能为空
+		{ required: true, message: t('message.device.rules.functionName'), trigger: 'blur' },
+		// 功能定义名称不能超过32个字符
+		{ max: 32, message: t('message.device.rules.functionNameMax32'), trigger: 'blur' },
+		// 功能定义名称不能包含空格
+		{ validator: validateNoSpace, message: t('message.device.rules.functionNameValidator'), trigger: 'blur' },
+	],
+	// 功能定义标识不能为空
+	key: [{ required: true, message: t('message.device.rules.functionKey'), trigger: 'blur' }],
+	// 请选择数据类型
+	type: [{ required: true, message: t('message.device.rules.functionType'), trigger: 'blur' }],
+}))
 
-		const addEnum = () => {
-			state.enumdata.push({
-				text: '',
-				value: '',
-			});
-		};
-		const delEnum = (index: number) => {
-			state.enumdata.splice(index, 1);
-		};
+// 打开弹窗
+const openDialog = (row: RuleFormState, productKeyData: string) => {
+	resetForm()
+	ruleForm.value = row
+	productKey.value = productKeyData
+	inputsdata.value = row.inputs || []
+	outputsdata.value = row.outputs || []
+	isShowDialog.value = true
+}
+const resetForm = () => {
+	ruleForm.value = JSON.parse(JSON.stringify(form))
+	type.value = ''
+	types.value = ''
+	inputsdata.value = []
+	outputsdata.value = []
+	elementType.value = {
+		type: '',
+		maxLength: '',
+	}
+}
 
-		const editjson = (index: number, type: string) => {
-			if (type == 'fun') {
-				editOptionRef.value.openDialog(state.inputsdata[index]);
-			} else {
-				editOptionRef.value.openDialog(state.jsondata[index]);
 
-			}
-		}
+const editjson = (index: number, type: string) => {
+	if (type == 'fun') {
+		editOptionRef.value.openDialog(inputsdata.value[index])
+	} else {
+		editOptionRef.value.openDialog(jsondata.value[index])
+	}
+}
 
-		const deljson = (index: number, type: string) => {
-			if (type == 'fun') {
-				state.inputsdata.splice(index, 1);
-			} else {
-				state.jsondata.splice(index, 1);
-			}
-		};
+const deljson = (index: number, type: string) => {
+	if (type == 'fun') {
+		inputsdata.value.splice(index, 1)
+	} else {
+		jsondata.value.splice(index, 1)
+	}
+}
 
-		const deljsonOut = (index: number, type: string) => {
-			if (type == 'fun') {
-				state.outputsdata.splice(index, 1);
-			} else {
-				state.outputsdata.splice(index, 1);
-			}
-		};
+const deljsonOut = (index: number, type: string) => {
+	if (type == 'fun') {
+		outputsdata.value.splice(index, 1)
+	} else {
+		outputsdata.value.splice(index, 1)
+	}
+}
 
-		const editjsonOut = (index: number) => {
-			editOptionOutRef.value.openDialog(state.outputsdata[index]);
-		}
+const editjsonOut = (index: number) => {
+	editOptionOutRef.value.openDialog(outputsdata.value[index])
+}
 
-		const addJson = (type: string) => {
-			editOptionRef.value.openDialog({ productKey: '', id: 0, type_data: type });
-		};
+const addJson = (type: string) => {
+	editOptionRef.value.openDialog({ productKey: '', id: 0, type_data: type })
+}
 
-		const addJsonOut = (type: string) => {
-			editOptionOutRef.value.openDialog({ productKey: '', id: 0, type_data: type });
-		};
+const addJsonOut = (type: string) => {
+	editOptionOutRef.value.openDialog({ productKey: '', id: 0, type_data: type })
+}
 
-		const getOptionData = (data: any, type_data: any) => {
-			if (type_data == 'fun') {
-				state.inputsdata.push(data);
-			} else {
-				state.jsondata.push(data);
-			}
-		};
-		const getOptionDataOut = (data: any, type_data: any) => {
-			if (type_data == 'fun') {
-				state.outputsdata.push(data);
+const getOptionData = (data: any, type_data: any) => {
+	if (type_data == 'fun') {
+		inputsdata.value.push(data)
+	} else {
+		jsondata.value.push(data)
+	}
+}
+const getOptionDataOut = (data: any, type_data: any) => {
+	if (type_data == 'fun') {
+		outputsdata.value.push(data)
+	} else {
+		outputsdata.value.push(data)
+	}
+}
+// 关闭弹窗
+const closeDialog = () => {
+	isShowDialog.value = false
+}
+// 取消
+const onCancel = () => {
+	closeDialog()
+}
+// 新增
+const onSubmit = () => {
+	const formWrap = unref(formRef) as any
+	if (!formWrap) return
+	formWrap.validate((valid: boolean) => {
+		if (valid) {
+			ruleForm.value.inputs = inputsdata.value
+			ruleForm.value.outputs = outputsdata.value
+			if (ruleForm.value.id !== 0) {
+				ruleForm.value.productKey = productKey.value
+				api.model.functionedit(ruleForm.value).then(() => {
+					// 编辑成功
+					ElMessage.success(t('message.tableI18nConfirm.editSuccess'))
+					closeDialog() // 关闭弹窗
+					emit('typeList')
+				})
 			} else {
-				state.outputsdata.push(data);
+				api.model.functionadd(ruleForm.value).then(() => {
+					// 新增成功
+					ElMessage.success(t('message.tableI18nConfirm.addSuccess'))
+					closeDialog() // 关闭弹窗
+					emit('typeList')
+				})
 			}
-		};
-		// 关闭弹窗
-		const closeDialog = () => {
-			state.isShowDialog = false;
-		};
-		// 取消
-		const onCancel = () => {
-			closeDialog();
-		};
-		// 新增
-		const onSubmit = () => {
-			const formWrap = unref(formRef) as any;
-			if (!formWrap) return;
-			formWrap.validate((valid: boolean) => {
-				if (valid) {
-					state.ruleForm.inputs = state.inputsdata;
-					state.ruleForm.outputs = state.outputsdata;
-					if (state.ruleForm.id !== 0) {
-						state.ruleForm.productKey = state.productKey;
-						api.model.functionedit(state.ruleForm).then(() => {
-							ElMessage.success('功能定义类型修改成功');
-							closeDialog(); // 关闭弹窗
-							emit('typeList');
-						});
-					} else {
-						api.model.functionadd(state.ruleForm).then(() => {
-							ElMessage.success('功能定义类型添加成功');
-							closeDialog(); // 关闭弹窗
-							emit('typeList');
-						});
-					}
-				}
-			});
-		};
-
-		return {
-			editOptionRef,
-			editOptionOutRef,
-			getOptionData,
-			getOptionDataOut,
-			openDialog,
-			editjson,
-			editjsonOut,
-			deljson,
-			deljsonOut,
-			addEnum,
-			delEnum,
-			addJson,
-			addJsonOut,
-			seletChange,
-			seletChanges,
-			closeDialog,
-			onCancel,
-			onSubmit,
-			formRef,
-			...toRefs(state),
-		};
-	},
-});
+		}
+	})
+}
+defineExpose({ openDialog })
 </script>
 <style scoped>
 .input-box {
@@ -359,8 +323,6 @@ export default defineComponent({
 	justify-content: space-between;
 }
 
-.jsonoption {}
-
 .jsonoption a {
 	margin: 0px 10px;
 }

+ 54 - 31
src/views/iot/device/product/component/editOption.vue

@@ -1,16 +1,23 @@
 <template>
 	<div class="system-edit-dic-container">
-		<el-dialog :title="(typeof ruleForm.valueType !== 'undefined' ? '修改' : '添加') + '参数'" v-model="isShowDialog" width="769px">
-			<el-form :model="ruleForm" ref="formRef" :rules="rules" label-width="120px">
-				<el-form-item label="参数标识" prop="key">
-					<el-input v-model="ruleForm.key" placeholder="请输入参数标识" />
+		<!-- 编辑参数 / 新增参数 -->
+		<el-dialog :title="typeof ruleForm.valueType !== 'undefined' ? $t('message.device.dialogI18n.editOption') : $t('message.device.dialogI18n.addOption')" v-model="isShowDialog" width="769px">
+			<el-form :model="ruleForm" ref="formRef" :rules="rules" :label-width="currentLocale == 'en' ? '150px' : '120px'">
+				<!-- 参数标识 -->
+				<el-form-item :label="$t('message.device.formI18nLabel.parameterIdentifier')" prop="key">
+					<!-- 请输入参数标识 -->
+					<el-input v-model="ruleForm.key" :placeholder="$t('message.device.formI18nPlaceholder.parameterIdentifier')" />
 				</el-form-item>
-				<el-form-item label="参数名称" prop="name">
-					<el-input v-model="ruleForm.name" placeholder="请输入参数名称" />
+				<!-- 参数名称 -->
+				<el-form-item :label="$t('message.device.formI18nLabel.parameterName')" prop="name">
+					<!-- 请输入参数名称 -->
+					<el-input v-model="ruleForm.name" :placeholder="$t('message.device.formI18nPlaceholder.parameterName')" />
 				</el-form-item>
 
-				<el-form-item label="数据类型" prop="type">
-					<el-select v-model="valueType.type" placeholder="请选择数据类型" @change="seletChange">
+				<!-- 数据类型 -->
+				<el-form-item :label="$t('message.device.formI18nLabel.dataType')" prop="type">
+					<!-- 请选择数据类型 -->
+					<el-select v-model="valueType.type" :placeholder="$t('message.device.formI18nPlaceholder.dataType')" @change="seletChange">
 						<el-option-group v-for="group in typeData" :key="group.label" :label="group.label">
 							<el-option v-for="item in group.options" :key="item.type" :label="item.title" :value="item.type" />
 						</el-option-group>
@@ -19,8 +26,10 @@
 
 				<TypeItem :valueType="valueType" :typeData="typeData"></TypeItem>
 				<div v-if="type == 'array'">
-					<el-form-item label="元素类型" prop="type">
-						<el-select v-model="elementType.type" placeholder="请选择元素类型" @change="seletChanges">
+				<!-- 元素类型 -->
+					<el-form-item :label="$t('message.device.formI18nLabel.elementType')" prop="type">
+						<!-- 请选择元素类型 -->
+						<el-select v-model="elementType.type" :placeholder="$t('message.device.formI18nPlaceholder.elementType')" @change="seletChanges">
 							<el-option-group v-for="group in typeData" :key="group.label" :label="group.label">
 								<el-option v-for="item in group.options" :key="item.type" :label="item.title" :value="item.type" :disabled="['array', 'enum'].includes(item.type)" />
 							</el-option-group>
@@ -28,15 +37,18 @@
 					</el-form-item>
 					<TypeItem :valueType="elementType" :typeData="typeData"></TypeItem>
 				</div>
-
-				<el-form-item label="参数描述	" prop="desc">
-					<el-input v-model="ruleForm.desc" type="textarea" placeholder="请输入参数描述"></el-input>
+				<!-- 参数描述 -->
+				<el-form-item :label="$t('message.device.formI18nLabel.parameterDescription')" prop="desc">
+					<!-- 请输入参数描述 -->
+					<el-input v-model="ruleForm.desc" type="textarea" :placeholder="$t('message.device.formI18nPlaceholder.parameterDescription')"></el-input>
 				</el-form-item>
 			</el-form>
 			<template #footer>
 				<span class="dialog-footer">
-					<el-button @click="onCancel">取 消</el-button>
-					<el-button type="primary" @click="onSubmit">{{ typeof ruleForm.valueType !== 'undefined' ? '修 改' : '添 加' }}</el-button>
+					<!-- 取 消 -->
+					<el-button @click="onCancel">{{ $t('message.tableI18nAction.cancel') }}</el-button>
+					<!-- 编辑 / 新增 -->
+					<el-button type="primary" @click="onSubmit">{{ typeof ruleForm.valueType !== 'undefined' ? $t('message.tableI18nAction.edit') : $t('message.tableI18nAction.add') }}</el-button>
 				</span>
 			</template>
 		</el-dialog>
@@ -44,16 +56,16 @@
 </template>
 
 <script lang="ts">
-import { reactive, toRefs, defineComponent, ref, unref } from 'vue';
+import { reactive, toRefs, defineComponent, ref, unref, computed } from 'vue';
 import api from '/@/api/device';
 import TypeItem from './typeItem.vue';
 import { Plus, Minus, Right } from '@element-plus/icons-vue';
 import { ElMessage } from 'element-plus';
 import { validateNoSpace } from '/@/utils/validator';
+import { useI18n } from 'vue-i18n';
 
 interface stateType {
 	isShowDialog: boolean
-	rules: any
 	ruleForm: any
 	valueType: any
 	typeData: any
@@ -100,7 +112,22 @@ export default defineComponent({
 	components: { Plus, Minus, Right, TypeItem },
 	setup(prop, { emit }) {
 		const formRef = ref<HTMLElement | null>(null);
-
+		const { locale, t } = useI18n();
+		const currentLocale = computed(() => locale.value)
+		const rules = computed(() => ({
+			name: [
+				// 参数名称不能为空
+				{ required: true, message: t('message.device.rules.parameterName'), trigger: 'blur' },
+				// 参数名称不能超过32个字符
+				{ max: 32, message: t('message.device.rules.parameterNameMax32'), trigger: 'blur' },
+				//  参数名称不能包含空格
+				{ validator: validateNoSpace, message: t('message.device.rules.parameterNameValidator'), trigger: 'blur' }
+			],
+			// 参数标识不能为空
+			key: [{ required: true, message: t('message.device.rules.parameterKey'), trigger: 'blur' }],
+			// 请选择是否只读
+			accessMode: [{ required: true, message: t('message.device.rules.accessMode'), trigger: 'blur' }],
+		}));
 		const state = reactive<stateType>({
 			isShowDialog: false,
 			typeData: [], //
@@ -124,16 +151,7 @@ export default defineComponent({
 				status: 1,
 				valueType: {},
 				desc: '',
-			},
-			rules: {
-				name: [
-					{ required: true, message: '参数名称不能为空', trigger: 'blur' },
-					{ max: 32, message: '参数名称不能超过32个字符', trigger: 'blur' },
-					{ validator: validateNoSpace, message: '参数名称不能包含空格', trigger: 'blur' }
-				],
-				key: [{ required: true, message: '参数标识不能为空', trigger: 'blur' }],
-				accessMode: [{ required: true, message: '请选择是否只读', trigger: 'blur' }],
-			},
+			}
 		});
 
 		// 打开弹窗
@@ -144,10 +162,12 @@ export default defineComponent({
 				const datat: any = Object.values(res.dataType);
 				datat.forEach((item: any, index: number) => {
 					if (index == 0) {
-						datat[index]['label'] = '基础类型';
+						// 基础类型
+						datat[index]['label'] = t('message.device.baseType');
 						datat[index]['options'] = item;
 					} else {
-						datat[index]['label'] = '扩展类型';
+						// 基础类型
+						datat[index]['label'] = t('message.device.extensionType');
 						datat[index]['options'] = item;
 					}
 				});
@@ -235,7 +255,8 @@ export default defineComponent({
 						}
 
 						state.ruleForm.valueType = state.valueType;
-						ElMessage.success('参数类型添加成功');
+						// 新增成功
+						ElMessage.success(t('message.tableI18nConfirm.addSuccess'));
 						closeDialog(); // 关闭弹窗
 						emit('typeList', state.ruleForm, state.ruleForm.type_data);
 					}
@@ -244,6 +265,8 @@ export default defineComponent({
 		};
 
 		return {
+			currentLocale,
+			rules,
 			openDialog,
 			addEnum,
 			delEnum,

+ 108 - 67
src/views/iot/device/product/component/editPro.vue

@@ -1,19 +1,26 @@
 <template>
   <div class="system-edit-dic-container">
-    <el-dialog :title="(ruleForm.id !== 0 ? '修改' : '添加') + '产品'" v-model="isShowDialog" width="769px">
-      <el-form :model="ruleForm" ref="formRef" :rules="rules" label-width="100px">
-        <el-form-item label="产品标识" prop="key">
-          <el-input v-model="ruleForm.key" placeholder="请输入产品标识" :disabled="ruleForm.id" />
+    <!-- 编辑产品/新增产品 -->
+    <el-dialog :title="ruleForm.id !== 0 ? $t('message.device.dialogI18n.editPro') : $t('message.device.dialogI18n.addPro')" v-model="isShowDialog" width="769px">
+      <el-form :model="ruleForm" ref="formRef" :rules="rules"  :label-width="currentLocale == 'en' ? '150px' : '120px'">
+        <!-- 产品标识 -->
+        <el-form-item :label="$t('message.device.formI18nLabel.productKey')" prop="key">
+          <!-- 请输入产品标识 -->
+          <el-input v-model="ruleForm.key" :placeholder="$t('message.device.formI18nPlaceholder.productKey')" :disabled="ruleForm.id" />
         </el-form-item>
-        <el-form-item label="产品名称" prop="name">
-          <el-input v-model="ruleForm.name" placeholder="请输入产品名称" />
+        <!-- 产品名称 -->
+        <el-form-item :label="$t('message.device.formI18nLabel.productName')" prop="name">
+          <!-- 请输入产品名称 -->
+          <el-input v-model="ruleForm.name" :placeholder="$t('message.device.formI18nPlaceholder.productName')" />
         </el-form-item>
-        <el-form-item label="产品图片" prop="imageUrl">
+        <!-- 产品图片 -->
+        <el-form-item :label="$t('message.device.formI18nLabel.productImage')" prop="imageUrl">
           <uploadVue :img="imageUrl" @set-img="handleAvatarSuccess"></uploadVue>
         </el-form-item>
-
-        <el-form-item label="产品分类" prop="categoryId">
-          <el-cascader :options="cateData" :props="{ checkStrictly: true, emitPath: false, value: 'id', label: 'name' }" placeholder="请选择分类" class="w" clearable v-model="ruleForm.categoryId">
+        <!-- 产品分类 -->
+        <el-form-item :label="$t('message.device.formI18nLabel.productCategory')" prop="categoryId">
+          <!-- 请选择分类 -->
+          <el-cascader :options="cateData" :props="{ checkStrictly: true, emitPath: false, value: 'id', label: 'name' }" :placeholder="$t('message.device.formI18nPlaceholder.productCategory')" clearable v-model="ruleForm.categoryId">
             <template #default="{ node, data }">
               <span>{{ data.name }}</span>
               <span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
@@ -21,80 +28,100 @@
           </el-cascader>
 
           <!-- 添加产品分类 -->
-          <el-button type="success" @click="onOpenAddCategory()" style="margin-left: 5px">添加产品分类</el-button>
+          <el-button type="success" @click="onOpenAddCategory()" style="margin-left: 5px">{{ $t('message.device.tableI18nAction.addProductCategory') }}</el-button>
         </el-form-item>
-
-        <el-form-item label="消息协议" prop="messageProtocol">
-          <el-select v-model="ruleForm.messageProtocol" placeholder="请选择消息协议">
+        <!-- 消息协议 -->
+        <el-form-item :label="$t('message.device.formI18nLabel.messageProtocol')" prop="messageProtocol">
+          <!-- 请选择消息协议 -->
+          <el-select v-model="ruleForm.messageProtocol" :placeholder="$t('message.device.formI18nPlaceholder.messageProtocol')">
             <el-option v-for="dict in messageData" :key="dict.types" :label="dict.title" :value="dict.name"> </el-option>
             <!-- 增加系统默认的mqtt选项 -->
             <el-option label="Sagoo Mqtt" value="SagooMqtt"> </el-option>
           </el-select>
         </el-form-item>
-
-        <el-form-item label="接入方式" prop="transportProtocol">
-          <el-select v-model="ruleForm.transportProtocol" placeholder="请选择接入方式" @change="transportProtocolChange">
+        <!-- 接入方式 -->
+        <el-form-item :label="$t('message.device.formI18nLabel.transportProtocol')" prop="transportProtocol">
+          <!-- 请选择接入方式 -->
+          <el-select v-model="ruleForm.transportProtocol" :placeholder="$t('message.device.formI18nPlaceholder.transportProtocol')" @change="transportProtocolChange">
             <el-option v-for="dict in network_server_type" :key="dict.value" :label="dict.label" :value="dict.value"> </el-option>
           </el-select>
         </el-form-item>
-        <!-- 1,mqtt协议设备的认证
-	在添加设备的页面,增加MQTT服务协议设备的认证方式的处理。
-	当传输协议选择为:MQTT服务的时候,下面出现 认证方式的下拉列,分别为Basic、AccessToken接入两个下拉选项。
-	Basic方式:
-	选择这个方式的时候:页面出现,用户名及密码输入框。
-	AccessToken接入方式:
-	选择这个方式的时候:页面出现 Aceess Token的输入框
-	2,TCP及其它协议认证
-	这些配合【网络组件】功能中涉及到的设备通讯协议认证的处理。如果涉及到证书的,需要调用【证书管理】功能维护的证书列表。 -->
-        <!-- 设备认证、网络组件服务增加认证方式authType(1=Basic,2=AccessToken,3=证书)
-				涉及接口:产品添加、编辑、扩展信息更新;设备添加、编辑;网络组件服务添加、编辑 -->
+        <!-- 
+          1,mqtt协议设备的认证
+            在添加设备的页面,增加MQTT服务协议设备的认证方式的处理。
+            当传输协议选择为:MQTT服务的时候,下面出现 认证方式的下拉列,分别为Basic、AccessToken接入两个下拉选项。
+            Basic方式:
+            选择这个方式的时候:页面出现,用户名及密码输入框。
+            AccessToken接入方式:
+            选择这个方式的时候:页面出现 Aceess Token的输入框
+          2,TCP及其它协议认证
+          这些配合【网络组件】功能中涉及到的设备通讯协议认证的处理。如果涉及到证书的,需要调用【证书管理】功能维护的证书列表。 
+        -->
+        <!-- 
+          设备认证、网络组件服务增加认证方式authType(1=Basic,2=AccessToken,3=证书)
+          涉及接口:产品添加、编辑、扩展信息更新;设备添加、编辑;网络组件服务添加、编辑 
+        -->
         <!-- 设备认证 -->
         <template v-if="ruleForm.authType === 1 || ruleForm.authType === 2">
-          <el-form-item label="认证方式" prop="">
+          <!-- 认证方式 -->
+          <el-form-item :label="$t('message.device.formI18nLabel.authType')" prop="">
             <el-radio-group v-model="ruleForm.authType">
               <el-radio :label="1">Basic</el-radio>
               <el-radio :label="2">AccessToken</el-radio>
             </el-radio-group>
           </el-form-item>
           <template v-if="ruleForm.authType === 1">
-            <el-form-item label="用户名" prop="authUser">
-              <el-input v-model="ruleForm.authUser" placeholder="请输入用户名" />
+            <!-- 用户名 -->
+            <el-form-item :label="$t('message.device.formI18nLabel.authUser')" prop="authUser">
+              <el-input v-model="ruleForm.authUser" :placeholder="$t('message.device.formI18nPlaceholder.authUser')" />
             </el-form-item>
-            <el-form-item label="密码" prop="authPasswd">
-              <el-input type="password" v-model="ruleForm.authPasswd" placeholder="请输入密码" />
+            <!-- 密码 -->
+            <el-form-item :label="$t('message.device.formI18nLabel.authPasswd')" prop="authPasswd">
+              <el-input type="password" v-model="ruleForm.authPasswd" :placeholder="$t('message.device.formI18nPlaceholder.authPasswd')" />
             </el-form-item>
           </template>
           <template v-else>
-            <el-form-item label="Aceess Token" prop="accessToken">
-              <el-input v-model="ruleForm.accessToken" placeholder="请输入Aceess Token" />
+            <!-- Aceess Token -->
+            <el-form-item :label="$t('message.device.formI18nLabel.accessToken')" prop="accessToken">
+              <!-- 请输入Aceess Token -->
+              <el-input v-model="ruleForm.accessToken" :placeholder="$t('message.device.formI18nPlaceholder.accessToken')" />
             </el-form-item>
           </template>
         </template>
         <template v-else-if="ruleForm.authType === 3">
-          <el-form-item label="认证证书" prop="certificateId">
-            <el-select v-model="ruleForm.certificateId" placeholder="请选择证书">
+          <!-- 认证证书 -->
+          <el-form-item :label="$t('message.device.formI18nLabel.certificateId')" prop="certificateId">
+            <!-- 请选择证书 -->
+            <el-select v-model="ruleForm.certificateId" :placeholder="$t('message.device.formI18nPlaceholder.certificateId')">
               <el-option v-for="cert in certList" :key="cert.id" :label="cert.name" :value="cert.id"> </el-option>
             </el-select>
           </el-form-item>
         </template>
-
-        <el-form-item label="设备类型" prop="deviceType">
+        <!-- 设备类型 -->
+        <el-form-item :label="$t('message.device.formI18nLabel.deviceType')" prop="deviceType">
           <el-radio-group v-model="ruleForm.deviceType">
-            <el-radio label="设备">设备</el-radio>
-            <el-radio label="网关">网关</el-radio>
+            <!-- 设备 -->
+            <el-radio label="设备">{{ $t('message.device.formI18nOption.device') }}</el-radio>
+            <!-- 网关 -->
+            <el-radio label="网关">{{ $t('message.device.formI18nOption.gateway') }}</el-radio>
             <!-- <el-radio label="设备">直连设备</el-radio>
             <el-radio label="网关">网关设备</el-radio> -->
-            <el-radio label="子设备">子设备</el-radio>
+            <!-- 子设备 -->
+            <el-radio label="子设备">{{ $t('message.device.formI18nOption.subDevice') }}</el-radio>
           </el-radio-group>
         </el-form-item>
-        <el-form-item label="产品描述	" prop="desc">
-          <el-input v-model="ruleForm.desc" type="textarea" placeholder="请输入产品描述"></el-input>
+        <!-- 产品描述 -->
+        <el-form-item :label="$t('message.device.formI18nLabel.desc')" prop="desc">
+          <!-- 请输入产品描述 -->
+          <el-input v-model="ruleForm.desc" type="textarea" :placeholder="$t('message.device.formI18nPlaceholder.desc')"></el-input>
         </el-form-item>
       </el-form>
       <template #footer>
         <span class="dialog-footer">
-          <el-button @click="onCancel">取 消</el-button>
-          <el-button type="primary" @click="onSubmit" :loading="submitLoading">{{ ruleForm.id !== 0 ? "修 改" : "添 加" }}</el-button>
+          <!-- 取 消 -->
+          <el-button @click="onCancel">{{ $t('message.tableI18nAction.cancel') }}</el-button>
+          <!-- 编辑 / 添加 -->
+          <el-button type="primary" @click="onSubmit" :loading="submitLoading">{{ ruleForm.id ? $t('message.tableI18nAction.edit') : $t('message.tableI18nAction.add') }}</el-button>
         </span>
       </template>
     </el-dialog>
@@ -103,7 +130,7 @@
 </template>
 
 <script lang="ts">
-import { reactive, toRefs, defineComponent, ref, unref, getCurrentInstance } from "vue";
+import { reactive, toRefs, defineComponent, ref, unref, getCurrentInstance, computed } from "vue";
 import api from "/@/api/device";
 import certApi from "/@/api/certificateManagement";
 import uploadVue from "/@/components/upload/index.vue";
@@ -111,7 +138,9 @@ import { validateNoSpace } from "/@/utils/validator";
 
 import { ElMessage, UploadProps } from "element-plus";
 import getOrigin from "/@/utils/origin";
-import EditCategory from "/@/views/iot/device/category/component/edit.vue";
+import EditCategory from "/@/views/iot/device/category/component/addOrEdit.vue";
+import { useI18n } from 'vue-i18n';
+
 
 interface RuleFormState {
   id: number;
@@ -138,7 +167,6 @@ interface DicState {
   messageData: any[];
   network_protocols: any[];
   tranData: any[];
-  rules: {};
   imageUrl: string;
   singleImg: string;
 }
@@ -174,6 +202,32 @@ export default defineComponent({
     const certList = ref<any[]>([]);
     const submitLoading = ref(false);
     const editCategoryRef = ref();
+    const { locale, t } = useI18n();
+    const currentLocale = computed(() => locale.value);	
+    const rules = computed(() => ({
+        name: [
+          // 产品名称不能为空
+          { required: true, message: t("message.device.rules.productName"), trigger: "change" },
+          // 产品名称不能超过32个字符
+          { max: 32, message: t("message.device.rules.productNameMax32"), trigger: "change" },
+          // 产品名称不能包含空格
+          { validator: validateNoSpace, message: t("message.device.rules.productNameValidator"), trigger: "change" },
+        ],
+        key: [
+          // 产品标识不能为空
+          { required: true, message: t("message.device.rules.productKey"), trigger: "change" },
+          // 产品标识不能包含空格
+          { validator: validateNoSpace, message: t("message.device.rules.productKeyValidator"), trigger: "change" },
+        ],
+        // 消息协议不能为空
+        messageProtocol: [{ required: true, message: t("message.device.rules.messageProtocol"), trigger: "change" }],
+        // 接入方式不能为空
+        transportProtocol: [{ required: true, message: t("message.device.rules.transportProtocol"), trigger: "change" }],
+        // 产品分类不能为空
+        categoryId: [{ required: true, message: t("message.device.rules.categoryId"), trigger: "change" }],
+        // 设备类型不能为空
+        deviceType: [{ required: true, message: t("message.device.rules.deviceType"), trigger: "change" }],
+      }))
 
     const state = reactive<DicState>({
       isShowDialog: false,
@@ -186,22 +240,7 @@ export default defineComponent({
       singleImg: baseURL + "/product/icon/upload",
       ruleForm: {
         ...form,
-      },
-      rules: {
-        name: [
-          { required: true, message: "产品名称不能为空", trigger: "change" },
-          { max: 32, message: "产品名称不能超过32个字符", trigger: "change" },
-          { validator: validateNoSpace, message: "产品名称不能包含空格", trigger: "change" },
-        ],
-        key: [
-          { required: true, message: "产品标识不能为空", trigger: "change" },
-          { validator: validateNoSpace, message: "产品标识不能包含空格", trigger: "change" },
-        ],
-        messageProtocol: [{ required: true, message: "消息协议不能为空", trigger: "change" }],
-        transportProtocol: [{ required: true, message: "接入方式不能为空", trigger: "change" }],
-        categoryId: [{ required: true, message: "产品分类不能为空", trigger: "change" }],
-        deviceType: [{ required: true, message: "设备类型不能为空", trigger: "change" }],
-      },
+      }
     });
 
     const handleAvatarSuccess: UploadProps["onSuccess"] = (response) => {
@@ -282,7 +321,7 @@ export default defineComponent({
             api.product
               .edit(state.ruleForm)
               .then(() => {
-                ElMessage.success("产品修改成功");
+                ElMessage.success(t("message.tableI18nConfirm.editSuccess"));
                 closeDialog(); // 关闭弹窗
                 emit("typeList");
               })
@@ -292,7 +331,7 @@ export default defineComponent({
             api.product
               .add(state.ruleForm)
               .then(() => {
-                ElMessage.success("产品添加成功");
+                ElMessage.success(t("message.tableI18nConfirm.addSuccess"));
                 closeDialog(); // 关闭弹窗
                 emit("typeList");
               })
@@ -313,6 +352,8 @@ export default defineComponent({
     };
 
     return {
+      rules,
+      currentLocale,
       transportProtocolChange,
       submitLoading,
       certList,

+ 145 - 74
src/views/iot/device/product/component/editTab.vue

@@ -1,16 +1,23 @@
 <template>
 	<div class="system-edit-dic-container">
-		<el-dialog :title="(ruleForm.id !== 0 ? '修改' : '添加') + '标签定义'" v-model="isShowDialog" width="769px">
+		<!-- 编辑标签定义/新增标签定义 -->
+		<el-dialog :title="ruleForm.id !== 0 ? $t('message.device.dialogI18n.editTagDefinition') : $t('message.device.dialogI18n.addTagDefinition')" v-model="isShowDialog" width="769px">
 			<el-form :model="ruleForm" ref="formRef" :rules="rules" label-width="120px">
-				<el-form-item label="标签定义标识" prop="key">
-					<el-input v-model="ruleForm.key" placeholder="请输入标签定义标识" :disabled="ruleForm.id !== 0 ? true : false" />
+				<!-- 标签定义标识 -->
+				<el-form-item :label="$t('message.device.formI18nLabel.tagKey')" prop="key">
+					<!-- 请输入标签定义标识 -->
+					<el-input v-model="ruleForm.key" :placeholder="$t('message.device.formI18nPlaceholder.tagKey')" :disabled="ruleForm.id !== 0 ? true : false" />
 				</el-form-item>
-				<el-form-item label="标签定义名称" prop="name">
-					<el-input v-model="ruleForm.name" placeholder="请输入标签定义名称" />
+				<!-- 标签定义名称 -->
+				<el-form-item :label="$t('message.device.formI18nLabel.tagName')" prop="name">
+					<!-- 请输入标签定义名称 -->
+					<el-input v-model="ruleForm.name" :placeholder="$t('message.device.formI18nPlaceholder.tagName')" />
 				</el-form-item>
 
-				<el-form-item label="数据类型" prop="type">
-					<el-select v-model="valueType.type" placeholder="请选择数据类型" @change="seletChange">
+				<!-- 数据类型 -->
+				<el-form-item :label="$t('message.device.formI18nLabel.dataType')" prop="type">
+					<!-- 请选择数据类型 -->
+					<el-select v-model="valueType.type" :placeholder="$t('message.device.formI18nPlaceholder.selectDataType')" @change="seletChange">
 						<el-option-group v-for="group in typeData" :key="group.label" :label="group.label">
 							<el-option v-for="item in group.options" :key="item.type" :label="item.title" :value="item.type" />
 						</el-option-group>
@@ -19,40 +26,56 @@
 
 				<!--根据数据类型输出不同表单-->
 
-				<el-form-item label="精度" prop="decimals" v-if="type == 'float' || type == 'double'">
-					<el-input v-model="valueType.decimals" placeholder="请输入精度" />
+				<!-- 精度 -->
+				<el-form-item :label="$t('message.device.formI18nLabel.precision')" prop="decimals" v-if="type == 'float' || type == 'double'">
+					<!-- 请输入精度 -->
+					<el-input v-model="valueType.decimals" :placeholder="$t('message.device.formI18nPlaceholder.inputPrecision')" />
 				</el-form-item>
 
-				<el-form-item label="单位" prop="unit" v-if="type == 'int' || type == 'long' || type == 'float' || type == 'double'">
-					<el-input v-model="valueType.unit" placeholder="请输入单位" />
+				<!-- 单位 -->
+				<el-form-item :label="$t('message.device.formI18nLabel.unit')" prop="unit" v-if="type == 'int' || type == 'long' || type == 'float' || type == 'double'">
+					<!-- 请输入单位 -->
+					<el-input v-model="valueType.unit" :placeholder="$t('message.device.formI18nPlaceholder.inputUnit')" />
 				</el-form-item>
 
-				<el-form-item label="最大长度" prop="maxLength" v-if="type == 'string'">
-					<el-input v-model="valueType.maxLength" placeholder="请输入最大长度" />
+					<!-- 最大长度 -->
+				<el-form-item :label="$t('message.device.formI18nLabel.maxLength')" prop="maxLength" v-if="type == 'string'">
+					<!-- 请输入最大长度 -->
+					<el-input v-model="valueType.maxLength" :placeholder="$t('message.device.formI18nPlaceholder.inputMaxLength')" />
 				</el-form-item>
 
-				<el-form-item label="时间格式" prop="maxLength" v-if="type == 'date'">
-					<el-input v-model="valueType.maxLength" placeholder="请输入时间格式" />
+				<!-- 时间格式 -->
+				<el-form-item :label="$t('message.device.formI18nLabel.timeFormat')" prop="maxLength" v-if="type == 'date'">
+					<!-- 请输入时间格式 -->
+					<el-input v-model="valueType.maxLength" :placeholder="$t('message.device.formI18nPlaceholder.inputTimeFormat')" />
 				</el-form-item>
 
-				<el-form-item label="布尔值11" prop="trueText" v-if="type == 'boolean'">
+				<!-- 布尔值 -->
+				<el-form-item :label="$t('message.device.formI18nLabel.booleanValue')" prop="trueText" v-if="type == 'boolean'">
 					<div class="input-box flex-row">
-						<el-input v-model="valueType.trueText" placeholder="请输入true时显示的文字" /><span style="margin: 0px 10px">~</span>
-						<el-input v-model="valueType.trueValue" placeholder="请输入布尔值" disabled />
+						<!-- 请输入true时显示的文字 -->
+						<el-input v-model="valueType.trueText" :placeholder="$t('message.device.formI18nPlaceholder.inputTrueText')" /><span style="margin: 0px 10px">~</span>
+						<!-- 请输入布尔值 -->
+						<el-input v-model="valueType.trueValue" :placeholder="$t('message.device.formI18nPlaceholder.inputBooleanValue')" disabled />
 					</div>
 
 					<div class="input-box flex-row">
-						<el-input v-model="valueType.falseText" placeholder="请输入false时显示的文字" /> <span style="margin: 0px 10px">~</span>
-						<el-input v-model="valueType.falseValue" placeholder="请输入布尔值" disabled />
+						<!-- 请输入false时显示的文字 -->
+						<el-input v-model="valueType.falseText" :placeholder="$t('message.device.formI18nPlaceholder.inputFalseText')" /> <span style="margin: 0px 10px">~</span>
+						<!-- 请输入布尔值 -->
+						<el-input v-model="valueType.falseValue" :placeholder="$t('message.device.formI18nPlaceholder.inputBooleanValue')" disabled />
 					</div>
 				</el-form-item>
 
-				<el-form-item label="枚举项" prop="maxLength" v-if="type == 'enum'">
+				<!-- 枚举项 -->	
+				<el-form-item :label="$t('message.device.formI18nLabel.enumValue')" prop="maxLength" v-if="type == 'enum'">
 					<div class="input-box flex-row" v-for="(item, index) in enumdata" :key="index">
-						<el-input v-model="item.text" placeholder="请输入枚举文本" /><span style="margin: 0px 10px"><el-icon>
+						<!-- 请输入枚举文本 -->
+						<el-input v-model="item.text" :placeholder="$t('message.device.formI18nPlaceholder.inputEnumText')" /><span style="margin: 0px 10px"><el-icon>
 								<Right />
 							</el-icon></span>
-						<el-input v-model="item.value" placeholder="请输入枚举值" />
+						<!-- 请输入枚举值 -->
+						<el-input v-model="item.value" :placeholder="$t('message.device.formI18nPlaceholder.inputEnumValue')" />
 						<div class="input-option">
 							<el-icon @click="addEnum" v-if="index == 0">
 								<Plus />
@@ -64,14 +87,17 @@
 					</div>
 				</el-form-item>
 
-				<el-form-item label="JSON对象" prop="maxLength" v-if="type == 'object'">
+				<!-- JSON对象 -->
+				<el-form-item :label="$t('message.device.formI18nLabel.jsonObject')" prop="maxLength" v-if="type == 'object'">
 					<div v-for="(item, index) in jsondata" :key="index" class="jslist">
 						<div class="jsonlist">
-							<div>参数名称:</div>
+							<!-- 参数名称 -->
+							<div>{{$t('message.device.formI18nLabel.jsonObject')}}:</div>
 							<div style="width: 60%">{{ item.name }}</div>
 							<div class="jsonoption">
 								<!-- <el-link type="primary">编辑</el-link> -->
-								<el-link type="primary" @click="deljson(index)">删除</el-link>
+								<!-- 删除 -->
+								<el-link type="primary" @click="deljson(index)">{{ $t('message.tableI18nAction.delete') }}</el-link>
 							</div>
 						</div>
 					</div>
@@ -81,54 +107,73 @@
 							<el-icon>
 								<Plus />
 							</el-icon>
-							<div>添加参数</div>
+							<!-- 添加参数 -->
+							<div>{{ $t('message.device.formI18nButton.addParameter') }}</div>
 						</div>
 					</div>
 				</el-form-item>
 
 				<div v-if="type == 'array'">
-					<el-form-item label="元素类型" prop="types">
-						<el-select v-model="elementType.type" placeholder="请选择元素类型" @change="seletChanges">
+					<!-- 元素类型 -->
+					<el-form-item :label="$t('message.device.formI18nLabel.elementType')" prop="types">
+						<!-- 请选择元素类型 -->
+						<el-select v-model="elementType.type" :placeholder="$t('message.device.formI18nPlaceholder.selectElementType')" @change="seletChanges">
 							<el-option-group v-for="group in typeData" :key="group.label" :label="group.label">
 								<el-option v-for="item in group.options" :key="item.type" :label="item.title" :value="item.type" :disabled="item.type == 'array'" />
 							</el-option-group>
 						</el-select>
 					</el-form-item>
 
-					<el-form-item label="精度" prop="decimals" v-if="types == 'float' || types == 'double'">
-						<el-input v-model="elementType.decimals" placeholder="请输入精度" />
+					<!-- 精度 -->
+					<el-form-item :label="$t('message.device.formI18nLabel.precision')" prop="decimals" v-if="types == 'float' || types == 'double'">
+						<!-- 请输入精度 -->
+						<el-input v-model="elementType.decimals" :placeholder="$t('message.device.formI18nPlaceholder.inputPrecision')" />
 					</el-form-item>
 
-					<el-form-item label="单位" prop="unit" v-if="types == 'int' || types == 'long' || types == 'float' || types == 'double'">
-						<el-input v-model="elementType.unit" placeholder="请输入单位" />
+					<!-- 单位 -->
+					<el-form-item :label="$t('message.device.formI18nLabel.unit')" prop="unit" v-if="types == 'int' || types == 'long' || types == 'float' || types == 'double'">
+						<!-- 请输入单位 -->
+						<el-input v-model="elementType.unit" :placeholder="$t('message.device.formI18nPlaceholder.inputUnit')" />
 					</el-form-item>
 
-					<el-form-item label="最大长度" prop="maxLength" v-if="types == 'string'">
-						<el-input v-model="elementType.maxLength" placeholder="请输入最大长度" />
+					<!-- 最大长度 -->
+					<el-form-item :label="$t('message.device.formI18nLabel.maxLength')" prop="maxLength" v-if="types == 'string'">
+						<!-- 请输入最大长度 -->
+						<el-input v-model="elementType.maxLength" :placeholder="$t('message.device.formI18nPlaceholder.inputMaxLength')" />
 					</el-form-item>
 
-					<el-form-item label="时间格式" prop="maxLength" v-if="types == 'date'">
-						<el-input v-model="elementType.maxLength" placeholder="请输入时间格式" />
+					<!-- 时间格式 -->
+					<el-form-item :label="$t('message.device.formI18nLabel.timeFormat')" prop="maxLength" v-if="types == 'date'">
+						<!-- 请输入时间格式 -->
+						<el-input v-model="elementType.maxLength" :placeholder="$t('message.device.formI18nPlaceholder.inputTimeFormat')" />
 					</el-form-item>
 
-					<el-form-item label="布尔值" prop="maxLength" v-if="types == 'boolean'">
+					<!-- 布尔值 -->
+					<el-form-item :label="$t('message.device.formI18nLabel.booleanValue')" prop="maxLength" v-if="types == 'boolean'">
 						<div class="input-box flex-row">
-							<el-input v-model="elementType.trueText" placeholder="请输入true时显示的文字" /><span style="margin: 0px 10px">~</span>
-							<el-input v-model="elementType.trueValue" placeholder="请输入布尔值" disabled />
+							<!-- 请输入true时显示的文字 -->
+							<el-input v-model="elementType.trueText" :placeholder="$t('message.device.formI18nPlaceholder.inputTrueText')" /><span style="margin: 0px 10px">~</span>
+							<!-- 请输入布尔值 -->
+							<el-input v-model="elementType.trueValue" :placeholder="$t('message.device.formI18nPlaceholder.inputBooleanValue')" disabled />
 						</div>
 
 						<div class="input-box flex-row">
-							<el-input v-model="elementType.falseText" placeholder="请输入false时显示的文字" /> <span style="margin: 0px 10px">~</span>
-							<el-input v-model="elementType.falseValue" placeholder="请输入布尔值" disabled />
+							<!-- 请输入false时显示的文字 -->
+							<el-input v-model="elementType.falseText" :placeholder="$t('message.device.formI18nPlaceholder.inputFalseText')" /> <span style="margin: 0px 10px">~</span>
+							<!-- 请输入布尔值 -->
+							<el-input v-model="elementType.falseValue" :placeholder="$t('message.device.formI18nPlaceholder.inputBooleanValue')" disabled />
 						</div>
 					</el-form-item>
 
-					<el-form-item label="枚举项" prop="maxLength" v-if="types == 'enum'">
+					<!-- 枚举项 -->
+					<el-form-item :label="$t('message.device.formI18nLabel.enumValue')" prop="maxLength" v-if="types == 'enum'">
 						<div class="input-box flex-row" v-for="(item, index) in enumdata" :key="index">
-							<el-input v-model="item.text" placeholder="请输入枚举文本" /><span style="margin: 0px 10px"><el-icon>
+							<!-- 请输入枚举文本 -->
+							<el-input v-model="item.text" :placeholder="$t('message.device.formI18nPlaceholder.inputEnumText')" /><span style="margin: 0px 10px"><el-icon>
 									<Right />
 								</el-icon></span>
-							<el-input v-model="item.value" placeholder="请输入枚举值" />
+								<!-- 请输入枚举文本 -->
+							<el-input v-model="item.value" :placeholder="$t('message.device.formI18nPlaceholder.inputEnumValue')" />
 							<div class="input-option">
 								<el-icon @click="addEnum" v-if="index == 0">
 									<Plus />
@@ -141,14 +186,18 @@
 					</el-form-item>
 				</div>
 
-				<el-form-item label="JSON对象" prop="maxLength" v-if="types == 'object'">
+				<!-- JSON对象 -->
+				<el-form-item :label="$t('message.device.formI18nLabel.jsonObject')" prop="maxLength" v-if="types == 'object'">
 					<div v-for="(item, index) in jsondata" :key="index" class="jslist">
 						<div class="jsonlist">
-							<div>参数名称:</div>
+							<!-- 参数名称 -->
+							<div>{{$t('message.device.formI18nLabel.parameterName')}}:</div>
 							<div style="width: 60%">{{ item.name }}</div>
 							<div class="jsonoption">
-								<el-link type="primary">编辑</el-link>
-								<el-link type="primary">删除</el-link>
+								<!-- 编辑 -->
+								<el-link type="primary">{{ $t('message.tableI18nAction.edit') }}</el-link>
+								<!-- 删除 -->
+								<el-link type="primary">{{ $t('message.tableI18nAction.delete') }}</el-link>
 							</div>
 						</div>
 					</div>
@@ -158,27 +207,36 @@
 							<el-icon>
 								<Plus />
 							</el-icon>
-							<div>添加参数</div>
+							<!-- 添加参数 -->
+							<div>{{ $t('message.device.formI18nButton.addParameter') }}</div>
 						</div>
 					</div>
 				</el-form-item>
 
 				<!--根据数据类型输出不同表单-->
 
-				<el-form-item label="是否只读" prop="accessMode">
+				<!-- 是否只读 -->
+				<el-form-item :label="$t('message.device.formI18nLabel.isReadOnly')" prop="accessMode">
 					<el-radio-group v-model="ruleForm.accessMode">
-						<el-radio :label="1">只读</el-radio>
-						<el-radio :label="0">读写</el-radio>
+						<!-- 只读 -->
+						<el-radio :label="1">{{ $t('message.device.formI18nOption.readonly') }}</el-radio>
+						<!-- 读写 -->
+						<el-radio :label="0">{{ $t('message.device.formI18nOption.readWrite') }}</el-radio>
 					</el-radio-group>
 				</el-form-item>
-				<el-form-item label="标签定义描述	" prop="desc">
-					<el-input v-model="ruleForm.desc" type="textarea" placeholder="请输入标签定义描述"></el-input>
+
+				<!-- 标签定义描述 -->
+				<el-form-item :label="$t('message.device.formI18nLabel.tagDescription')" prop="desc">
+					<!-- 请输入标签定义描述 -->
+					<el-input v-model="ruleForm.desc" type="textarea" :placeholder="$t('message.device.formI18nPlaceholder.tagDescription')"></el-input>
 				</el-form-item>
 			</el-form>
 			<template #footer>
 				<span class="dialog-footer">
-					<el-button @click="onCancel">取 消</el-button>
-					<el-button type="primary" @click="onSubmit">{{ ruleForm.id !== 0 ? '修 改' : '添 加' }}</el-button>
+					<!-- 取消 -->
+					<el-button @click="onCancel">{{ $t('message.tableI18nAction.cancel') }}</el-button>
+					<!-- 新增/编辑 -->
+					<el-button type="primary" @click="onSubmit">{{ ruleForm.id !== 0 ? $t('message.tableI18nAction.edit') : $t('message.tableI18nAction.add') }}</el-button>
 				</span>
 			</template>
 		</el-dialog>
@@ -187,12 +245,12 @@
 </template>
 
 <script lang="ts">
-import { reactive, toRefs, defineComponent, ref, unref } from 'vue';
+import { reactive, toRefs, defineComponent, ref, unref, computed } from 'vue';
 import api from '/@/api/device';
 import { Plus, Minus, Right } from '@element-plus/icons-vue';
 import EditOption from './editOption.vue';
 import { validateNoSpace } from '/@/utils/validator';
-
+import { useI18n } from 'vue-i18n';
 import { ElMessage } from 'element-plus';
 
 interface RuleFormState {
@@ -219,7 +277,7 @@ interface DicState {
 	jsondata: any;
 	typeData: any[];
 	enumdata: any[];
-	rules: {};
+	// rules: {};
 }
 
 const ruleForm = {
@@ -245,6 +303,23 @@ export default defineComponent({
 	setup(prop, { emit }) {
 		const formRef = ref<HTMLElement | null>(null);
 		const editOptionRef = ref();
+		const { t, locale } = useI18n();
+		const rules = computed(() => ({
+			name: [
+				// 标签定义名称不能为空
+				{ required: true, message: t('message.device.rules.tagKeyName'), trigger: 'blur' },
+				// 标签定义名称不能超过32个字符
+				{ max: 32, message: t('message.device.rules.tagKeyMax32'), trigger: 'blur' },
+				// 标签定义名称不能包含空格
+				{ validator: validateNoSpace, message: t('message.device.rules.tagKeyValidator'), trigger: 'blur' }
+			],
+			// 标签定义标识不能为空
+			key: [{ required: true, message: t('message.device.rules.tagKey'), trigger: 'blur' }],
+			// 请选择是否只读
+			accessMode: [{ required: true, message: t('message.device.rules.tagAccessMode'), trigger: 'blur' }],
+			// 请选择数据类型
+			type: [{ required: true, message: t('message.device.rules.tagType'), trigger: 'blur' }],
+		}))
 		const state = reactive<DicState>({
 			isShowDialog: false,
 			typeData: [], //
@@ -273,16 +348,7 @@ export default defineComponent({
 			jsondata: [],
 
 			ruleForm: JSON.parse(JSON.stringify(ruleForm)),
-			rules: {
-				name: [
-					{ required: true, message: '标签定义名称不能为空', trigger: 'blur' },
-					{ max: 32, message: '标签定义名称不能超过32个字符', trigger: 'blur' },
-					{ validator: validateNoSpace, message: '标签定义名称不能包含空格', trigger: 'blur' }
-				],
-				key: [{ required: true, message: '标签定义标识不能为空', trigger: 'blur' }],
-				accessMode: [{ required: true, message: '请选择是否只读', trigger: 'blur' }],
-				type: [{ required: true, message: '请选择数据类型', trigger: 'blur' }],
-			},
+
 		});
 
 		// 打开弹窗
@@ -293,10 +359,12 @@ export default defineComponent({
 				const datat = Object.values(res.dataType) as any[];
 				datat.forEach((item, index) => {
 					if (index == 0) {
-						datat[index]['label'] = '基础类型';
+						// 基础类型
+						datat[index]['label'] = t('message.device.baseType');
 						datat[index]['options'] = item;
 					} else {
-						datat[index]['label'] = '扩展类型';
+						// 扩展类型
+						datat[index]['label'] = t('message.device.extendType');
 						datat[index]['options'] = item;
 					}
 				});
@@ -423,7 +491,8 @@ export default defineComponent({
 						state.ruleForm.productKey = state.productKey;
 
 						api.model.tagedit(state.ruleForm).then(() => {
-							ElMessage.success('标签定义类型修改成功');
+							// 标签定义类型修改成功
+							ElMessage.success(t('message.tableI18nConfirm.editSuccess'));
 							closeDialog(); // 关闭弹窗
 							emit('typeList');
 						});
@@ -456,7 +525,8 @@ export default defineComponent({
 
 						state.ruleForm.valueType = state.valueType;
 						api.model.tagadd(state.ruleForm).then(() => {
-							ElMessage.success('标签定义类型添加成功');
+							// 标签定义类型添加成功
+							ElMessage.success(t('message.tableI18nConfirm.addSuccess'));
 							closeDialog(); // 关闭弹窗
 							emit('typeList');
 						});
@@ -466,6 +536,7 @@ export default defineComponent({
 		};
 
 		return {
+			rules,
 			editOptionRef,
 			getOptionData,
 			openDialog,

+ 42 - 25
src/views/iot/device/product/component/typeItem.vue

@@ -2,49 +2,62 @@
   <div class="type-item">
     <template v-if="['int', 'long', 'float', 'double'].includes(valueType.type)">
       <div class="flex-row" style="margin-bottom: 0;">
-        <el-form-item label="取值范围" prop="min" class="flex1">
-          <el-input v-model="valueType.min" type="number" @input="setNull(valueType, 'min', $event)" placeholder="最小值" />
+        <!-- 取值范围 -->
+        <el-form-item :label="$t('message.device.formI18nLabel.valueRange')" prop="min" class="flex1">
+          <!-- 最小值 -->
+          <el-input v-model="valueType.min" type="number" @input="setNull(valueType, 'min', $event)" :placeholder="$t('message.device.formI18nPlaceholder.valueRangeMin')" />
         </el-form-item>
         <div class="split" style="margin-bottom: 20px;">~</div>
         <el-form-item prop="max" class="flex1" label-width="0">
-          <el-input v-model="valueType.max" type="number" @input="setNull(valueType, 'max', $event)" placeholder="最大值" />
+          <!-- 最大值 -->
+          <el-input v-model="valueType.max" type="number" @input="setNull(valueType, 'max', $event)" :placeholder="$t('message.device.formI18nPlaceholder.valueRangeMax')" />
         </el-form-item>
       </div>
-      <el-form-item label="单位" prop="unit">
-        <el-input v-model="valueType.unit" placeholder="请输入单位" />
+      <!-- 单位 -->
+      <el-form-item :label="$t('message.device.formI18nLabel.unit')" prop="unit">
+        <!-- 请输入单位 -->
+        <el-input v-model="valueType.unit" :placeholder="$t('message.device.formI18nPlaceholder.inputUnit')" />
       </el-form-item>
     </template>
-
-    <el-form-item label="精度" prop="decimals" v-if="['float', 'double'].includes(valueType.type)">
-      <el-input v-model="valueType.decimals" type="number" placeholder="请输入精度" />
+    <!-- 精度 -->
+    <el-form-item :label="$t('message.device.formI18nLabel.precision')" prop="decimals" v-if="['float', 'double'].includes(valueType.type)">
+      <!-- 请输入精度 -->
+      <el-input v-model="valueType.decimals" type="number" :placeholder="$t('message.device.formI18nPlaceholder.inputPrecision')" />
     </el-form-item>
 
-
-    <el-form-item label="最大长度" prop="maxLength" v-if="valueType.type === 'string'">
-      <el-input v-model="valueType.maxLength" type="number" placeholder="请输入最大长度" />
+    <!-- 最大长度 -->
+    <el-form-item :label="$t('message.device.formI18nLabel.maxLength')" prop="maxLength" v-if="valueType.type === 'string'">
+      <!-- 请输入最大长度 -->
+      <el-input v-model="valueType.maxLength" type="number" :placeholder="$t('message.device.formI18nPlaceholder.inputMaxLength')" />
     </el-form-item>
-
-    <el-form-item label="布尔值" v-else-if="valueType.type === 'boolean'">
+    <!-- 布尔值-->
+    <el-form-item :label="$t('message.device.formI18nLabel.booleanValue')" v-else-if="valueType.type === 'boolean'">
       <div class="input-box flex-row">
-        <el-input v-model="valueType.trueText" placeholder="请输入true时显示的文字" /><span style="margin: 0px 10px">~</span>
-        <el-input v-model="valueType.trueValue" placeholder="请输入布尔值" disabled />
+        <!-- 请输入true时显示的文字 -->
+        <el-input v-model="valueType.trueText" :laceholder="$t('message.device.formI18nPlaceholder.inputTrueText')" /><span style="margin: 0px 10px">~</span>
+        <!-- 请输入布尔值 -->
+        <el-input v-model="valueType.trueValue" :placeholder="$t('message.device.formI18nPlaceholder.inputBooleanValue')" disabled />
       </div>
 
       <div class="input-box flex-row">
-        <el-input v-model="valueType.falseText" placeholder="请输入false时显示的文字" /> <span style="margin: 0px 10px">~</span>
-        <el-input v-model="valueType.falseValue" placeholder="请输入布尔值" disabled />
+        <!-- 请输入false时显示的文字 -->
+        <el-input v-model="valueType.falseText" :placeholder="$t('message.device.formI18nPlaceholder.inputFalseText')" /> <span style="margin: 0px 10px">~</span>
+        <!-- 请输入布尔值 -->
+        <el-input v-model="valueType.falseValue" :placeholder="$t('message.device.formI18nPlaceholder.inputBooleanValue')" disabled />
       </div>
     </el-form-item>
-
-    <el-form-item label="枚举项" prop="" v-else-if="valueType.type === 'enum'">
+    <!-- 枚举项 -->
+    <el-form-item :label="$t('message.device.formI18nLabel.enumItems')" prop="" v-else-if="valueType.type === 'enum'">
       <div class="input-box flex-row" v-for="(item, index) in valueType.elements" :key="index">
-        <el-input v-model="item.text" placeholder="请输入枚举文本" />
+        <!-- 请输入枚举文本 -->
+        <el-input v-model="item.text" :placeholder="$t('message.device.formI18nPlaceholder.inputEnumText')" />
         <span style="margin: 0px 10px">
           <el-icon>
             <Right />
           </el-icon>
         </span>
-        <el-input v-model="item.value" placeholder="请输入枚举值" />
+        <!-- 请输入枚举值 -->
+        <el-input v-model="item.value" :placeholder="$t('message.device.formI18nPlaceholder.inputEnumValue')" />
         <div class="input-option">
           <el-icon @click="addEnum" v-if="index == 0">
             <Plus />
@@ -55,12 +68,16 @@
         </div>
       </div>
     </el-form-item>
-    <el-form-item label="对象属性" prop="" v-else-if="valueType.type === 'object'">
+    <!-- 对象属性 -->
+    <el-form-item :label="$t('message.device.formI18nLabel.objectProperty')" prop="" v-else-if="valueType.type === 'object'">
       <div class="w-full" v-for="(item, index) in valueType.properties" :key="index">
         <div class="flex-row">
-          <el-input v-model="item.key" placeholder="属性标识" class="flex1" />
-          <el-input v-model="item.name" placeholder="属性名称" class="flex1" />
-          <el-select v-model="item.valueType.type" placeholder="请选择元素类型" style="width: 140px;">
+          <!-- 属性标识 -->
+          <el-input v-model="item.key" :placeholder="$t('message.device.formI18nPlaceholder.propertyIdentifier1')" class="flex1" />
+          <!-- 属性名称 -->
+          <el-input v-model="item.name" :placeholder="$t('message.device.formI18nPlaceholder.propertyName1')" class="flex1" />
+          <!-- 请选择元素类型 -->
+          <el-select v-model="item.valueType.type" :placeholder="$t('message.device.formI18nPlaceholder.selectElementType')" style="width: 140px;">
             <el-option-group v-for="group in typeData" :key="group.label" :label="group.label">
               <el-option v-for="item in group.options" :key="item.type" :label="item.title" :value="item.type" :disabled="['array', 'object', 'enum', 'date'].includes(item.type)" />
             </el-option-group>

+ 240 - 181
src/views/iot/device/product/detail.vue

@@ -2,154 +2,209 @@
 	<div class="page bg page-full">
 		<div class="content">
 			<div class="cont_box" style="align-items: center;">
-				<div class="title">产品:{{ detail.name }}</div>
+				<!-- 产品 -->
+				<div class="title">{{ `${$t('message.device.product')}:${detail.name}` }}</div>
 				<!-- <el-tag :type="developer_status == 1 ? 'success' : 'danger'" style="margin:0 20px;">{{ developer_status == 1 ? '已发布' : '未发布' }}</el-tag> -->
-				<el-switch v-auth="'startOrStop'" style="margin:0 20px;" v-model="developer_status" inline-prompt :active-value="1" :inactive-value="0" active-text="启用" inactive-text="停用" @change="CkOption"></el-switch>
+				<el-switch v-auth="'startOrStop'" style="margin:0 20px;" v-model="developer_status" inline-prompt :active-value="1" :inactive-value="0" :active-text="$t('message.device.productDetail.enable')" :inactive-text="$t('message.device.productDetail.disable')" @change="CkOption"></el-switch>
 			</div>
 		</div>
 
 		<el-tabs v-model="activeName" style="padding: 0 20px;" @tab-click="handleClick">
-			<el-tab-pane label="产品信息" name="1">
+			<!-- 产品信息 -->
+			<el-tab-pane :label="$t('message.device.productDetail.productInfo')" name="1">
 				<div class="pro-box">
-					<div class="protitle">产品信息</div>
-					<el-button type="" :icon="Edit" class="buttonedit" v-auth="'edit'" @click="onOpenEditDic(detail)">编辑</el-button>
+					<div class="protitle">{{$t('message.device.productDetail.productInfo')}}</div>
+					<!-- 编辑 -->
+					<el-button type="" :icon="Edit" class="buttonedit" v-auth="'edit'" @click="onOpenEditDic(detail)">{{ $t('message.tableI18nAction.edit') }}</el-button>
 				</div>
 
 				<el-descriptions class="margin-top" :column="3" border>
-					<el-descriptions-item label="产品标识"><copy :text="detail.key"></copy></el-descriptions-item>
-					<el-descriptions-item label="产品分类">{{ detail.categoryName }}</el-descriptions-item>
-					<el-descriptions-item label="设备类型">{{ detail.deviceType }}</el-descriptions-item>
-					<el-descriptions-item label="产品图片">
+					<!-- 产品标识 -->
+					<el-descriptions-item :label="$t('message.device.productDetail.productIdentifier')"><copy :text="detail.key"></copy></el-descriptions-item>
+					<!-- 产品分类 -->
+					<el-descriptions-item :label="$t('message.device.productDetail.productCategory')">{{ detail.categoryName }}</el-descriptions-item>
+					<!-- 设备类型 -->
+					<el-descriptions-item :label="$t('message.device.productDetail.deviceType')">{{ detail.deviceType }}</el-descriptions-item>
+					<!-- 产品图片 -->
+					<el-descriptions-item :label="$t('message.device.productDetail.productImage')">
 						<el-image style="width: 80px; height: 80px" :src="detail.icon" :previewSrcList="[detail.icon]" fit="contain">
 							<template #error>
 								<div class="image-slot">
 									<ele-Picture style="width: 30px;" />
-									加载失败
+									<!-- 加载失败 -->
+									{{ $t('message.device.productDetail.loadFailed') }}
 								</div>
 							</template>
 						</el-image>
 					</el-descriptions-item>
-					<el-descriptions-item label="消息协议">{{ detail.messageProtocol }}</el-descriptions-item>
-					<el-descriptions-item label="接入方式">{{ detail.transportProtocol }}</el-descriptions-item>
-					<el-descriptions-item label="描述">{{ detail.desc }}</el-descriptions-item>
+					<!-- 消息协议 -->
+					<el-descriptions-item :label="$t('message.device.productDetail.messageProtocol')">{{ detail.messageProtocol }}</el-descriptions-item>
+					<!-- 接入方式 -->
+					<el-descriptions-item :label="$t('message.device.tableI18nColumn.transportProtocol')">{{ detail.transportProtocol }}</el-descriptions-item>
+					<!-- 描述 -->
+					<el-descriptions-item :label="$t('message.tableI18nColumn.des')">{{ detail.desc }}</el-descriptions-item>
 				</el-descriptions>
 			</el-tab-pane>
-			<el-tab-pane label="物模型" name="2">
+			<!-- 物模型 -->
+			<el-tab-pane :label="$t('message.device.productDetail.thingModel')" name="2">
 				<el-tabs type="border-card" v-model="activetab" @tab-click="wuhandleClick">
-					<el-tab-pane label="属性定义" name="attr">
+					<!-- 属性定义 -->
+					<el-tab-pane :label="$t('message.device.productDetail.propertyDefinition')" name="attr">
 						<div class="wu-title">
-							<div class="title">属性定义</div>
-							<div><el-button size="small" type="primary" v-auth="'edit'" @click="onOpenEditAttr()">添加</el-button>
+							<div class="title">{{$t('message.device.productDetail.propertyDefinition')}}</div>
+							<!-- 新增 -->
+							<div>
+								<el-button size="small" type="primary" v-auth="'edit'" @click="onOpenEditAttr()">{{$t('message.tableI18nAction.add')}}</el-button>
 							</div>
 						</div>
 
-						<el-table style="width: 100%" :data="tableData.data" v-if="activetab == 'attr'">
-							<el-table-column label="属性标识" align="center" prop="key" />
-							<el-table-column label="属性名称" prop="name" show-overflow-tooltip />
-							<el-table-column prop="valueType" label="数据类型" width="100" align="center">
+						<el-table style="width: 100%" :data="tableData.data" v-loading="tableData.loading" v-if="activetab == 'attr'">
+							<!-- 属性标识 -->
+							<el-table-column :label="$t('message.device.productDetail.propertyIdentifier')" align="center" prop="key" />
+							<!-- 属性名称 -->
+							<el-table-column :label="$t('message.device.productDetail.propertyName')" prop="name" show-overflow-tooltip />
+							<!-- 数据类型 -->
+							<el-table-column prop="valueType" :label="$t('message.device.productDetail.dataType')" width="100" align="center">
 								<template #default="scope">
 									<span>{{ scope.row.valueType.type }}</span>
 								</template>
 							</el-table-column>
-							<el-table-column prop="decimals" label="精度" width="60" align="center">
+							<!-- 精度 -->
+							<el-table-column prop="decimals" :label="$t('message.device.productDetail.precision')" :width="currentLang == 'en' ? '100' : '60'" align="center">
 								<template #default="scope">
 									<span>{{ scope.row.valueType.decimals }}</span>
 								</template>
 							</el-table-column>
-							<el-table-column prop="unit" label="单位" width="60" align="center">
+							<!-- 单位 -->
+							<el-table-column prop="unit" :label="$t('message.device.productDetail.unit')" width="60" align="center">
 								<template #default="scope">
 									<span>{{ scope.row.valueType.unit }}</span>
 								</template>
 							</el-table-column>
-							<el-table-column prop="accessMode" label="是否只读" width="120" align="center">
+							<!-- 是否只读 -->
+							<el-table-column prop="accessMode" :label="$t('message.device.productDetail.readOnly')" width="120" align="center">
 								<template #default="scope">
-									<el-tag type="info" size="small" v-if="scope.row.accessMode">只读</el-tag>
-									<el-tag type="success" size="small" v-else>读写</el-tag>
+									<!-- 只读 -->
+									<el-tag type="info" size="small" v-if="scope.row.accessMode">{{ $t('message.device.productDetail.readonly') }}</el-tag>
+									<!-- 读写 -->
+									<el-tag type="success" size="small" v-else>{{ $t('message.device.productDetail.readWrite') }}</el-tag>
 								</template>
 							</el-table-column>
-							<el-table-column label="说明" prop="desc" show-overflow-tooltip />
-							<el-table-column label="操作" width="300" align="center" fixed="right">
+							<!-- 说明 -->
+							<el-table-column :label="$t('message.device.productDetail.remark')" prop="desc" show-overflow-tooltip />
+							<!-- 操作 -->
+							<el-table-column :label="$t('message.tableI18nColumn.operation')" width="120" align="center" fixed="right">
 								<template #default="scope">
-									<el-button size="small" text type="warning" v-auth="'edit'" @click="onEditAttr(scope.row)">修改</el-button>
-									<el-button size="small" text type="danger" v-auth="'del'" @click="onRowDel(scope.row.key, 'attr')">删除</el-button>
+									<el-button size="small" text type="warning" v-auth="'edit'" @click="onEditAttr(scope.row)">{{ $t('message.tableI18nAction.edit') }}</el-button>
+									<el-button size="small" text type="danger" v-auth="'del'" @click="onRowDel(scope.row.key, 'attr')">{{ $t('message.tableI18nAction.delete') }}</el-button>
 								</template>
 							</el-table-column>
 						</el-table>
 					</el-tab-pane>
-					<el-tab-pane label="功能定义" name="fun">
+					<!-- 功能定义 -->
+					<el-tab-pane :label="$t('message.device.productDetail.functionDefinition')" name="fun">
 						<div class="wu-title">
-							<div class="title">功能定义</div>
-							<div><el-button size="small" type="primary" v-auth="'add'" @click="onOpenEditFun()">添加</el-button>
+							<div class="title">{{$t('message.device.productDetail.functionDefinition')}}</div>
+							<!-- 新增 -->
+							<div>
+								<el-button size="small" type="primary" v-auth="'add'" @click="onOpenEditFun()">{{$t('message.tableI18nAction.add')}}</el-button>
 							</div>
 						</div>
 
 						<el-table style="width: 100%" :data="tableData.data" v-if="activetab == 'fun'">
-							<el-table-column label="功能标识" align="center" prop="key" />
-							<el-table-column label="名称" prop="name" show-overflow-tooltip />
-
-							<el-table-column label="描述" prop="desc" show-overflow-tooltip />
-							<el-table-column label="操作" width="300" align="center" fixed="right">
+							<el-table-column :label="$t('message.device.productDetail.functionIdentifier')" align="center" prop="key" />
+							<el-table-column :label="$t('message.device.productDetail.name')" prop="name" show-overflow-tooltip />
+							<!-- 描述 -->
+							<el-table-column :label="$t('message.tableI18nColumn.des')" prop="desc" show-overflow-tooltip />
+							<!-- 操作 -->
+							<el-table-column :label="$t('message.tableI18nColumn.operation')" width="120" align="center" fixed="right">
 								<template #default="scope">
-									<el-button size="small" text type="warning" v-auth="'edit'" @click="onEditFun(scope.row)">修改</el-button>
-									<el-button size="small" text type="danger" v-auth="'del'" @click="onRowDel(scope.row.key, 'fun')">删除</el-button>
+									<!-- 编辑 -->
+									<el-button size="small" text type="warning" v-auth="'edit'" @click="onEditFun(scope.row)">{{ $t('message.tableI18nAction.edit') }}</el-button>
+									<!-- 删除 -->
+									<el-button size="small" text type="danger" v-auth="'del'" @click="onRowDel(scope.row.key, 'fun')">{{ $t('message.tableI18nAction.delete') }}</el-button>
 								</template>
 							</el-table-column>
 						</el-table>
 					</el-tab-pane>
-					<el-tab-pane label="事件定义" name="event">
+					<!-- 事件定义 -->
+					<el-tab-pane :label="$t('message.device.productDetail.eventDefinition')" name="event">
 						<div class="wu-title">
-							<div class="title">事件定义</div>
+							<div class="title">{{$t('message.device.productDetail.eventDefinition')}}</div>
 							<div>
-								<el-button size="small" type="primary" v-auth="'add'" @click="onOpenEditEvent()">添加</el-button>
+								<el-button size="small" type="primary" v-auth="'add'" @click="onOpenEditEvent()">{{$t('message.tableI18nAction.add')}}</el-button>
 							</div>
 						</div>
 
 						<el-table style="width: 100%" :data="tableData.data" v-if="activetab == 'event'">
-							<el-table-column label="事件标识" align="center" prop="key" />
-							<el-table-column label="名称" prop="name" show-overflow-tooltip />
-							<el-table-column prop="level" label="事件级别" width="120" align="center">
+							<!-- 事件标识 -->
+							<el-table-column :label="$t('message.device.productDetail.eventIdentifier')" align="center" prop="key" />
+							<!-- 事件名称 -->
+							<el-table-column :label="$t('message.device.productDetail.name')" prop="name" show-overflow-tooltip />
+							<!-- 事件级别 -->
+							<el-table-column prop="level" :label="$t('message.device.productDetail.eventLevel')" width="120" align="center">
 								<template #default="scope">
-									<el-tag type="primary" size="small" v-if="scope.row.level == 0">普通</el-tag>
-									<el-tag type="warning" size="small" v-if="scope.row.level == 1">警告</el-tag>
-									<el-tag type="danger" size="small" v-if="scope.row.level == 2">紧急</el-tag>
+									<!-- 普通 -->
+									<el-tag size="small" v-if="scope.row.level == 0">{{ $t('message.device.productDetail.normal') }}</el-tag>
+									<!-- 警告 -->
+									<el-tag type="warning" size="small" v-if="scope.row.level == 1">{{ $t('message.device.productDetail.warning') }}</el-tag>
+									<!-- 紧急 -->
+									<el-tag type="danger" size="small" v-if="scope.row.level == 2">{{ $t('message.device.productDetail.urgent') }}</el-tag>
 								</template>
 							</el-table-column>
-							<el-table-column label="描述" prop="desc" show-overflow-tooltip />
-
-							<el-table-column label="操作" width="300" align="center" fixed="right">
+							<!-- 描述 -->
+							<el-table-column :label="$t('message.tableI18nColumn.des')" prop="desc" show-overflow-tooltip />
+							<!-- 操作 -->
+							<el-table-column :label="$t('message.tableI18nColumn.operation')" width="120" align="center" fixed="right">
 								<template #default="scope">
-									<el-button size="small" text type="warning" v-auth="'edit'" @click="onEditEvent(scope.row)">修改</el-button>
-									<el-button size="small" text type="danger" v-auth="'del'" @click="onRowDel(scope.row.key, 'event')">删除</el-button>
+									<!-- 编辑 -->
+									<el-button size="small" text type="warning" v-auth="'edit'" @click="onEditEvent(scope.row)">{{ $t('message.tableI18nAction.edit') }}</el-button>
+									<!-- 删除 -->
+									<el-button size="small" text type="danger" v-auth="'del'" @click="onRowDel(scope.row.key, 'event')">{{ $t('message.tableI18nAction.delete') }}</el-button>
 								</template>
 							</el-table-column>
 						</el-table>
 					</el-tab-pane>
-					<el-tab-pane label="标签定义" name="tab">
+					<!-- 标签定义 -->
+					<el-tab-pane :label="$t('message.device.productDetail.tagDefinition')" name="tab">
 						<div class="wu-title">
-							<div class="title">标签定义</div>
-							<div><el-button size="small" type="primary" v-auth="'add'" @click="onOpenEditTab()">添加</el-button>
+							<!-- 标签定义 -->
+							<div class="title">{{ $t('message.device.productDetail.tagDefinition') }}</div>
+							<!-- 添加 -->
+							<div>
+								<el-button size="small" type="primary" v-auth="'add'" @click="onOpenEditTab()">{{ $t('message.tableI18nAction.add') }}</el-button>
 							</div>
 						</div>
 
 						<el-table style="width: 100%" :data="tableData.data" v-if="activetab == 'tab'">
-							<el-table-column label="属性标识" align="center" prop="key" />
-							<el-table-column label="属性名称" prop="name" show-overflow-tooltip />
-							<el-table-column prop="valueType" label="数据类型" width="120" align="center">
+							<!-- 标签标识 -->
+							<el-table-column :label="$t('message.device.productDetail.propertyIdentifier')" align="center" prop="key" />
+							<!-- 属性名称 -->
+							<el-table-column :label="$t('message.device.productDetail.propertyName')" prop="name" show-overflow-tooltip />
+							<!-- 数据类型 -->
+							<el-table-column prop="valueType" :label="$t('message.device.productDetail.dataType')" width="120" align="center">
 								<template #default="scope">
 									<span>{{ scope.row.valueType.type }}</span>
 								</template>
 							</el-table-column>
-							<el-table-column prop="accessMode" label="是否只读" width="120" align="center">
+							<!-- 是否只读 -->
+							<el-table-column prop="accessMode" :label="$t('message.device.productDetail.readOnly')" width="120" align="center">
 								<template #default="scope">
-									<el-tag type="info" size="small" v-if="scope.row.accessMode">只读</el-tag>
-									<el-tag type="success" size="small" v-else>读写</el-tag>
+									<!-- 只读 -->
+									<el-tag type="info" size="small" v-if="scope.row.accessMode">{{ $t('message.device.productDetail.readonly') }}</el-tag>
+									<!-- 读写 -->
+									<el-tag type="success" size="small" v-else>{{ $t('message.device.productDetail.readWrite') }}</el-tag>
 								</template>
 							</el-table-column>
-							<el-table-column label="描述" prop="desc" show-overflow-tooltip />
-							<el-table-column label="操作" width="300" align="center" fixed="right">
+							<!-- 描述 -->
+							<el-table-column :label="$t('message.tableI18nColumn.des')" prop="desc" show-overflow-tooltip />
+							<!-- 操作 -->
+							<el-table-column :label="$t('message.tableI18nColumn.operation')" width="120" align="center" fixed="right">
 								<template #default="scope">
-									<el-button size="small" text type="warning" v-auth="'edit'" @click="onEditTag(scope.row)">修改</el-button>
-									<el-button size="small" text type="danger" v-auth="'del'" @click="onRowDel(scope.row.key, 'tab')">删除</el-button>
+									<!-- 编辑 -->
+									<el-button size="small" text type="warning" v-auth="'edit'" @click="onEditTag(scope.row)">{{ $t('message.tableI18nAction.edit') }}</el-button>
+									<!-- 删除 -->
+									<el-button size="small" text type="danger" v-auth="'del'" @click="onRowDel(scope.row.key, 'tab')">{{ $t('message.tableI18nAction.delete') }}</el-button>
 								</template>
 							</el-table-column>
 						</el-table>
@@ -158,25 +213,29 @@
 				<div class="import">
 					<div class="row_bet">
 						<el-upload accept="json" :show-file-list="false" :limit="1" :data="{ productKey: detail.key }" :headers="headers" :action="uploadUrl" :on-success="updateImg">
+							<!-- 导入物模型 -->
 							<el-button size="small">
 								<el-icon> <ele-Upload /> </el-icon>
-								导入物模型
+								{{ $t('message.device.productDetail.importModel') }}
 							</el-button>
 						</el-upload>
+						<!-- 导出物模型 -->
 						<el-button size="small" type="default" class="ml10" @click="onRowExport()">
 							<el-icon>
 								<ele-Download />
 							</el-icon>
-							导出物模型
+							{{ $t('message.device.productDetail.exportModel') }}
 						</el-button>
 					</div>
 				</div>
 				<pagination v-show="tableData.total > 0" :total="tableData.total" v-model:page="tableData.param.pageNum" v-model:limit="tableData.param.pageSize" @pagination="getList()" />
 			</el-tab-pane>
-			<el-tab-pane label="设备接入" name="3">
+			<!-- 设备接入 -->
+			<el-tab-pane :label="$t('message.device.productDetail.deviceAccess')" name="3">
 				<deviceIn></deviceIn>
 			</el-tab-pane>
-			<el-tab-pane label="数据解析" name="4" lazy>
+			<!-- 数据解析 -->
+			<el-tab-pane :label="$t('message.device.productDetail.dataParsing')" name="4" lazy>
 				<dataParse v-if="activeName === '4'" :script="detail.scriptInfo" @updateScript="updateScript">
 				</dataParse>
 			</el-tab-pane>
@@ -188,13 +247,13 @@
 		<EditTab ref="editTabRef" @typeList="gettab" />
 	</div>
 </template>
-<script lang="ts">
-import { toRefs, reactive, onMounted, ref, defineComponent } from 'vue';
+<script lang="ts" setup>
+import { toRefs, reactive, onMounted, ref, defineComponent, computed } from 'vue';
 import { Edit } from '@element-plus/icons-vue';
 import { ElMessageBox, ElMessage, FormInstance } from 'element-plus';
 import downloadFile from '/@/utils/download';
 import getOrigin from '/@/utils/origin';
-
+import { useI18n } from 'vue-i18n';
 
 import EditDic from './component/editPro.vue';
 import deviceIn from './component/deviceIn.vue';
@@ -208,32 +267,36 @@ import { useRoute } from 'vue-router';
 
 import api from '/@/api/device';
 
-interface TableDataState {
-	isShowDialog: boolean;
-	detail: any;
-	developer_status: number;
-	activeName: string;
-	activetab: string;
-	tableData: {
-		data: [];
-		total: number;
-		loading: boolean;
-		param: {
-			pageNum: number;
-			pageSize: number;
-			name: string;
-			productKey: string | string[];
-			deviceType: string;
-			status: string;
-			dateRange: string[];
-		} | any;
-	};
-}
-export default defineComponent({
-	name: 'deviceEditPro',
-	components: { EditDic, EditAttr, EditFun, EditEvent, EditTab, deviceIn, dataParse },
-
-	setup(prop, context) {
+const { locale } = useI18n();
+
+const currentLang = computed(() => locale.value);
+
+// interface TableDataState {
+// 	isShowDialog: boolean;
+// 	detail: any;
+// 	developer_status: number;
+// 	activeName: string;
+// 	activetab: string;
+// 	tableData: {
+// 		data: [];
+// 		total: number;
+// 		loading: boolean;
+// 		param: {
+// 			pageNum: number;
+// 			pageSize: number;
+// 			name: string;
+// 			productKey: string | string[];
+// 			deviceType: string;
+// 			status: string;
+// 			dateRange: string[];
+// 		} | any;
+// 	};
+// }
+// export default defineComponent({
+// 	name: 'deviceEditPro',
+// 	components: { EditDic, EditAttr, EditFun, EditEvent, EditTab, deviceIn, dataParse },
+
+// 	setup(prop, context) {
 
 		const uploadUrl: string = getOrigin("/product/tsl/import");
 		const headers = {
@@ -245,40 +308,36 @@ export default defineComponent({
 		const editFunRef = ref();
 		const editEventRef = ref();
 		const editTabRef = ref();
-		const state = reactive<TableDataState>({
-			isShowDialog: false,
-			activeName: '1', // 分类数据
-			activetab: 'attr', // 分类数据
-			detail: {},
-			developer_status: 0,
-			tableData: {
-				data: [],
-				total: 0,
-				loading: false,
-				param: {
-					pageNum: 1,
-					productKey: route.params?.id,
-					pageSize: 20,
-					status: '',
-					dateRange: [],
-				},
+		const activeName = ref('1');// 分类数据
+		const activetab = ref('attr');// 分类数据
+		const detail = ref<any>({});
+		const developer_status = ref(0);
+		const tableData = ref({
+			data: [],
+			total: 0,
+			loading: false,
+			param: {
+				pageNum: 1,
+				productKey: route.params?.id,
+				pageSize: 20,
+				status: '',
+				dateRange: [],
 			},
 		});
 
 		onMounted(() => {
 			productDetail()
 			//第一次加载
-			api.model.property(state.tableData.param).then((res: any) => {
-				state.tableData.data = res.Data;
-				state.tableData.total = res.Total;
+			api.model.property(tableData.value.param).then((res: any) => {
+				tableData.value.data = res.Data;
+				tableData.value.total = res.Total;
 			});
 		});
-
 		function productDetail() {
 			const productKey = route.params?.id;
 			api.product.detail(productKey).then((res: any) => {
-				state.detail = res.data;
-				state.developer_status = res.data.status
+				detail.value = res.data;
+				developer_status.value = res.data.status
 			});
 		}
 
@@ -375,7 +434,7 @@ export default defineComponent({
 
 		//根据不同类型获取列表
 		const getList = () => {
-			switch (state.activetab) {
+			switch (activetab.value) {
 				case 'attr':
 					getproperty();
 					break;
@@ -392,34 +451,34 @@ export default defineComponent({
 		};
 
 		const getproperty = () => {
-			api.model.property(state.tableData.param).then((res: any) => {
-				state.tableData.data = res.Data;
-				state.tableData.total = res.Total;
+			api.model.property(tableData.value.param).then((res: any) => {
+				tableData.value.data = res.Data;
+				tableData.value.total = res.Total;
 			});
 		};
 
 		const getfunction = () => {
-			api.model.function(state.tableData.param).then((res: any) => {
-				state.tableData.data = res.Data;
-				state.tableData.total = res.Total;
+			api.model.function(tableData.value.param).then((res: any) => {
+				tableData.value.data = res.Data;
+				tableData.value.total = res.Total;
 			});
 		};
 		const getevent = () => {
-			api.model.event(state.tableData.param).then((res: any) => {
-				state.tableData.data = res.Data;
-				state.tableData.total = res.Total;
+			api.model.event(tableData.value.param).then((res: any) => {
+				tableData.value.data = res.Data;
+				tableData.value.total = res.Total;
 			});
 		};
 
 		const gettab = () => {
-			api.model.tag(state.tableData.param).then((res: any) => {
-				state.tableData.data = res.Data;
-				state.tableData.total = res.Total;
+			api.model.tag(tableData.value.param).then((res: any) => {
+				tableData.value.data = res.Data;
+				tableData.value.total = res.Total;
 			});
 		};
 
 		const wuhandleClick = (tab: any) => {
-			state.activetab = tab.props.name;
+			activetab.value = tab.props.name;
 			switch (tab.props.name) {
 				case 'attr':
 					getproperty();
@@ -440,27 +499,27 @@ export default defineComponent({
 		};
 
 		const updateScript = (scriptInfo: string) => {
-			state.detail.scriptInfo = scriptInfo
+			detail.value.scriptInfo = scriptInfo
 		};
 
-		const CkOption = (developer_status: number) => {
-			if (developer_status == 0) {
+		const CkOption = (developerStatus: number) => {
+			if (developerStatus == 0) {
 				api.product.undeploy(route.params.id).then((res: any) => {
 					ElMessage.success('操作成功');
-					state.developer_status = 0;
-				}).catch(() => state.developer_status = 1)
+					developer_status.value = 0;
+				}).catch(() => developer_status.value = 1)
 			} else {
 				api.product.deploy(route.params.id).then((res: any) => {
 					ElMessage.success('操作成功');
-					state.developer_status = 1;
-				}).catch(() => state.developer_status = 0)
+					developer_status.value = 1;
+				}).catch(() => developer_status.value = 0)
 			}
 		}
 
 		// 导出
 		const onRowExport = () => {
 
-			api.product.export({ productKey: state.detail.key }).then((res: any) => downloadFile(res, "TSL-" + state.detail.key + "-" + getCurrentTime() + ".json"))
+			api.product.export({ productKey: detail.value.key }).then((res: any) => downloadFile(res, "TSL-" + detail.value.key + "-" + getCurrentTime() + ".json"))
 		};
 
 
@@ -482,42 +541,42 @@ export default defineComponent({
 			}
 		};
 
-		return {
-			updateImg,
-			headers,
-			uploadUrl,
-			getCurrentTime,
-			onRowExport,
-			Edit,
-			updateScript,
-			editDicRef,
-			editAttrRef,
-			editFunRef,
-			editEventRef,
-			editTabRef,
-			CkOption,
-			onRowDel,
-			onEditFun,
-			onEditEvent,
-			onEditTag,
-			onEditAttr,
-			getList,
-			getproperty,
-			getfunction,
-			getevent,
-			gettab,
-			wuhandleClick,
-			onOpenEditTab,
-			onOpenEditEvent,
-			productDetail,
-			onOpenEditAttr,
-			onOpenEditFun,
-			onOpenEditDic,
-			handleClick,
-			...toRefs(state),
-		};
-	},
-});
+// 		return {
+// 			updateImg,
+// 			headers,
+// 			uploadUrl,
+// 			getCurrentTime,
+// 			onRowExport,
+// 			Edit,
+// 			updateScript,
+// 			editDicRef,
+// 			editAttrRef,
+// 			editFunRef,
+// 			editEventRef,
+// 			editTabRef,
+// 			CkOption,
+// 			onRowDel,
+// 			onEditFun,
+// 			onEditEvent,
+// 			onEditTag,
+// 			onEditAttr,
+// 			getList,
+// 			getproperty,
+// 			getfunction,
+// 			getevent,
+// 			gettab,
+// 			wuhandleClick,
+// 			onOpenEditTab,
+// 			onOpenEditEvent,
+// 			productDetail,
+// 			onOpenEditAttr,
+// 			onOpenEditFun,
+// 			onOpenEditDic,
+// 			handleClick,
+// 			...toRefs(state),
+// 		};
+// 	},
+// });
 </script>
 <style scoped>
 .import {

+ 145 - 147
src/views/iot/device/product/index.vue

@@ -1,93 +1,124 @@
 <template>
   <div class="page">
-    <el-card shadow="nover">
+    <el-card shadow="never">
       <el-form :model="tableData.param" ref="queryRef" inline label-width="68px">
-        <el-form-item label="关键字" prop="name">
-          <el-input v-model="tableData.param.name" placeholder="输入名称或标识" clearable style="width: 150px" @keyup.enter.native="typeList" />
+        <!-- 关键字 -->
+        <el-form-item :label="$t('message.device.formI18nLabel.keyword')" prop="name">
+          <el-input v-model="tableData.param.name" :placeholder="$t('message.device.formI18nPlaceholder.keyword')" clearable style="width: 150px" @keyup.enter.native="getList" />
         </el-form-item>
-        <el-form-item label="类型" prop="deviceType" style="width: 180px">
-          <el-select v-model="tableData.param.deviceType" placeholder="类型" clearable style="width: 240px" @keyup.enter.native="typeList">
-            <el-option label="设备" :value="'设备'" />
-            <el-option label="设备" :value="'网关'" />
+        <!-- 类型 -->
+        <el-form-item :label="$t('message.device.formI18nLabel.type')" prop="deviceType" style="width: 180px">
+          <el-select v-model="tableData.param.deviceType" :placeholder="$t('message.device.formI18nPlaceholder.type')" clearable style="width: 240px" @keyup.enter.native="getList">
+            <!-- 网关 -->
+            <el-option :label="$t('message.device.formI18nOption.gateway')" :value="'网关'" />
+            <!-- 设备 -->
+            <el-option :label="$t('message.device.formI18nOption.device')" :value="'设备'" />
+            <!-- 子设备 -->
+            <el-option :label="$t('message.device.formI18nOption.subDevice')" :value="'子设备'" />
             <!-- <el-option label="直连设备" :value="'设备'" />
             <el-option label="网关设备" :value="'网关'" /> -->
-            <el-option label="子设备" :value="'子设备'" />
           </el-select>
         </el-form-item>
-        <el-form-item label="状态" prop="status" style="width: 180px">
-          <el-select v-model="tableData.param.status" placeholder="启用状态" clearable style="width: 240px">
-            <el-option label="已启用" :value="1" />
-            <el-option label="未启用" :value="0" />
+        <!-- 状态 -->
+        <el-form-item :label="$t('message.device.formI18nLabel.status')" prop="status" style="width: 180px">
+          <el-select v-model="tableData.param.status" :placeholder="$t('message.device.formI18nPlaceholder.status')" clearable style="width: 240px">
+            <!-- 已启用 -->
+            <el-option :label="$t('message.device.formI18nOption.on')" :value="1" />
+            <!-- 未启用 -->
+            <el-option :label="$t('message.device.formI18nOption.off')" :value="0" />
           </el-select>
         </el-form-item>
         <!--        <el-form-item label="创建时间" prop="dateRange">-->
         <!--          <el-date-picker v-model="tableData.param.dateRange" style="width: 240px" value-format="YYYY-MM-DD" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker>-->
         <!--        </el-form-item>-->
         <el-form-item>
-          <el-button type="primary" class="ml10" @click="typeList">
+          <!-- 查询 -->
+          <el-button type="primary" class="ml10" @click="getList">
             <el-icon>
               <ele-Search />
             </el-icon>
-            查询
+            {{ $t('message.formI18nButton.query') }}
           </el-button>
+          <!-- 重置 -->
           <!-- <el-button @click="resetQuery(queryRef)">
             <el-icon>
               <ele-Refresh />
             </el-icon>
-            重置
+            {{ $t('iotmanagerI18n.formI18nButton.reset') }}
           </el-button> -->
+          <!-- 新增产品 -->
           <el-button type="primary" class="ml10" @click="onOpenAddDic" v-auth="'add'">
             <el-icon>
               <ele-FolderAdd />
             </el-icon>
-            新增产品
+            {{ $t('message.device.formI18nButton.addProduct') }}
           </el-button>
+          <!-- 删除 -->
           <el-button type="info" class="ml10" @click="onRowDel()" v-auth="'del'">
             <el-icon>
               <ele-Delete />
             </el-icon>
-            删除
+            {{ $t('message.formI18nButton.delete') }}
           </el-button>
         </el-form-item>
       </el-form>
       <el-table :data="tableData.data" style="width: 100%" @selection-change="handleSelectionChange" v-loading="tableData.loading">
         <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="标识" prop="key" min-width="130" show-overflow-tooltip v-col="'key'">
+        <!-- 标识 -->
+        <el-table-column :label="$t('message.device.tableI18nColumn.key')" prop="key" min-width="130" show-overflow-tooltip v-col="'key'">
           <template #default="{ row }"><copy :text="row.key"></copy></template>
         </el-table-column>
-        <el-table-column label="名称" prop="name" min-width="160" show-overflow-tooltip v-col="'name'" />
-        <el-table-column label="分类" prop="categoryName" align="center" width="140" show-overflow-tooltip v-col="'categoryName'" />
+        <!-- 名称 -->
+        <el-table-column :label="$t('message.device.tableI18nColumn.name')" prop="name" min-width="160" show-overflow-tooltip v-col="'name'" />
+        <!-- 分类 -->
+        <el-table-column :label="$t('message.device.tableI18nColumn.category')" prop="categoryName" min-width="160" align="center" show-overflow-tooltip v-col="'categoryName'" />
         <!--        <el-table-column label="消息协议" prop="messageProtocol" align="center" min-width="150" show-overflow-tooltip v-col="'messageProtocol'" />-->
-        <el-table-column label="接入方式" prop="transportProtocol" min-width="120" align="center" show-overflow-tooltip v-col="'transportProtocol'" />
-        <el-table-column label="类型" prop="deviceType" min-width="90" align="center" show-overflow-tooltip v-col="'deviceType'" />
-        <el-table-column prop="status" label="状态" min-width="90" align="center" v-col="'status'">
+        <!-- 接入方式 -->
+        <el-table-column :label="$t('message.device.tableI18nColumn.transportProtocol')" prop="transportProtocol" min-width="120" align="center" show-overflow-tooltip v-col="'transportProtocol'" />
+        <!-- 类型 -->
+        <el-table-column :label="$t('message.device.tableI18nColumn.deviceType')" prop="deviceType" min-width="90" align="center" show-overflow-tooltip v-col="'deviceType'" />
+        <!-- 状态 -->
+        <el-table-column prop="status" :label="$t('message.device.tableI18nColumn.status')" min-width="90" align="center" v-col="'status'">
           <template #default="scope">
-            <el-tag type="success" size="small" v-if="scope.row.status">已启用</el-tag>
-            <el-tag type="info" size="small" v-else>未启用</el-tag>
+            <el-tag type="success" size="small" v-if="scope.row.status">{{ $t('message.device.tableI18nStatus.enabled') }}</el-tag>
+            <el-tag type="info" size="small" v-else>{{ $t('message.device.tableI18nStatus.disabled') }}</el-tag>
           </template>
         </el-table-column>
-        <el-table-column label="操作" width="150" align="center" fixed="right">
+        <!-- 操作 -->
+        <el-table-column :label="$t('message.tableI18nColumn.operation')" align="center" fixed="right" :min-width="currentLang === 'en' ? 240 : 160">
           <template #default="scope">
+            <!-- 详情 -->
             <router-link :to="'/iotmanager/device/product/detail/' + scope.row.key" class="link-type" style="padding-right: 12px; font-size: 12px; color: #409eff">
-              <span>详情</span>
+              <span>{{ $t('message.tableI18nAction.detail') }}</span>
             </router-link>
-            <router-link :to="{ path: '/iotmanager/device/instance', query: { productKey: scope.row.key } }" class="link-type" style="padding-right: 12px; font-size: 12px; color: #409eff">设备管理</router-link>
-            <!--            <el-button size="small" text type="warning" @click="onOpenEditDic(scope.row)" v-auth="'edit'">修改</el-button>-->
-            <el-button size="small" text type="info" @click="onRowDel(scope.row)" v-auth="'del'">删除</el-button>
+            <!-- 设备管理 -->
+            <router-link :to="{ path: '/iotmanager/device/instance', query: { productKey: scope.row.key } }" class="link-type" style="padding-right: 12px; font-size: 12px; color: #409eff">
+              <span>{{ $t('message.device.tableI18nAction.deviceManagement') }}</span>
+            </router-link>
+            <!-- <el-button size="small" text type="warning" @click="onOpenEditDic(scope.row)" v-auth="'edit'">修改</el-button>-->
+             <!-- 删除 -->
+            <el-button size="small" text type="info" @click="onRowDel(scope.row)" v-auth="'del'">{{ $t('message.tableI18nAction.delete') }}</el-button>
           </template>
         </el-table-column>
       </el-table>
-      <pagination v-show="tableData.total > 0" :total="tableData.total" v-model:page="tableData.param.pageNum" v-model:limit="tableData.param.pageSize" @pagination="typeList" />
+      <pagination v-show="tableData.total > 0" :total="tableData.total" v-model:page="tableData.param.pageNum" v-model:limit="tableData.param.pageSize" @pagination="getList" />
     </el-card>
-    <EditDic ref="editDicRef" @typeList="typeList" />
+    <EditDic ref="editDicRef" @typeList="getList" />
   </div>
 </template>
 
-<script lang="ts">
+<script lang="ts" setup>
 import { toRefs, reactive, onMounted, ref, defineComponent } from "vue";
 import { ElMessageBox, ElMessage, FormInstance } from "element-plus";
 import EditDic from "./component/editPro.vue";
 import api from "/@/api/device";
+import { computed } from "vue";
+import { useI18n } from "vue-i18n";
+
+const { t, locale } = useI18n();
+
+const currentLang = computed(() => locale.value);
+
 
 // 定义接口来定义对象的类型
 interface TableDataRow {
@@ -99,121 +130,88 @@ interface TableDataRow {
   desc: string;
   createBy: string;
 }
-interface TableDataState {
-  keys: string[];
-  tableData: {
-    data: Array<TableDataRow>;
-    total: number;
-    loading: boolean;
-    param: {
-      pageNum: number;
-      pageSize: number;
-      name: string;
-      deviceType: string;
-      status: string;
-      dateRange: string[];
-    };
-  };
-}
 
-export default defineComponent({
-  name: "deviceproduct",
-  components: { EditDic },
-  setup() {
-    const addDicRef = ref();
-    const editDicRef = ref();
-    const queryRef = ref();
-    const state = reactive<TableDataState>({
-      keys: [],
-      tableData: {
-        data: [],
-        total: 0,
-        loading: false,
-        param: {
-          pageNum: 1,
-          pageSize: 20,
-          status: "",
-          name: "",
-          deviceType: "",
-          dateRange: [],
-        },
-      },
-    });
-    // 初始化表格数据
-    const initTableData = () => {
-      typeList();
-    };
-    const typeList = () => {
-      state.tableData.loading = true;
-      api.product
-        .getList(state.tableData.param)
-        .then((res: any) => {
-          state.tableData.data = res.product;
-          state.tableData.total = res.total;
-        })
-        .finally(() => (state.tableData.loading = false));
-    };
-    // 打开新增产品弹窗
-    const onOpenAddDic = () => {
-      editDicRef.value.openDialog();
-    };
-    // 打开修改产品弹窗
-    const onOpenEditDic = (row: TableDataRow) => {
-      editDicRef.value.openDialog(row);
-    };
-    // 删除产品
-    const onRowDel = (row?: TableDataRow) => {
-      let msg = "你确定要删除所选数据?";
-      let keys: string[] = [];
-      if (row) {
-        msg = `此操作将永久删除产品:“${row.name}”,是否继续?`;
-        keys = [row.key];
-      } else {
-        keys = state.keys;
-      }
-      if (keys.length === 0) {
-        ElMessage.error("请选择要删除的数据。");
-        return;
-      }
-      ElMessageBox.confirm(msg, "提示", {
-        confirmButtonText: "确认",
-        cancelButtonText: "取消",
-        type: "warning",
-      })
-        .then(() => {
-          api.product.delete(keys).then(() => {
-            ElMessage.success("删除成功");
-            typeList();
-          });
-        })
-        .catch(() => {});
-    };
-    // 页面加载时
-    onMounted(() => {
-      initTableData();
-    });
-    /** 重置按钮操作 */
-    const resetQuery = (formEl: FormInstance | undefined) => {
-      if (!formEl) return;
-      formEl.resetFields();
-      typeList();
-    };
-    // 多选框选中数据
-    const handleSelectionChange = (selection: TableDataRow[]) => {
-      state.keys = selection.map((item) => item.key);
-    };
-    return {
-      addDicRef,
-      editDicRef,
-      queryRef,
-      onOpenAddDic,
-      onOpenEditDic,
-      onRowDel,
-      typeList,
-      resetQuery,
-      handleSelectionChange,
-      ...toRefs(state),
-    };
+
+// const addDicRef = ref();
+const editDicRef = ref();
+const queryRef = ref();
+const keys = ref<any>([]);
+const tableData = ref({
+  data: [],
+  total: 0,
+  loading: false,
+  param: {
+    pageNum: 1,
+    pageSize: 20,
+    status: "",
+    name: "",
+    deviceType: "",
+    dateRange: [],
   },
+})
+// 初始化表格数据
+// const initTableData = () => {
+//   typeList();
+// };
+const getList = () => {
+  tableData.value.loading = true;
+  api.product
+    .getList(tableData.value.param)
+    .then((res: any) => {
+      tableData.value.data = res.product;
+      tableData.value.total = res.total;
+    })
+    .finally(() => (tableData.value.loading = false));
+};
+// 打开新增产品弹窗
+const onOpenAddDic = () => {
+  editDicRef.value.openDialog();
+};
+// 打开修改产品弹窗
+// const onOpenEditDic = (row: TableDataRow) => {
+//   editDicRef.value.openDialog(row);
+// };
+// 删除产品
+const onRowDel = (row?: TableDataRow) => {
+  let msg = t('message.tableI18nConfirm.deleteSelectedMessage');
+  let keysData: string[] = [];
+  if (row) {
+    msg = t('message.device.tableI18nConfirm.deleteProductMessage', { name: row.name });
+    keysData = [row.key];
+  } else {
+    keysData = keys.value;
+  }
+  if (keysData.length === 0) {
+    ElMessage.error(t('message.tableI18nConfirm.selectDataFirst'));
+    return;
+  }
+  ElMessageBox.confirm(msg, t('message.tableI18nConfirm.deleteTitle'), {
+    confirmButtonText: t('message.tableI18nConfirm.confirmText'),
+    cancelButtonText: t('message.tableI18nConfirm.cancelText'),
+    type: "warning",
+  })
+    .then(() => {
+      api.product.delete(keysData).then(() => {
+        ElMessage.success(t('message.tableI18nConfirm.deleteSuccess'));
+        getList();
+      });
+    })
+    .catch(() => {});
+};
+// 页面加载时
+onMounted(() => {
+  // initTableData();
+  getList()
 });
+/** 重置按钮操作 */
+// const resetQuery = (formEl: FormInstance | undefined) => {
+//   if (!formEl) return;
+//   formEl.resetFields();
+//   typeList();
+// };
+// 多选框选中数据
+const handleSelectionChange = (selection: TableDataRow[]) => {
+  keys.value = selection.map((item) => item.key);
+};
+
 </script>

+ 1 - 1
src/views/iot/device/template/index.vue

@@ -1,6 +1,6 @@
 <template>
 	<div class="page">
-		<el-card shadow="nover">
+		<el-card shadow="never">
 			<div class="search">
 				<el-form :model="params" inline ref="queryRef">
 					<el-form-item label="模板名称" prop="title">

+ 1 - 1
src/views/iot/ice104/device/index.vue

@@ -1,6 +1,6 @@
 <template>
 	<div class="page">
-		<el-card shadow="nover">
+		<el-card shadow="never">
 			<el-form :model="params" inline ref="queryRef" @submit.prevent>
 				<el-form-item label="设备名称" prop="title">
 					<el-input v-model="params.title" placeholder="请输入设备名称" clearable style="width: 240px" @keyup.enter.native="getList(1)" />

+ 1 - 1
src/views/iot/ice104/template/index.vue

@@ -1,7 +1,7 @@
 
 <template>
 	<div class="page">
-		<el-card shadow="nover">
+		<el-card shadow="never">
 			<el-form :model="params" inline ref="queryRef">
 				<el-form-item label="模版名称" prop="title">
 					<el-input v-model="params.title" placeholder="请输入模版名称" clearable style="width: 240px" @keyup.enter.native="getList(1)" />

+ 39 - 11
src/views/iot/iotmanager/dashboard.vue

@@ -7,7 +7,7 @@
 						<img :src="'/imgs/' + v.icoimg" class="icoimg" />
 						<div class="card-right">
 							<span class="font30">{{ v.allnum }}</span>
-							<div class="label">{{ v.num3 }}</div>
+							<div class="label">{{ $t('message.dashboard.'+v.num3) }}</div>
 						</div>
 					</div>
 					<div class="divider"></div>
@@ -15,14 +15,14 @@
 						<div class="flex" style="gap: 10px">
 							<img src="/@/assets/ok.svg" v-if="k < 2" alt="" class="icon" />
 							<img src="/@/assets/date.svg" v-else alt="" class="icon" />
-							<span class="info" :style="{ color: v.title1_bgcolor }">{{ v.title1 }}</span>
+							<span class="info" :style="{ color: v.title1_bgcolor }">{{ $t('message.dashboard.'+v.title1) }}</span>
 							<div class="num">{{ v.num1 }}</div>
 						</div>
 						<div class="split"></div>
 						<div class="flex" style="gap: 10px">
 							<img src="/@/assets/stop.svg" v-if="k < 2" alt="" class="icon" />
 							<img src="/@/assets/today.svg" v-else alt="" class="icon" />
-							<span class="info" :style="{ color: v.title2_bgcolor }">{{ v.title2 }}</span>
+							<span class="info" :style="{ color: v.title2_bgcolor }">{{ $t('message.dashboard.'+v.title2) }}</span>
 							<div class="num">{{ v.num2 }}</div>
 						</div>
 					</div>
@@ -31,11 +31,11 @@
 		</el-row>
 		<div class="chart-wrapper">
 			<div class="chart-item" style="flex: 2">
-				<div class="chart-title">设备消息</div>
+				<div class="chart-title">{{ t('message.dashboard.设备消息') }}</div>
 				<VueUiXy :dataset="dataset" :config="config" />
 			</div>
 			<div class="chart-item">
-				<div class="chart-title">预警类型</div>
+				<div class="chart-title">{{ t('message.dashboard.warningType') }}</div>
 				<VueUiDonut :dataset="pieDataset" :config="pieConfig" />
 			</div>
 		</div>
@@ -53,6 +53,11 @@ import api from '/@/api/datahub'
 import dayjs from 'dayjs'
 import AlarmList from '/@/views/iot/alarm/list/index.vue'
 import { useThemeChange } from '/@/hooks/useCommon'
+import { store } from '/@/store/index';
+
+import { useI18n } from 'vue-i18n'
+
+const { t } = useI18n()
 
 // 页面是显示状态
 let isActice = true
@@ -62,7 +67,7 @@ let isActice = true
 // 获取默认图形配置数据
 const chartData = getLineData({
 	xAxis: [],
-	legend: ['消息量', '预警量'],
+	legend: [t('message.dashboard.messageCount'), t('message.dashboard.warningCount')],
 	datas: [[], []],
 })
 
@@ -101,7 +106,7 @@ watch(
 	(list) => {
 		if (!list.length) return
 		list.forEach((item: any) => {
-			alarmTypeMap[item.value] = item.label
+			alarmTypeMap[item.value] = t('message.dashboard.'+item.label)
 		})
 
 		// 预警类型需要类型返回后才能请求
@@ -112,6 +117,24 @@ watch(
 	}
 )
 
+watch(() => store.state.themeConfig.themeConfig.globalI18n, (globalI18n) => {
+	if(globalI18n) {
+		// 更新圖表數據的圖例
+		dataset.value[0].name = t('message.dashboard.messageCount')
+		dataset.value[1].name = t('message.dashboard.warningCount')
+		// 更新餅圖數據 - 重新生成圖例標籤
+		if (pieDataset.value && pieDataset.value.length > 0) {
+			// 更新 alarmTypeMap 中的翻譯
+			alarm_type.value.forEach((item: any) => {
+					alarmTypeMap[item.value] = t('message.dashboard.'+item.label)
+			})
+			
+			// 重新調用API獲取最新數據並重新渲染
+			getDeviceAlarmLevelCount()
+		}
+	}
+});
+
 const editDicRef = ref()
 const detailRef = ref()
 const router = useRouter()
@@ -133,6 +156,7 @@ const state = reactive({
 			allnum: 0,
 			num1: 0,
 			num2: 0,
+			// num3: t('message.dashboard.product'),
 			num3: '产品',
 			num4: 'icon-zidingyibuju',
 			color1: '#6690F9',
@@ -148,6 +172,7 @@ const state = reactive({
 			allnum: 0,
 			num1: 0,
 			num2: 0,
+			// num3: t('message.dashboard.onlineDevice'),
 			num3: '在线设备',
 			num4: 'icon-putong',
 			color1: '#FF6462',
@@ -163,14 +188,15 @@ const state = reactive({
 			allnum: 0,
 			num1: 0,
 			num2: 0,
+			// num3: t('message.dashboard.deviceMessage'),
 			num3: '设备消息',
 			num4: 'icon-shidu',
 			color1: '#6690F9',
 			color2: '--el-color-success-lighter',
 			color3: '--el-color-success',
 			icoimg: 'dashboard-icon3.svg',
-			title1: '本月',
-			title2: '今日',
+			title1: "本月",
+			title2: "今日",
 			title1_bgcolor: '#4285F4',
 			title2_bgcolor: '#FFBB73',
 		},
@@ -178,7 +204,8 @@ const state = reactive({
 			allnum: 0,
 			num1: 0,
 			num2: 0,
-			num3: '设备告警',
+			// num3: t('message.dashboard.deviceWarning'),
+			num3: '设备警告',
 			num4: 'icon-zaosheng',
 			color1: '#6690F9',
 			color2: '--el-color-warning-lighter',
@@ -246,7 +273,7 @@ function getDeviceDataCount() {
 
 			const chartData = getLineData({
 				xAxis: month,
-				legend: ['消息量', '预警量'],
+				legend: [t('message.dashboard.messageCount'), t('message.dashboard.warningCount')],
 				datas: [list1, list2],
 			})
 
@@ -269,6 +296,7 @@ function getDeviceAlarmLevelCount() {
 					legend: list.map((item: any) => alarmTypeMap[item.Title]),
 					types: list.map((item: any) => item.Title),
 					datas: list.map((item: any) => [item.Value]),
+					totalText: t('message.dashboard.total')
 				})
 				pieConfig.value = pieData.config
 				pieDataset.value = pieData.dataset

+ 2 - 2
src/views/iot/network/server/component/list.vue

@@ -16,12 +16,12 @@
     <el-table-column align="center" label="操作" width="200" v-col="'auth'">
       <template #default="scope">
 
-        <el-button @click="toDetail(scope.row.id)" size="small" type="text" v-auth="'detail'">详情</el-button>
+        <el-button @click="toDetail(scope.row.id)" size="small" text type="primary" v-auth="'detail'">详情</el-button>
         <el-button size="small" link key="info" type="info" v-auth="'edit'" @click="toEdit(scope.row.id)">编辑</el-button>
 
         <el-popover placement="bottom" :width="170" trigger="click">
           <template #reference>
-            <el-button size="small" type="text" class="more-btn" @click="isShowMore = !isShowMore" v-auth="'more'">更多
+            <el-button size="small" text type="primary" class="more-btn" @click="isShowMore = !isShowMore" v-auth="'more'">更多
               <i style="margin-left: 2px;" :class="isShowMore ? 'fa fa-angle-down' : 'fa fa-angle-up'"></i>
             </el-button>
           </template>

+ 1 - 1
src/views/iot/network/server/index.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="page">
-    <el-card shadow="nover">
+    <el-card shadow="never">
       <el-form inline label-width="auto" @keyup.enter="searchData">
         <el-form-item label="服务器名称">
           <el-input style="width: 200px;" class="search-input" v-model="key" placeholder="请输入搜索关键字" clearable>

+ 2 - 2
src/views/iot/network/tunnel/component/list.vue

@@ -13,11 +13,11 @@
     </el-table-column>
     <el-table-column align="center" label="操作" v-col="'auth'" width="160">
       <template #default="scope">
-        <el-button @click="toDetail(scope.row.id)" size="small" v-auth="'detail'" type="text">详情</el-button>
+        <el-button @click="toDetail(scope.row.id)" size="small" v-auth="'detail'" text type="primary">详情</el-button>
         <el-button size="small" link key="info" type="info" v-auth="'edit'" @click="toEdit(scope.row.id)">编辑</el-button>
         <el-popover placement="bottom" :width="160" trigger="click">
           <template #reference>
-            <el-button size="small" type="text" v-auth="'more'" class="more-btn" @click="isShowMore = !isShowMore">更多
+            <el-button size="small" text type="primary" v-auth="'more'" class="more-btn" @click="isShowMore = !isShowMore">更多
               <i style="margin-left: 2px;" :class="isShowMore ? 'fa fa-angle-down' : 'fa fa-angle-up'"></i>
             </el-button>
           </template>

+ 1 - 1
src/views/iot/network/tunnel/index.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="page">
-    <el-card shadow="nover">
+    <el-card shadow="never">
       <el-form inline label-width="68px">
         <el-form-item label="通道名称" @keyup.enter.native="searchData()">
           <el-input style="width: 200px;margin-left: 20px;" class="search-input" v-model="key" placeholder="请输入搜索关键字" clearable>

+ 1 - 1
src/views/iot/noticeservices/config/index.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="page">
-    <el-card shadow="nover">
+    <el-card shadow="never">
       <el-row>
         <el-col :span="12" v-for="item in notice_send_gateway">
           <div class="ant-cards " v-if="item.status == 1">

+ 1 - 1
src/views/iot/noticeservices/config/setting.vue

@@ -1,5 +1,5 @@
 <template>
-	<el-card shadow="nover" class="page">
+	<el-card shadow="never" class="page">
 		<div class="system-user-search">
 			<el-form :model="tableData.param" ref="queryRef" inline @keyup.enter.native="dataList">
 				<el-form-item label="配置名称" prop="keyWord">

+ 1 - 1
src/views/iot/noticeservices/log/index.vue

@@ -1,6 +1,6 @@
 <template>
 	<div class="page">
-		<el-card shadow="nover">
+		<el-card shadow="never">
 			<el-form :model="tableData.param" ref="queryRef" inline label-width="68px">
 				<el-form-item label="发送时间" prop="dateRange">
 					<el-date-picker v-model="tableData.param.dateRange" style="width: 240px" value-format="YYYY-MM-DD" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker>

+ 2 - 2
src/views/iot/operate/remoteconf/index.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="page">
-    <el-card shadow="nover">
+    <el-card shadow="never">
       <h2>远程配置</h2>
       <div class="container">
         <div class="selectContainer">
@@ -53,7 +53,7 @@
           <el-table-column prop="gmtCreate" label="版本更新时间"></el-table-column>
           <el-table-column label="操作" width="200">
             <template #default="scope">
-              <el-button size="small" type="text" @click="look(scope.row, scope.$index)">查看</el-button>
+              <el-button size="small" text type="primary" @click="look(scope.row, scope.$index)">查看</el-button>
             </template>
           </el-table-column>
         </el-table>

+ 1 - 1
src/views/iot/ota-update/data/index.vue

@@ -1,5 +1,5 @@
 <template>
-	<el-card shadow="nover">
+	<el-card shadow="never">
 		<el-tabs v-model="activeTab">
 			<el-tab-pane label="版本分布" name="tab1">
 				<div class="flex">

+ 1 - 1
src/views/iot/ota-update/module/index.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="page">
-    <el-card shadow="nover">
+    <el-card shadow="never">
       <el-form :model="tableData.param" ref="queryRef" inline label-width="68px" @keyup.enter.native="getList(1)">
         <el-form-item label="模块名称" prop="name">
           <el-input v-model="tableData.param.name" placeholder="请输入模块名称" clearable style="width: 200px;" />

+ 2 - 2
src/views/iot/ota-update/update/component/batch.vue

@@ -32,13 +32,13 @@
       <el-table-column label="类型" prop="types" width="120" align="center">
         <template #default="scope">
           <el-tag type="success" size="small" v-if="scope.row.types == 0">验证</el-tag>
-          <el-tag type="primary" size="small" v-else>升级</el-tag>
+          <el-tag size="small" v-else>升级</el-tag>
         </template>
       </el-table-column>
       <el-table-column label="状态" prop="status" width="120" align="center">
         <template #default="scope">
           <!-- 0待升级 1升级中,2完成 -->
-          <el-tag type="primary" size="small" v-if="scope.row.status === 1">升级中</el-tag>
+          <el-tag size="small" v-if="scope.row.status === 1">升级中</el-tag>
           <el-tag type="success" size="small" v-else-if="scope.row.status === 2">完成</el-tag>
           <el-tag type="warning" size="small" v-else>待升级</el-tag>
         </template>

+ 1 - 1
src/views/iot/ota-update/update/component/device.vue

@@ -1,6 +1,6 @@
 <template>
 	<div>
-		<el-card shadow="nover">
+		<el-card shadow="never">
 			<div class="search">
 				<el-form inline ref="queryRef">
 					<el-form-item label="设备名称:" prop="name">

+ 3 - 3
src/views/iot/ota-update/update/index.vue

@@ -1,6 +1,6 @@
 <template>
 	<div class="page">
-		<el-card shadow="nover">
+		<el-card shadow="never">
 			<el-form :model="tableData.param" ref="queryRef" inline>
 				<el-form-item label="升级包名称" prop="keyWord">
 					<el-input
@@ -58,14 +58,14 @@
 				<el-table-column label="需要验证" prop="check" v-col="'check'" width="120" align="center">
 					<template #default="scope">
 						<!-- 1需要,2不需要  -->
-						<el-tag type="primary" size="small" v-if="scope.row.check === 1">需要</el-tag>
+						<el-tag size="small" v-if="scope.row.check === 1">需要</el-tag>
 						<el-tag type="danger" size="small" v-if="scope.row.check === 2">不需要</el-tag>
 					</template>
 				</el-table-column>
 				<el-table-column label="状态" prop="checkres" v-col="'checkres'" width="120" align="center">
 					<template #default="scope">
 						<!-- 升级包验证 0未验证,1验证中 2已验证 -->
-						<el-tag type="primary" size="small" v-if="scope.row.checkres === 1">验证中</el-tag>
+						<el-tag size="small" v-if="scope.row.checkres === 1">验证中</el-tag>
 						<el-tag type="success" size="small" v-if="scope.row.checkres === 2">已验证</el-tag>
 						<el-tag type="danger" size="small" v-if="scope.row.checkres === 0">未验证</el-tag>
 					</template>

+ 33 - 32
src/views/iot/projects/detail/device.vue

@@ -1,49 +1,49 @@
 <template>
   <div class="tab-content h-full">
-    <div class="subtitle"><span></span> <el-button type="primary" v-auth="'add'" size="small" @click="addDevice()">添加设备</el-button></div>
+    <div class="subtitle"><span></span> <el-button type="primary" v-auth="'add'" size="small" @click="addDevice()">{{ $t('message.projects.detail.device.actions.add') }}</el-button></div>
     <el-table :data="tableData" style="width: 100%" v-loading="loading" max-height="calc(100vh - 280px)">
-      <el-table-column label="标识" prop="key" min-width="150" show-overflow-tooltip v-col="'key'">
+      <el-table-column :label="$t('message.projects.detail.device.table.columns.key')" prop="key" min-width="150" show-overflow-tooltip>
         <template #default="{ row }">
           <copy :text="row.key"></copy>
         </template>
       </el-table-column>
-      <el-table-column label="设备名称" prop="name" min-width="160" show-overflow-tooltip v-col="'name'" />
-      <el-table-column label="设备类型" prop="product.deviceType" min-width="100" align="center" show-overflow-tooltip v-col="'deviceType'" />
-      <el-table-column label="所属产品" prop="productName" min-width="120" align="center" show-overflow-tooltip v-col="'productName'" />
-      <el-table-column prop="status" label="状态" min-width="80" align="center" v-col="'status'">
+      <el-table-column :label="$t('message.projects.detail.device.table.columns.name')" prop="name" min-width="160" show-overflow-tooltip />
+      <el-table-column :label="$t('message.projects.detail.device.table.columns.deviceType')" prop="product.deviceType" min-width="100" align="center" show-overflow-tooltip />
+      <el-table-column :label="$t('message.projects.detail.device.table.columns.productName')" prop="productName" min-width="120" align="center" show-overflow-tooltip />
+      <el-table-column prop="status" :label="$t('message.projects.detail.device.table.columns.status')" min-width="80" align="center">
         <template #default="scope">
-          <el-tag type="info" size="small" v-if="scope.row.status == 1">离线</el-tag>
-          <el-tag type="success" size="small" v-if="scope.row.status == 2">在线</el-tag>
-          <el-tag type="info" size="small" v-if="scope.row.status == 0">未启用</el-tag>
+          <el-tag type="info" size="small" v-if="scope.row.status == 1">{{ $t('message.projects.detail.device.table.statusTags.offline') }}</el-tag>
+          <el-tag type="success" size="small" v-if="scope.row.status == 2">{{ $t('message.projects.detail.device.table.statusTags.online') }}</el-tag>
+          <el-tag type="info" size="small" v-if="scope.row.status == 0">{{ $t('message.projects.detail.device.table.statusTags.inactive') }}</el-tag>
         </template>
       </el-table-column>
-      <el-table-column prop="lastOnlineTime" label="最后上线时间" align="center" width="160" v-col="'lastOnlineTime'"></el-table-column>
-      <el-table-column prop="desc" label="说明" show-overflow-tooltip v-col="'desc'"></el-table-column>
-      <el-table-column label="操作" width="120" align="center">
+      <el-table-column prop="lastOnlineTime" :label="$t('message.projects.detail.device.table.columns.lastOnlineTime')" align="center" width="160"></el-table-column>
+      <el-table-column prop="desc" :label="$t('message.projects.detail.device.table.columns.desc')" show-overflow-tooltip></el-table-column>
+      <el-table-column :label="$t('message.projects.detail.device.table.columns.actions')" width="170" align="center">
         <template #default="scope">
-          <el-button size="small" text type="primary" v-auth="'detail'" @click="$router.push('/iotmanager/device/instance/' + scope.row.key)">设备详情</el-button>
-          <el-button size="small" text type="warning" v-auth="'del'" @click="onDel(scope.row)">解绑</el-button>
+          <el-button size="small" text type="primary" v-auth="'detail'" @click="$router.push('/iotmanager/device/instance/' + scope.row.key)">{{ $t('message.projects.detail.device.table.actions.detail') }}</el-button>
+          <el-button size="small" text type="warning" v-auth="'del'" @click="onDel(scope.row)">{{ $t('message.projects.detail.device.table.actions.unbind') }}</el-button>
         </template>
       </el-table-column>
     </el-table>
     <pagination v-if="params.total" :total="params.total" v-model:page="params.pageNum" v-model:limit="params.pageSize" @pagination="getList()" />
-    <el-dialog title="添加设备" v-model="isShowDialog" width="400">
+    <el-dialog :title="$t('message.projects.detail.device.dialog.title')" v-model="isShowDialog" width="400">
       <el-form v-if="isShowDialog">
-        <el-form-item label="产品">
-          <el-select v-model="form.productKey" @change="getDivices" filterable placeholder="选择产品" :clearable="false" style="width: 100%">
+        <el-form-item :label="$t('message.projects.detail.device.form.labels.product')">
+          <el-select v-model="form.productKey" @change="getDivices" filterable :placeholder="$t('message.projects.detail.device.form.placeholders.product')" :clearable="false" style="width: 100%">
             <el-option v-for="item in productList" :key="item.value" :label="item.label" :value="item.value" />
           </el-select>
         </el-form-item>
-        <el-form-item label="设备" style="margin-bottom: 0;">
-          <el-select v-model="form.resourcesKey" filterable placeholder="选择设备" :clearable="false" style="width: 100%">
+        <el-form-item :label="$t('message.projects.detail.device.form.labels.device')" style="margin-bottom: 0;">
+          <el-select v-model="form.resourcesKey" filterable :placeholder="$t('message.projects.detail.device.form.placeholders.device')" :clearable="false" style="width: 100%">
             <el-option v-for="item in deviceList" :key="item.value" :label="item.label" :value="item.value" />
           </el-select>
         </el-form-item>
       </el-form>
       <template #footer>
         <span class="dialog-footer">
-          <el-button @click="onCancel">取 消</el-button>
-          <el-button type="primary" @click="onSubmit">确 定</el-button>
+          <el-button @click="onCancel">{{ $t('message.projects.detail.device.actions.cancel') }}</el-button>
+          <el-button type="primary" @click="onSubmit">{{ $t('message.projects.detail.device.actions.confirm') }}</el-button>
         </span>
       </template>
     </el-dialog>
@@ -57,20 +57,21 @@ import api from '/@/api/projects';
 import { useSearch } from '/@/hooks/useCommon';
 import { useRoute } from 'vue-router';
 import { ElMessage, ElMessageBox } from 'element-plus';
+import { useI18n } from 'vue-i18n';
 const route = useRoute();
+const { t } = useI18n();
 
 const props = defineProps({ resourcesTypes: Number })
 const isShowDialog = ref(false)
 const productList = ref<any[]>([])
 const deviceList = ref<any[]>([])
-const resourcesTypes = props.resourcesTypes
 const projectsCode = route.params.id
 
 const baseForm = {
   productKey: '',
   resourcesKey: '',
   projectsCode,
-  resourcesTypes
+  resourcesTypes: props.resourcesTypes
 }
 
 const form = reactive({
@@ -84,7 +85,7 @@ getSourceList();
 
 function getSourceList() {
   api.getProjectResourcesByCode({ projectsCode }).then((res: any) => {
-    params.keys = (res || []).filter((item: any) => item.resourcesTypes === resourcesTypes).map((item: any) => item.resourcesKey)
+    params.keys = (res || []).filter((item: any) => item.resourcesTypes === props.resourcesTypes).map((item: any) => item.resourcesKey)
     if (!params.keys.length) {
       params.pageNum = 1
       tableData.value = []
@@ -107,11 +108,11 @@ function onCancel() {
 }
 
 function onSubmit() {
-  if (!form.resourcesKey) return ElMessage('请先选择设备')
-  api.bindResources(form).then((res: any) => {
+  if (!form.resourcesKey) return ElMessage(t('message.projects.detail.device.messages.selectDeviceFirst'))
+  api.bindResources(form).then(() => {
     onCancel()
     getSourceList()
-    ElMessage.success('添加成功')
+    ElMessage.success(t('message.projects.detail.device.messages.addSuccess'))
   })
 }
 
@@ -120,18 +121,18 @@ function addDevice() {
 }
 
 function onDel(row: any) {
-  ElMessageBox.confirm(`确定要删除?`, '提示', {
-    confirmButtonText: '确认',
-    cancelButtonText: '取消',
+  ElMessageBox.confirm(t('message.projects.detail.device.messages.deleteConfirm'), t('message.projects.detail.device.messages.tip'), {
+    confirmButtonText: t('message.projects.detail.device.messages.confirm'),
+    cancelButtonText: t('message.projects.detail.device.messages.cancel'),
     type: 'warning',
   }).then(() => {
     api.unbindResources({
       projectsCode,
-      resourcesTypes,
+      resourcesTypes: props.resourcesTypes,
       resourcesKey: row.key
     }).then(() => {
       getSourceList()
-      ElMessage.success('解绑成功')
+      ElMessage.success(t('message.projects.detail.device.messages.unbindSuccess'))
     })
   });
 }

+ 6 - 6
src/views/iot/projects/detail/index.vue

@@ -1,20 +1,20 @@
 <template>
   <div class="page">
-    <el-card shadow="nover" class="small-padding">
+    <el-card shadow="never" class="small-padding">
       <el-tabs :model-value="'0'" size="small" class="h-full">
-        <el-tab-pane label="项目概况" name="0" lazy>
+        <el-tab-pane :label="$t('message.projects.detail.tabs.overview')" name="0" lazy>
           <InfoVue></InfoVue>
         </el-tab-pane>
-        <el-tab-pane label="网关和设备" name="1" lazy>
+        <el-tab-pane :label="$t('message.projects.detail.tabs.devices')" name="1" lazy>
           <DeviceVue :resourcesTypes="1"></DeviceVue>
         </el-tab-pane>
-        <el-tab-pane label="场景联动" name="4" lazy>
+        <el-tab-pane :label="$t('message.projects.detail.tabs.scene')" name="4" lazy>
           <SceneVue :resourcesTypes="4"></SceneVue>
         </el-tab-pane>
-        <el-tab-pane label="组态应用" name="2" lazy>
+        <el-tab-pane :label="$t('message.projects.detail.tabs.topo')" name="2" lazy>
           <TopoVue :resourcesTypes="2"></TopoVue>
         </el-tab-pane>
-        <el-tab-pane label="视频监控" name="3" lazy>
+        <el-tab-pane :label="$t('message.projects.detail.tabs.video')" name="3" lazy>
           <VideoVue :resourcesTypes="3"></VideoVue>
         </el-tab-pane>
       </el-tabs>

+ 8 - 8
src/views/iot/projects/detail/info.vue

@@ -1,14 +1,14 @@
 <template>
   <div class="tab-content" v-loading="laoding">
-    <div class="subtitle"><span></span> <el-button type="primary" v-auth="'edit'" size="small" @click="edit()">编辑</el-button></div>
+    <div class="subtitle"><span></span> <el-button type="primary" v-auth="'edit'" size="small" @click="edit()">{{ $t('message.projects.detail.info.actions.edit') }}</el-button></div>
     <el-descriptions class="margin-top" :column="2" border>
-      <el-descriptions-item label="项目名称">{{ data.name }} </el-descriptions-item>
-      <el-descriptions-item label="关键客户">{{ data.customName }} </el-descriptions-item>
-      <el-descriptions-item label="地区">{{ data.address ? JSON.parse(data.address)?.name : '' }} </el-descriptions-item>
-      <el-descriptions-item label="渠道商">{{ data.channelMerchants }} </el-descriptions-item>
-      <el-descriptions-item label="维修公司">{{ data.repairCompany }} </el-descriptions-item>
-      <el-descriptions-item label="维修电话">{{ data.repairMobile }} </el-descriptions-item>
-      <el-descriptions-item label="详细地址">{{ data.addressDetail }} </el-descriptions-item>
+      <el-descriptions-item :label="$t('message.projects.detail.info.descriptions.name')">{{ data.name }} </el-descriptions-item>
+      <el-descriptions-item :label="$t('message.projects.detail.info.descriptions.customName')">{{ data.customName }} </el-descriptions-item>
+      <el-descriptions-item :label="$t('message.projects.detail.info.descriptions.address')">{{ data.address ? JSON.parse(data.address)?.name : '' }} </el-descriptions-item>
+      <el-descriptions-item :label="$t('message.projects.detail.info.descriptions.channelMerchants')">{{ data.channelMerchants }} </el-descriptions-item>
+      <el-descriptions-item :label="$t('message.projects.detail.info.descriptions.repairCompany')">{{ data.repairCompany }} </el-descriptions-item>
+      <el-descriptions-item :label="$t('message.projects.detail.info.descriptions.repairMobile')">{{ data.repairMobile }} </el-descriptions-item>
+      <el-descriptions-item :label="$t('message.projects.detail.info.descriptions.addressDetail')">{{ data.addressDetail }} </el-descriptions-item>
     </el-descriptions>
     <EditForm ref="editFormRef" @getList="getInfo()"></EditForm>
   </div>

+ 32 - 31
src/views/iot/projects/detail/scene.vue

@@ -1,45 +1,45 @@
 <template>
   <div class="tab-content h-full">
-    <div class="subtitle"><span></span> <el-button type="primary" v-auth="'add'" size="small" @click="addDevice()">添加场景</el-button></div>
+    <div class="subtitle"><span></span> <el-button type="primary" v-auth="'add'" size="small" @click="addDevice()">{{ $t('message.projects.detail.scene.actions.add') }}</el-button></div>
     <el-table :data="tableData" style="width: 100%" v-loading="loading" max-height="calc(100vh - 280px)">
-      <el-table-column prop="id" label="ID" min-width="100" show-overflow-tooltip></el-table-column>
-      <el-table-column prop="name" label="场景名称" show-overflow-tooltip></el-table-column>
-      <el-table-column prop="sceneType" label="触发方式" align="center">
+      <el-table-column prop="id" :label="$t('message.projects.detail.scene.table.columns.id')" min-width="100" show-overflow-tooltip></el-table-column>
+      <el-table-column prop="name" :label="$t('message.projects.detail.scene.table.columns.name')" show-overflow-tooltip></el-table-column>
+      <el-table-column prop="sceneType" :label="$t('message.projects.detail.scene.table.columns.sceneType')" align="center">
         <template #default="scope">
-          <el-tag size="small" v-if="scope.row.sceneType == 'device'">设备触发</el-tag>
-          <el-tag size="small" v-if="scope.row.sceneType == 'manual'">手动触发</el-tag>
-          <el-tag size="small" v-if="scope.row.sceneType == 'timer'">定时触发</el-tag>
+          <el-tag size="small" v-if="scope.row.sceneType == 'device'">{{ $t('message.projects.detail.scene.table.sceneTypeTags.device') }}</el-tag>
+          <el-tag size="small" v-if="scope.row.sceneType == 'manual'">{{ $t('message.projects.detail.scene.table.sceneTypeTags.manual') }}</el-tag>
+          <el-tag size="small" v-if="scope.row.sceneType == 'timer'">{{ $t('message.projects.detail.scene.table.sceneTypeTags.timer') }}</el-tag>
         </template>
       </el-table-column>
 
-      <el-table-column prop="status" label="状态" align="center">
+      <el-table-column prop="status" :label="$t('message.projects.detail.scene.table.columns.status')" align="center">
         <template #default="scope">
-          <el-tag size="small" type="success" v-if="scope.row.status == 1">启用</el-tag>
-          <el-tag size="small" type="info" v-if="scope.row.status == 0">禁用</el-tag>
+          <el-tag size="small" type="success" v-if="scope.row.status == 1">{{ $t('message.projects.detail.scene.table.statusTags.enabled') }}</el-tag>
+          <el-tag size="small" type="info" v-if="scope.row.status == 0">{{ $t('message.projects.detail.scene.table.statusTags.disabled') }}</el-tag>
         </template>
       </el-table-column>
-      <el-table-column prop="description" label="场景描述" show-overflow-tooltip></el-table-column>
-      <el-table-column prop="createdAt" label="创建时间" width="160" align="center"></el-table-column>
-      <el-table-column label="操作" width="120" align="center">
+      <el-table-column prop="description" :label="$t('message.projects.detail.scene.table.columns.description')" show-overflow-tooltip></el-table-column>
+      <el-table-column prop="createdAt" :label="$t('message.projects.detail.scene.table.columns.createdAt')" width="160" align="center"></el-table-column>
+      <el-table-column :label="$t('message.projects.detail.scene.table.columns.actions')" width="150" align="center">
         <template #default="scope">
-          <el-button size="small" text type="primary" v-auth="'detail'" @click="$router.push('/iotmanager/scene/manage/' + scope.row.id)">场景详情</el-button>
-          <el-button size="small" text type="warning" v-auth="'del'" @click="onDel(scope.row)">解绑</el-button>
+          <el-button size="small" text type="primary" v-auth="'detail'" @click="$router.push('/iotmanager/scene/manage/' + scope.row.id)">{{ $t('message.projects.detail.scene.table.actions.detail') }}</el-button>
+          <el-button size="small" text type="warning" v-auth="'del'" @click="onDel(scope.row)">{{ $t('message.projects.detail.scene.table.actions.unbind') }}</el-button>
         </template>
       </el-table-column>
     </el-table>
     <pagination v-if="params.total" :total="params.total" v-model:page="params.pageNum" v-model:limit="params.pageSize" @pagination="getList()" />
-    <el-dialog title="添加场景" v-model="isShowDialog" width="400">
+    <el-dialog :title="$t('message.projects.detail.scene.dialog.title')" v-model="isShowDialog" width="400">
       <el-form v-if="isShowDialog">
-        <el-form-item label="场景" style="margin-bottom: 0;">
-          <el-select v-model="form.resourcesKey" filterable placeholder="选择场景" :clearable="false" style="width: 100%">
+        <el-form-item :label="$t('message.projects.detail.scene.form.labels.scene')" style="margin-bottom: 0;">
+          <el-select v-model="form.resourcesKey" filterable :placeholder="$t('message.projects.detail.scene.form.placeholders.scene')" :clearable="false" style="width: 100%">
             <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
           </el-select>
         </el-form-item>
       </el-form>
       <template #footer>
         <span class="dialog-footer">
-          <el-button @click="onCancel">取 消</el-button>
-          <el-button type="primary" @click="onSubmit">确 定</el-button>
+          <el-button @click="onCancel">{{ $t('message.projects.detail.scene.actions.cancel') }}</el-button>
+          <el-button type="primary" @click="onSubmit">{{ $t('message.projects.detail.scene.actions.confirm') }}</el-button>
         </span>
       </template>
     </el-dialog>
@@ -53,18 +53,19 @@ import api from '/@/api/projects';
 import { useSearch } from '/@/hooks/useCommon';
 import { useRoute } from 'vue-router';
 import { ElMessage, ElMessageBox } from 'element-plus';
+import { useI18n } from 'vue-i18n';
 const route = useRoute();
+const { t } = useI18n();
 
 const props = defineProps({ resourcesTypes: Number })
 const isShowDialog = ref(false)
 const options = ref<any[]>([])
-const resourcesTypes = props.resourcesTypes
 const projectsCode = route.params.id
 
 const baseForm = {
   resourcesKey: '',
   projectsCode,
-  resourcesTypes
+  resourcesTypes: props.resourcesTypes
 }
 
 const form = reactive({
@@ -78,7 +79,7 @@ getSourceList();
 
 function getSourceList() {
   api.getProjectResourcesByCode({ projectsCode }).then((res: any) => {
-    params.ids = (res || []).filter((item: any) => item.resourcesTypes === resourcesTypes).map((item: any) => item.resourcesKey)
+    params.ids = (res || []).filter((item: any) => item.resourcesTypes === props.resourcesTypes).map((item: any) => item.resourcesKey)
     if (!params.ids.length) {
       params.pageNum = 1
       tableData.value = []
@@ -101,11 +102,11 @@ function onCancel() {
 }
 
 function onSubmit() {
-  if (!form.resourcesKey) return ElMessage('请先选择场景')
-  api.bindResources(form).then((res: any) => {
+  if (!form.resourcesKey) return ElMessage(t('message.projects.detail.scene.messages.selectSceneFirst'))
+  api.bindResources(form).then(() => {
     onCancel()
     getSourceList()
-    ElMessage.success('添加成功')
+    ElMessage.success(t('message.projects.detail.scene.messages.addSuccess'))
   })
 }
 
@@ -114,18 +115,18 @@ function addDevice() {
 }
 
 function onDel(row: any) {
-  ElMessageBox.confirm(`确定要解绑该场景?`, '提示', {
-    confirmButtonText: '确认',
-    cancelButtonText: '取消',
+  ElMessageBox.confirm(t('message.projects.detail.scene.messages.unbindConfirm'), t('message.projects.detail.scene.messages.tip'), {
+    confirmButtonText: t('message.projects.detail.scene.messages.confirm'),
+    cancelButtonText: t('message.projects.detail.scene.messages.cancel'),
     type: 'warning',
   }).then(() => {
     api.unbindResources({
       projectsCode,
-      resourcesTypes,
+      resourcesTypes: props.resourcesTypes,
       resourcesKey: row.id
     }).then(() => {
       getSourceList()
-      ElMessage.success('解绑成功')
+      ElMessage.success(t('message.projects.detail.scene.messages.unbindSuccess'))
     })
   });
 }

+ 28 - 26
src/views/iot/projects/detail/topo.vue

@@ -1,32 +1,32 @@
 <template>
   <div class="tab-content h-full">
-    <div class="subtitle"><span></span> <el-button type="primary" v-auth="'add'" size="small" @click="addDevice()">添加组态</el-button></div>
+    <div class="subtitle"><span></span> <el-button type="primary" v-auth="'add'" size="small" @click="addDevice()">{{ $t('message.projects.detail.topo.actions.add') }}</el-button></div>
     <el-table :data="tableData" style="width: 100%" v-loading="loading" max-height="calc(100vh - 280px)">
-      <el-table-column prop="id" label="ID" width="100" show-overflow-tooltip></el-table-column>
-      <el-table-column prop="name" label="组态图名称" show-overflow-tooltip></el-table-column>
-      <el-table-column prop="createdAt" label="创建时间" min-width="100" align="center"></el-table-column>
-      <el-table-column prop="updatedAt" label="更新时间" min-width="100" align="center"></el-table-column>
-      <el-table-column label="操作" width="170" align="center">
+      <el-table-column prop="id" :label="$t('message.projects.detail.topo.table.columns.id')" width="100" show-overflow-tooltip></el-table-column>
+      <el-table-column prop="name" :label="$t('message.projects.detail.topo.table.columns.name')" show-overflow-tooltip></el-table-column>
+      <el-table-column prop="createdAt" :label="$t('message.projects.detail.topo.table.columns.createdAt')" min-width="100" align="center"></el-table-column>
+      <el-table-column prop="updatedAt" :label="$t('message.projects.detail.topo.table.columns.updatedAt')" min-width="100" align="center"></el-table-column>
+      <el-table-column :label="$t('message.projects.detail.topo.table.columns.actions')" width="210" align="center">
         <template #default="scope">
-          <el-button size="small" text type="primary" v-auth="'detail'" v-if="!scope.row.folderName" @click="view(scope.row)">预览</el-button>
-          <el-button size="small" text type="primary" v-auth="'edit'" @click="edit(scope.row)">编辑组态图</el-button>
-          <el-button size="small" text type="warning" v-auth="'del'" @click="onDel(scope.row)">解绑</el-button>
+          <el-button size="small" text type="primary" v-auth="'detail'" v-if="!scope.row.folderName" @click="view(scope.row)">{{ $t('message.projects.detail.topo.table.actions.preview') }}</el-button>
+          <el-button size="small" text type="primary" v-auth="'edit'" @click="edit(scope.row)">{{ $t('message.projects.detail.topo.table.actions.edit') }}</el-button>
+          <el-button size="small" text type="warning" v-auth="'del'" @click="onDel(scope.row)">{{ $t('message.projects.detail.topo.table.actions.unbind') }}</el-button>
         </template>
       </el-table-column>
     </el-table>
     <pagination v-if="params.total" :total="params.total" v-model:page="params.pageNum" v-model:limit="params.pageSize" @pagination="getList()" />
-    <el-dialog title="添加组态" v-model="isShowDialog" width="400">
+    <el-dialog :title="$t('message.projects.detail.topo.dialog.title')" v-model="isShowDialog" width="400">
       <el-form v-if="isShowDialog">
-        <el-form-item label="组态" style="margin-bottom: 0;">
-          <el-select v-model="form.resourcesKey" filterable placeholder="选择组态" :clearable="false" style="width: 100%">
+        <el-form-item :label="$t('message.projects.detail.topo.form.labels.topo')" style="margin-bottom: 0;">
+          <el-select v-model="form.resourcesKey" filterable :placeholder="$t('message.projects.detail.topo.form.placeholders.topo')" :clearable="false" style="width: 100%">
             <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
           </el-select>
         </el-form-item>
       </el-form>
       <template #footer>
         <span class="dialog-footer">
-          <el-button @click="onCancel">取 消</el-button>
-          <el-button type="primary" @click="onSubmit">确 定</el-button>
+          <el-button @click="onCancel">{{ $t('message.projects.detail.topo.actions.cancel') }}</el-button>
+          <el-button type="primary" @click="onSubmit">{{ $t('message.projects.detail.topo.actions.confirm') }}</el-button>
         </span>
       </template>
     </el-dialog>
@@ -34,25 +34,27 @@
 </template>
 
 <script lang="ts" setup>
-import { reactive, ref } from 'vue';
+import { reactive, ref, toRef } from 'vue';
 import topoApi from '/@/api/configuration';
 import api from '/@/api/projects';
 import { useSearch } from '/@/hooks/useCommon';
 import { useRoute } from 'vue-router';
 import { ElMessage, ElMessageBox } from 'element-plus';
+import { useI18n } from 'vue-i18n';
 import getOrigin from '/@/utils/origin'
 const route = useRoute();
+const { t } = useI18n();
 
 const props = defineProps({ resourcesTypes: Number })
 const isShowDialog = ref(false)
 const options = ref<any[]>([])
-const resourcesTypes = props.resourcesTypes
+const resourcesTypes = toRef(props, 'resourcesTypes')
 const projectsCode = route.params.id
 
 const baseForm = {
   resourcesKey: '',
   projectsCode,
-  resourcesTypes
+  resourcesTypes: resourcesTypes.value
 }
 
 const form = reactive({
@@ -66,7 +68,7 @@ getSourceList();
 
 function getSourceList() {
   api.getProjectResourcesByCode({ projectsCode }).then((res: any) => {
-    params.ids = (res || []).filter((item: any) => item.resourcesTypes === resourcesTypes).map((item: any) => item.resourcesKey)
+    params.ids = (res || []).filter((item: any) => item.resourcesTypes === resourcesTypes.value).map((item: any) => item.resourcesKey)
     if (!params.ids.length) {
       params.pageNum = 1
       tableData.value = []
@@ -88,11 +90,11 @@ function onCancel() {
 }
 
 function onSubmit() {
-  if (!form.resourcesKey) return ElMessage('请先选择组态')
-  api.bindResources(form).then((res: any) => {
+  if (!form.resourcesKey) return ElMessage(t('message.projects.detail.topo.messages.selectTopoFirst'))
+  api.bindResources(form).then(() => {
     onCancel()
     getSourceList()
-    ElMessage.success('添加成功')
+    ElMessage.success(t('message.projects.detail.topo.messages.addSuccess'))
   })
 }
 
@@ -116,18 +118,18 @@ const edit = (row: any) => {
 };
 
 function onDel(row: any) {
-  ElMessageBox.confirm(`确定要解绑该组态?`, '提示', {
-    confirmButtonText: '确认',
-    cancelButtonText: '取消',
+  ElMessageBox.confirm(t('message.projects.detail.topo.messages.unbindConfirm'), t('message.projects.detail.topo.messages.tip'), {
+    confirmButtonText: t('message.projects.detail.topo.messages.confirm'),
+    cancelButtonText: t('message.projects.detail.topo.messages.cancel'),
     type: 'warning',
   }).then(() => {
     api.unbindResources({
       projectsCode,
-      resourcesTypes,
+      resourcesTypes: resourcesTypes.value,
       resourcesKey: row.id
     }).then(() => {
       getSourceList()
-      ElMessage.success('解绑成功')
+      ElMessage.success(t('message.projects.detail.topo.messages.unbindSuccess'))
     })
   });
 }

+ 34 - 32
src/views/iot/projects/detail/video.vue

@@ -1,42 +1,42 @@
 <template>
   <div class="tab-content h-full">
-    <div class="subtitle"><span></span> <el-button type="primary" v-auth="'add'" size="small" @click="addDevice()">添加监控</el-button></div>
+    <div class="subtitle"><span></span> <el-button type="primary" v-auth="'add'" size="small" @click="addDevice()">{{ $t('message.projects.detail.video.actions.add') }}</el-button></div>
     <el-table :data="tableData" style="width: 100%" v-loading="loading" max-height="calc(100vh - 280px)">
-      <el-table-column prop="name" label="设备名称" min-width="130" align="center" show-overflow-tooltip></el-table-column>
-      <el-table-column prop="deviceId" label="设备ID" min-width="210" align="center" show-overflow-tooltip></el-table-column>
-      <el-table-column prop="name" label="通道名称" min-width="140" align="center" show-overflow-tooltip></el-table-column>
-      <el-table-column prop="model" label="型号" width="100" align="center" show-overflow-tooltip></el-table-column>
-      <el-table-column prop="manufacturer" label="厂商" width="100" align="center" show-overflow-tooltip></el-table-column>
-      <el-table-column prop="liveStatus" label="状态" width="100" align="center" :formatter="formatLiveStatus" show-overflow-tooltip></el-table-column>
-      <el-table-column prop="keepAliveTime" label="最后心跳时间" :formatter="formatTime" width="170" align="center" show-overflow-tooltip></el-table-column>
-      <el-table-column prop="registerTime" label="注册时间" :formatter="formatTime" width="170" align="center" show-overflow-tooltip></el-table-column>
-      <el-table-column prop="updateAt" label="更新时间" :formatter="formatTime" width="170" align="center" show-overflow-tooltip></el-table-column>
-      <el-table-column label="操作" width="120" align="center" fixed="right">
+      <el-table-column prop="name" :label="$t('message.projects.detail.video.table.columns.deviceName')" min-width="130" align="center" show-overflow-tooltip></el-table-column>
+      <el-table-column prop="deviceId" :label="$t('message.projects.detail.video.table.columns.deviceId')" min-width="210" align="center" show-overflow-tooltip></el-table-column>
+      <el-table-column prop="name" :label="$t('message.projects.detail.video.table.columns.channelName')" min-width="140" align="center" show-overflow-tooltip></el-table-column>
+      <el-table-column prop="model" :label="$t('message.projects.detail.video.table.columns.model')" width="100" align="center" show-overflow-tooltip></el-table-column>
+      <el-table-column prop="manufacturer" :label="$t('message.projects.detail.video.table.columns.manufacturer')" width="100" align="center" show-overflow-tooltip></el-table-column>
+      <el-table-column prop="liveStatus" :label="$t('message.projects.detail.video.table.columns.liveStatus')" width="100" align="center" :formatter="formatLiveStatus" show-overflow-tooltip></el-table-column>
+      <el-table-column prop="keepAliveTime" :label="$t('message.projects.detail.video.table.columns.keepAliveTime')" :formatter="formatTime" width="170" align="center" show-overflow-tooltip></el-table-column>
+      <el-table-column prop="registerTime" :label="$t('message.projects.detail.video.table.columns.registerTime')" :formatter="formatTime" width="170" align="center" show-overflow-tooltip></el-table-column>
+      <el-table-column prop="updateAt" :label="$t('message.projects.detail.video.table.columns.updateAt')" :formatter="formatTime" width="170" align="center" show-overflow-tooltip></el-table-column>
+      <el-table-column :label="$t('message.projects.detail.video.table.columns.actions')" width="120" align="center" fixed="right">
         <template #default="scope">
-          <el-button size="small" text type="primary" v-auth="'detail'" @click="view(scope.row)">查看监控</el-button>
-          <el-button size="small" text type="warning" v-auth="'del'" @click="onDel(scope.row)">解绑</el-button>
+          <el-button size="small" text type="primary" v-auth="'detail'" @click="view(scope.row)">{{ $t('message.projects.detail.video.table.actions.view') }}</el-button>
+          <el-button size="small" text type="warning" v-auth="'del'" @click="onDel(scope.row)">{{ $t('message.projects.detail.video.table.actions.unbind') }}</el-button>
         </template>
       </el-table-column>
     </el-table>
-    <el-dialog title="添加监控" v-model="isShowDialog" width="400">
+    <el-dialog :title="$t('message.projects.detail.video.dialog.addTitle')" v-model="isShowDialog" width="400">
       <el-form v-if="isShowDialog">
-        <el-form-item label="监控" style="margin-bottom: 0">
-          <el-select v-model="form.resourcesKey" filterable placeholder="选择监控" :clearable="false" style="width: 100%">
+        <el-form-item :label="$t('message.projects.detail.video.form.labels.video')" style="margin-bottom: 0">
+          <el-select v-model="form.resourcesKey" filterable :placeholder="$t('message.projects.detail.video.form.placeholders.video')" :clearable="false" style="width: 100%">
             <el-option v-for="item in options" :disabled="item.disabled" :key="item.value" :label="item.label" :value="item.value" />
           </el-select>
         </el-form-item>
       </el-form>
       <template #footer>
         <span class="dialog-footer">
-          <el-button @click="onCancel">取 消</el-button>
-          <el-button type="primary" @click="onSubmit">确 定</el-button>
+          <el-button @click="onCancel">{{ $t('message.projects.detail.video.actions.cancel') }}</el-button>
+          <el-button type="primary" @click="onSubmit">{{ $t('message.projects.detail.video.actions.confirm') }}</el-button>
         </span>
       </template>
     </el-dialog>
-    <el-dialog title="预览监控" v-model="isShowPreviewDialog" width="90vh">
+    <el-dialog :title="$t('message.projects.detail.video.dialog.previewTitle')" v-model="isShowPreviewDialog" width="90vh">
       <template #header>
         <span class="dialog-footer">
-          <el-button type="primary" size="small" @click="openToNewWindow">新窗口预览</el-button>
+          <el-button type="primary" size="small" @click="openToNewWindow">{{ $t('message.projects.detail.video.actions.openInNewWindow') }}</el-button>
         </span>
       </template>
       <iframe v-if="isShowPreviewDialog" :src="previewUrl" style="width: 100%; height: 50vh"></iframe>
@@ -45,13 +45,15 @@
 </template>
 
 <script lang="ts" setup>
-import { reactive, ref } from "vue";
+import { reactive, ref, toRef } from "vue";
 import api from "/@/api/projects";
 import { useRoute } from "vue-router";
 import { dayjs, ElMessage, ElMessageBox } from "element-plus";
 import axios from "axios";
 import { getMediaOrigin } from "/@/utils/origin";
+import { useI18n } from 'vue-i18n';
 const route = useRoute();
+const { t } = useI18n();
 
 const props = defineProps({ resourcesTypes: Number });
 const isShowDialog = ref(false);
@@ -61,13 +63,13 @@ const previewUrl = ref("");
 const options = ref<any[]>([]);
 const tableData = ref<any[]>([]);
 const channels = ref<any[]>([]);
-const resourcesTypes = props.resourcesTypes!;
+const resourcesTypes = toRef(props, 'resourcesTypes');
 const projectsCode = route.params.id;
 
 const baseForm = {
   resourcesKey: "",
   projectsCode,
-  resourcesTypes,
+  resourcesTypes: resourcesTypes.value,
 };
 
 const form = reactive({
@@ -78,7 +80,7 @@ getOptions();
 
 function getSourceList() {
   api.getProjectResourcesByCode({ projectsCode }).then((res: any) => {
-    const ids = (res || []).filter((item: any) => item.resourcesTypes === resourcesTypes).map((item: any) => item.resourcesKey);
+    const ids = (res || []).filter((item: any) => item.resourcesTypes === resourcesTypes.value).map((item: any) => item.resourcesKey);
 
     const tableDataList: any[] = [];
 
@@ -126,11 +128,11 @@ function onCancel() {
 }
 
 function onSubmit() {
-  if (!form.resourcesKey) return ElMessage("请先选择监控");
+  if (!form.resourcesKey) return ElMessage(t('message.projects.detail.video.messages.selectVideoFirst'));
   api.bindResources(form).then(() => {
     onCancel();
     getSourceList();
-    ElMessage.success("添加成功");
+    ElMessage.success(t('message.projects.detail.video.messages.addSuccess'));
   });
 }
 
@@ -149,20 +151,20 @@ function openToNewWindow() {
 }
 
 function onDel(row: any) {
-  ElMessageBox.confirm(`确定要解绑该监控?`, "提示", {
-    confirmButtonText: "确认",
-    cancelButtonText: "取消",
+  ElMessageBox.confirm(t('message.projects.detail.video.messages.unbindConfirm'), t('message.projects.detail.video.messages.tip'), {
+    confirmButtonText: t('message.projects.detail.video.messages.confirm'),
+    cancelButtonText: t('message.projects.detail.video.messages.cancel'),
     type: "warning",
   }).then(() => {
     api
       .unbindResources({
         projectsCode,
-        resourcesTypes,
+        resourcesTypes: resourcesTypes.value,
         resourcesKey: row.deviceId + "-" + row.channelId,
       })
       .then(() => {
         getSourceList();
-        ElMessage.success("解绑成功");
+        ElMessage.success(t('message.projects.detail.video.messages.unbindSuccess'));
       });
   });
 }
@@ -173,6 +175,6 @@ function formatTime(row: any, column: any) {
 
 function formatLiveStatus(row: any, column: any) {
   // 0 代表空闲,1 代表正在调用 invite,2 代表正在拉流
-  return row[column.property] === 0 ? "空闲" : row[column.property] === 1 ? "调用中" : "拉流中";
+  return row[column.property] === 0 ? t('message.projects.detail.video.liveStatusTags.idle') : row[column.property] === 1 ? t('message.projects.detail.video.liveStatusTags.inviting') : t('message.projects.detail.video.liveStatusTags.streaming');
 }
 </script>

Some files were not shown because too many files changed in this diff