Browse Source

Merge branch 'professional2' into feature-workflow

# Conflicts:
#	src/views/system/monitor/task-logs/index.vue
kagg886 2 months ago
parent
commit
f45d993cf4
100 changed files with 3998 additions and 1954 deletions
  1. 2 2
      .env.development
  2. 2 2
      .env.golocal
  3. 1 1
      src/components/vue3cron/vue3cron.vue
  4. 1 1
      src/hooks/useCommon.ts
  5. 34 2
      src/i18n/index.ts
  6. 184 0
      src/i18n/lang/en.ts
  7. 177 0
      src/i18n/lang/zh-cn.ts
  8. 185 0
      src/i18n/lang/zh-tw.ts
  9. 28 0
      src/i18n/pages/formI18n/en.ts
  10. 36 0
      src/i18n/pages/formI18n/zh-cn.ts
  11. 36 0
      src/i18n/pages/formI18n/zh-tw.ts
  12. 222 0
      src/i18n/pages/iotCard/en.ts
  13. 222 0
      src/i18n/pages/iotCard/zh-cn.ts
  14. 222 0
      src/i18n/pages/iotCard/zh-tw.ts
  15. 144 0
      src/i18n/pages/iotmanagerI18n/en.ts
  16. 142 0
      src/i18n/pages/iotmanagerI18n/zh-cn.ts
  17. 144 0
      src/i18n/pages/iotmanagerI18n/zh-tw.ts
  18. 34 0
      src/i18n/pages/tableI18n/en.ts
  19. 34 0
      src/i18n/pages/tableI18n/zh-cn.ts
  20. 34 0
      src/i18n/pages/tableI18n/zh-tw.ts
  21. 1 0
      src/layout/navBars/breadcrumb/setings.vue
  22. 12 0
      src/layout/navBars/breadcrumb/user.vue
  23. 3 1
      src/layout/navBars/tagsView/tagsView.vue
  24. 14 19
      src/layout/navMenu/subItem.vue
  25. 27 20
      src/layout/navMenu/vertical.vue
  26. 2 0
      src/main.ts
  27. 2 1
      src/store/modules/themeConfig.ts
  28. 4 0
      src/theme/dark.scss
  29. 3 3
      src/utils/dataUiOptions.ts
  30. 12 3
      src/utils/origin.ts
  31. 2 2
      src/views/apihub/plugin.vue
  32. 1 1
      src/views/designer/index.vue
  33. 32 19
      src/views/iot/alarm/list/index.vue
  34. 101 119
      src/views/iot/alarm/log/index.vue
  35. 3 3
      src/views/iot/cascade/index.vue
  36. 1 1
      src/views/iot/certificate/index.vue
  37. 1 1
      src/views/iot/configuration/list/index.vue
  38. 1 1
      src/views/iot/configuration/screen/index.vue
  39. 1 1
      src/views/iot/dataAnalysis/IndicatorAggregation/index.vue
  40. 1 1
      src/views/iot/dataAnalysis/exponentialTrend/index.vue
  41. 2 2
      src/views/iot/device-tree/tree/index.vue
  42. 169 0
      src/views/iot/device/category/component/addOrEdit.vue
  43. 0 175
      src/views/iot/device/category/component/edit.vue
  44. 76 83
      src/views/iot/device/category/index.vue
  45. 1 1
      src/views/iot/device/channel/index.vue
  46. 41 46
      src/views/iot/device/instance/component/excel.vue
  47. 1 1
      src/views/iot/device/instance/detail.vue
  48. 1 1
      src/views/iot/device/instance/index.vue
  49. 8 11
      src/views/iot/device/product/component/dataParse.vue
  50. 1 1
      src/views/iot/device/product/component/editPro.vue
  51. 258 181
      src/views/iot/device/product/detail.vue
  52. 145 147
      src/views/iot/device/product/index.vue
  53. 1 1
      src/views/iot/device/template/index.vue
  54. 1 1
      src/views/iot/ice104/device/index.vue
  55. 1 1
      src/views/iot/ice104/template/index.vue
  56. 39 11
      src/views/iot/iotmanager/dashboard.vue
  57. 1 1
      src/views/iot/network/server/index.vue
  58. 1 1
      src/views/iot/network/tunnel/index.vue
  59. 1 1
      src/views/iot/noticeservices/config/index.vue
  60. 1 1
      src/views/iot/noticeservices/config/setting.vue
  61. 1 1
      src/views/iot/noticeservices/log/index.vue
  62. 1 1
      src/views/iot/operate/remoteconf/index.vue
  63. 1 1
      src/views/iot/ota-update/data/index.vue
  64. 1 1
      src/views/iot/ota-update/module/index.vue
  65. 2 2
      src/views/iot/ota-update/update/component/batch.vue
  66. 1 1
      src/views/iot/ota-update/update/component/device.vue
  67. 3 3
      src/views/iot/ota-update/update/index.vue
  68. 1 1
      src/views/iot/projects/detail/index.vue
  69. 1 1
      src/views/iot/projects/filter/index.vue
  70. 1 1
      src/views/iot/projects/list/index.vue
  71. 2 2
      src/views/iot/property/attribute/index.vue
  72. 2 2
      src/views/iot/property/dossier/index.vue
  73. 2 2
      src/views/iot/property/relationship/index.vue
  74. 4 3
      src/views/iot/rule-engine/edit.vue
  75. 26 22
      src/views/iot/rule-engine/index.vue
  76. 1 1
      src/views/iot/rule-engine/log.vue
  77. 1 1
      src/views/iot/scene/list/index.vue
  78. 4 4
      src/views/iot/scene/manage/detail.vue
  79. 1 1
      src/views/iot/scene/manage/index.vue
  80. 3 3
      src/views/limits/frontEnd/btn/index.vue
  81. 1 1
      src/views/limits/frontEnd/page/index.vue
  82. 388 299
      src/views/login/index.vue
  83. 1 1
      src/views/modules/assess/index.vue
  84. 332 369
      src/views/modules/iotCard/dashboard.vue
  85. 32 27
      src/views/modules/iotCard/index/detail.vue
  86. 38 33
      src/views/modules/iotCard/index/index.vue
  87. 31 29
      src/views/modules/iotCard/platformManage/addOrEditItem.vue
  88. 29 20
      src/views/modules/iotCard/platformManage/index.vue
  89. 161 168
      src/views/modules/policy/index/components/addItem.vue
  90. 58 71
      src/views/modules/policy/index/components/execute.vue
  91. 1 1
      src/views/modules/policy/index/index.vue
  92. 1 1
      src/views/modules/policy/runLog/index.vue
  93. 1 1
      src/views/modules/tenant/index.vue
  94. 1 1
      src/views/personal/index.vue
  95. 1 1
      src/views/system/api/index.vue
  96. 1 1
      src/views/system/application/index.vue
  97. 2 2
      src/views/system/basicConfig/index.vue
  98. 1 1
      src/views/system/city/index.vue
  99. 1 1
      src/views/system/config/index.vue
  100. 1 1
      src/views/system/datahub/modeling/index.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'

+ 2 - 2
.env.golocal

@@ -6,5 +6,5 @@ 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_RULE_SERVER_URL = ':9090'
+VITE_MEDIA_SERVER_URL = ':8080'

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

@@ -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>

+ 1 - 1
src/hooks/useCommon.ts

@@ -39,7 +39,7 @@ export function useSearch<T>(api: any, resKey: string, expandParams?: any) {
 
   const loading = ref(false)
 
-  const tableData = ref<T[] | any[]>([])
+  const tableData = ref<T[] | any>([])
 
   const getList = async (pageNum?: number) => {
     typeof pageNum === 'number' && (params.pageNum = pageNum);

+ 34 - 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,17 @@ 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';
 
 // 定义语言国际化内容
 /**
@@ -28,7 +47,10 @@ const messages = {
 			...nextZhcn,
 			...pagesLoginZhcn,
 			...pagesFormI18nZhcn,
-		},
+			...pagesTable18nZhcn,
+			...pagesIotmanagerZhcn,
+			iotCard: pagesIotcardZhcn
+		}
 	},
 	[enLocale.name]: {
 		...enLocale,
@@ -36,7 +58,10 @@ const messages = {
 			...nextEn,
 			...pagesLoginEn,
 			...pagesFormI18nEn,
-		},
+			...pagesTable18nEn,
+			...pagesIotmanagerEn,
+			iotCard: pagesIotcardEn
+		},	
 	},
 	[zhtwLocale.name]: {
 		...zhtwLocale,
@@ -44,6 +69,9 @@ const messages = {
 			...nextZhtw,
 			...pagesLoginZhtw,
 			...pagesFormI18nZhtw,
+			...pagesTable18nZhtw,
+			...pagesIotmanagerZhtw,
+			iotCard: pagesIotcardZhtw
 		},
 	},
 };
@@ -53,4 +81,8 @@ export const i18n = createI18n({
 	locale: store.state.themeConfig.themeConfig.globalI18n,
 	fallbackLocale: zhcnLocale.name,
 	messages,
+	silentTranslationWarn: true,
+	missingWarn: false,
+	silentFallbackWarn: true,
+	fallbackWarn: false
 });

+ 184 - 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,183 @@ 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 Management",
+			productClass: "Product Category",
+			product: "Product",
+			productDetail: "Product Details",
+			device: "Device",
+			device1: "Device",
+			deviceDetail: "Device Details",
+			deviceDetail1: "Device Details",
+			configuration: "Configuration Management",
+			configurationList: "Configuration List",
+			screen: "Dashboard Design",
+			ruleEngine: "Rule Engine",
+			ruleInstance: "Rule Orchestration",
+			gridComponent: "Grid Component",
+			serverManager: "Server Management",
+			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 Management",
+			dataAnalysis: "Data Analysis",
+			indicatorTrend: "Indicator Trend",
+			indicatorAggregation: "Indicator Aggregation",
+			certificateManagement: "Certificate Management",
+			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 Management",
+			remoteConfiguration: "Remote Configuration",
+			otaUpgrade: "OTA Upgrade",
+			dataAnalysis1: "Data Analysis",
+			moduleManagement: "Module Management",
+			upgradePackageManagement: "Upgrade Package Management",
+			upgradePackageDetail: "Details",
+			notificationService: "Notification Service",
+			notificationLog: "Notification Log",
+			notificationConfiguration: "Notification Configuration",
+			notificationConfigurationManagement: "Notification Configuration Management",
+			cascadeManagement: "Cascade Management",
+		},
+		iotCard: {
+			title: "IoT Card",
+			homePage: "Home Page",
+			iotCardManagement: "IoT Card Management",
+			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 Management",
+			deviceArchiveManagement: "Device Archive Management",
+			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",
+		},
+		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 Management",
+			regionManagement: "Region Management",
+			organizationManagement: "Organization Management",
+			positionManagement: "Position Management",
+			roleManagement: "Role Management",
+			userManagement: "User Management",
+			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 Management",
+			pluginManagement: "Plugin Management",
+			applicationManagement: "Application Management",
+			menuManagement: "Menu Management",
+			parameterManagement: "Parameter Management",
+			dictionaryManagement: "Dictionary Management",
+			dictionaryDataManagement: "Dictionary Data Management",
+			scheduledTask: "Scheduled Task",
+			apiManagement: "API Management",
+			cityManagement: "City Management",
+			remoteUpgrade: "Remote Upgrade",
+			authorizationConfiguration: "Authorization Configuration",
+			tenantManagement: "Tenant Management",
+			// 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 Management",
+			clientManagement: "Client Management",
+			pluginManagement: "Plugin Management",
+
+		},
+		policyManagement: {
+			title: "Policy Management",
+			policyControl: "Policy Control",
+		}
+	}
 };

+ 177 - 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,176 @@ 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: "数据源管理",
+
+		},
+		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: "策略控制",
+		}
+	}
 };

+ 185 - 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,176 @@ 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: "數據源管理",
+
+		},
+		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: "政策控制",
+		}
+	}
 };

+ 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: '新增成功'
+			}
+		}
+	}
+};

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

@@ -0,0 +1,144 @@
+/*
+ * @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",
+		producDetail: {
+			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"
+		},
+		tableI18nColumn: {
+			categoryName: "Category Name",
+			desc: "Description",
+			sort: "Sort",
+			key: "Key",
+			name: "Name",
+			category: "Category",
+			transportProtocol: "Transport Protocol",
+			deviceType: "Device Type",
+			status: "Status",
+		},
+		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"
+		},
+		formI18nLabel: {
+			keyword: "Keyword",
+			type: "Type",
+			status: "Status"
+		},
+		formI18nPlaceholder: {
+			keyword: "Enter name or identifier",
+			type: "Type",
+			status: "Status"
+		},
+		formI18nOption: {
+			device: "Device",
+			gateway: "Gateway",
+			subDevice: "Sub Device",
+			on: "Enabled",
+			off: "Disabled"
+		},
+		formI18nButton: {
+			addProduct: "Add Product"
+		}
+	},
+	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"
+		}
+	}
+};

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

@@ -0,0 +1,142 @@
+/*
+ * @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:12:36
+ * @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: "产品",
+		producDetail: {
+			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: "数据解析"
+		},
+		tableI18nColumn: {
+			categoryName: "分类名称",
+			desc: "描述",
+			sort: "排序",
+			key: "标识",
+			name: "名称",
+			category: "分类",
+			transportProtocol: "接入方式",
+			deviceType: "类型",
+			status: "状态",
+		},
+		tableI18nAlarmType: {
+		},
+		tableI18nStatus: {
+			enabled: "已启用",
+			disabled: "未启用"
+		},
+		tableI18nConfirm: {
+			deleteCatrgoryMessage: "此操作将永久删除分类:{name}, 是否继续?",
+			deleteProductMessage: "此操作将永久删除产品:{name},是否继续?"
+		},
+		tableI18nAction: {
+			addCategory: "新增分类",
+			editCategory: "编辑分类",
+			deviceManagement: "设备管理",
+		},
+		formI18nLabel: {
+			keyword: "关键字",
+			type: "类型",
+			status: "状态"
+		},
+		formI18nPlaceholder: {
+			keyword: "输入名称或标识",
+			type: "类型",
+			status: "启用状态"
+		},
+		formI18nOption: {
+			device: "设备",
+			gateway: "网关",
+			subDevice: "子设备",
+			on: "已启用",
+			off: "未启用"
+		}
+			
+	},
+	alarm: {
+		tableI18nColumn: {
+			id: "ID",
+			alarmType: "告警类型",
+			alarmLevel: "规则级别",
+			ruleName: "规则名称",
+			productKey: "产品标识",
+			deviceKey: "设备标识",
+			alarmStatus: "告警状态",
+			alarmTime: "告警时间",
+		},
+		tableI18nAlarmType: {
+			ruleAlarm: "规则告警",
+			deviceSelfAlarm: "设备自主告警",
+			ruleAlarmUpgrade: "规侧告警升级"
+		},
+		tableI18nStatus: {
+			unprocessed: "未处理",
+			processed: "已处理",
+			ignored: "已忽略"
+		}
+	}
+};

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

@@ -0,0 +1,144 @@
+/*
+ * @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: "產品",
+		producDetail: {
+			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: "資料解析"
+		},
+		tableI18nColumn: {
+			categoryName: "分類名稱",
+			desc: "描述",
+			sort: "排序",
+			key: "標識",
+			name: "名稱",
+			category: "分類",
+			transportProtocol: "接入方式",
+			deviceType: "類型",
+			status: "狀態"
+		},
+		tableI18nAlarmType: {
+		},
+		tableI18nStatus: {
+			enabled: "已啟用",
+			disabled: "未啟用"
+		},
+		tableI18nConfirm: {
+			deleteCatrgoryMessage: "此操作將永久刪除分類:{name}, 是否繼續?",
+			deleteProductMessage: "此操作將永久刪除產品:{name},是否繼續?"
+		},
+		tableI18nAction: {
+			addCategory: "添加分類",
+			editCategory: "編輯分類",
+			deviceManagement: "設備管理"
+		},
+		formI18nLabel: {
+			keyword: "關鍵字",
+			type: "類型",
+			status: "狀態"
+		},
+		formI18nPlaceholder: {
+			keyword: "輸入名稱或標識",
+			type: "類型",
+			status: "啟用狀態"
+		},
+		formI18nOption: {
+			device: "設備",
+			gateway: "網關",
+			subDevice: "子設備",
+			on: "已啟用",
+			off: "未啟用"
+		},
+		formI18nButton: {
+			addProduct: "新增產品"
+		}
+	},
+	alarm: {
+		tableI18nColumn: {
+			id: "ID",
+			alarmType: "告警類型",
+			alarmLevel: "規則級別",
+			ruleName: "規則名稱",
+			productKey: "產品標識",
+			deviceKey: "設備標識",
+			alarmStatus: "告警狀態",
+			alarmTime: "告警時間",
+		},
+		tableI18nAlarmType: {
+			ruleAlarm: "規則告警",
+			deviceSelfAlarm: "設備自主告警",
+			ruleAlarmUpgrade: "規側告警升級"
+		},
+		tableI18nStatus: {
+			unprocessed: "未處理",
+			processed: "已處理",
+			ignored: "已忽略"
+		}
+	}
+};

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

@@ -0,0 +1,34 @@
+/*
+ * @Author: vera_min vera_min@163.com
+ * @Date: 2025-08-06 01:16:57
+ * @LastEditors: vera_min vera_min@163.com
+ * @LastEditTime: 2025-08-06 14:37:43
+ * @FilePath: /sagoo-admin-ui/src/i18n/pages/tableI18n/en.ts
+ * @Description: 表格国际化 - 英文
+ */
+// 定义内容
+export default {
+	tableI18nColumn: {
+		id: "ID",
+		operation: "Operation",
+		des: "Description",
+	},
+	tableI18nConfirm: {
+		deleteTitle: "Confirm",
+		confirmText: "Delete",
+		cancelText: "Cancel",
+		deleteSuccess: "Delete Success",
+		addSuccess: "Create Success",
+		addFailed: "Create Failed",
+		editSuccess: "Edit Success",
+		deleteSelectedMessage: "Are you sure you want to delete the selected data?",
+		selectDataFirst: "Please select the data to delete first."
+	},
+	tableI18nAction: {
+		detail: "Detail",
+		handle: "Handle",
+		add: "Add",
+		edit: "Edit",
+		delete: "Delete"
+	}
+};

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

@@ -0,0 +1,34 @@
+/*
+ * @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",
+		operation: "操作",
+		des: "描述",
+	},
+	tableI18nConfirm: {
+		deleteTitle: "提示",
+		confirmText: "删除",
+		cancelText: "取消",
+		deleteSuccess: "删除成功",
+		addSuccess: "新增成功",
+		addFailed: "新增失败",
+		editSuccess: "编辑成功",
+		deleteSelectedMessage: "你确定要删除所选数据?",
+		selectDataFirst: "请选择要删除的数据。"
+	},
+	tableI18nAction: {
+		detail: "详情",
+		handle: "处理",
+		add: "新增",
+		edit: "编辑",
+		delete: "删除"
+	}
+};

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

@@ -0,0 +1,34 @@
+/*
+ * @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",
+		operation: "操作",
+		des: "描述",
+	},
+	tableI18nConfirm: {
+		deleteTitle: "提示",
+		confirmText: "刪除",
+		cancelText: "取消",
+		deleteSuccess: "刪除成功",
+		addSuccess: "新增成功",
+		addFailed: "新增失敗",
+		editSuccess: "編輯成功",
+		deleteSelectedMessage: "你確定要刪除所選數據?",
+		selectDataFirst: "請選擇要刪除的數據。",
+	},
+	tableI18nAction: {
+		detail: "詳情",
+		handle: "處理",
+		add: "新增",
+		edit: "編輯",
+		delete: "刪除"
+	}
+};

+ 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>

+ 27 - 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>
@@ -47,6 +62,7 @@ export default defineComponent({
 		});
 		// 获取父级菜单数据
 		const menuLists = computed(() => {
+			console.log(props.menuList)
 			return <any>props.menuList;
 		});
 		// 获取布局配置信息
@@ -89,12 +105,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

@@ -25,6 +25,8 @@ import { parseTime } from '/@/utils/gfast'
 import { getOptionValue, setItems } from '/@/utils/items'
 
 const app = createApp(App);
+// 取消 vue warn
+app.config.warnHandler = function () { }
 
 directive(app);
 other.elSvg(app);

+ 2 - 1
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 优化
@@ -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;

+ 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,
                 }

+ 12 - 3
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
@@ -35,8 +35,7 @@ export function getOtherServersOrigin(urlStr: string = '') {
 
 // 规则引擎
 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 +44,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 +55,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
 }

+ 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>

+ 3 - 3
src/views/iot/cascade/index.vue

@@ -1,7 +1,7 @@
 <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>
           <span class="font30">{{ count.PlatformTotal }}</span>
@@ -21,7 +21,7 @@
           </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>
           <span class="font30">{{ count.DeviceTotal }}</span>
@@ -42,7 +42,7 @@
         </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>

+ 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.formI18nButton.cancel') }}</el-button>
+					<el-button type="primary" @click="onSubmit" :loading="loading">{{ ruleForm.id ? $t('message.formI18nButton.edit') : $t('message.formI18nButton.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" type="text" @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">

+ 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>

+ 1 - 1
src/views/iot/device/instance/detail.vue

@@ -194,7 +194,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>

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

@@ -1,6 +1,6 @@
 <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">

+ 8 - 11
src/views/iot/device/product/component/dataParse.vue

@@ -5,7 +5,7 @@
 			<el-icon v-else @click="toggleFullScreen" title="退出全屏"><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>
@@ -58,22 +58,19 @@ 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 +89,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 +107,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

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

@@ -111,7 +111,7 @@ 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";
 
 interface RuleFormState {
   id: number;

+ 258 - 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.producDetail.enable')" :inactive-text="$t('message.device.producDetail.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.producDetail.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.producDetail.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.producDetail.productIdentifier')"><copy :text="detail.key"></copy></el-descriptions-item>
+					<!-- 产品分类 -->
+					<el-descriptions-item :label="$t('message.device.producDetail.productCategory')">{{ detail.categoryName }}</el-descriptions-item>
+					<!-- 设备类型 -->
+					<el-descriptions-item :label="$t('message.device.producDetail.deviceType')">{{ detail.deviceType }}</el-descriptions-item>
+					<!-- 产品图片 -->
+					<el-descriptions-item :label="$t('message.device.producDetail.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.producDetail.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.producDetail.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.producDetail.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.producDetail.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.producDetail.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.producDetail.propertyIdentifier')" align="center" prop="key" />
+							<!-- 属性名称 -->
+							<el-table-column :label="$t('message.device.producDetail.propertyName')" prop="name" show-overflow-tooltip />
+							<!-- 数据类型 -->
+							<el-table-column prop="valueType" :label="$t('message.device.producDetail.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.producDetail.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.producDetail.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.producDetail.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.producDetail.readonly') }}</el-tag>
+									<!-- 读写 -->
+									<el-tag type="success" size="small" v-else>{{ $t('message.device.producDetail.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.producDetail.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.producDetail.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.producDetail.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.producDetail.functionIdentifier')" align="center" prop="key" />
+							<el-table-column :label="$t('message.device.producDetail.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.producDetail.eventDefinition')" name="event">
 						<div class="wu-title">
-							<div class="title">事件定义</div>
+							<div class="title">{{$t('message.device.producDetail.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.producDetail.eventIdentifier')" align="center" prop="key" />
+							<!-- 事件名称 -->
+							<el-table-column :label="$t('message.device.producDetail.name')" prop="name" show-overflow-tooltip />
+							<!-- 事件级别 -->
+							<el-table-column prop="level" :label="$t('message.device.producDetail.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.producDetail.normal') }}</el-tag>
+									<!-- 警告 -->
+									<el-tag type="warning" size="small" v-if="scope.row.level == 1">{{ $t('message.device.producDetail.warning') }}</el-tag>
+									<!-- 紧急 -->
+									<el-tag type="danger" size="small" v-if="scope.row.level == 2">{{ $t('message.device.producDetail.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.producDetail.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.producDetail.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.producDetail.propertyIdentifier')" align="center" prop="key" />
+							<!-- 属性名称 -->
+							<el-table-column :label="$t('message.device.producDetail.propertyName')" prop="name" show-overflow-tooltip />
+							<!-- 数据类型 -->
+							<el-table-column prop="valueType" :label="$t('message.device.producDetail.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.producDetail.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.producDetail.readonly') }}</el-tag>
+									<!-- 读写 -->
+									<el-tag type="success" size="small" v-else>{{ $t('message.device.producDetail.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.producDetail.importModel') }}
 							</el-button>
 						</el-upload>
+						<!-- 导出物模型 -->
 						<el-button size="small" type="default" class="ml10" @click="onRowExport()">
 							<el-icon>
 								<ele-Download />
 							</el-icon>
-							导出物模型
+							{{ $t('message.device.producDetail.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.producDetail.deviceAccess')" name="3">
 				<deviceIn></deviceIn>
 			</el-tab-pane>
-			<el-tab-pane label="数据解析" name="4" lazy>
+			<!-- 数据解析 -->
+			<el-tab-pane :label="$t('message.device.producDetail.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,54 @@ 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: [],
 			},
 		});
+		// const state = reactive<TableDataState>({
+		// 	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: [],
+		// 		},
+		// 	},
+		// });
 
 		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 +452,7 @@ export default defineComponent({
 
 		//根据不同类型获取列表
 		const getList = () => {
-			switch (state.activetab) {
+			switch (activetab.value) {
 				case 'attr':
 					getproperty();
 					break;
@@ -392,34 +469,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 +517,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 +559,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

+ 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>

+ 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>

+ 1 - 1
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">

+ 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>

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

@@ -1,6 +1,6 @@
 <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>
           <InfoVue></InfoVue>

+ 1 - 1
src/views/iot/projects/filter/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>

+ 1 - 1
src/views/iot/projects/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>

+ 2 - 2
src/views/iot/property/attribute/index.vue

@@ -28,7 +28,7 @@
 			</el-form-item>
 		</el-form>
 		<div class="page page-full-part flex-row gap-4">
-			<el-card style="width: 250px" shadow="nover">
+			<el-card style="width: 250px" shadow="never">
 				<el-tree
 					:data="mergedData"
 					:props="defaultProps"
@@ -51,7 +51,7 @@
 					</template>
 				</el-tree>
 			</el-card>
-			<el-card class="flex1" shadow="nover">
+			<el-card class="flex1" shadow="never">
 				<div class="page page-full">
 					<el-table :data="tableData" @selection-change="handleSelectionChange" style="width: 100%" row-key="id" v-loading="loading">
 						<el-table-column type="selection" width="55" align="center" />

+ 2 - 2
src/views/iot/property/dossier/index.vue

@@ -23,7 +23,7 @@
 			</el-form-item>
 		</el-form>
 		<div class="page page-full-part flex-row gap-4">
-			<el-card style="width: 250px;" shadow="nover">
+			<el-card style="width: 250px;" shadow="never">
 				<el-tree :data="mergedData" :props="defaultProps" accordion default-expand-all @node-click="handleNodeClick" :node-key="'id'" highlight-current>
 					<template #default="{ node, data }">
 						<div class="custom-tree-node">
@@ -38,7 +38,7 @@
 					</template>
 				</el-tree>
 			</el-card>
-			<el-card class="flex1" shadow="nover">
+			<el-card class="flex1" shadow="never">
 				<div class="page page-full">
 					<el-table :data="tableData" style="width: 100%" @selection-change="handleSelectionChange" row-key="id" v-loading="loading">
 						<el-table-column type="selection" width="55" align="center" />

+ 2 - 2
src/views/iot/property/relationship/index.vue

@@ -28,7 +28,7 @@
 			</el-form-item>
 		</el-form>
 		<div class="page page-full-part flex-row gap-4">
-			<el-card style="width: 250px;" shadow="nover">
+			<el-card style="width: 250px;" shadow="never">
 				<el-tree :data="treeData" :props="defaultProps" accordion default-expand-all @node-click="handleNodeClick" :node-key="'id'" highlight-current>
 					<template #default="{ node, data }">
 						<div class="custom-tree-node">
@@ -37,7 +37,7 @@
 					</template>
 				</el-tree>
 			</el-card>
-			<el-card class="flex1" shadow="nover">
+			<el-card class="flex1" shadow="never">
 				<div class="page page-full">
 					<el-table :data="tableData" style="width: 100%" @selection-change="handleSelectionChange" row-key="id" v-loading="loading">
 						<el-table-column type="selection" width="55" align="center" />

+ 4 - 3
src/views/iot/rule-engine/edit.vue

@@ -32,6 +32,7 @@ import { ruleRequired } from "/@/utils/validator";
 import { ElMessage } from "element-plus";
 import { getToken } from "/@/utils/auth";
 import { v4 as uuid } from "uuid";
+import { getRuleServerOrigin } from "/@/utils/origin";
 
 const emit = defineEmits(["getList"]);
 
@@ -76,7 +77,7 @@ const onSubmit = async () => {
     if (!formData.id) {
       const id = uuid();
       await axios.post(
-        import.meta.env.VITE_RULE_SERVER_URL + "/api/v1/rules/" + id,
+        getRuleServerOrigin("/api/v1/rules/" + id),
         {
           ruleChain: {
             id: id,
@@ -100,7 +101,7 @@ const onSubmit = async () => {
       formData.flowId = id;
     } else {
       // 找到规则
-      const { data } = (await axios.get(import.meta.env.VITE_RULE_SERVER_URL + "/api/v1/rules/" + formData.flowId, { headers }).catch(() => {
+      const { data } = (await axios.get(getRuleServerOrigin("/api/v1/rules/" + formData.flowId), { headers }).catch(() => {
         ElMessage.error("规则不存在");
       })) as any;
 
@@ -109,7 +110,7 @@ const onSubmit = async () => {
       data.ruleChain.additionalInfo.description = formData.expound;
 
       // 保存
-      await axios.post(import.meta.env.VITE_RULE_SERVER_URL + "/api/v1/rules/" + formData.flowId, data, { headers });
+      await axios.post(getRuleServerOrigin("/api/v1/rules/" + formData.flowId), data, { headers });
     }
   } else if (props.model === "node-red") {
     if (!formData.id) {

+ 26 - 22
src/views/iot/rule-engine/index.vue

@@ -1,23 +1,22 @@
 <template>
   <div class="page">
-    <el-card shadow="nover">
+    <el-card shadow="never">
       <el-form inline>
         <el-form-item label="名称">
           <el-input v-model="searchParams.keyWord" placeholder="请输入关键字" clearable @keyup.enter="handleSearch" />
         </el-form-item>
         <el-form-item label="类型">
-          <el-select v-model="searchParams.types" placeholder="请选择类型" clearable @clear="() => searchParams.types = -1" @change="handleSearch">
+          <el-select v-model="searchParams.types" placeholder="请选择类型" clearable @clear="() => (searchParams.types = -1)" @change="handleSearch">
             <el-option label="主链" :value="1" />
             <el-option label="子链" :value="2" />
             <el-option label="全部" :value="-1" />
           </el-select>
         </el-form-item>
         <el-form-item label="状态">
-          <el-select v-model="searchParams.status" placeholder="请选择状态" clearable @clear="() => searchParams.status = -1" @change="handleSearch">
+          <el-select v-model="searchParams.status" placeholder="请选择状态" clearable @clear="() => (searchParams.status = -1)" @change="handleSearch">
             <el-option label="已启动" :value="1" />
             <el-option label="已停止" :value="0" />
             <el-option label="全部" :value="-1" />
-
           </el-select>
         </el-form-item>
         <el-form-item>
@@ -43,7 +42,7 @@
         <el-table-column type="index" label="序号" width="80" align="center" />
         <el-table-column prop="types" label="类型" width="70" align="center">
           <template #default="scope">
-            <el-tag type="primary" size="small" v-if="scope.row.types == 1">主链</el-tag>
+            <el-tag size="small" v-if="scope.row.types == 1">主链</el-tag>
             <el-tag type="success" size="small" v-else>子链</el-tag>
           </template>
         </el-table-column>
@@ -80,6 +79,7 @@ import { useSearch } from "/@/hooks/useCommon";
 import EditForm from "./edit.vue";
 import axios from "axios";
 import { getToken } from "/@/utils/auth";
+import { getRuleServerOrigin } from "/@/utils/origin";
 
 const editFormRef = ref();
 
@@ -87,28 +87,32 @@ const editFormRef = ref();
 const model: "node-red" | "sagoo-rule" = import.meta.env.VITE_RULE_MODEL;
 
 const searchParams = reactive({
-  keyWord: '',
+  keyWord: "",
   types: -1,
-  status: -1
+  status: -1,
 });
 
 // 监听搜索参数变化,确保在API请求中正确传递搜索条件
-watch(searchParams, (newVal: any) => {
-  // 处理搜索参数的值
-  for (const key in newVal) {
-    if (newVal[key] === '' || newVal[key] === null) {
-      // 如果是类型或状态字段,在清除时设置为-1(全部)
-      if (key === 'types' || key === 'status') {
-        params[key] = -1;
-        searchParams[key] = -1; // 同步更新searchParams
+watch(
+  searchParams,
+  (newVal: any) => {
+    // 处理搜索参数的值
+    for (const key in newVal) {
+      if (newVal[key] === "" || newVal[key] === null) {
+        // 如果是类型或状态字段,在清除时设置为-1(全部)
+        if (key === "types" || key === "status") {
+          params[key] = -1;
+          searchParams[key] = -1; // 同步更新searchParams
+        } else {
+          delete params[key];
+        }
       } else {
-        delete params[key];
+        params[key] = newVal[key];
       }
-    } else {
-      params[key] = newVal[key];
     }
-  }
-}, { deep: true });
+  },
+  { deep: true }
+);
 
 const { params, tableData, getList, loading } = useSearch<any[]>(api.getList, "Data", searchParams);
 
@@ -148,7 +152,7 @@ const addOrEdit = async (row?: any) => {
 const setStatus = async (row: any, status: number) => {
   if (model === "sagoo-rule") {
     axios
-      .get(`${import.meta.env.VITE_RULE_SERVER_URL}/api/v1/rule/${row.flowId}/${status ? "enable" : "stop"}`, { headers })
+      .get(getRuleServerOrigin(`/api/v1/rule/${row.flowId}/${status ? "enable" : "stop"}`), { headers })
       .then(() => {
         api
           .setStatus(row.id, status)
@@ -209,7 +213,7 @@ const onDel = (row: any) => {
     type: "warning",
   }).then(async () => {
     if (model == "sagoo-rule") {
-      await axios.delete(import.meta.env.VITE_RULE_SERVER_URL + "/api/v1/rules/" + row.flowId, { headers }).catch(() => {
+      await axios.delete(getRuleServerOrigin("/api/v1/rules/" + row.flowId), { headers }).catch(() => {
         ElMessage.error("规则不存在");
       });
     } else if (model == "node-red") {

+ 1 - 1
src/views/iot/rule-engine/log.vue

@@ -1,5 +1,5 @@
 <template>
-	<el-card shadow="nover" class="page">
+	<el-card shadow="never" class="page">
 		<el-table :data="tableData" style="width: 100%" row-key="id" v-loading="loading">
 			<el-table-column prop="id" label="ID" width="100" show-overflow-tooltip v-col="'id'"></el-table-column>
 			<el-table-column label="操作" align="center"></el-table-column>

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

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

+ 4 - 4
src/views/iot/scene/manage/detail.vue

@@ -1,6 +1,6 @@
 <template>
 	<div v-loading="loading">
-		<el-card class="system-dic-container" shadow="nover" style="position: relative;">
+		<el-card class="system-dic-container" shadow="never" style="position: relative;">
 			<div class="content">
 				<div class="flex cont_box">
 					<div class="font26">场景名称:{{ detail.name }}</div>
@@ -16,11 +16,11 @@
 				</div>
 			</div>
 		</el-card>
-		<el-card shadow="nover" style="margin-top: 15px;" v-if="detail.sceneType === 'device'">
+		<el-card shadow="never" style="margin-top: 15px;" v-if="detail.sceneType === 'device'">
 			<div class="font20">场景定义</div>
 			<SceneItem v-if="showstatus && sourceData.length > 0" :sceneList="sceneList" :sourceData="sourceData" :sceneType="detail.sceneType" @addScenesDetail="addScenesDetail" @delScenesDetail="delScenesDetail" @editScenesDetail="editScenesDetail"></SceneItem>
 		</el-card>
-		<el-card shadow="nover" style="  margin-top: 15px;" v-if="detail.sceneType === 'timer'">
+		<el-card shadow="never" style="  margin-top: 15px;" v-if="detail.sceneType === 'timer'">
 			<el-form-item label="定时触发" style="margin-bottom: 0;">
 				<div style="display:flex">
 					<el-input v-model="timerData.timer" placeholder="请输入cron表达式" />
@@ -31,7 +31,7 @@
 				</div>
 			</el-form-item>
 		</el-card>
-		<el-card shadow="nover" style="margin-top: 15px;">
+		<el-card shadow="never" style="margin-top: 15px;">
 			<div class="font20">场景动作</div>
 			<ActionItem v-if="detail.id && sourceData.length > 0" :scene_id="detail.id" :sourceData="sourceData"></ActionItem>
 		</el-card>

+ 1 - 1
src/views/iot/scene/manage/index.vue

@@ -1,6 +1,6 @@
 <template>
 	<div class="page">
-		<el-card shadow="nover">
+		<el-card shadow="never">
 			<div class="search">
 				<el-form inline ref="queryRef" @keyup.enter="getList(1)">
 					<el-form-item label="关键字" prop="keyWord">

+ 3 - 3
src/views/limits/frontEnd/btn/index.vue

@@ -2,7 +2,7 @@
 	<div>
 		<LimitsFrontEndPage />
 		<!-- 演示1:组件方式 -->
-		<el-card shadow="nover" header="演示1:组件方式" class="mt15">
+		<el-card shadow="never" header="演示1:组件方式" class="mt15">
 			<el-row class="mb10" style="color: #808080">单个权限验证(:value="xxx"):</el-row>
 			<div class="flex-warp">
 				<Auth :value="'btn.add'">
@@ -161,7 +161,7 @@
 		</el-card>
 
 		<!-- 演示2:指令方式 -->
-		<el-card shadow="nover" header="演示2:指令方式(页面初始化时执行)" class="mt15">
+		<el-card shadow="never" header="演示2:指令方式(页面初始化时执行)" class="mt15">
 			<el-row class="mb10" style="color: #808080">单个权限验证(v-auth="xxx"):</el-row>
 			<div class="flex-warp">
 				<div class="flex-warp-item" v-auth="'btn.add'">
@@ -296,7 +296,7 @@
 		</el-card>
 
 		<!-- 演示3:函数方式 -->
-		<el-card shadow="nover" header="演示3:函数方式(点击按钮查看有无权限,用于判断)" class="mt15">
+		<el-card shadow="never" header="演示3:函数方式(点击按钮查看有无权限,用于判断)" class="mt15">
 			<el-row class="mb10" style="color: #808080">auth('xxx')、auths(['xxx','xxx'])、authAll(['xxx','xxx']):</el-row>
 			<div class="flex-warp">
 				<div class="flex-warp-item">

+ 1 - 1
src/views/limits/frontEnd/page/index.vue

@@ -13,7 +13,7 @@
 			:closable="false"
 			class="mt15"
 		></el-alert>
-		<el-card shadow="nover" header="切换用户演示,前端控制不同用户显示不同页面、按钮权限" class="mt15">
+		<el-card shadow="never" header="切换用户演示,前端控制不同用户显示不同页面、按钮权限" class="mt15">
 			<el-radio-group v-model="userAuth" @change="onRadioChange">
 				<el-radio-button label="admin"></el-radio-button>
 				<el-radio-button label="common"></el-radio-button>

+ 388 - 299
src/views/login/index.vue

@@ -1,327 +1,416 @@
 <template>
-	<div class="login-container flex-row" v-if="showImg">
-		<el-switch class="switch" v-model="getThemeConfig.isIsDark" size="large" inline-prompt @change="onAddDarkChange" :active-icon="Sunny" :inactive-icon="Moon" style="--el-switch-on-color: #fff; --el-switch-off-color: #151515"></el-switch>
-		<div class="part left">
-			<div class="flex logo">
-				<el-image class="logoimg" :src="sysinfo.systemLogo" />
-				{{ sysinfo.systemName }}
-			</div>
-			<el-image class="img" :src="sysinfo.systemLoginPIC" />
-			<div>
-				<span class="text" v-if="sysinfo.buildTime" style="margin-right: 5px">服务端版本:{{ sysinfo.buildVersion }} </span>
-				<span class="text" v-if="sysinfo.buildTime">{{ dayjs(sysinfo.buildTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
-				<div style="height: 10px;"></div>
-				<span class="text" v-if="versionInfo.version" style="margin-right: 5px">前端版本:{{ versionInfo.version }} </span>
-				<span class="text" v-if="versionInfo.updateTime">{{ versionInfo.updateTime }}</span>
-			</div>
-		</div>
-		<div class="part">
-			<div class="title">登录</div>
-			<Account :showSSO="showSSO" />
-		</div>
-	</div>
+  <div class="login-container flex-row" v-if="showImg">
+    <!-- 多语言切换 -->
+    <el-dropdown class="language-switch" :show-timeout="70" :hide-timeout="50" trigger="click" @command="onLanguageChange">
+      <div class="language-switch-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>
+    <!-- 深色模式切换 -->
+    <el-switch class="switch" v-model="getThemeConfig.isIsDark" size="large" inline-prompt @change="onAddDarkChange" :active-icon="Sunny" :inactive-icon="Moon" style="--el-switch-on-color: #fff; --el-switch-off-color: #151515"></el-switch>
+    <div class="part left">
+      <div class="flex logo">
+        <el-image class="logoimg" :src="sysinfo.systemLogo" />
+        {{ sysinfo.systemName }}
+      </div>
+      <el-image class="img" :src="sysinfo.systemLoginPIC" />
+      <div>
+        <span class="text" v-if="sysinfo.buildTime" style="margin-right: 5px">{{ $t('message.login.serverVersion') }}:{{ sysinfo.buildVersion }} </span>
+        <span class="text" v-if="sysinfo.buildTime">{{ dayjs(sysinfo.buildTime).format("YYYY-MM-DD HH:mm:ss") }}</span>
+        <div style="height: 10px"></div>
+        <span class="text" v-if="versionInfo.version" style="margin-right: 5px">{{ $t('message.login.frontendVersion') }}:{{ versionInfo.version }} </span>
+        <span class="text" v-if="versionInfo.updateTime">{{ versionInfo.updateTime }}</span>
+      </div>
+    </div>
+    <div class="part">
+      <div class="title">{{ $t('message.login.title') }}</div>
+      <Account :showSSO="showSSO" />
+    </div>
+  </div>
 </template>
 
 <script lang="ts">
-import { toRefs, reactive, computed, defineComponent } from 'vue';
-import Account from '/@/views/login/component/account.vue';
-import { useStore } from '/@/store/index';
-import logoMini from '/imgs/logo.png';
-import { Sunny, Moon } from '@element-plus/icons-vue';
-import dayjs from 'dayjs';
-import api from '/@/api/system';
-import axios from 'axios';
-import { setSystemInfo, setTenantInfo } from '/@/utils/auth';
+import { toRefs, reactive, computed, defineComponent, getCurrentInstance } from "vue";
+import Account from "/@/views/login/component/account.vue";
+import { useStore } from "/@/store/index";
+import logoMini from "/imgs/logo.png";
+import { Sunny, Moon } from "@element-plus/icons-vue";
+import dayjs from "dayjs";
+import api from "/@/api/system";
+import axios from "axios";
+import { setSystemInfo, setTenantInfo } from "/@/utils/auth";
+import { Local } from "/@/utils/storage";
+import other from "/@/utils/other";
 // import PackageJson from '/public/version.json';
 // 定义接口来定义对象的类型
 interface LoginState {
-	tabsActiveName: string;
-	isScan: boolean;
+  tabsActiveName: string;
+  isScan: boolean;
+  disabledI18n: string;
 }
 
 export default defineComponent({
-	name: 'loginIndex',
-	components: {
-		Account,
-	},
-	data: function () {
-		return {
-			Sunny,
-			Moon,
-			dayjs,
-			showImg: false,
-			showSSO: false,
-			sysinfo: {
-				buildVersion: '',
-				systemName: '',
-				buildTime: '',
-				systemCopyright: '',
-				systemLogo: '',
-				systemLoginPIC: '',
-			},
-		};
-	},
-	created() {
-		setTenantInfo();
-	},
-	mounted() {
-		api.sysinfo().then((res: any) => {
-			setSystemInfo(res);
-			this.sysinfo = res
-			const isSSOEnabled = window.atob(res.target).split('|')[4]
-			if (isSSOEnabled == '1') {
-				this.showSSO = true
-			}
-		}).finally(() => this.showImg = true)
-	},
-	setup() {
-		const store = useStore();
-		const state = reactive<LoginState>({
-			tabsActiveName: 'account',
-			isScan: false,
-		});
-		// 获取布局配置信息
-		const getThemeConfig = computed(() => {
-			return store.state.themeConfig.themeConfig;
-		});
-
-		const versionInfo = reactive({
-			version: '',
-			updateTime: '',
-		})
-		// 加载版本信息
-		axios.get('/versionInfo.json').then(res => {
-			versionInfo.version = res.data.version
-			versionInfo.updateTime = res.data.updateTime
-		})
-
-		// 4、界面显示 --> 深色模式
-		const onAddDarkChange = () => {
-			const body = document.documentElement as HTMLElement;
-			if (getThemeConfig.value.isIsDark) {
-				body.setAttribute('data-theme', 'dark');
-				document.querySelector('html')!.className = 'dark'
-			} else {
-				body.setAttribute('data-theme', '');
-				document.querySelector('html')!.className = ''
-			}
-			store.dispatch('themeConfig/setThemeConfig', getThemeConfig.value);
-		};
-
-		return {
-			onAddDarkChange,
-			logoMini,
-			getThemeConfig,
-			versionInfo,
-			...toRefs(state),
-		};
-	},
+  name: "loginIndex",
+  components: {
+    Account,
+  },
+  data: function () {
+    return {
+      Sunny,
+      Moon,
+      dayjs,
+      showImg: false,
+      showSSO: false,
+      sysinfo: {
+        buildVersion: "",
+        systemName: "",
+        buildTime: "",
+        systemCopyright: "",
+        systemLogo: "",
+        systemLoginPIC: "",
+      },
+    };
+  },
+  created() {
+    setTenantInfo();
+  },
+  mounted() {
+    api
+      .sysinfo()
+      .then((res: any) => {
+        setSystemInfo(res);
+        this.sysinfo = res;
+        const isSSOEnabled = window.atob(res.target).split("|")[4];
+        if (isSSOEnabled == "1") {
+          this.showSSO = true;
+        }
+      })
+      .finally(() => (this.showImg = true));
+  },
+  setup() {
+    const store = useStore();
+    const { proxy } = <any>getCurrentInstance();
+    const state = reactive<LoginState>({
+      tabsActiveName: "account",
+      isScan: false,
+      disabledI18n: "zh-cn",
+    });
+    // 获取布局配置信息
+    const getThemeConfig = computed(() => {
+      return store.state.themeConfig.themeConfig;
+    });
+
+    const versionInfo = reactive({
+      version: "",
+      updateTime: "",
+    });
+    // 加载版本信息
+    axios.get("/versionInfo.json").then((res) => {
+      versionInfo.version = res.data.version;
+      versionInfo.updateTime = res.data.updateTime;
+    });
+
+    // 语言切换
+    const onLanguageChange = (lang: string) => {
+      Local.remove("themeConfig");
+      getThemeConfig.value.globalI18n = lang;
+      Local.set("themeConfig", getThemeConfig.value);
+      proxy.$i18n.locale = lang;
+      initI18n();
+      other.useTitle();
+    };
+
+    // 设置 element plus 组件的国际化
+    const setI18nConfig = (locale: string) => {
+      proxy.mittBus.emit("getI18nConfig", proxy.$i18n.messages[locale]);
+    };
+    // 初始化言语国际化
+    const initI18n = () => {
+      switch (Local.get("themeConfig").globalI18n) {
+        case "zh-cn":
+          state.disabledI18n = "zh-cn";
+          setI18nConfig("zh-cn");
+          break;
+        case "en":
+          state.disabledI18n = "en";
+          setI18nConfig("en");
+          break;
+        case "zh-tw":
+          state.disabledI18n = "zh-tw";
+          setI18nConfig("zh-tw");
+          break;
+      }
+    };
+
+    // 4、界面显示 --> 深色模式
+    const onAddDarkChange = () => {
+      const body = document.documentElement as HTMLElement;
+      if (getThemeConfig.value.isIsDark) {
+        body.setAttribute("data-theme", "dark");
+        document.querySelector("html")!.className = "dark";
+      } else {
+        body.setAttribute("data-theme", "");
+        document.querySelector("html")!.className = "";
+      }
+      store.dispatch("themeConfig/setThemeConfig", getThemeConfig.value);
+    };
+
+    // 初始化当前语言状态
+    state.disabledI18n = getThemeConfig.value.globalI18n || "zh-cn";
+
+    return {
+      onAddDarkChange,
+      onLanguageChange,
+      logoMini,
+      getThemeConfig,
+      versionInfo,
+      ...toRefs(state),
+    };
+  },
 });
 </script>
 
 <style scoped lang="scss">
-html[data-theme='dark'] {
-	.login-container {
-		background: #293146;
-	}
-
-	.left {
-		background-image: url(/@/assets/login-bg-dark.svg);
-	}
-
-	.title {
-		color: #aaa;
-	}
+html[data-theme="dark"] {
+  .login-container {
+    background: #293146;
+  }
+
+  .left {
+    background-image: url(/@/assets/login-bg-dark.svg);
+  }
+
+  .title {
+    color: #aaa;
+  }
 }
 
 .flex {
-	display: flex;
-	align-items: center;
+  display: flex;
+  align-items: center;
 }
 
 .text {
-	color: #fff;
+  color: #fff;
+}
+
+.language-switch {
+  position: fixed;
+  right: 80px;
+  top: 20px;
+  z-index: 1000;
+
+  .language-switch-icon {
+    width: 40px;
+    height: 40px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    border-radius: 50%;
+    background: rgba(255, 255, 255, 0.1);
+    backdrop-filter: blur(10px);
+    border: 1px solid rgba(255, 255, 255, 0.2);
+    cursor: pointer;
+    transition: all 0.3s ease;
+
+    &:hover {
+      background: rgba(255, 255, 255, 0.2);
+      transform: scale(1.05);
+    }
+
+    i {
+      font-size: 20px;
+      color: #fff;
+    }
+  }
 }
 
 .switch {
-	position: fixed;
-	right: 20px;
-	top: 20px;
+  position: fixed;
+  right: 20px;
+  top: 20px;
 }
 
 .login-container {
-	width: 100vw;
-	height: 100vh;
-	position: relative;
-	background: #fff;
-
-	.title {
-		font-size: 30px;
-		color: #333;
-		font-weight: bold;
-		letter-spacing: 20px;
-	}
-
-	.logo {
-		font-size: 30px;
-		color: #fff;
-
-		.logoimg {
-			height: 50px;
-			display: block;
-			margin-right: 12px;
-		}
-	}
-
-	.img {
-		max-width: 50%;
-		display: block;
-		max-height: 40vh;
-	}
-
-	.part {
-		flex: 1;
-		display: flex;
-		flex-flow: column nowrap;
-		justify-content: center;
-		align-items: center;
-	}
-
-	.left {
-		height: 100vh;
-		background-image: url(/@/assets/login-bg.svg);
-		background-repeat: no-repeat;
-		background-size: auto 100%;
-		background-position: right center;
-		align-items: flex-start;
-		padding-left: 8%;
-		justify-content: space-around;
-		padding-top: 10vh;
-		padding-bottom: 10vh;
-	}
-
-	.login-icon-group {
-		width: 100%;
-		height: 100%;
-		position: relative;
-
-		.login-icon-group-title {
-			display: flex;
-			align-items: center;
-			justify-content: center;
-			margin: 12px 0;
-
-			img {
-				width: auto;
-				height: 40px;
-			}
-
-			&-text {
-				padding-left: 20px;
-				color: var(--el-color-primary);
-			}
-		}
-
-		&-icon {
-			width: 60%;
-			height: 70%;
-			position: absolute;
-			left: 0;
-			bottom: 0;
-		}
-	}
-
-	.login-content-out {
-		width: 100%;
-		height: 100%;
-		padding-top: calc(50vh - 227px);
-	}
-
-	.login-content {
-		width: 500px;
-		padding: 20px;
-		margin-left: calc(50% - 500px);
-		background-color: rgba(255, 255, 255, 0.8);
-		border: 5px solid var(--el-color-primary-light-8);
-		border-radius: 5px;
-		overflow: hidden;
-		z-index: 1;
-		position: relative;
-
-		.login-content-main {
-			margin: 0 auto;
-			width: 80%;
-
-			.login-content-title {
-				color: var(--el-text-color-primary);
-				font-weight: 500;
-				font-size: 22px;
-				text-align: center;
-				letter-spacing: 4px;
-				margin: 15px 0 30px;
-				white-space: nowrap;
-				z-index: 5;
-				position: relative;
-				transition: all 0.3s ease;
-			}
-		}
-
-		.login-content-main-sacn {
-			position: absolute;
-			top: 0;
-			right: 0;
-			width: 50px;
-			height: 50px;
-			overflow: hidden;
-			cursor: pointer;
-			transition: all ease 0.3s;
-			color: var(--el-text-color-primary);
-
-			&-delta {
-				position: absolute;
-				width: 35px;
-				height: 70px;
-				z-index: 2;
-				top: 2px;
-				right: 21px;
-				background: var(--el-color-white);
-				transform: rotate(-45deg);
-			}
-
-			&:hover {
-				opacity: 1;
-				transition: all ease 0.3s;
-				color: var(--el-color-primary) !important;
-			}
-
-			i {
-				width: 47px;
-				height: 50px;
-				display: inline-block;
-				font-size: 48px;
-				position: absolute;
-				right: 2px;
-				top: -1px;
-			}
-		}
-	}
-
-	.login-footer {
-		position: absolute;
-		bottom: 5px;
-		width: 100%;
-
-		&-content {
-			width: 100%;
-			display: flex;
-
-			&-warp {
-				margin: auto;
-				color: #e0e3e9;
-				text-align: center;
-				animation: error-num 1s ease-in-out;
-			}
-		}
-	}
+  width: 100vw;
+  height: 100vh;
+  position: relative;
+  background: #fff;
+
+  .title {
+    font-size: 30px;
+    color: #333;
+    font-weight: bold;
+    letter-spacing: 20px;
+  }
+
+  .logo {
+    font-size: 30px;
+    color: #fff;
+
+    .logoimg {
+      height: 50px;
+      display: block;
+      margin-right: 12px;
+    }
+  }
+
+  .img {
+    max-width: 50%;
+    display: block;
+    max-height: 40vh;
+  }
+
+  .part {
+    flex: 1;
+    display: flex;
+    flex-flow: column nowrap;
+    justify-content: center;
+    align-items: center;
+  }
+
+  .left {
+    height: 100vh;
+    background-image: url(/@/assets/login-bg.svg);
+    background-repeat: no-repeat;
+    background-size: auto 100%;
+    background-position: right center;
+    align-items: flex-start;
+    padding-left: 8%;
+    justify-content: space-around;
+    padding-top: 10vh;
+    padding-bottom: 10vh;
+  }
+
+  .login-icon-group {
+    width: 100%;
+    height: 100%;
+    position: relative;
+
+    .login-icon-group-title {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      margin: 12px 0;
+
+      img {
+        width: auto;
+        height: 40px;
+      }
+
+      &-text {
+        padding-left: 20px;
+        color: var(--el-color-primary);
+      }
+    }
+
+    &-icon {
+      width: 60%;
+      height: 70%;
+      position: absolute;
+      left: 0;
+      bottom: 0;
+    }
+  }
+
+  .login-content-out {
+    width: 100%;
+    height: 100%;
+    padding-top: calc(50vh - 227px);
+  }
+
+  .login-content {
+    width: 500px;
+    padding: 20px;
+    margin-left: calc(50% - 500px);
+    background-color: rgba(255, 255, 255, 0.8);
+    border: 5px solid var(--el-color-primary-light-8);
+    border-radius: 5px;
+    overflow: hidden;
+    z-index: 1;
+    position: relative;
+
+    .login-content-main {
+      margin: 0 auto;
+      width: 80%;
+
+      .login-content-title {
+        color: var(--el-text-color-primary);
+        font-weight: 500;
+        font-size: 22px;
+        text-align: center;
+        letter-spacing: 4px;
+        margin: 15px 0 30px;
+        white-space: nowrap;
+        z-index: 5;
+        position: relative;
+        transition: all 0.3s ease;
+      }
+    }
+
+    .login-content-main-sacn {
+      position: absolute;
+      top: 0;
+      right: 0;
+      width: 50px;
+      height: 50px;
+      overflow: hidden;
+      cursor: pointer;
+      transition: all ease 0.3s;
+      color: var(--el-text-color-primary);
+
+      &-delta {
+        position: absolute;
+        width: 35px;
+        height: 70px;
+        z-index: 2;
+        top: 2px;
+        right: 21px;
+        background: var(--el-color-white);
+        transform: rotate(-45deg);
+      }
+
+      &:hover {
+        opacity: 1;
+        transition: all ease 0.3s;
+        color: var(--el-color-primary) !important;
+      }
+
+      i {
+        width: 47px;
+        height: 50px;
+        display: inline-block;
+        font-size: 48px;
+        position: absolute;
+        right: 2px;
+        top: -1px;
+      }
+    }
+  }
+
+  .login-footer {
+    position: absolute;
+    bottom: 5px;
+    width: 100%;
+
+    &-content {
+      width: 100%;
+      display: flex;
+
+      &-warp {
+        margin: auto;
+        color: #e0e3e9;
+        text-align: center;
+        animation: error-num 1s ease-in-out;
+      }
+    }
+  }
 }
 </style>

+ 1 - 1
src/views/modules/assess/index.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="page">
-    <el-card shadow="nover">
+    <el-card shadow="never">
       <div class="system-user-search mb15">
         <!-- <el-input placeholder="请输入用户名称" style="max-width: 180px"> </el-input>
 				<el-button type="success" class="ml10">

+ 332 - 369
src/views/modules/iotCard/dashboard.vue

@@ -1,142 +1,134 @@
 <!-- 物联网卡-详情 -->
 <template>
   <div class="page">
-		<el-card shadow="nover" class="page-full-part">
-      <div shadow="nover" class="top-wrap">
+    <el-card shadow="never" class="page-full-part">
+      <div shadow="never" class="top-wrap">
         <div class="title flex">
-          数据统计
+          {{ $t("message.iotCard.dashboard.title") }}
           <div class="select-wrap">
-            <el-select v-model="types" placeholder="请选择" style="width: 120px" @change="typeChange()">
+            <el-select v-model="types" :placeholder="$t('message.iotCard.dashboard.carrier.placeholder')" style="width: 120px" @change="typeChange()">
               <!-- 1电信,2联通,3移动 -->
-              <el-option label="电信" :value="1" />
-<!--              <el-option label="联通" :value="2" disabled/>-->
-<!--              <el-option label="移动" :value="3" disabled/>-->
+              <el-option :label="$t('message.iotCard.dashboard.carrier.telecom')" :value="1" />
+              <!--              <el-option :label="$t('message.iotCard.dashboard.carrier.unicom')" :value="2" disabled/>-->
+              <!--              <el-option :label="$t('message.iotCard.dashboard.carrier.mobile')" :value="3" disabled/>-->
             </el-select>
           </div>
         </div>
         <div class="top-inner-wrap">
           <div class="line-wrap flow-line-wrap">
             <div class="text-wrap">
-              <div class="text">昨日流量消耗</div>
-              <div>{{formatSize(statisticsData.yesterdayTotal * 1024 * 1024)}}</div>
+              <div class="text">{{ $t("message.iotCard.dashboard.statistics.yesterdayFlow") }}</div>
+              <div>{{ formatSize(statisticsData.yesterdayTotal * 1024 * 1024) }}</div>
             </div>
             <div class="line-inner-wrap" ref="yesterdayLine"></div>
           </div>
           <div class="line-wrap flow-line-wrap">
             <div class="text-wrap">
-              <div class="text">当月流量消耗</div>
-              <div>{{formatSize(statisticsData.realMonthTotal * 1024 * 1024)}}</div>
+              <div class="text">{{ $t("message.iotCard.dashboard.statistics.monthFlow") }}</div>
+              <div>{{ formatSize(statisticsData.realMonthTotal * 1024 * 1024) }}</div>
             </div>
             <div class="line-inner-wrap" ref="monthLine"></div>
           </div>
 
           <div class="line-wrap flow-line-wrap">
             <div class="text-wrap">
-              <div class="text">本年流量消耗</div>
-              <div>{{formatSize(statisticsData.yearTotal * 1024 * 1024)}}</div>
+              <div class="text">{{ $t("message.iotCard.dashboard.statistics.yearFlow") }}</div>
+              <div>{{ formatSize(statisticsData.yearTotal * 1024 * 1024) }}</div>
             </div>
             <div class="line-inner-wrap" ref="yearLine"></div>
           </div>
         </div>
-
-        
       </div>
-			<div class="statistics-wrap">
-				<el-card shadow="nover" class="left-wrap">
-					<div class="top-title-wrap">
-							<div class="title">流量统计</div>
-							<div class="operate-wrap">
-								<el-button-group>
-									<el-button @click="changeDate(1)" :type="activeIndex == 1 ?  'primary' : ''">昨日</el-button>
-									<el-button @click="changeDate(2)" :type="activeIndex == 2 ?  'primary' : ''">近一周</el-button>
-									<el-button @click="changeDate(3)" :type="activeIndex == 3 ?  'primary' : ''">近一月</el-button>
-									<el-button @click="changeDate(4)" :type="activeIndex == 4 ?  'primary' : ''">近一年</el-button>
-								</el-button-group>
-									<el-date-picker
-										class="date-picker-wrap"
-										v-model="dateTimeRange"
-										:disabled-date="disabledDate"
-										type="datetimerange"
-										range-separator="至"
-										start-placeholder="开始时间"
-										end-placeholder="结束时间"
-										format="YYYY-MM-DD HH:mm:ss"
-										date-format="YYYY/MM/DD"
-										time-format="hh:mm:ss"
-                    @change="handleDateChange"
-									/>
-							</div>
-					</div>
-					<div style="height: 508px;" ref="flowLine"></div>
-				</el-card>
-
-				<el-card shadow="nover" class="right-wrap">
-					<div class="top-title-wrap">
-						<div class="title">流量使用TOP10</div>
-						<el-date-picker
-							class="date-picker-wrap"
-							v-model="dateRange"
-							:disabled-date="disabledDate"
-							type="daterange"
-							range-separator="至"
-							start-placeholder="开始日期"
-							end-placeholder="结束日期"
-							format="YYYY-MM-DD"
-							date-format="YYYY/MM/DD"
+      <div class="statistics-wrap">
+        <el-card shadow="never" class="left-wrap">
+          <div class="top-title-wrap">
+            <div class="title">{{ $t("message.iotCard.dashboard.flowChart.title") }}</div>
+            <div class="operate-wrap">
+              <el-button-group>
+                <el-button @click="changeDate(1)" :type="activeIndex == 1 ? 'primary' : ''">{{ $t("message.iotCard.dashboard.flowChart.buttons.yesterday") }}</el-button>
+                <el-button @click="changeDate(2)" :type="activeIndex == 2 ? 'primary' : ''">{{ $t("message.iotCard.dashboard.flowChart.buttons.week") }}</el-button>
+                <el-button @click="changeDate(3)" :type="activeIndex == 3 ? 'primary' : ''">{{ $t("message.iotCard.dashboard.flowChart.buttons.month") }}</el-button>
+                <el-button @click="changeDate(4)" :type="activeIndex == 4 ? 'primary' : ''">{{ $t("message.iotCard.dashboard.flowChart.buttons.year") }}</el-button>
+              </el-button-group>
+              <el-date-picker
+                class="date-picker-wrap"
+                v-model="dateTimeRange"
+                :disabled-date="disabledDate"
+                type="datetimerange"
+                :range-separator="$t('message.iotCard.dashboard.flowChart.datePicker.rangeSeparator')"
+                :start-placeholder="$t('message.iotCard.dashboard.flowChart.datePicker.startPlaceholder')"
+                :end-placeholder="$t('message.iotCard.dashboard.flowChart.datePicker.endPlaceholder')"
+                format="YYYY-MM-DD HH:mm:ss"
+                date-format="YYYY/MM/DD"
+                time-format="hh:mm:ss"
+                @change="handleDateChange"
+              />
+            </div>
+          </div>
+          <div style="height: 508px" ref="flowLine"></div>
+        </el-card>
+
+        <el-card shadow="never" class="right-wrap">
+          <div class="top-title-wrap">
+            <div class="title">{{ $t("message.iotCard.dashboard.top10.title") }}</div>
+            <el-date-picker
+              class="date-picker-wrap"
+              v-model="dateRange"
+              :disabled-date="disabledDate"
+              type="daterange"
+              :range-separator="$t('message.iotCard.dashboard.top10.datePicker.rangeSeparator')"
+              :start-placeholder="$t('message.iotCard.dashboard.top10.datePicker.startPlaceholder')"
+              :end-placeholder="$t('message.iotCard.dashboard.top10.datePicker.endPlaceholder')"
+              format="YYYY-MM-DD"
+              date-format="YYYY/MM/DD"
               @change="handleTop10DataChange"
-						/>
-					</div>
-					<div class="ranking-list">
-						<div class="rank-item" v-for="(item, index) in rankList" :key="index">
-							<div :class="`number-item-${++index}`" class="number">{{index++}}</div>
-							<div class="card-num">{{item.accessNumber}}</div>
-							<el-progress class="progress-wrap" :text-inside="true" :stroke-width="16" :percentage="totalNum ? (item.value / totalNum * 100).toFixed(2) : 0" />
-							<div class="flow-num">{{formatSize(item.value * 1024 * 1024)}}</div>
-						</div>
-					</div>
-					
-				</el-card>
-			</div>
-		</el-card>
+            />
+          </div>
+          <div class="ranking-list">
+            <div class="rank-item" v-for="(item, index) in rankList" :key="index">
+              <div :class="`number-item-${index + 1}`" class="number">{{ index + 1 }}</div>
+              <div class="card-num">{{ item.accessNumber }}</div>
+              <el-progress class="progress-wrap" :text-inside="true" :stroke-width="16" :percentage="totalNum ? ((item.value / totalNum) * 100).toFixed(2) : 0" />
+              <div class="flow-num">{{ formatSize(item.value * 1024 * 1024) }}</div>
+            </div>
+          </div>
+        </el-card>
+      </div>
+    </el-card>
   </div>
 </template>
 
 <script lang="ts" setup>
-import { ref, reactive, nextTick, watch, markRaw } from "vue"
+import { ref, reactive, nextTick, watch, markRaw } from "vue";
+import { useI18n } from "vue-i18n";
 import { formatSize } from "/@/utils/common";
-import api from '/@/api/modules/iotCard';
-import { useStore } from '/@/store/index';
-import * as echarts from 'echarts';
-import dayjs from 'dayjs';
+import api from "/@/api/modules/iotCard";
+import { useStore } from "/@/store/index";
+import * as echarts from "echarts";
+import dayjs from "dayjs";
 
 const store = useStore();
+const { t } = useI18n();
 
 const types = ref(1);
 const statisticsData = ref({
-	realMonthTotal: 0,
-	yearTotal: 0,
-	yesterdayTotal: 0
-})
+  realMonthTotal: 0,
+  yearTotal: 0,
+  yesterdayTotal: 0,
+});
 
 const flowLine = ref();
 const yesterdayLine = ref();
 const monthLine = ref();
 const yearLine = ref();
 
-const dateRange = ref<any>([
-  dayjs(new Date()).subtract(1, 'month'),
-  dayjs(new Date()),
-])
-
+const dateRange = ref<any>([dayjs(new Date()).subtract(1, "month"), dayjs(new Date())]);
 
-const dateTimeRange = ref<any>([
-  dayjs(new Date()).subtract(6, 'day'),
-  dayjs(new Date()),
-])
+const dateTimeRange = ref<any>([dayjs(new Date()).subtract(6, "day"), dayjs(new Date())]);
 
 const totalNum = ref(0);
 
-const activeIndex= ref(2);
+const activeIndex = ref(2);
 
 const rankList = ref([]);
 
@@ -149,176 +141,150 @@ const yearLineData = ref<any>([]);
 const monthLineXAxisData = ref<any>([]);
 const monthLineData = ref<any>([]);
 
-const yesterdayLineXAxisData = ref<any>([dayjs(new Date()).subtract(1, 'day').format('YYYY-MM-DD')]);
+const yesterdayLineXAxisData = ref<any>([dayjs(new Date()).subtract(1, "day").format("YYYY-MM-DD")]);
 const yesterdayLineData = ref<any>([]);
 
-
-
 const state = reactive({
-	global: {
-		yesterdayLine: null,
+  global: {
+    yesterdayLine: null,
     monthLine: null,
     yearLine: null,
-		dispose: [null, '', undefined],
-	} as any,
-	myCharts: [],
-	charts: {
-		theme: '',
-		bgColor: '',
-		color: '#303133',
-	},
+    dispose: [null, "", undefined],
+  } as any,
+  myCharts: [],
+  charts: {
+    theme: "",
+    bgColor: "",
+    color: "#303133",
+  },
 });
 
 const disabledDate = (time: Date) => {
-  return time.getTime() > Date.now()
-}
+  return time.getTime() > Date.now();
+};
 
 // 仪表盘折线
-const getFlowDataByDateRange = async (dateRangeData:any) => {
+const getFlowDataByDateRange = async (dateRangeData: any) => {
   const simFlowRes = await api.dashboard.getFlowDataByDateRange({
-    sdate: dayjs(dateRangeData[0]).format('YYYY-MM-DD HH:mm:ss'),
-    edate: dayjs(dateRangeData[1]).format('YYYY-MM-DD HH:mm:ss'),
-    types: types.value
-  })
-	return simFlowRes.data;
-}
+    sdate: dayjs(dateRangeData[0]).format("YYYY-MM-DD HH:mm:ss"),
+    edate: dayjs(dateRangeData[1]).format("YYYY-MM-DD HH:mm:ss"),
+    types: types.value,
+  });
+  return simFlowRes.data;
+};
 
 // 昨日仪表盘折线
 const getYesterdayFlowData = async () => {
-  const yesterday = dayjs(new Date()).subtract(1, 'day').format('YYYY-MM-DD')
-  const res = await getFlowDataByDateRange([
-    yesterday + " 00:00:00",
-    yesterday + " 23:59:59"
-  ])
+  const yesterday = dayjs(new Date()).subtract(1, "day").format("YYYY-MM-DD");
+  const res = await getFlowDataByDateRange([yesterday + " 00:00:00", yesterday + " 23:59:59"]);
   yesterdayLineData.value = [res[0].value];
   initYesterdayLineChart();
-}
+};
 
 // 近一月仪表盘折线
 const getMonthFlowData = async () => {
   monthLineXAxisData.value = [];
   monthLineData.value = [];
-  const monthDay1 = dayjs(new Date()).startOf('month').format('YYYY-MM-DD');
-  const monthDay2 = dayjs(new Date()).endOf('month').format('YYYY-MM-DD');
-  const res = await getFlowDataByDateRange([monthDay1, monthDay2])
-  res.reverse().forEach((item:any) => {
+  const monthDay1 = dayjs(new Date()).startOf("month").format("YYYY-MM-DD");
+  const monthDay2 = dayjs(new Date()).endOf("month").format("YYYY-MM-DD");
+  const res = await getFlowDataByDateRange([monthDay1, monthDay2]);
+  res.reverse().forEach((item: any) => {
     monthLineXAxisData.value.push(item.date);
     monthLineData.value.push(item.value);
-  })
+  });
 
   initMonthLineChart();
-}
+};
 
 // 近一年仪表盘折线
 const getYearFlowData = async () => {
-	yearLineXAxisData.value = [];
-	yearLineData.value = [];
-	const year = dayjs(new Date()).startOf('year').format('YYYY');
-	const res = await getFlowDataByDateRange([
-		year + '-01-01',
-		year + '-12-31'
-	])
-	res.reverse().forEach((item:any) => {
-		yearLineXAxisData.value.push(item.date);
-		yearLineData.value.push(item.value);
-	})
-	initYearLineChart();
-}
+  yearLineXAxisData.value = [];
+  yearLineData.value = [];
+  const year = dayjs(new Date()).startOf("year").format("YYYY");
+  const res = await getFlowDataByDateRange([year + "-01-01", year + "-12-31"]);
+  res.reverse().forEach((item: any) => {
+    yearLineXAxisData.value.push(item.date);
+    yearLineData.value.push(item.value);
+  });
+  initYearLineChart();
+};
 
 // 按钮切换统一调用方法
 const getFlowData = async () => {
-	flowLineXAxisData.value = [];
+  flowLineXAxisData.value = [];
   flowLineData.value = [];
-	const res = await getFlowDataByDateRange(dateTimeRange.value)
-	res.reverse().forEach((item:any) => {
-		flowLineXAxisData.value.push(item.date);
-		flowLineData.value.push(item.value);
-	})
-	iniFlowLineChart();
-}
+  const res = await getFlowDataByDateRange(dateTimeRange.value);
+  res.reverse().forEach((item: any) => {
+    flowLineXAxisData.value.push(item.date);
+    flowLineData.value.push(item.value);
+  });
+  iniFlowLineChart();
+};
 
 // 仪表盘折线按钮
-const changeDate = (key:number) => {
+const changeDate = (key: number) => {
   // 1 昨天 2近一周 3近一月 4近一年
   activeIndex.value = key;
-  if(key === 1) {
+  if (key === 1) {
     // 昨天
-    const yesterday = dayjs(new Date()).subtract(1, 'day').format('YYYY-MM-DD')
-    dateTimeRange.value = [
-      yesterday + " 00:00:00",
-      yesterday + " 23:59:59"
-    ]
-  }else if(key === 2) {
+    const yesterday = dayjs(new Date()).subtract(1, "day").format("YYYY-MM-DD");
+    dateTimeRange.value = [yesterday + " 00:00:00", yesterday + " 23:59:59"];
+  } else if (key === 2) {
     // 近一周
-    dateTimeRange.value = [
-      dayjs(new Date()).subtract(6, 'day'),
-      dayjs(new Date()),
-    ]
-  }else if(key === 3) {
+    dateTimeRange.value = [dayjs(new Date()).subtract(6, "day"), dayjs(new Date())];
+  } else if (key === 3) {
     // 近一月
-    dateTimeRange.value = [
-      dayjs(new Date()).subtract(1, 'month'),
-      dayjs(new Date()),
-    ]
-  }else if(key === 4) {
+    dateTimeRange.value = [dayjs(new Date()).subtract(1, "month"), dayjs(new Date())];
+  } else if (key === 4) {
     // 近一年
-    dateTimeRange.value = [
-      dayjs(new Date()).subtract(1, 'year'),
-      dayjs(new Date()),
-    ]
+    dateTimeRange.value = [dayjs(new Date()).subtract(1, "year"), dayjs(new Date())];
   }
   getFlowData();
-}
+};
 
 // 流量统计时间筛选
-const handleDateChange = async (value:any) => {
-  dateTimeRange.value = [
-    value[0],
-    value[1]
-  ]
+const handleDateChange = async (value: any) => {
+  dateTimeRange.value = [value[0], value[1]];
   // 这里可以添加相应的处理逻辑
   getFlowData();
-}
+};
 
 // 流量使用Top10时间筛选
-const handleTop10DataChange = async (value:any) => {
-  dateTimeRange.value = [
-    value[0],
-    value[1]
-  ]
+const handleTop10DataChange = async (value: any) => {
+  dateTimeRange.value = [value[0], value[1]];
   getTop10Data();
   // 这里可以添加相应的处理逻辑
-}
+};
 
 const typeChange = () => {
-	getYesterdayFlowData();
-	getMonthFlowData();
-	getYearFlowData();
-	getFlowData();
-	getTop10Data();
+  getYesterdayFlowData();
+  getMonthFlowData();
+  getYearFlowData();
+  getFlowData();
+  getTop10Data();
   getFlowAllData();
-}
+};
 
 const getTop10Data = async () => {
   const top10Res = await api.dashboard.getTop10Data({
-    sdate: dayjs(dateRange.value[0]).format('YYYY-MM-DD'),
-    edate: dayjs(dateRange.value[1]).format('YYYY-MM-DD'),
-    types: types.value
-  })
-	rankList.value = top10Res.data || [];
-	totalNum.value = top10Res.data ? top10Res.data[0].value : 0;
-}
+    sdate: dayjs(dateRange.value[0]).format("YYYY-MM-DD"),
+    edate: dayjs(dateRange.value[1]).format("YYYY-MM-DD"),
+    types: types.value,
+  });
+  rankList.value = top10Res.data || [];
+  totalNum.value = top10Res.data ? top10Res.data[0].value : 0;
+};
 const getFlowAllData = async () => {
   const res = await api.dashboard.getFlowData({
-    types: types.value
-  })
-	statisticsData.value = res
-}
+    types: types.value,
+  });
+  statisticsData.value = res;
+};
 
 // 折线图 - 昨日流量消耗
 const initYesterdayLineChart = () => {
   if (!state.global.dispose.some((b: any) => b === state.global.yesterdayLine)) state.global.yesterdayLine.dispose();
-	state.global.yesterdayLine = markRaw(echarts.init(yesterdayLine.value, state.charts.theme));
+  state.global.yesterdayLine = markRaw(echarts.init(yesterdayLine.value, state.charts.theme));
   const option = {
     backgroundColor: state.charts.bgColor,
     xAxis: {
@@ -327,48 +293,47 @@ const initYesterdayLineChart = () => {
     },
     yAxis: [
       {
-        type: 'value',
-        name: '',
+        type: "value",
+        name: "",
         show: false,
-        splitLine: { show: false, lineStyle: { type: 'dashed', color: '#f5f5f5' } },
+        splitLine: { show: false, lineStyle: { type: "dashed", color: "#f5f5f5" } },
       },
     ],
-    tooltip: { 
-			trigger: 'axis',
-			formatter: function (params:any) {
-					var relVal = params[0].name
-					let circle = `<i style="margin-right:4px;display: inline-block;width: 10px;height: 10px;border-radius: 50%;background-color:${params[0].color}"></i>`
-					relVal += '<br/>' + circle + ' 流量: ' + formatSize(params[0].value*1024*1024)
-					return relVal;
-				}
-		},
+    tooltip: {
+      trigger: "axis",
+      formatter: function (params: any) {
+        var relVal = params[0].name;
+        let circle = `<i style="margin-right:4px;display: inline-block;width: 10px;height: 10px;border-radius: 50%;background-color:${params[0].color}"></i>`;
+        relVal += "<br/>" + circle + " " + t("message.iotCard.dashboard.charts.tooltip.traffic") + ": " + formatSize(params[0].value * 1024 * 1024);
+        return relVal;
+      },
+    },
     grid: { top: 10, right: 10, bottom: 10, left: 10 },
     series: [
       {
-        name: '流量',
-        type: 'line',
+        name: t("message.iotCard.dashboard.charts.series.traffic"),
+        type: "line",
         smooth: true,
         data: yesterdayLineData.value,
-        lineStyle: { color: '#fe9a8b' },
-        itemStyle: { color: '#fe9a8b', borderColor: '#fe9a8b' },
+        lineStyle: { color: "#fe9a8b" },
+        itemStyle: { color: "#fe9a8b", borderColor: "#fe9a8b" },
         areaStyle: {
           color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
-            { offset: 0, color: '#fe9a8bb3' },
-            { offset: 1, color: '#fe9a8b03' },
+            { offset: 0, color: "#fe9a8bb3" },
+            { offset: 1, color: "#fe9a8b03" },
           ]),
         },
-      }
+      },
     ],
   };
   (<any>state.global.yesterdayLine).setOption(option);
   (<any>state.myCharts).push(state.global.yesterdayLine);
 };
 
-
 // 折线图 - 当月流量消耗
 const initMonthLineChart = () => {
   if (!state.global.dispose.some((b: any) => b === state.global.monthLine)) state.global.monthLine.dispose();
-	state.global.monthLine = markRaw(echarts.init(monthLine.value, state.charts.theme));
+  state.global.monthLine = markRaw(echarts.init(monthLine.value, state.charts.theme));
   const option = {
     backgroundColor: state.charts.bgColor,
     xAxis: {
@@ -377,36 +342,36 @@ const initMonthLineChart = () => {
     },
     yAxis: [
       {
-        type: 'value',
-        name: '',
+        type: "value",
+        name: "",
         show: false,
       },
     ],
-    tooltip: { 
-			trigger: 'axis',
-			formatter: function (params:any) {
-					var relVal = params[0].name
-					let circle = `<i style="margin-right:4px;display: inline-block;width: 10px;height: 10px;border-radius: 50%;background-color:${params[0].color}"></i>`
-					relVal += '<br/>' + circle + ' 流量: ' + formatSize(params[0].value*1024*1024)
-					return relVal;
-				}
-		},
+    tooltip: {
+      trigger: "axis",
+      formatter: function (params: any) {
+        var relVal = params[0].name;
+        let circle = `<i style="margin-right:4px;display: inline-block;width: 10px;height: 10px;border-radius: 50%;background-color:${params[0].color}"></i>`;
+        relVal += "<br/>" + circle + " " + t("message.iotCard.dashboard.charts.tooltip.traffic") + ": " + formatSize(params[0].value * 1024 * 1024);
+        return relVal;
+      },
+    },
     grid: { top: 10, right: 10, bottom: 10, left: 10 },
     series: [
       {
-        name: '流量',
-        type: 'line',
+        name: t("message.iotCard.dashboard.charts.series.traffic"),
+        type: "line",
         smooth: true,
         data: monthLineData.value,
-        lineStyle: { color: '#9E87FF' },
-        itemStyle: { color: '#9E87FF', borderColor: '#9E87FF' },
-				areaStyle: {
-					color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
-						{ offset: 0, color: '#9E87FFb3' },
-						{ offset: 1, color: '#9E87FF03' },
-					]),
-				},
-      }
+        lineStyle: { color: "#9E87FF" },
+        itemStyle: { color: "#9E87FF", borderColor: "#9E87FF" },
+        areaStyle: {
+          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+            { offset: 0, color: "#9E87FFb3" },
+            { offset: 1, color: "#9E87FF03" },
+          ]),
+        },
+      },
     ],
   };
   (<any>state.global.monthLine).setOption(option);
@@ -416,7 +381,7 @@ const initMonthLineChart = () => {
 // 折线图 - 本年流量消耗
 const initYearLineChart = () => {
   if (!state.global.dispose.some((b: any) => b === state.global.yearLine)) state.global.yearLine.dispose();
-	state.global.yearLine = markRaw(echarts.init(yearLine.value, state.charts.theme));
+  state.global.yearLine = markRaw(echarts.init(yearLine.value, state.charts.theme));
   const option = {
     backgroundColor: state.charts.bgColor,
     xAxis: {
@@ -425,37 +390,37 @@ const initYearLineChart = () => {
     },
     yAxis: [
       {
-        type: 'value',
-        name: '',
+        type: "value",
+        name: "",
         show: false,
-        splitLine: { show: false, lineStyle: { type: 'dashed', color: '#f5f5f5' } }
-			}
+        splitLine: { show: false, lineStyle: { type: "dashed", color: "#f5f5f5" } },
+      },
     ],
-    tooltip: { 
-			trigger: 'axis',
-			formatter: function (params:any) {
-					var relVal = params[0].name
-					let circle = `<i style="margin-right:4px;display: inline-block;width: 10px;height: 10px;border-radius: 50%;background-color:${params[0].color}"></i>`
-					relVal += '<br/>' + circle + ' 流量: ' + formatSize(params[0].value*1024*1024)
-					return relVal;
-				}
-		},
+    tooltip: {
+      trigger: "axis",
+      formatter: function (params: any) {
+        var relVal = params[0].name;
+        let circle = `<i style="margin-right:4px;display: inline-block;width: 10px;height: 10px;border-radius: 50%;background-color:${params[0].color}"></i>`;
+        relVal += "<br/>" + circle + " " + t("message.iotCard.dashboard.charts.tooltip.traffic") + ": " + formatSize(params[0].value * 1024 * 1024);
+        return relVal;
+      },
+    },
     grid: { top: 10, right: 10, bottom: 10, left: 10 },
     series: [
       {
-        name: '流量',
-        type: 'line',
+        name: t("message.iotCard.dashboard.charts.series.traffic"),
+        type: "line",
         smooth: true,
         data: yearLineData.value,
-        lineStyle: { color: '#fe9a8b' },
-        itemStyle: { color: '#fe9a8b', borderColor: '#fe9a8b' },
-				areaStyle: {
-					color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
-						{ offset: 0, color: '#fe9a8bb3' },
-						{ offset: 1, color: '#fe9a8b03' },
-					]),
-				},
-      }
+        lineStyle: { color: "#fe9a8b" },
+        itemStyle: { color: "#fe9a8b", borderColor: "#fe9a8b" },
+        areaStyle: {
+          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+            { offset: 0, color: "#fe9a8bb3" },
+            { offset: 1, color: "#fe9a8b03" },
+          ]),
+        },
+      },
     ],
   };
   (<any>state.global.yearLine).setOption(option);
@@ -465,73 +430,73 @@ const initYearLineChart = () => {
 // 折线图 - 流量统计
 const iniFlowLineChart = async () => {
   if (!state.global.dispose.some((b: any) => b === state.global.flowLine)) state.global.flowLine.dispose();
-	state.global.flowLine = markRaw(echarts.init(flowLine.value, state.charts.theme));
+  state.global.flowLine = markRaw(echarts.init(flowLine.value, state.charts.theme));
   const option = {
     backgroundColor: state.charts.bgColor,
     grid: { top: 70, right: 20, bottom: 30, left: 30 },
-    tooltip: { 
-			trigger: 'axis',
-			formatter: function (params:any) {
-					var relVal = params[0].name
-					let circle = `<i style="margin-right:4px;display: inline-block;width: 10px;height: 10px;border-radius: 50%;background-color:${params[0].color}"></i>`
-					relVal += '<br/>' + circle + ' 流量: ' + params[0].value + 'MB'
-					return relVal;
-				}
-		},
+    tooltip: {
+      trigger: "axis",
+      formatter: function (params: any) {
+        var relVal = params[0].name;
+        let circle = `<i style="margin-right:4px;display: inline-block;width: 10px;height: 10px;border-radius: 50%;background-color:${params[0].color}"></i>`;
+        relVal += "<br/>" + circle + " " + t("message.iotCard.dashboard.charts.tooltip.traffic") + ": " + params[0].value + "MB";
+        return relVal;
+      },
+    },
     xAxis: {
       data: flowLineXAxisData.value,
     },
     yAxis: [
       {
-        type: 'value',
-        name: '',
-        splitLine: { show: true, lineStyle: { type: 'dashed', color: '#f5f5f5' } },
+        type: "value",
+        name: "",
+        splitLine: { show: true, lineStyle: { type: "dashed", color: "#f5f5f5" } },
         axisLabel: {
           margin: 2,
-          formatter: function (value:any) {
+          formatter: function (value: any) {
             if (value >= 10000 && value < 10000000) {
               value = value / 10000 + "W";
             } else if (value >= 10000000) {
               value = value / 10000000 + "KW";
             }
             return value;
-          }
+          },
         },
       },
     ],
     series: [
       {
-        name: '流量',
-        type: 'line',
+        name: t("message.iotCard.dashboard.charts.series.traffic"),
+        type: "line",
         symbolSize: 6,
-        symbol: 'circle',
+        symbol: "circle",
         smooth: true,
         data: flowLineData.value,
-        lineStyle: { color: '#9E87FF' },
-        itemStyle: { color: '#9E87FF', borderColor: '#9E87FF' },
+        lineStyle: { color: "#9E87FF" },
+        itemStyle: { color: "#9E87FF", borderColor: "#9E87FF" },
         areaStyle: {
           color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
-            { offset: 0, color: '#9E87FFb3' },
-            { offset: 1, color: '#9E87FF03' },
+            { offset: 0, color: "#9E87FFb3" },
+            { offset: 1, color: "#9E87FF03" },
           ]),
         },
         emphasis: {
           itemStyle: {
             color: {
-              type: 'radial',
+              type: "radial",
               x: 0.5,
               y: 0.5,
               r: 0.5,
               colorStops: [
-                { offset: 0, color: '#9E87FF' },
-                { offset: 0.4, color: '#9E87FF' },
-                { offset: 0.5, color: '#fff' },
-                { offset: 0.7, color: '#fff' },
-                { offset: 0.8, color: '#fff' },
-                { offset: 1, color: '#fff' },
+                { offset: 0, color: "#9E87FF" },
+                { offset: 0.4, color: "#9E87FF" },
+                { offset: 0.5, color: "#fff" },
+                { offset: 0.7, color: "#fff" },
+                { offset: 0.8, color: "#fff" },
+                { offset: 1, color: "#fff" },
               ],
             },
-            borderColor: '#9E87FF',
+            borderColor: "#9E87FF",
             borderWidth: 2,
           },
         },
@@ -554,9 +519,9 @@ watch(
   () => store.state.themeConfig.themeConfig.isIsDark,
   (isIsDark) => {
     nextTick(() => {
-      state.charts.theme = isIsDark ? 'dark' : '';
-      state.charts.bgColor = isIsDark ? 'transparent' : '';
-      state.charts.color = isIsDark ? '#dadada' : '#303133';
+      state.charts.theme = isIsDark ? "dark" : "";
+      state.charts.bgColor = isIsDark ? "transparent" : "";
+      state.charts.color = isIsDark ? "#dadada" : "#303133";
       setTimeout(() => {
         iniFlowLineChart();
         initYesterdayLineChart();
@@ -574,58 +539,57 @@ watch(
 
 <style lang="scss" scoped>
 .select-wrap {
-	text-align: right;
+  text-align: right;
 }
 .top-wrap {
-	width: 100%;
+  width: 100%;
   .flex {
     justify-content: space-between;
   }
-	.top-inner-wrap {
-		display: flex;
-		margin-top: 10px;
-
-	}
-	.line-wrap {
-		flex: 1;
-		background-color: #fff;
-		background: #fcfcfc;
-		border: 1px solid #e0e4e8;
-		display: flex;
-		justify-content: space-between;
-		align-items: center;
-		padding: 20px;
-		position: relative;
-		.text-wrap {
-			position: absolute;
-			left: 20px;
-			top: 40px;
-			.text {
-				font-size: 14px;
-				color: #000000a3;
-			}
-			div:nth-child(2) {
-				font-size: 32px;
-				font-weight: 700;
-				overflow: hidden;
-				white-space: nowrap;
-				text-overflow: ellipsis;
-			}
-		}
-		.line-inner-wrap {
-			height: 100px;
-			width: calc(100% - 150px);
-			margin-left: 150px;
-		}
-	}
-	.line-wrap:not(:nth-child(1)) {
-		margin-left: 20px;
-	}
+  .top-inner-wrap {
+    display: flex;
+    margin-top: 10px;
+  }
+  .line-wrap {
+    flex: 1;
+    background-color: #fff;
+    background: #fcfcfc;
+    border: 1px solid #e0e4e8;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 20px;
+    position: relative;
+    .text-wrap {
+      position: absolute;
+      left: 20px;
+      top: 40px;
+      .text {
+        font-size: 14px;
+        color: #000000a3;
+      }
+      div:nth-child(2) {
+        font-size: 32px;
+        font-weight: 700;
+        overflow: hidden;
+        white-space: nowrap;
+        text-overflow: ellipsis;
+      }
+    }
+    .line-inner-wrap {
+      height: 100px;
+      width: calc(100% - 150px);
+      margin-left: 150px;
+    }
+  }
+  .line-wrap:not(:nth-child(1)) {
+    margin-left: 20px;
+  }
 }
 .title {
-	color: var(--el-text-color-primary);
-	font-size: 16px;
-	font-weight: 700;
+  color: var(--el-text-color-primary);
+  font-size: 16px;
+  font-weight: 700;
 }
 .statistics-wrap {
   display: flex;
@@ -642,7 +606,7 @@ watch(
       align-items: center;
       margin-bottom: 16px;
       :deep(.el-date-editor.el-input__wrapper) {
-        width: 360px!important;
+        width: 360px !important;
         margin-left: 12px;
       }
       .operate-wrap {
@@ -655,23 +619,23 @@ watch(
   .right-wrap {
     width: calc(33.7% - 20px);
     margin-left: 20px;
-		.top-title-wrap {
+    .top-title-wrap {
       display: flex;
       justify-content: space-between;
       align-items: center;
       margin-bottom: 16px;
       :deep(.el-date-editor.el-input__wrapper) {
-        max-width: 220px!important;
+        max-width: 220px !important;
       }
     }
-		.ranking-list {
-			width: 100%;
-			.rank-item {
-				width: 100%;
-				display: flex;
-				justify-content: space-between;
-				align-items: center;
-				margin-bottom: 24px;
+    .ranking-list {
+      width: 100%;
+      .rank-item {
+        width: 100%;
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        margin-bottom: 24px;
 
         .number {
           flex: 0 0 24px;
@@ -694,19 +658,18 @@ watch(
           color: #597ef7;
           background-color: #597ef71a;
         }
-				.card-num {
-					width: 110px;
-				}
-				.progress-wrap {
-					width: 32%;
-				}
-				.flow-num {
-					width: 90px;
-					text-align: right;
-				}
-			}
-		}
+        .card-num {
+          width: 110px;
+        }
+        .progress-wrap {
+          width: 32%;
+        }
+        .flow-num {
+          width: 90px;
+          text-align: right;
+        }
+      }
+    }
   }
 }
-  
-</style>
+</style>

+ 32 - 27
src/views/modules/iotCard/index/detail.vue

@@ -1,7 +1,7 @@
 <!-- 物联网卡-详情 -->
 <template>
   <div>
-    <el-card shadow="nover">
+    <el-card shadow="never">
        <el-descriptions class="margin-top" title="基本信息" :column="3" :size="size" border>
         <!-- 卡号 -->
         <el-descriptions-item>
@@ -189,23 +189,23 @@
     </el-card>
 
     <div class="statistics-wrap gap-3">
-      <el-card shadow="nover" class="left-wrap">
+      <el-card shadow="never" class="left-wrap">
         <div class="top-title-wrap">
-            <div class="title">流量统计</div>
+            <div class="title">{{ $t('message.iotCard.detail.flowChart.title') }}</div>
             <div class="operate-wrap">
               <el-button-group>
-                <el-button @click="changeDate(1)" :type="activeIndex == 1 ?  'primary' : ''">昨日</el-button>
-                <el-button @click="changeDate(2)" :type="activeIndex == 2 ?  'primary' : ''">近一周</el-button>
-                <el-button @click="changeDate(3)" :type="activeIndex == 3 ?  'primary' : ''">近一月</el-button>
-                <el-button @click="changeDate(4)" :type="activeIndex == 4 ?  'primary' : ''">近一年</el-button>
+                <el-button @click="changeDate(1)" :type="activeIndex == 1 ?  'primary' : ''">{{ $t('message.iotCard.detail.flowChart.buttons.yesterday') }}</el-button>
+                <el-button @click="changeDate(2)" :type="activeIndex == 2 ?  'primary' : ''">{{ $t('message.iotCard.detail.flowChart.buttons.week') }}</el-button>
+                <el-button @click="changeDate(3)" :type="activeIndex == 3 ?  'primary' : ''">{{ $t('message.iotCard.detail.flowChart.buttons.month') }}</el-button>
+                <el-button @click="changeDate(4)" :type="activeIndex == 4 ?  'primary' : ''">{{ $t('message.iotCard.detail.flowChart.buttons.year') }}</el-button>
               </el-button-group>
                 <el-date-picker
                   class="date-picker-wrap"
                   v-model="dateRange"
-                  type="datetimerange"
-                  range-separator="至"
-                  start-placeholder="开始时间"
-                  end-placeholder="结束时间"
+                  type="daterange"
+                  :range-separator="$t('message.iotCard.detail.flowChart.datePicker.rangeSeparator')"
+                  :start-placeholder="$t('message.iotCard.detail.flowChart.datePicker.startPlaceholder')"
+                  :end-placeholder="$t('message.iotCard.detail.flowChart.datePicker.endPlaceholder')"
                   format="YYYY-MM-DD HH:mm:ss"
                   date-format="YYYY/MM/DD"
                   time-format="hh:mm:ss"
@@ -216,18 +216,18 @@
         <div style="height: 460px;" ref="flowLine"></div>
       </el-card>
 
-      <el-card shadow="nover" class="right-wrap">
-        <div class="title">数据统计</div>
+      <el-card shadow="never" class="right-wrap">
+        <div class="title">{{ $t('message.iotCard.detail.dataStatistics.title') }}</div>
         <div class="line-wrap flow-line-wrap">
           <div class="text-wrap">
-            <div class="text">昨日流量消耗</div>
+            <div class="text">{{ $t('message.iotCard.detail.dataStatistics.yesterdayFlow') }}</div>
             <div>{{formatSize(statisticsData.yesterdayTotal * 1024 * 1024)}}</div>
           </div>
           <div class="line-inner-wrap" ref="yesterdayLine"></div>
         </div>
         <div class="line-wrap flow-line-wrap">
           <div class="text-wrap">
-            <div class="text">当月流量消耗</div>
+            <div class="text">{{ $t('message.iotCard.detail.dataStatistics.monthFlow') }}</div>
             <div>{{formatSize(statisticsData.monthTotal * 1024 * 1024)}}</div>
           </div>
           <div class="line-inner-wrap" ref="monthLine"></div>
@@ -235,7 +235,7 @@
 
         <div class="line-wrap flow-line-wrap">
           <div class="text-wrap">
-            <div class="text">本年流量消耗</div>
+            <div class="text">{{ $t('message.iotCard.detail.dataStatistics.yearFlow') }}</div>
             <div>{{formatSize(statisticsData.yearTotal * 1024 * 1024)}}</div>
           </div>
           <div class="line-inner-wrap" ref="yearLine"></div>
@@ -254,9 +254,11 @@ import { useStore } from '/@/store/index';
 import { useRoute } from 'vue-router';
 import * as echarts from 'echarts';
 import dayjs from 'dayjs';
+import { useI18n } from 'vue-i18n';
 
 const store = useStore();
 const route = useRoute();
+const { t } = useI18n();
 const sim = ref({
   accNumber: "",// 卡号
   iccid: "",// ICCID
@@ -407,17 +409,20 @@ const handleDateChange = async (value:any) => {
 
 const formatOperator = (val:number) => {
   // 1电信,2联通,3移动
-  return ['', '电信', '联通', '移动'][val];
+  const operators = ['', t('message.iotCard.index.operators.telecom'), t('message.iotCard.index.operators.unicom'), t('message.iotCard.index.operators.mobile')];
+  return operators[val];
 }
 
 const formatType = (val:number) => {
   // 1月卡,2季卡,3年卡,4其他
-  return ['', '月卡', '季卡', '年卡', '其他'][val];
+  const types = ['', t('message.iotCard.index.types.monthly'), t('message.iotCard.index.types.quarterly'), t('message.iotCard.index.types.yearly'), t('message.iotCard.index.types.other')];
+  return types[val];
 }
 
 const formatStatus = (val:any) => {
   // 1:可激活 2:测试激活 3:测试去激活 4:在用 5:停机 6:运营商管理状态
-  return ['', '可激活', '测试激活', '测试去激活', '在用', '停机', '运营商管理状态'][val];
+  const statuses = ['', t('message.iotCard.index.status.activatable'), t('message.iotCard.index.status.testActivated'), t('message.iotCard.index.status.testDeactivated'), t('message.iotCard.index.status.inUse'), t('message.iotCard.index.status.suspended'), t('message.iotCard.index.status.operatorManaged')];
+  return statuses[val];
 }
 
 // 折线图 - 昨日流量消耗
@@ -443,14 +448,14 @@ const initYesterdayLineChart = () => {
 			formatter: function (params:any) {
 					var relVal = params[0].name
 					let circle = `<i style="margin-right:4px;display: inline-block;width: 10px;height: 10px;border-radius: 50%;background-color:${params[0].color}"></i>`
-					relVal += '<br/>' + circle + ' 流量: ' + formatSize(params[0].value*1024*1024)
+					relVal += '<br/>' + circle + ' ' + t('message.iotCard.detail.charts.tooltip.flow') + ': ' + formatSize(params[0].value*1024*1024)
 					return relVal;
 				}
 		},
     grid: { top: 10, right: 10, bottom: 10, left: 10 },
     series: [
       {
-        name: '流量',
+        name: t('message.iotCard.detail.charts.series.flow'),
         type: 'line',
         smooth: true,
         data: yesterdayLineData.value,
@@ -487,14 +492,14 @@ const initMonthLineChart = () => {
 			formatter: function (params:any) {
 					var relVal = params[0].name
 					let circle = `<i style="margin-right:4px;display: inline-block;width: 10px;height: 10px;border-radius: 50%;background-color:${params[0].color}"></i>`
-					relVal += '<br/>' + circle + ' 流量: ' + formatSize(params[0].value*1024*1024)
+					relVal += '<br/>' + circle + ' ' + t('message.iotCard.detail.charts.tooltip.flow') + ': ' + formatSize(params[0].value*1024*1024)
 					return relVal;
 				}
 		},
     grid: { top: 10, right: 10, bottom: 10, left: 10 },
     series: [
       {
-        name: '流量',
+        name: t('message.iotCard.detail.charts.series.flow'),
         type: 'line',
         smooth: true,
         data: monthLineData.value,
@@ -530,14 +535,14 @@ const initYearLineChart = () => {
 			formatter: function (params:any) {
 					var relVal = params[0].name
 					let circle = `<i style="margin-right:4px;display: inline-block;width: 10px;height: 10px;border-radius: 50%;background-color:${params[0].color}"></i>`
-					relVal += '<br/>' + circle + ' 流量: ' + formatSize(params[0].value*1024*1024)
+					relVal += '<br/>' + circle + ' ' + t('message.iotCard.detail.charts.tooltip.flow') + ': ' + formatSize(params[0].value*1024*1024)
 					return relVal;
 				}
 		},
     grid: { top: 10, right: 10, bottom: 10, left: 10 },
     series: [
       {
-        name: '流量',
+        name: t('message.iotCard.detail.charts.series.flow'),
         type: 'line',
         smooth: true,
         data: yearLineData.value,
@@ -562,7 +567,7 @@ const iniFlowLineChart = async () => {
 			formatter: function (params:any) {
 					var relVal = params[0].name
 					let circle = `<i style="margin-right:4px;display: inline-block;width: 10px;height: 10px;border-radius: 50%;background-color:${params[0].color}"></i>`
-					relVal += '<br/>' + circle + ' 流量: ' + params[0].value + 'MB'
+					relVal += '<br/>' + circle + ' ' + t('message.iotCard.detail.charts.tooltip.flow') + ': ' + params[0].value + 'MB'
 					return relVal;
 				}
 		},
@@ -589,7 +594,7 @@ const iniFlowLineChart = async () => {
     ],
     series: [
       {
-        name: '流量',
+        name: t('message.iotCard.detail.charts.series.flow'),
         type: 'line',
         symbolSize: 6,
         symbol: 'circle',

+ 38 - 33
src/views/modules/iotCard/index/index.vue

@@ -1,49 +1,49 @@
 <!-- 物联网卡列表 -->
 <template>
   <div class="page-full">
-    <el-card shadow="nover" class="page-full-part">
+    <el-card shadow="never" class="page-full-part">
       <el-form :model="params" inline ref="queryRef">
         <el-form-item prop="deptName" class="mr10">
-          <el-input @keyup.enter.native="getList" style="width: 240px;" v-model="params.keyWord" placeholder="请输入ICCID或卡号" clearable />
+          <el-input @keyup.enter.native="getList" style="width: 240px;" v-model="params.keyWord" :placeholder="$t('message.iotCard.index.search.placeholder')" clearable />
         </el-form-item>
         <el-form-item>
           <el-button type="primary" @click="getList">
             <el-icon>
               <ele-Search />
             </el-icon>
-            查询
+            {{ $t('message.iotCard.index.search.query') }}
           </el-button>
           <el-button v-auth="'reset'" @click="resetQuery()">
             <el-icon>
               <ele-Refresh />
             </el-icon>
-            重置
+            {{ $t('message.iotCard.index.search.reset') }}
           </el-button>
         </el-form-item>
       </el-form>
-      <el-table :data="tableData" max-height="calc(100vh  - 210px);" v-loading="loading" style="width: 100%">
-        <el-table-column v-col="'accNumber'" fixed="left" min-width="130" label="卡号" prop="accNumber" align="center" />
-        <el-table-column v-col="'ICCID'" min-width="180" label="ICCID" prop="iccid" align="center" />
-        <el-table-column v-col="'bindDeviceName'" label="绑定设备" prop="bindDeviceName" align="center" />
-        <el-table-column v-col="'platName'" label="平台对接" prop="platName" align="center" />
-        <el-table-column v-col="'types'" label="运营商" prop="types" align="center">
+      <el-table :data="tableData" max-height="calc(100vh - 210px)" v-loading="loading" style="width: 100%">
+        <el-table-column v-col="'accNumber'" fixed="left" min-width="130" :label="$t('message.iotCard.index.table.columns.cardNumber')" prop="accNumber" align="center" />
+        <el-table-column v-col="'ICCID'" min-width="180" :label="$t('message.iotCard.index.table.columns.iccid')" prop="iccid" align="center" />
+        <el-table-column v-col="'bindDeviceName'" width="120" :label="$t('message.iotCard.index.table.columns.bindDevice')" prop="bindDeviceName" align="center" />
+        <el-table-column v-col="'platName'" width="120" :label="$t('message.iotCard.index.table.columns.platform')" prop="platName" align="center" />
+        <el-table-column v-col="'types'" width="120" :label="$t('message.iotCard.index.table.columns.operator')" prop="types" align="center">
           <template #default="scope">{{ formatOperator(scope.row.types) }}</template>
         </el-table-column>
-        <el-table-column v-col="'simTypes'" label="类型" prop="simTypes" align="center">
+        <el-table-column v-col="'simTypes'" width="120" :label="$t('message.iotCard.index.table.columns.type')" prop="simTypes" align="center">
           <template #default="scope">{{ formatType(scope.row.simTypes) }}</template>
         </el-table-column>
-        <el-table-column v-col="'totalFlow'" label="总流量" prop="totalFlow" align="center" />
-        <el-table-column v-col="'usedFlow'" label="使用流量" prop="usedFlow" align="center" />
-        <el-table-column v-col="'leaveFlow'" label="剩余流量" prop="leaveFlow" align="center" />
-        <el-table-column v-col="'activationTime'" width="160" label="激活日期" prop="activationTime" align="center" />
-        <el-table-column v-col="'updatedAt'" width="160" label="更新时间" prop="updatedAt" align="center" />
-        <el-table-column v-col="'simStatus'" label="状态" prop="simStatus" align="center">
+        <el-table-column v-col="'totalFlow'" width="120" :label="$t('message.iotCard.index.table.columns.totalFlow')" prop="totalFlow" align="center" />
+        <el-table-column v-col="'usedFlow'" width="120" :label="$t('message.iotCard.index.table.columns.usedFlow')" prop="usedFlow" align="center" />
+        <el-table-column v-col="'leaveFlow'" width="120" :label="$t('message.iotCard.index.table.columns.remainFlow')" prop="leaveFlow" align="center" />
+        <el-table-column v-col="'activationTime'" width="140" :label="$t('message.iotCard.index.table.columns.activationDate')" prop="activationTime" align="center" />
+        <el-table-column v-col="'updatedAt'" width="120" :label="$t('message.iotCard.index.table.columns.updateTime')" prop="updatedAt" align="center" />
+        <el-table-column v-col="'simStatus'" width="120" :label="$t('message.iotCard.index.table.columns.status')" prop="simStatus" align="center">
           <template #default="scope">{{ formatStatus(scope.row.simStatus) }}</template>
         </el-table-column>
-        <el-table-column v-col="'handle'" width="110" label="操作" fixed="right" prop="handle" align="center">
+        <el-table-column v-col="'handle'" width="110" :label="$t('message.iotCard.index.table.columns.actions')" fixed="right" prop="handle" align="center">
           <template #default="scope">
-            <el-button size="small" text type="primary" @click="onOpenDetail(scope.row)" v-auth="'detail'">详情</el-button>
-            <el-button size="small" text type="warning" @click="onDel(scope.row)" v-auth="'del'">删除</el-button>
+            <el-button size="small" text type="primary" @click="onOpenDetail(scope.row)" v-auth="'detail'">{{ $t('message.iotCard.index.table.actions.detail') }}</el-button>
+            <el-button size="small" text type="warning" @click="onDel(scope.row)" v-auth="'del'">{{ $t('message.iotCard.index.table.actions.delete') }}</el-button>
           </template>
         </el-table-column>
       </el-table>
@@ -57,6 +57,8 @@ import api from '/@/api/modules/iotCard';
 import { ElMessageBox, ElMessage } from 'element-plus';
 import { useSearch } from "/@/hooks/useCommon"
 import { useRouter } from 'vue-router';
+import { useI18n } from 'vue-i18n';
+const { t } = useI18n();
 const { params, tableData, getList, loading } = useSearch<any[]>(
   api.simCard.getList,
   "Data"
@@ -76,30 +78,33 @@ const resetQuery = () => {
  * 单一删除
  */
 const onDel = (row: any) => {
-  ElMessageBox.confirm(`此操作将卡号为:“${row.accNumber}”进行删除,是否继续?`, '提示', {
-    confirmButtonText: '确认',
-    cancelButtonText: '取消',
+  ElMessageBox.confirm(t('message.iotCard.index.messages.deleteConfirm', { cardNumber: row.accNumber }), t('message.iotCard.index.messages.tip'), {
+    confirmButtonText: t('message.iotCard.index.messages.confirm'),
+    cancelButtonText: t('message.iotCard.index.messages.cancel'),
     type: 'warning',
   }).then(async () => {
     await api.simCard.deleteItem({ ids: [row.id] });
-    ElMessage.success('删除成功');
+    ElMessage.success(t('message.iotCard.index.messages.deleteSuccess'));
     getList();
   });
 };
 
-const formatOperator = (val: number) => {
-  // 1电信,2联通,3移动
-  return ['', '电信', '联通', '移动'][val];
+const formatOperator = (val: any) => {
+  // 1:电信 2:联通 3:移动
+  const operators = ['', t('message.iotCard.index.operators.telecom'), t('message.iotCard.index.operators.unicom'), t('message.iotCard.index.operators.mobile')];
+  return operators[val];
 }
 
-const formatType = (val: number) => {
-  // 1月卡,2季卡,3年卡,4其他
-  return ['', '月卡', '季卡', '年卡', '其他'][val];
+const formatType = (val: any) => {
+  // 1:月卡 2:季卡 3:年卡 4:其他
+  const types = ['', t('message.iotCard.index.types.monthly'), t('message.iotCard.index.types.quarterly'), t('message.iotCard.index.types.yearly'), t('message.iotCard.index.types.other')];
+  return types[val];
 }
 
-const formatStatus = (val: number) => {
-  // 1:可激活 2:测试激活 3:测试去激活 4:在用5:停机6:运营商管理状态
-  return ['', '可激活', '测试激活', '测试去激活', '在用', '停机', '运营商管理状态'][val];
+const formatStatus = (val: any) => {
+  // 1:可激活 2:测试激活 3:测试去激活 4:在用 5:停机 6:运营商管理状态
+  const statuses = ['', t('message.iotCard.index.status.activatable'), t('message.iotCard.index.status.testActivated'), t('message.iotCard.index.status.testDeactivated'), t('message.iotCard.index.status.inUse'), t('message.iotCard.index.status.suspended'), t('message.iotCard.index.status.operatorManaged')];
+  return statuses[val];
 }
 
 const onOpenDetail = (item: any) => {

+ 31 - 29
src/views/modules/iotCard/platformManage/addOrEditItem.vue

@@ -1,44 +1,44 @@
 <!-- 平台接入-新增或者编辑 -->
 <template>
-  <el-dialog :title="ruleForm.id ? '编辑' : '新增'" v-model="isShowDialog" width="650px">
-    <el-form :model="ruleForm" ref="formRef" :rules="rules" v-if="isShowDialog" label-width="100px">
-      <el-form-item label="平台类型" prop="types">
-        <el-select style="width: 100%;" v-model="ruleForm.types" placeholder="请选择">
-          <el-option label="电信" value="1"></el-option>
+  <el-dialog :title="ruleForm.id ? $t('message.iotCard.platformManage.addOrEdit.title.edit') : $t('message.iotCard.platformManage.addOrEdit.title.add')" v-model="isShowDialog" width="650px">
+    <el-form :model="ruleForm" ref="formRef" :rules="rules" v-if="isShowDialog" label-width="110px">
+      <el-form-item :label="$t('message.iotCard.platformManage.addOrEdit.form.platformType')" prop="types">
+        <el-select style="width: 100%;" v-model="ruleForm.types" :placeholder="$t('message.iotCard.platformManage.addOrEdit.placeholders.select')">
+          <el-option :label="$t('message.iotCard.platformManage.addOrEdit.operators.telecom')" value="1"></el-option>
           <!--            <el-option disabled label="联通" value="2"></el-option>-->
           <!--            <el-option label="移动" value="3"></el-option>-->
         </el-select>
       </el-form-item>
-      <el-form-item label="名称" prop="name">
-        <el-input v-model="ruleForm.name" placeholder="请输入名称" />
+      <el-form-item :label="$t('message.iotCard.platformManage.addOrEdit.form.name')" prop="name">
+        <el-input v-model="ruleForm.name" :placeholder="$t('message.iotCard.platformManage.addOrEdit.placeholders.name')" />
       </el-form-item>
-      <el-form-item label="App ID" prop="appKey">
-        <el-input v-model="ruleForm.appKey" placeholder="请输入App ID" />
+      <el-form-item :label="$t('message.iotCard.platformManage.addOrEdit.form.appId')" prop="appKey">
+        <el-input v-model="ruleForm.appKey" :placeholder="$t('message.iotCard.platformManage.addOrEdit.placeholders.appId')" />
       </el-form-item>
-      <el-form-item v-if="ruleForm.types == '1'" label="secretKey" prop="appSecret">
-        <el-input v-model="ruleForm.appSecret" placeholder="请输入secretKey" />
+      <el-form-item v-if="ruleForm.types == '1'" :label="$t('message.iotCard.platformManage.addOrEdit.form.secretKey')" prop="appSecret">
+        <el-input v-model="ruleForm.appSecret" :placeholder="$t('message.iotCard.platformManage.addOrEdit.placeholders.secretKey')" />
       </el-form-item>
-      <el-form-item v-if="ruleForm.types == '1'" label="用户id" prop="userId">
-        <el-input v-model="ruleForm.userId" placeholder="请输入用户id" />
+      <el-form-item v-if="ruleForm.types == '1'" :label="$t('message.iotCard.platformManage.addOrEdit.form.userId')" prop="userId">
+        <el-input v-model="ruleForm.userId" :placeholder="$t('message.iotCard.platformManage.addOrEdit.placeholders.userId')" />
       </el-form-item>
       <!-- <el-form-item label="密码" prop="password">
         <el-input v-model="ruleForm.password" placeholder="请输入密码" />
       </el-form-item> -->
-      <el-form-item v-if="ruleForm.types == '3'" label="接口地址" prop="restUrl">
-        <el-input v-model="ruleForm.restUrl" placeholder="请输入接口地址" />
+      <el-form-item v-if="ruleForm.types == '3'" :label="$t('message.iotCard.platformManage.addOrEdit.form.interfaceUrl')" prop="restUrl">
+        <el-input v-model="ruleForm.restUrl" :placeholder="$t('message.iotCard.platformManage.addOrEdit.placeholders.interfaceUrl')" />
       </el-form-item>
-      <el-form-item label="状态">
+      <el-form-item :label="$t('message.iotCard.platformManage.addOrEdit.form.status')">
         <!-- 1启用,0禁用 -->
-        <el-switch v-model="ruleForm.status" active-value="1" inactive-value="0" inline-prompt active-text="启" inactive-text="禁"></el-switch>
+        <el-switch v-model="ruleForm.status" active-value="1" inactive-value="0" inline-prompt :active-text="$t('message.iotCard.platformManage.addOrEdit.switch.enabled')" :inactive-text="$t('message.iotCard.platformManage.addOrEdit.switch.disabled')"></el-switch>
       </el-form-item>
-      <el-form-item label="说明">
-        <el-input :rows="6" type="textarea" v-model="ruleForm.remark" placeholder="请输入说明" />
+      <el-form-item :label="$t('message.iotCard.platformManage.addOrEdit.form.description')">
+        <el-input :rows="6" type="textarea" v-model="ruleForm.remark" :placeholder="$t('message.iotCard.platformManage.addOrEdit.placeholders.description')" />
       </el-form-item>
     </el-form>
     <template #footer>
       <span class="dialog-footer">
-        <el-button @click="onCancel">取 消</el-button>
-        <el-button :loading="btnLoading" type="primary" @click="onSubmit">确定</el-button>
+        <el-button @click="onCancel">{{ $t('message.iotCard.platformManage.addOrEdit.buttons.cancel') }}</el-button>
+        <el-button :loading="btnLoading" type="primary" @click="onSubmit">{{ $t('message.iotCard.platformManage.addOrEdit.buttons.confirm') }}</el-button>
       </span>
     </template>
   </el-dialog>
@@ -48,11 +48,13 @@
 import api from '/@/api/modules/iotCard';
 import { ref, unref } from 'vue';
 import { ElMessage } from 'element-plus';
+import { useI18n } from 'vue-i18n';
 
 const isShowDialog = ref(false);
 const formRef = ref<HTMLElement | null>(null);
 const emit = defineEmits(['updateList']);
 const btnLoading = ref(false);
+const { t } = useI18n();
 
 const baseForm = {
   id: undefined,
@@ -72,13 +74,13 @@ const ruleForm = ref({
 })
 
 const rules = ref({
-  types: [{ required: true, message: '请选择平台类型', trigger: 'change' }],
-  name: [{ required: true, message: '请输入名称', trigger: 'change' }],
-  userId: [{ required: true, message: '请输入用户id', trigger: 'change' }],
+  types: [{ required: true, message: t('message.iotCard.platformManage.addOrEdit.validation.platformType'), trigger: 'change' }],
+  name: [{ required: true, message: t('message.iotCard.platformManage.addOrEdit.validation.name'), trigger: 'change' }],
+  userId: [{ required: true, message: t('message.iotCard.platformManage.addOrEdit.validation.userId'), trigger: 'change' }],
   // password: [{ required: true, message: '请输入密码', trigger: 'change' }],
-  appSecret: [{ required: true, message: '请输入secretKey', trigger: 'change' }],
-  appKey: [{ required: true, message: '请输入App ID', trigger: 'change' }],
-  restUrl: [{ required: true, message: '请输入接口地址', trigger: 'change' }]
+  appSecret: [{ required: true, message: t('message.iotCard.platformManage.addOrEdit.validation.secretKey'), trigger: 'change' }],
+  appKey: [{ required: true, message: t('message.iotCard.platformManage.addOrEdit.validation.appId'), trigger: 'change' }],
+  restUrl: [{ required: true, message: t('message.iotCard.platformManage.addOrEdit.validation.interfaceUrl'), trigger: 'change' }]
 })
 
 /**
@@ -94,7 +96,7 @@ const onSubmit = () => {
       // 修改
       api.platform.editItem(ruleForm.value)
         .then(() => {
-          ElMessage({ type: 'success', message: '修改成功' })
+          ElMessage({ type: 'success', message: t('message.iotCard.platformManage.addOrEdit.messages.editSuccess') })
           emit('updateList')
           closeDialog();
           resetForm();
@@ -104,7 +106,7 @@ const onSubmit = () => {
       // 新增
       api.platform.addItem(ruleForm.value)
         .then(() => {
-          ElMessage({ type: 'success', message: '添加成功' })
+          ElMessage({ type: 'success', message: t('message.iotCard.platformManage.addOrEdit.messages.addSuccess') })
           emit('updateList')
           closeDialog();
           resetForm();

+ 29 - 20
src/views/modules/iotCard/platformManage/index.vue

@@ -1,46 +1,46 @@
 <!-- 平台接入列表 -->
 <template>
   <div class="page-full">
-    <el-card shadow="nover" class="page-full-part">
+    <el-card shadow="never" class="page-full-part">
       <el-form :model="params" inline ref="queryRef">
         <el-form-item prop="deptName" class="mr10">
-          <el-input @keyup.enter.native="getList" style="width: 240px;" v-model="params.keyWord" placeholder="请输入关键字搜索" clearable />
+          <el-input @keyup.enter.native="getList" style="width: 240px;" v-model="params.keyWord" :placeholder="$t('message.iotCard.platformManage.search.placeholder')" clearable />
         </el-form-item>
         <el-form-item>
           <el-button type="primary" @click="getList">
             <el-icon>
               <ele-Search />
             </el-icon>
-            查询
+            {{ $t('message.iotCard.platformManage.search.query') }}
           </el-button>
           <el-button v-auth="'reset'" @click="resetQuery()">
             <el-icon>
               <ele-Refresh />
             </el-icon>
-            重置
+            {{ $t('message.iotCard.platformManage.search.reset') }}
           </el-button>
           <el-button v-auth="'add'" type="primary" @click="toAddItemPage()">
             <el-icon>
               <ele-FolderAdd />
             </el-icon>
-            新增
+            {{ $t('message.iotCard.platformManage.search.add') }}
           </el-button>
         </el-form-item>
       </el-form>
       <el-table :data="tableData" max-height="calc(100vh  - 210px);" v-loading="loading" style="width: 100%">
-        <el-table-column v-col="'name'" label="名称" prop="name" align="left" />
-        <el-table-column v-col="'type'" label="平台类型" prop="types" :formatter="function (row: any) { return ['', '电信', '联通', '移动'][row.types] }" align="center" width="120" />
-        <el-table-column v-col="'appKey'" label="App ID" prop="appKey" align="center" />
-        <el-table-column v-col="'simStatus'" label="状态" prop="simStatus" align="center" width="80">
+        <el-table-column v-col="'name'" :label="$t('message.iotCard.platformManage.table.columns.name')" prop="name" align="left" />
+        <el-table-column v-col="'type'" :label="$t('message.iotCard.platformManage.table.columns.platformType')" prop="types" :formatter="formatPlatformType" align="center" width="120" />
+        <el-table-column v-col="'appKey'" :label="$t('message.iotCard.platformManage.table.columns.appId')" prop="appKey" align="center" />
+        <el-table-column v-col="'simStatus'" :label="$t('message.iotCard.platformManage.table.columns.status')" prop="simStatus" align="center" width="140">
           <template #default="scope">
-            <el-tag :type="scope.row.status === '0' ? 'danger' : 'primary'">{{ formatStatus(scope.row.status) }}</el-tag>
+            <el-tag :type="scope.row.status === '0' ? 'danger' : ''">{{ formatStatus(scope.row.status) }}</el-tag>
           </template>
         </el-table-column>
-        <el-table-column v-col="'remark'" label="说明" prop="remark" align="center" show-overflow-tooltip />
-        <el-table-column v-col="'handle'" width="110" label="操作" fixed="right" prop="handle" align="center">
+        <el-table-column v-col="'remark'" :label="$t('message.iotCard.platformManage.table.columns.description')" prop="remark" align="center" show-overflow-tooltip />
+        <el-table-column v-col="'handle'" width="110" :label="$t('message.iotCard.platformManage.table.columns.actions')" fixed="right" prop="handle" align="center">
           <template #default="scope">
-            <el-button v-auth="'detail'" size="small" text type="primary" @click="onOpenDetail(scope.row)">编辑</el-button>
-            <el-button v-auth="'del'" size="small" text type="warning" @click="onDel(scope.row)">删除</el-button>
+            <el-button v-auth="'detail'" size="small" text type="primary" @click="onOpenDetail(scope.row)">{{ $t('message.iotCard.platformManage.table.actions.edit') }}</el-button>
+            <el-button v-auth="'del'" size="small" text type="warning" @click="onDel(scope.row)">{{ $t('message.iotCard.platformManage.table.actions.delete') }}</el-button>
           </template>
         </el-table-column>
       </el-table>
@@ -53,8 +53,10 @@
 <script lang="ts" setup>
 import api from '/@/api/modules/iotCard';
 import { defineAsyncComponent, ref } from 'vue';
-import { ElMessageBox, ElMessage, formatter } from 'element-plus';
+import { ElMessageBox, ElMessage } from 'element-plus';
 import { useSearch } from "/@/hooks/useCommon"
+import { useI18n } from 'vue-i18n';
+const { t } = useI18n();
 const AddOrEditItem = defineAsyncComponent(() => import('./addOrEditItem.vue'));
 
 const { params, tableData, getList, loading } = useSearch<any[]>(
@@ -75,20 +77,27 @@ const resetQuery = () => {
  * 单一删除
  */
 const onDel = (row: any) => {
-  ElMessageBox.confirm(`此操作将卡号为:“${row.accNumber}”,是否继续?`, '提示', {
-    confirmButtonText: '确认',
-    cancelButtonText: '取消',
+  ElMessageBox.confirm(t('message.iotCard.platformManage.messages.deleteConfirm', { cardNumber: row.accNumber }), t('message.iotCard.platformManage.messages.tip'), {
+    confirmButtonText: t('message.iotCard.platformManage.messages.confirm'),
+    cancelButtonText: t('message.iotCard.platformManage.messages.cancel'),
     type: 'warning',
   }).then(async () => {
     await api.simCard.deleteItem({ ids: [row.id] });
-    ElMessage.success('删除成功');
+    ElMessage.success(t('message.iotCard.platformManage.messages.deleteSuccess'));
     getList();
   });
 };
 
 const formatStatus = (val: any) => {
   // 1:开启 0:禁用
-  return ['禁用', '开启'][val];
+  const statuses = [t('message.iotCard.platformManage.status.disabled'), t('message.iotCard.platformManage.status.enabled')];
+  return statuses[val];
+}
+
+const formatPlatformType = (row: any) => {
+  // 1:电信 2:联通 3:移动
+  const types = ['', t('message.iotCard.platformManage.addOrEdit.operators.telecom'), t('message.iotCard.platformManage.addOrEdit.operators.unicom'), t('message.iotCard.platformManage.addOrEdit.operators.mobile')];
+  return types[row.types];
 }
 
 const onOpenDetail = (item: any) => {

+ 161 - 168
src/views/modules/policy/index/components/addItem.vue

@@ -1,13 +1,7 @@
 <template>
   <el-dialog :title="ruleForm.id ? '修改控制策略' : '添加控制策略'" v-model="isShowDialog" width="650px">
-    <el-form
-      ref="ruleFormRef"
-      :model="ruleForm"
-      :rules="rules"
-      label-width="auto"
-      status-icon
-    >
-      <el-form-item label="标题" prop="title" style="width: 388px;">
+    <el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" label-width="auto" status-icon>
+      <el-form-item label="标题" prop="title" style="width: 388px">
         <el-input clearable v-model="ruleForm.title" />
       </el-form-item>
       <el-form-item label="类型" prop="types">
@@ -22,20 +16,20 @@
           <el-radio :label="2">禁用</el-radio>
         </el-radio-group>
       </el-form-item>
-      <el-form-item label="说明" style="width: 388px;">
-        <el-input  v-model="ruleForm.description" type="textarea" />
+      <el-form-item label="说明" style="width: 388px">
+        <el-input v-model="ruleForm.description" type="textarea" />
       </el-form-item>
       <el-form-item label="参数配置" prop="param">
-        <div style="width: 100%;">
+        <div style="width: 100%">
           <div class="param-wrap" v-for="(item, index) in ruleForm.param" :key="index">
-            <el-input  clearable placeholder="key" v-model="item.key" />
+            <el-input clearable placeholder="key" v-model="item.key" />
             <el-input clearable placeholder="标题" v-model="item.title" />
             <el-select v-model="item.types" placeholder="请选择数据类型">
-							<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-button v-if="index === ruleForm.param.length -1" @click="plusParam()">
+              <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-button v-if="index === ruleForm.param.length - 1" @click="plusParam()">
               <el-icon>
                 <ele-Plus />
               </el-icon>
@@ -56,145 +50,146 @@
 </template>
 
 <script lang="ts" setup>
-import { ref } from 'vue'
-import api from '/@/api/device';
-import policyApi from '/@/api/modules/policy';
-import axios from 'axios';
-import { ElMessage } from 'element-plus';
-import type { FormInstance } from 'element-plus'
+import { ref } from "vue";
+import api from "/@/api/device";
+import policyApi from "/@/api/modules/policy";
+import axios from "axios";
+import { ElMessage } from "element-plus";
+import type { FormInstance } from "element-plus";
 import { getToken } from "/@/utils/auth";
 import { v4 as uuid } from "uuid";
+import { getRuleServerOrigin } from "/@/utils/origin";
 
 interface RuleForm {
-  id: any
-  title: string
-  types: number
-  status: number
-  description: string,
-  param: any[],
-  flowId: string,
+  id: any;
+  title: string;
+  types: number;
+  status: number;
+  description: string;
+  param: any[];
+  flowId: string;
 }
 
-const emit = defineEmits(['getList']);
+const emit = defineEmits(["getList"]);
 
 const headers = {
-	Authorization: 'Bearer ' + getToken(),
+  Authorization: "Bearer " + getToken(),
 };
 const isShowDialog = ref(false);
-const ruleFormRef = ref<FormInstance>()
+const ruleFormRef = ref<FormInstance>();
 const ruleForm = ref<RuleForm>({
   id: null,
-  title: '',
+  title: "",
   types: 2,
   status: 1,
-  description: '',
-  param: [ {
-    key: "",
-    title: "",
-    types: ""
-  }],
-  flowId: ''
-})
+  description: "",
+  param: [
+    {
+      key: "",
+      title: "",
+      types: "",
+    },
+  ],
+  flowId: "",
+});
 
 // 数据类型下拉数据
 const typeData = ref([]);
 const validateParam = (rule: any, value: any, callback: any) => {
-  if (value === '') {
-    callback(new Error('请输入参数配置'))
+  if (value === "") {
+    callback(new Error("请输入参数配置"));
   } else if (value) {
-    verification(value).then(() => {
-      callback()
-    })
-    .catch((e:string) => {
-      callback(new Error(e))
-    })
+    verification(value)
+      .then(() => {
+        callback();
+      })
+      .catch((e: string) => {
+        callback(new Error(e));
+      });
   } else {
-    callback()
+    callback();
   }
-}
+};
 
-const verification = (data:any) => {
-  return new Promise(function(resolve, reject) {
+const verification = (data: any) => {
+  return new Promise(function (resolve, reject) {
     try {
-      data.forEach((item:any, indexs:number) => {
+      data.forEach((item: any, indexs: number) => {
         if (!item.key && !item.title && !item.types) {
-          throw '参数配置未完善,请进行完善'
+          throw "参数配置未完善,请进行完善";
         }
         if (!item.key) {
-          throw '第' + (indexs + 1) + '个参数配置的key未完善,请进行完善'
+          throw "第" + (indexs + 1) + "个参数配置的key未完善,请进行完善";
         } else if (!item.title) {
-          throw '第' + (indexs + 1) + '个参数配置的标题未完善,请进行完善'
+          throw "第" + (indexs + 1) + "个参数配置的标题未完善,请进行完善";
         } else if (!item.types) {
-          throw '第' + (indexs + 1) + '个参数配置的数据类型未完善,请进行完善'
+          throw "第" + (indexs + 1) + "个参数配置的数据类型未完善,请进行完善";
         }
-
-      })
+      });
     } catch (e) {
-      return reject(e)
+      return reject(e);
     }
-    resolve('成功')
-  })
-}
+    resolve("成功");
+  });
+};
 
 const rules = ref({
-  title: [
-    { required: true, message: '请输入标题', trigger: 'blur' },
-  ],
+  title: [{ required: true, message: "请输入标题", trigger: "blur" }],
   types: [
     {
       required: true,
-      message: '请输入说明',
-      trigger: 'change',
+      message: "请输入说明",
+      trigger: "change",
     },
   ],
-  status: [
-    { required: true, message: '请选择状态', trigger: 'blur' },
-  ],
-  param: [{ required: true, validator: validateParam, trigger: 'change' }],
-})
+  status: [{ required: true, message: "请选择状态", trigger: "blur" }],
+  param: [{ required: true, validator: validateParam, trigger: "change" }],
+});
 
 const onSubmit = async (formEl: FormInstance | undefined) => {
-  if (!formEl) return
+  if (!formEl) return;
   await formEl.validate();
   if (!ruleForm.value.id) {
-      const id = uuid();
-      await axios.post(
-        import.meta.env.VITE_RULE_SERVER_URL + "/api/v1/rules/" + id,
-        {
-          ruleChain: {
-            id: id,
-            name: ruleForm.value.title,
-            root: true,
-            additionalInfo: {
-              description: ruleForm.value.description,
-              layoutX: "130",
-              layoutY: "220",
-            },
+    const id = uuid();
+    await axios.post(
+      getRuleServerOrigin("/api/v1/rules/" + id),
+      {
+        ruleChain: {
+          id: id,
+          name: ruleForm.value.title,
+          root: true,
+          additionalInfo: {
+            description: ruleForm.value.description,
+            layoutX: "130",
+            layoutY: "220",
           },
-          metadata: {
-            nodes: [{
-              "id": "node_2",
-              "additionalInfo": {
-                "layoutX": 606,
-                "layoutY": 424
+        },
+        metadata: {
+          nodes: [
+            {
+              id: "node_2",
+              additionalInfo: {
+                layoutX: 606,
+                layoutY: 424,
               },
-              "type": "log",
-              "name": "日志",
-              "debugMode": true,
-              "configuration": {
-                "jsScript": "return 'Incoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
-              }
-            }],
-            endpoints: [],
-            connections: [],
-          },
+              type: "log",
+              name: "日志",
+              debugMode: true,
+              configuration: {
+                jsScript: "return 'Incoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);",
+              },
+            },
+          ],
+          endpoints: [],
+          connections: [],
         },
-        { headers }
-      );
-      ruleForm.value.flowId = id;
-	} else {
+      },
+      { headers }
+    );
+    ruleForm.value.flowId = id;
+  } else {
     // 找到规则
-    const { data } = (await axios.get(import.meta.env.VITE_RULE_SERVER_URL + "/api/v1/rules/" + ruleForm.value.flowId, { headers }).catch(() => {
+    const { data } = (await axios.get(getRuleServerOrigin("/api/v1/rules/" + ruleForm.value.flowId), { headers }).catch(() => {
       ElMessage.error("规则不存在");
     })) as any;
 
@@ -203,39 +198,41 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
     data.ruleChain.additionalInfo.description = ruleForm.value.description;
 
     // 保存
-    await axios.post(import.meta.env.VITE_RULE_SERVER_URL + "/api/v1/rules/" + ruleForm.value.flowId, data, { headers });
+    await axios.post(getRuleServerOrigin("/api/v1/rules/" + ruleForm.value.flowId), data, { headers });
   }
   const theApi = ruleForm.value.id ? policyApi.edit : policyApi.add;
 
-	await theApi(ruleForm.value);
+  await theApi(ruleForm.value);
 
-	ElMessage.success('操作成功');
-	resetForm();
-	isShowDialog.value = false;
-	emit('getList');
-}
+  ElMessage.success("操作成功");
+  resetForm();
+  isShowDialog.value = false;
+  emit("getList");
+};
 
 const resetForm = () => {
   ruleFormRef.value && ruleFormRef.value.resetFields();
 
-  ruleForm.value.param = [{
-    key: "",
-    title: "",
-    types: ""
-  }]
-}
+  ruleForm.value.param = [
+    {
+      key: "",
+      title: "",
+      types: "",
+    },
+  ];
+};
 
 const plusParam = () => {
   ruleForm.value.param.push({
-    key: '',
-    title: '',
-    types: ''
-  })
-}
+    key: "",
+    title: "",
+    types: "",
+  });
+};
 
-const minusParam = (index:number) => {
+const minusParam = (index: number) => {
   ruleForm.value.param.splice(index, 1);
-}
+};
 
 // 打开弹窗
 const openDialog = (row?: any) => {
@@ -250,57 +247,53 @@ const openDialog = (row?: any) => {
         status: status,
         description: description,
         param: paramData,
-        flowId: flowId
-      }
+        flowId: flowId,
+      };
     });
-
-
   }
   api.product.getDataType({ status: -1 }).then((res: any) => {
-    const data:any = Object.values(res.dataType)
-    data.forEach((item:any, index:number) => {
+    const data: any = Object.values(res.dataType);
+    data.forEach((item: any, index: number) => {
       if (index == 0) {
-        data[index]['label'] = '基础类型';
-        data[index]['options'] = item;
+        data[index]["label"] = "基础类型";
+        data[index]["options"] = item;
       } else {
-        data[index]['label'] = '扩展类型';
-        data[index]['options'] = item.filter((i:any) => ['enum', 'array', 'object'].indexOf(i.type) === -1);
-        
+        data[index]["label"] = "扩展类型";
+        data[index]["options"] = item.filter((i: any) => ["enum", "array", "object"].indexOf(i.type) === -1);
       }
     });
     typeData.value = data || [];
   });
   isShowDialog.value = true;
-
 };
 
-defineExpose({ openDialog })
+defineExpose({ openDialog });
 </script>
 
 <style lang="scss" scoped>
-  .param-wrap {
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-    margin-bottom: 10px;
-    .el-input,
-    .el-select {
-      width: 150px;
-    }
-  }
-  .el-icon {
-    margin-right: 0!important;
+.param-wrap {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 10px;
+  .el-input,
+  .el-select {
+    width: 150px;
   }
-  ::v-deep .el-dialog__body {
-    position: relative;
-  }
-  .form-btn-wrap {
-    position: absolute;
-    bottom: 20px;
-    right: 20px;
-    display: flex;
-    justify-content: flex-end;
-    align-items: center;
-    width: 100%;
-  }
-</style>
+}
+.el-icon {
+  margin-right: 0 !important;
+}
+::v-deep .el-dialog__body {
+  position: relative;
+}
+.form-btn-wrap {
+  position: absolute;
+  bottom: 20px;
+  right: 20px;
+  display: flex;
+  justify-content: flex-end;
+  align-items: center;
+  width: 100%;
+}
+</style>

+ 58 - 71
src/views/modules/policy/index/components/execute.vue

@@ -2,38 +2,24 @@
   <el-dialog title="执行" v-model="isShowDialog" width="600px">
     <el-form>
       <el-form-item>
-        <p>标题:{{currentPolicy.title}}</p>
-        <p class="ml30">执行类型:{{typesText[currentPolicy.types]}}</p>
+        <p>标题:{{ currentPolicy.title }}</p>
+        <p class="ml30">执行类型:{{ typesText[currentPolicy.types] }}</p>
       </el-form-item>
-      <el-form-item >
-        <div style="width: 100%;">
+      <el-form-item>
+        <div style="width: 100%">
           <p>参数配置</p>
           <div class="param-wrap" v-for="(item, index) in currentPolicy.paramData" :key="index">
-            <p class="mr6 label">{{item.title}}:</p>
+            <p class="mr6 label">{{ item.title }}:</p>
             <!-- int、long、float、double、string、boolean、date、timestamp -->
             <el-input v-if="item.types === 'string'" clearable :placeholder="`请输入${item.title}`" v-model="item.value" />
-            <el-input-number v-if="['int',  'long'].indexOf(item.types) > -1" :placeholder="`请输入${item.title}`" v-model="item.value" :step="1" step-strictly />
-            <el-input-number v-if="['float',  'double'].indexOf(item.types) > -1" :placeholder="`请输入${item.title}`" v-model="item.value" :precision="2" />
+            <el-input-number v-if="['int', 'long'].indexOf(item.types) > -1" :placeholder="`请输入${item.title}`" v-model="item.value" :step="1" step-strictly />
+            <el-input-number v-if="['float', 'double'].indexOf(item.types) > -1" :placeholder="`请输入${item.title}`" v-model="item.value" :precision="2" />
             <el-radio-group v-if="item.types === 'boolean'" v-model="item.value">
               <el-radio :label="0">否</el-radio>
               <el-radio :label="1">是</el-radio>
             </el-radio-group>
-            <el-date-picker
-              v-if="item.types === 'date'"
-              v-model="item.value"
-              type="datetime"
-              :placeholder="`请选择${item.title}`"
-              format="YYYY/MM/DD hh:mm:ss"
-              value-format="YYYY-MM-DD h:m:s"
-            />
-            <el-date-picker
-              v-if="item.types === 'timestamp'"
-              v-model="item.value"
-              type="datetime"
-              :placeholder="`请选择${item.title}`"
-              format="YYYY/MM/DD hh:mm:ss"
-              value-format="x"
-            />
+            <el-date-picker v-if="item.types === 'date'" v-model="item.value" type="datetime" :placeholder="`请选择${item.title}`" format="YYYY/MM/DD hh:mm:ss" value-format="YYYY-MM-DD h:m:s" />
+            <el-date-picker v-if="item.types === 'timestamp'" v-model="item.value" type="datetime" :placeholder="`请选择${item.title}`" format="YYYY/MM/DD hh:mm:ss" value-format="x" />
           </div>
         </div>
       </el-form-item>
@@ -46,44 +32,45 @@
 </template>
 
 <script lang="ts" setup>
-import { ref } from 'vue'
+import { ref } from "vue";
 import { ElMessage } from "element-plus";
-import api from '/@/api/modules/policy';
-import axios from 'axios';
+import api from "/@/api/modules/policy";
+import axios from "axios";
 import { getToken } from "/@/utils/auth";
+import { getRuleServerOrigin } from "/@/utils/origin";
 
 const isShowDialog = ref(false);
 const typesText = {
-  1: '手动',
-  2: '自动'
+  1: "手动",
+  2: "自动",
 };
 const headers = {
   Authorization: "Bearer " + getToken(),
 };
 const currentPolicy = ref<any>({});
 
-const emit = defineEmits(['getList']);
+const emit = defineEmits(["getList"]);
 
 // 手动执行/开始执行
 const onSubmit = async () => {
-  const params:any = {};
-  currentPolicy.value.paramData.forEach((item:any) => {
-    params[item.key] = item.value
-  })
+  const params: any = {};
+  currentPolicy.value.paramData.forEach((item: any) => {
+    params[item.key] = item.value;
+  });
   await api.saveParam({
     id: currentPolicy.value.id,
     param: {
-      ...params
-    }
-  })
-  await axios.post(`${import.meta.env.VITE_RULE_SERVER_URL}/api/v1/rules/${currentPolicy.value.flowId}/execute/${currentPolicy.value.types}`, params, { headers }).catch(() => {
+      ...params,
+    },
+  });
+  (await axios.post(getRuleServerOrigin(`/api/v1/rules/${currentPolicy.value.flowId}/execute/${currentPolicy.value.types}`), params, { headers }).catch(() => {
     ElMessage.error("规则不存在");
-  }) as any;
+  })) as any;
 
-	ElMessage.success('操作成功');
-	isShowDialog.value = false;
-	emit('getList');
-}
+  ElMessage.success("操作成功");
+  isShowDialog.value = false;
+  emit("getList");
+};
 
 // 打开弹窗
 const openDialog = (row?: any) => {
@@ -99,34 +86,34 @@ defineExpose({ openDialog });
 </script>
 
 <style lang="scss" scoped>
-  .param-wrap {
-    display: flex;
-    justify-content: flex-start;
-    align-items: center;
-    margin-bottom: 10px;
-    .label {
-      width: 120px;
-      text-align: right;
-    }
-  }
-  .el-input-number,
-  .el-input {
-    width: 260px!important;
-  }
-  ::v-deep   .el-date-editor.el-input {
-    width: 260px!important;
+.param-wrap {
+  display: flex;
+  justify-content: flex-start;
+  align-items: center;
+  margin-bottom: 10px;
+  .label {
+    width: 120px;
+    text-align: right;
   }
+}
+.el-input-number,
+.el-input {
+  width: 260px !important;
+}
+::v-deep .el-date-editor.el-input {
+  width: 260px !important;
+}
 
-  ::v-deep .el-dialog__body {
-    position: relative;
-  }
-  .form-btn-wrap {
-    position: absolute;
-    bottom: 20px;
-    right: 20px;
-    display: flex;
-    justify-content: flex-end;
-    align-items: center;
-    width: 100%;
-  }
-</style>
+::v-deep .el-dialog__body {
+  position: relative;
+}
+.form-btn-wrap {
+  position: absolute;
+  bottom: 20px;
+  right: 20px;
+  display: flex;
+  justify-content: flex-end;
+  align-items: center;
+  width: 100%;
+}
+</style>

+ 1 - 1
src/views/modules/policy/index/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <el-card shadow="nover" class="page page-wrapper">
+  <el-card shadow="never" class="page page-wrapper">
     <el-form :model="tableData.param" ref="queryRef" inline>
       <el-form-item>
         <el-button @click="onOpenEdit('')">

+ 1 - 1
src/views/modules/policy/runLog/index.vue

@@ -1,5 +1,5 @@
 <template>
-	<el-card shadow="nover" class="page">
+	<el-card shadow="never" class="page">
 		<el-table :data="tableData" style="width: 100%" row-key="id" v-loading="loading">
 			<el-table-column prop="id" label="ID" width="100" show-overflow-tooltip v-col="'id'"></el-table-column>
 			<el-table-column label="操作" align="center"></el-table-column>

+ 1 - 1
src/views/modules/tenant/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">
 				<el-form-item label="名称搜索" prop="name">
 					<el-input v-model="params.name" placeholder="请输入租户名称" clearable style="width: 180px" @keyup.enter.native="getList(1)" />

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

@@ -3,7 +3,7 @@
 		<el-row>
 			<!-- 个人信息 -->
 			<el-col :xs="24" :sm="24">
-				<el-card shadow="nover" header="个人信息" v-loading="!info.userName">
+				<el-card shadow="never" header="个人信息" v-loading="!info.userName">
 					<div class="personal-user">
 						<div class="personal-user-left">
 							<el-image v-if="isEditStatus && info.avatar" style="width: 140px; height: 140px" :src="info.avatar" />

+ 1 - 1
src/views/system/api/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">
         <el-form-item label="接口名称" prop="name">
           <el-input v-model="params.name" placeholder="请输入接口名称" clearable style="width: 180px" @keyup.enter.native="getList(1)" />

+ 1 - 1
src/views/system/application/index.vue

@@ -1,5 +1,5 @@
 <template>
-	<el-card shadow="nover" class="page">
+	<el-card shadow="never" class="page">
 		<div class="search">
 			<el-form :model="params" inline ref="queryRef">
 				<el-form-item label="关键字" prop="keyWord" @submit.prevent>

+ 2 - 2
src/views/system/basicConfig/index.vue

@@ -1,12 +1,12 @@
 <template>
 	<div class="page">
-		<el-card shadow="nover">
+		<el-card shadow="never">
 			<el-menu style="--el-menu-bg-color: transparent; --el-menu-hover-bg-color: rgba(0, 0, 0, 0.2); --el-menu-level: 0;" :default-active="activeName" @select="menuChange" class="el-menu-vertical-demo" size="small">
 				<el-menu-item index="1">基础配置</el-menu-item>
 				<el-menu-item index="2">安全配置</el-menu-item>
 			</el-menu>
 		</el-card>
-		<el-card shadow="nover">
+		<el-card shadow="never">
 			<div class="scroll-wrapper">
 				<BasicVue v-if="activeName === '1'"></BasicVue>
 				<SafeVue v-else-if="activeName === '2'"></SafeVue>

+ 1 - 1
src/views/system/city/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="name">
           <el-input v-model="tableData.param.name" placeholder="请输入城市名称" clearable @keyup.enter="queryList" />

+ 1 - 1
src/views/system/config/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="configName">
           <el-input v-model="tableData.param.configName" placeholder="请输入参数名称" clearable @keyup.enter.native="dataList" />

+ 1 - 1
src/views/system/datahub/modeling/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="key">
             <el-input v-model="tableData.param.key" placeholder="请输入模型标识" clearable style="width: 240px" @keyup.enter.native="typeList" />

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