style:数据采集添加多语言适配

main
黄伟杰 2 months ago
parent ad456eb7ea
commit a7331d5154

@ -295,6 +295,7 @@ export default {
delete: 'Delete', delete: 'Delete',
edit: 'Edit', edit: 'Edit',
update: 'Update', update: 'Update',
copy: 'Copy',
preview: 'Preview', preview: 'Preview',
more: 'More', more: 'More',
sync: 'Sync', sync: 'Sync',
@ -3055,5 +3056,347 @@ export default {
exportFilename: 'FormingRecord.xls' exportFilename: 'FormingRecord.xls'
} }
},
DataCollection: {
DeviceAttributeType: {
moduleName: 'Acquisition Point Type',
index: 'Index',
code: 'Type Code',
name: 'Type Name',
sort: 'Display Order',
remark: 'Remark',
createTime: 'Create Time',
operate: 'Operate',
search: 'Search',
reset: 'Reset',
create: 'Create',
batchDelete: 'Batch Delete',
export: 'Export',
placeholderCode: 'Please enter type code',
placeholderName: 'Please enter type name',
placeholderSort: 'Please enter display order',
placeholderRemark: 'Please enter remark',
dialogOk: 'Confirm',
dialogCancel: 'Cancel',
validatorCodeRequired: 'Type code can not be empty',
validatorNameRequired: 'Type name can not be empty',
validatorSortRequired: 'Display order can not be empty',
exportFilename: 'AcquisitionPointType.xls'
},
DeviceModel: {
moduleName: 'Acquisition Device Model',
index: 'Index',
code: 'Model Code',
name: 'Model Name',
protocol: 'Protocol',
remark: 'Remark',
createTime: 'Create Time',
operate: 'Operate',
search: 'Search',
reset: 'Reset',
create: 'Create',
batchDelete: 'Batch Delete',
export: 'Export',
placeholderCode: 'Please enter model code',
placeholderName: 'Please enter model name',
placeholderProtocol: 'Please select protocol',
placeholderRemark: 'Please enter remark',
dialogOk: 'Confirm',
dialogCancel: 'Cancel',
validatorCodeRequired: 'Model code can not be empty',
validatorNameRequired: 'Model name can not be empty',
validatorProtocolRequired: 'Protocol can not be empty',
exportFilename: 'AcquisitionDeviceModel.xls',
attributeModuleName: 'Point Management',
attributeCode: 'Point Code',
attributeName: 'Point Name',
attributeType: 'Point Type',
dataType: 'Data Type',
address: 'Register Address',
dataUnit: 'Unit',
ratio: 'Ratio',
placeholderAttributeCode: 'Please enter point code',
placeholderAttributeName: 'Please enter point name',
placeholderAttributeType: 'Please select point type',
placeholderDataType: 'Please select data type',
placeholderAddress: 'Please enter register address',
placeholderDataUnit: 'Please select unit',
placeholderRatio: 'Please enter ratio',
placeholderAttributeRemark: 'Please enter remark',
validatorAttributeCodeRequired: 'Point code can not be empty',
validatorAttributeNameRequired: 'Point name can not be empty',
attributeExportFilename: 'AcquisitionDeviceModelPoint.xls',
ruleTabLabel: 'Point Rule',
ruleIdentifier: 'Identifier',
ruleFieldName: 'Name',
ruleFieldRule: 'Rule',
ruleDefaultValue: 'Default Value',
ruleCreateTime: 'Create Time',
ruleOperate: 'Operate',
ruleSearchIdentifierPlaceholder: 'Please enter identifier',
ruleSearchFieldNamePlaceholder: 'Please enter name',
ruleSearchDefaultValuePlaceholder: 'Please enter default value',
ruleSearch: 'Search',
ruleReset: 'Reset',
ruleCreateButton: 'Add Alarm Rule',
ruleEditRuleButton: 'Edit',
ruleDeleteRuleButton: 'Delete',
ruleDialogTitle: 'Edit Point Rule',
ruleDialogIdentifier: 'Identifier',
ruleDialogFieldName: 'Name',
ruleDialogDefaultValue: 'Default Value',
ruleDialogAlarmLevel: 'Alarm Level',
ruleDialogFieldRule: 'Point Rule',
ruleDialogRule: 'Rule',
ruleDialogRuleAttribute: 'Point',
ruleDialogRuleOperator: 'Condition',
ruleDialogRuleValue: 'Value',
ruleDialogAlarmLevelPlaceholder: 'Please select alarm level',
ruleDialogFieldRulePlaceholder: 'Please select point rule',
ruleDialogRuleAttributePlaceholder: 'Please select point',
ruleDialogRuleOperatorPlaceholder: 'Please select condition',
ruleDialogRuleValuePlaceholder: 'Please enter value'
},
Device: {
moduleName: 'Acquisition Device',
index: 'Index',
deviceCode: 'Device Code',
deviceName: 'Device Name',
operatingStatus: 'Operating Status',
protocol: 'Protocol',
status: 'Connection Status',
sampleCycle: 'Sample Cycle(s)',
isEnable: 'Enabled',
collectionTime: 'Collection Time',
operate: 'Operation',
search: 'Search',
reset: 'Reset',
create: 'Create',
batchDelete: 'Batch Delete',
export: 'Export',
placeholderDeviceCode: 'Please enter device code',
placeholderDeviceName: 'Please enter device name',
placeholderModel: 'Please select device model',
placeholderSampleCycle: 'Please enter sample cycle',
placeholderUrl: 'Please enter endpoint URL',
placeholderUsername: 'Please enter username',
placeholderPassword: 'Please enter password',
model: 'Device Model',
url: 'Endpoint URL',
username: 'Username',
password: 'Password',
settingDialogTitle: 'Device Settings',
connect: 'Connect',
disconnect: 'Disconnect',
attributeModuleName: 'Point',
attributeCode: 'Point Code',
attributeName: 'Point Name',
attributeType: 'Point Type',
dataType: 'Data Type',
address: 'Register Address',
dataUnit: 'Unit',
ratio: 'Ratio',
remark: 'Remark',
deviceAttributeTabLabel: 'Device Attributes',
deviceRuleTabLabel: 'Point Rules',
currentDeviceLabel: 'Current Device: ',
alarmHistoryTitle: 'Device Alarm History Data',
alarmRuleName: 'Rule Name',
alarmPointName: 'Point Name',
alarmPointValue: 'Point Value',
alarmLevel: 'Alarm Level',
emptyDescription: 'Click "Point" in the device list to view points and rules',
exportFilename: 'IoTDevice.xls',
attributeExportFilename: 'AcquisitionDevice-Point.xls',
attributeLatestValue: 'Latest Value',
attributeLatestCollectionTime: 'Latest Collection Time',
attributeSort: 'Order',
attributePlaceholderSort: 'Please enter order',
placeholderAttributeCode: 'Please enter point code',
placeholderAttributeName: 'Please enter point name',
placeholderAttributeType: 'Please select point type',
placeholderDataType: 'Please select data type',
placeholderAddress: 'Please enter register address',
placeholderDataUnit: 'Please enter unit',
placeholderRatio: 'Please enter ratio',
placeholderRemark: 'Please enter remark',
validatorDeviceCodeRequired: 'Device code can not be empty',
validatorDeviceNameRequired: 'Device name can not be empty',
validatorSampleCycleRequired: 'Sample cycle can not be empty',
validatorIsEnableRequired: 'Enable status can not be empty',
validatorUrlRequired: 'Endpoint URL can not be empty',
attributeValidatorCodeRequired: 'Point code can not be empty',
attributeValidatorNameRequired: 'Point name can not be empty',
attributeValidatorCodeNoChinese: 'Point code can not contain Chinese characters',
attributeValidatorSortNumber: 'Order must contain only numbers',
attributeValidatorRemarkTooLong: 'Remark can not exceed 100 characters',
messageSelectDeviceRequired: 'Please select a device',
messageDeviceInfoMissingForRules: 'Device information is missing, unable to load point rules'
},
RunReport: {
moduleName: 'Device Operation Report',
searchDeviceCodeLabel: 'Device Code',
searchDeviceCodePlaceholder: 'Please enter device code',
searchDeviceNameLabel: 'Device Name',
searchDeviceNamePlaceholder: 'Please enter device name',
searchTimeRangeLabel: 'Device Running Time',
searchTimeRangeStartPlaceholder: 'Start Time',
searchTimeRangeEndPlaceholder: 'End Time',
searchButtonText: 'Search',
resetButtonText: 'Reset',
exportButtonText: 'Export',
tableDeviceCodeColumn: 'Device Code',
tableDeviceNameColumn: 'Device Name',
tableRunningTimeColumn: 'Running Time (Hours)',
tableStandbyTimeColumn: 'Standby Time (Hours)',
tableFaultTimeColumn: 'Fault Time (Hours)',
tableWarningTimeColumn: 'Warning Time (Hours)',
tableUtilizationRateColumn: 'Utilization Rate',
tableStartTimeColumn: 'Device Run Start Time',
tableEndTimeColumn: 'Device Run End Time',
exportFilename: 'DeviceOperationReport.xls'
},
RealTimeMonitoring: {
moduleName: 'Real-time Data Monitoring',
searchLineCodeLabel: 'Line Code',
searchLineCodePlaceholder: 'Please enter line code',
searchLineNameLabel: 'Line Name',
searchLineNamePlaceholder: 'Please enter line name',
searchDeviceCodeLabel: 'Device Code',
searchDeviceCodePlaceholder: 'Please enter device code',
searchDeviceNameLabel: 'Device Name',
searchDeviceNamePlaceholder: 'Please enter device name',
searchButtonText: 'Search',
resetButtonText: 'Reset',
exportButtonText: 'Export',
tableLineCodeColumn: 'Line Code',
tableLineNameColumn: 'Line Name',
tableDeviceCodeColumn: 'Device Code',
tableDeviceNameColumn: 'Device Name',
tableStatusColumn: 'Connection Status',
tableCollectionTimeColumn: 'Latest Collection Time',
tableOperateColumn: 'Operation',
tableActionSingleMonitorLabel: 'Single Device Monitoring',
dialogTitle: 'Single Device Monitoring',
dialogDeviceNameLabel: 'Device Name: ',
dialogCollectionTimeLabel: 'Collection Time: ',
emptyDescription: 'No data',
defaultGroupName: 'Default',
defaultFieldLabelPrefix: 'Field ',
messageDeviceInfoIncomplete: 'Device information is incomplete',
exportFilename: 'RealTimeMonitoringDevice.xls'
},
HistoryData: {
moduleName: 'History Record Query',
searchLineCodeLabel: 'Line Code',
searchLineCodePlaceholder: 'Please enter line code',
searchLineNameLabel: 'Line Name',
searchLineNamePlaceholder: 'Please enter line name',
searchDeviceCodeLabel: 'Device Code',
searchDeviceCodePlaceholder: 'Please enter device code',
searchDeviceNameLabel: 'Device Name',
searchDeviceNamePlaceholder: 'Please enter device name',
searchButtonText: 'Search',
resetButtonText: 'Reset',
exportButtonText: 'Export',
tableLineCodeColumn: 'Line Code',
tableLineNameColumn: 'Line Name',
tableDeviceCodeColumn: 'Device Code',
tableDeviceNameColumn: 'Device Name',
tableCollectionTimeColumn: 'Collection Time',
tableOperateColumn: 'Operation',
tableActionHistoryLabel: 'History',
dialogTitlePrefix: 'History: ',
dialogCollectionTimeLabel: 'Collection Time',
dialogCollectionTimeStartPlaceholder: 'Start Time',
dialogCollectionTimeEndPlaceholder: 'End Time',
dialogSearchButtonText: 'Search',
dialogResetButtonText: 'Reset',
dialogRecordCollectionTimePrefix: 'Collection Time: ',
emptyDescription: 'No data',
defaultFieldLabelPrefix: 'Field ',
exportFilename: 'HistoryDataDevice.xls'
},
DeviceParamAnalysis: {
moduleName: 'Device Operation Parameter Analysis',
treeSearchPlaceholder: 'Search device or parameter',
formTimeLabel: 'Time',
formTimeStartPlaceholder: 'Start Date',
formTimeEndPlaceholder: 'End Date',
shortcutLast7Days: 'Last 7 Days',
shortcutLastWeek: 'Last Week',
shortcutLastMonth: 'Last Month',
shortcutLast3Months: 'Last 3 Months',
searchButtonText: 'Search',
resetButtonText: 'Reset',
emptyDescription: 'No data',
emptySelectNodeDescription: 'Please select a node on the left',
selectedParamTitle: 'Node: {label}{unit}',
defaultSeriesName: 'Parameter',
messageLoadTreeFailed: 'Failed to load tree data',
messageNodeNoParams: 'No parameters under this node',
messageDeviceNoParams: 'No parameters under this device',
messageFetchChartFailed: 'Failed to load chart data'
}
} }
} }

@ -298,6 +298,7 @@ export default {
delete: '删除', delete: '删除',
edit: '编辑', edit: '编辑',
update: '编辑', update: '编辑',
copy: '复制',
preview: '预览', preview: '预览',
more: '更多', more: '更多',
sync: '同步', sync: '同步',
@ -3044,5 +3045,347 @@ export default {
exportFilename: '成型记录.xls' exportFilename: '成型记录.xls'
} }
},
DataCollection: {
DeviceAttributeType: {
moduleName: '采集点类型',
index: '序号',
code: '类型编码',
name: '类型名称',
sort: '显示顺序',
remark: '备注',
createTime: '创建时间',
operate: '操作',
search: '搜索',
reset: '重置',
create: '新增',
batchDelete: '批量删除',
export: '导出',
placeholderCode: '请输入类型编码',
placeholderName: '请输入类型名称',
placeholderSort: '请输入显示顺序',
placeholderRemark: '请输入备注',
dialogOk: '确 定',
dialogCancel: '取 消',
validatorCodeRequired: '类型编码不能为空',
validatorNameRequired: '类型名称不能为空',
validatorSortRequired: '显示顺序不能为空',
exportFilename: '采集点类型.xls'
},
DeviceModel: {
moduleName: '采集设备模型',
index: '序号',
code: '模型编码',
name: '模型名称',
protocol: '通讯协议',
remark: '备注',
createTime: '创建时间',
operate: '操作',
search: '搜索',
reset: '重置',
create: '新增',
batchDelete: '批量删除',
export: '导出',
placeholderCode: '请输入模型编码',
placeholderName: '请输入模型名称',
placeholderProtocol: '请选择通讯协议',
placeholderRemark: '请输入备注',
dialogOk: '确 定',
dialogCancel: '取 消',
validatorCodeRequired: '模型编码不能为空',
validatorNameRequired: '模型名称不能为空',
validatorProtocolRequired: '通讯协议不能为空',
exportFilename: '采集设备模型.xls',
attributeModuleName: '采集点管理',
attributeCode: '点位编码',
attributeName: '点位名称',
attributeType: '点位类型',
dataType: '数据类型',
address: '寄存器地址',
dataUnit: '单位',
ratio: '倍率',
placeholderAttributeCode: '请输入点位编码',
placeholderAttributeName: '请输入点位名称',
placeholderAttributeType: '请选择点位类型',
placeholderDataType: '请选择数据类型',
placeholderAddress: '请输入寄存器地址',
placeholderDataUnit: '请选择单位',
placeholderRatio: '请输入倍率',
placeholderAttributeRemark: '请输入备注',
validatorAttributeCodeRequired: '点位编码不能为空',
validatorAttributeNameRequired: '点位名称不能为空',
attributeExportFilename: '采集设备模型-点位管理.xls',
ruleTabLabel: '点位规则',
ruleIdentifier: '标识符',
ruleFieldName: '名称',
ruleFieldRule: '规则',
ruleDefaultValue: '默认值',
ruleCreateTime: '创建时间',
ruleOperate: '操作',
ruleSearchIdentifierPlaceholder: '请输入标识符',
ruleSearchFieldNamePlaceholder: '请输入名称',
ruleSearchDefaultValuePlaceholder: '请输入默认值',
ruleSearch: '搜索',
ruleReset: '重置',
ruleCreateButton: '新增告警规则',
ruleEditRuleButton: '编辑',
ruleDeleteRuleButton: '删除',
ruleDialogTitle: '编辑点位规则',
ruleDialogIdentifier: '标识符',
ruleDialogFieldName: '名称',
ruleDialogDefaultValue: '默认值',
ruleDialogAlarmLevel: '告警登记',
ruleDialogFieldRule: '点位规则',
ruleDialogRule: '规则',
ruleDialogRuleAttribute: '点位',
ruleDialogRuleOperator: '条件',
ruleDialogRuleValue: '数值',
ruleDialogAlarmLevelPlaceholder: '请选择告警登记',
ruleDialogFieldRulePlaceholder: '请选择点位规则',
ruleDialogRuleAttributePlaceholder: '请选择点位',
ruleDialogRuleOperatorPlaceholder: '请选择条件',
ruleDialogRuleValuePlaceholder: '请输入数值'
},
Device: {
moduleName: '采集设备',
index: '序号',
deviceCode: '设备编号',
deviceName: '设备名称',
operatingStatus: '运行状态',
protocol: '采集协议',
status: '连接状态',
sampleCycle: '采集周期(s)',
isEnable: '是否启用',
collectionTime: '采集时间',
operate: '操作',
search: '搜索',
reset: '重置',
create: '新增',
batchDelete: '批量删除',
export: '导出',
placeholderDeviceCode: '请输入设备编号',
placeholderDeviceName: '请输入设备名称',
placeholderModel: '请选择设备模型',
placeholderSampleCycle: '请输入采集周期',
placeholderUrl: '请输入端点URL',
placeholderUsername: '请输入用户名',
placeholderPassword: '请输入密码',
model: '设备模型',
url: '端点URL',
username: '用户名',
password: '密码',
settingDialogTitle: '设备设置',
connect: '连接',
disconnect: '断开连接',
attributeModuleName: '点位',
attributeCode: '点位编码',
attributeName: '点位名称',
attributeType: '点位类型',
dataType: '数据类型',
address: '寄存器地址',
dataUnit: '单位',
ratio: '倍率',
remark: '备注',
deviceAttributeTabLabel: '设备属性',
deviceRuleTabLabel: '点位规则',
currentDeviceLabel: '当前设备:',
alarmHistoryTitle: '设备告警历史数据',
alarmRuleName: '规则名称',
alarmPointName: '点位名称',
alarmPointValue: '点位值',
alarmLevel: '告警等级',
emptyDescription: '请点击设备列表的“点位”查看采集点和点位规则',
exportFilename: '物联设备.xls',
attributeExportFilename: '采集设备-点位管理.xls',
attributeLatestValue: '最新值',
attributeLatestCollectionTime: '最新采集时间',
attributeSort: '顺序',
attributePlaceholderSort: '请输入顺序',
placeholderAttributeCode: '请输入点位编码',
placeholderAttributeName: '请输入点位名称',
placeholderAttributeType: '请选择点位类型',
placeholderDataType: '请选择数据类型',
placeholderAddress: '请输入寄存器地址',
placeholderDataUnit: '请输入单位',
placeholderRatio: '请输入倍率',
placeholderRemark: '请输入备注',
validatorDeviceCodeRequired: '设备编号不能为空',
validatorDeviceNameRequired: '设备名称不能为空',
validatorSampleCycleRequired: '采集周期不能为空',
validatorIsEnableRequired: '是否启用不能为空',
validatorUrlRequired: '端点URL不能为空',
attributeValidatorCodeRequired: '点位编码不能为空',
attributeValidatorNameRequired: '点位名称不能为空',
attributeValidatorCodeNoChinese: '点位编码不允许输入中文',
attributeValidatorSortNumber: '顺序只能输入数字',
attributeValidatorRemarkTooLong: '备注不能超过100字',
messageSelectDeviceRequired: '请选择一个物联设备',
messageDeviceInfoMissingForRules: '设备信息缺失,无法加载点位规则'
},
RunReport: {
moduleName: '设备运行报表',
searchDeviceCodeLabel: '设备编码',
searchDeviceCodePlaceholder: '请输入设备编码',
searchDeviceNameLabel: '设备名称',
searchDeviceNamePlaceholder: '请输入设备名称',
searchTimeRangeLabel: '设备运行时间',
searchTimeRangeStartPlaceholder: '开始时间',
searchTimeRangeEndPlaceholder: '结束时间',
searchButtonText: '搜索',
resetButtonText: '重置',
exportButtonText: '导出',
tableDeviceCodeColumn: '设备编码',
tableDeviceNameColumn: '设备名称',
tableRunningTimeColumn: '运行时间(小时)',
tableStandbyTimeColumn: '待机时间(小时)',
tableFaultTimeColumn: '故障时间(小时)',
tableWarningTimeColumn: '警告时间(小时)',
tableUtilizationRateColumn: '稼动率',
tableStartTimeColumn: '设备运行开始时间',
tableEndTimeColumn: '设备运行结束时间',
exportFilename: '设备运行报表.xls'
},
RealTimeMonitoring: {
moduleName: '数据实时监控',
searchLineCodeLabel: '产线编码',
searchLineCodePlaceholder: '请输入产线编码',
searchLineNameLabel: '产线名称',
searchLineNamePlaceholder: '请输入产线名称',
searchDeviceCodeLabel: '设备编码',
searchDeviceCodePlaceholder: '请输入设备编码',
searchDeviceNameLabel: '设备名称',
searchDeviceNamePlaceholder: '请输入设备名称',
searchButtonText: '搜索',
resetButtonText: '重置',
exportButtonText: '导出',
tableLineCodeColumn: '产线编码',
tableLineNameColumn: '产线名称',
tableDeviceCodeColumn: '设备编码',
tableDeviceNameColumn: '设备名称',
tableStatusColumn: '连接状态',
tableCollectionTimeColumn: '最新采集时间',
tableOperateColumn: '操作',
tableActionSingleMonitorLabel: '单设备监控',
dialogTitle: '单设备监控',
dialogDeviceNameLabel: '设备名称:',
dialogCollectionTimeLabel: '采集时间:',
emptyDescription: '暂无数据',
defaultGroupName: '默认',
defaultFieldLabelPrefix: '字段',
messageDeviceInfoIncomplete: '设备信息不完整',
exportFilename: '实时监控设备.xls'
},
HistoryData: {
moduleName: '历史记录查询',
searchLineCodeLabel: '产线编码',
searchLineCodePlaceholder: '请输入产线编码',
searchLineNameLabel: '产线名称',
searchLineNamePlaceholder: '请输入产线名称',
searchDeviceCodeLabel: '设备编码',
searchDeviceCodePlaceholder: '请输入设备编码',
searchDeviceNameLabel: '设备名称',
searchDeviceNamePlaceholder: '请输入设备名称',
searchButtonText: '搜索',
resetButtonText: '重置',
exportButtonText: '导出',
tableLineCodeColumn: '产线编码',
tableLineNameColumn: '产线名称',
tableDeviceCodeColumn: '设备编码',
tableDeviceNameColumn: '设备名称',
tableCollectionTimeColumn: '采集时间',
tableOperateColumn: '操作',
tableActionHistoryLabel: '历史记录',
dialogTitlePrefix: '历史记录:',
dialogCollectionTimeLabel: '采集时间',
dialogCollectionTimeStartPlaceholder: '开始时间',
dialogCollectionTimeEndPlaceholder: '结束时间',
dialogSearchButtonText: '查询',
dialogResetButtonText: '重置',
dialogRecordCollectionTimePrefix: '采集时间:',
emptyDescription: '暂无数据',
defaultFieldLabelPrefix: '字段',
exportFilename: '历史数据设备.xls'
},
DeviceParamAnalysis: {
moduleName: '设备运行参数分析',
treeSearchPlaceholder: '搜索设备或参数',
formTimeLabel: '时间',
formTimeStartPlaceholder: '开始日期',
formTimeEndPlaceholder: '结束日期',
shortcutLast7Days: '最近 7 天',
shortcutLastWeek: '上周',
shortcutLastMonth: '上个月',
shortcutLast3Months: '三个月内',
searchButtonText: '搜索',
resetButtonText: '重置',
emptyDescription: '暂无数据',
emptySelectNodeDescription: '请选择左侧节点',
selectedParamTitle: '节点:{label}{unit}',
defaultSeriesName: '参数',
messageLoadTreeFailed: '获取树数据失败',
messageNodeNoParams: '该节点下暂无参数',
messageDeviceNoParams: '该设备下没有参数',
messageFetchChartFailed: '获取图表数据失败'
}
} }
} }

@ -7,7 +7,7 @@
v-if="formType === 'setting' && formData.deviceName" v-if="formType === 'setting' && formData.deviceName"
class="text-12px leading-16px text-[var(--el-text-color-secondary)]" class="text-12px leading-16px text-[var(--el-text-color-secondary)]"
> >
设备名称{{ formData.deviceName }} {{ t('DataCollection.Device.deviceName') + t('common.colon') + formData.deviceName }}
</div> </div>
</div> </div>
</template> </template>
@ -15,15 +15,21 @@
ref="formRef" ref="formRef"
:model="formData" :model="formData"
:rules="activeRules" :rules="activeRules"
label-width="100px" label-width="120px"
v-loading="formLoading" v-loading="formLoading"
> >
<template v-if="formType === 'create'"> <template v-if="formType === 'create'">
<el-form-item label="设备编号" prop="deviceCode"> <el-form-item :label="t('DataCollection.Device.deviceCode')" prop="deviceCode">
<el-input v-model="formData.deviceCode" placeholder="请输入设备编号" /> <el-input
v-model="formData.deviceCode"
:placeholder="t('DataCollection.Device.placeholderDeviceCode')"
/>
</el-form-item> </el-form-item>
<el-form-item label="设备名称" prop="deviceName"> <el-form-item :label="t('DataCollection.Device.deviceName')" prop="deviceName">
<el-input v-model="formData.deviceName" placeholder="请输入设备名称" /> <el-input
v-model="formData.deviceName"
:placeholder="t('DataCollection.Device.placeholderDeviceName')"
/>
</el-form-item> </el-form-item>
</template> </template>
<!-- <el-form-item label="设备类型" prop="deviceType"> <!-- <el-form-item label="设备类型" prop="deviceType">
@ -63,23 +69,26 @@
<el-input v-model="formData.offLineDuration" placeholder="请输入离线间隔" /> <el-input v-model="formData.offLineDuration" placeholder="请输入离线间隔" />
</el-form-item> --> </el-form-item> -->
<template v-if="formType === 'create'"> <template v-if="formType === 'create'">
<el-form-item label="模型选择" prop="deviceModelId"> <el-form-item :label="t('DataCollection.Device.model')" prop="deviceModelId">
<el-select <el-select
v-model="formData.deviceModelId" v-model="formData.deviceModelId"
clearable clearable
filterable filterable
placeholder="请选择设备模型" :placeholder="t('DataCollection.Device.placeholderModel')"
> >
<el-option v-for="item in modelList" :key="item.id" :label="item.name" :value="item.id" /> <el-option v-for="item in modelList" :key="item.id" :label="item.name" :value="item.id" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="采集周期/s" prop="sampleCycle"> <el-form-item :label="t('DataCollection.Device.sampleCycle')" prop="sampleCycle">
<el-input v-model="formData.sampleCycle" placeholder="请输入采集周期" /> <el-input
v-model="formData.sampleCycle"
:placeholder="t('DataCollection.Device.placeholderSampleCycle')"
/>
</el-form-item> </el-form-item>
<!-- <el-form-item label="备注" prop="remark"> <!-- <el-form-item label="备注" prop="remark">
<el-input v-model="formData.remark" placeholder="请输入备注" /> <el-input v-model="formData.remark" placeholder="请输入备注" />
</el-form-item> --> </el-form-item> -->
<el-form-item label="是否启用" prop="isEnable"> <el-form-item :label="t('DataCollection.Device.isEnable')" prop="isEnable">
<el-radio-group v-model="formData.isEnable"> <el-radio-group v-model="formData.isEnable">
<el-radio <el-radio
v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)" v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
@ -93,10 +102,13 @@
</template> </template>
<template v-else-if="formType === 'update'"> <template v-else-if="formType === 'update'">
<el-form-item label="采集周期/s" prop="sampleCycle"> <el-form-item :label="t('DataCollection.Device.sampleCycle')" prop="sampleCycle">
<el-input v-model="formData.sampleCycle" placeholder="请输入采集周期" /> <el-input
v-model="formData.sampleCycle"
:placeholder="t('DataCollection.Device.placeholderSampleCycle')"
/>
</el-form-item> </el-form-item>
<el-form-item label="是否启用" prop="isEnable"> <el-form-item :label="t('DataCollection.Device.isEnable')" prop="isEnable">
<el-radio-group v-model="formData.isEnable"> <el-radio-group v-model="formData.isEnable">
<el-radio <el-radio
v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)" v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
@ -110,20 +122,32 @@
</template> </template>
<template v-else> <template v-else>
<el-form-item label="端点URL" prop="url"> <el-form-item :label="t('DataCollection.Device.url')" prop="url">
<el-input v-model="formData.url" placeholder="请输入端点URL" /> <el-input
v-model="formData.url"
:placeholder="t('DataCollection.Device.placeholderUrl')"
/>
</el-form-item> </el-form-item>
<el-form-item label="用户名" prop="username"> <el-form-item :label="t('DataCollection.Device.username')" prop="username">
<el-input v-model="formData.username" placeholder="请输入用户名" /> <el-input
v-model="formData.username"
:placeholder="t('DataCollection.Device.placeholderUsername')"
/>
</el-form-item> </el-form-item>
<el-form-item label="密码" prop="password"> <el-form-item :label="t('DataCollection.Device.password')" prop="password">
<el-input v-model="formData.password" placeholder="请输入密码" show-password /> <el-input
v-model="formData.password"
:placeholder="t('DataCollection.Device.placeholderPassword')"
show-password
/>
</el-form-item> </el-form-item>
</template> </template>
</el-form> </el-form>
<template #footer> <template #footer>
<el-button @click="dialogVisible = false"> </el-button> <el-button @click="dialogVisible = false">{{ t('common.cancel') }}</el-button>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button> <el-button @click="submitForm" type="primary" :disabled="formLoading">
{{ t('common.ok') }}
</el-button>
</template> </template>
</Dialog> </Dialog>
</template> </template>
@ -188,7 +212,8 @@ const formRef = ref() // 表单 Ref
/** 打开弹窗 */ /** 打开弹窗 */
const open = async (type: string, id?: number) => { const open = async (type: string, id?: number) => {
dialogVisible.value = true dialogVisible.value = true
dialogTitle.value = type === 'setting' ? '设备设置' : t('action.' + type) dialogTitle.value =
type === 'setting' ? t('DataCollection.Device.settingDialogTitle') : t('action.' + type)
formType.value = type formType.value = type
resetForm() resetForm()
// //

@ -4,33 +4,39 @@
ref="formRef" ref="formRef"
:model="formData" :model="formData"
:rules="formRules" :rules="formRules"
label-width="100px" label-width="120px"
v-loading="formLoading" v-loading="formLoading"
> >
<el-form-item label="点位编码" prop="attributeCode"> <el-form-item :label="t('DataCollection.Device.attributeCode')" prop="attributeCode">
<el-input <el-input
v-model="formData.attributeCode" v-model="formData.attributeCode"
placeholder="请输入点位编码" :placeholder="t('DataCollection.Device.placeholderAttributeCode')"
@input="handleAttributeCodeInput" @input="handleAttributeCodeInput"
:disabled = "formType === 'update'" :disabled = "formType === 'update'"
/> />
</el-form-item> </el-form-item>
<el-form-item label="点位名称" prop="attributeName"> <el-form-item :label="t('DataCollection.Device.attributeName')" prop="attributeName">
<el-input v-model="formData.attributeName" placeholder="请输入点位名称" /> <el-input
v-model="formData.attributeName"
:placeholder="t('DataCollection.Device.placeholderAttributeName')"
/>
</el-form-item> </el-form-item>
<el-form-item label="点位类型" prop="attributeType"> <el-form-item :label="t('DataCollection.Device.attributeType')" prop="attributeType">
<el-select <el-select
v-model="formData.attributeType" v-model="formData.attributeType"
clearable clearable
filterable filterable
placeholder="请选择点位类型" :placeholder="t('DataCollection.Device.placeholderAttributeType')"
@change="handleAttributeTypeChange" @change="handleAttributeTypeChange"
> >
<el-option v-for="item in typeList" :key="item.id" :label="item.name" :value="item.id" /> <el-option v-for="item in typeList" :key="item.id" :label="item.name" :value="item.id" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="数据类型" prop="dataType"> <el-form-item :label="t('DataCollection.Device.dataType')" prop="dataType">
<el-select v-model="formData.dataType" placeholder="请选择数据类型"> <el-select
v-model="formData.dataType"
:placeholder="t('DataCollection.Device.placeholderDataType')"
>
<el-option <el-option
v-for="dict in getStrDictOptions(DICT_TYPE.IOT_DEVICE_DATA_TYPE)" v-for="dict in getStrDictOptions(DICT_TYPE.IOT_DEVICE_DATA_TYPE)"
:key="dict.value" :key="dict.value"
@ -39,30 +45,46 @@
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="寄存器地址" prop="address"> <el-form-item :label="t('DataCollection.Device.address')" prop="address">
<el-input v-model="formData.address" placeholder="请输入寄存器地址" /> <el-input
v-model="formData.address"
:placeholder="t('DataCollection.Device.placeholderAddress')"
/>
</el-form-item> </el-form-item>
<el-form-item label="单位" prop="dataUnit"> <el-form-item :label="t('DataCollection.Device.dataUnit')" prop="dataUnit">
<el-input v-model="formData.dataUnit" placeholder="请输入单位" /> <el-input
v-model="formData.dataUnit"
:placeholder="t('DataCollection.Device.placeholderDataUnit')"
/>
</el-form-item> </el-form-item>
<el-form-item label="倍率" prop="ratio"> <el-form-item :label="t('DataCollection.Device.ratio')" prop="ratio">
<el-input v-model="formData.ratio" placeholder="请输入倍率" :disabled="!ratioEnabled" /> <el-input
v-model="formData.ratio"
:placeholder="t('DataCollection.Device.placeholderRatio')"
:disabled="!ratioEnabled"
/>
</el-form-item> </el-form-item>
<el-form-item label="顺序" prop="sort"> <el-form-item :label="t('DataCollection.Device.attributeSort')" prop="sort">
<el-input v-model="formData.sort" placeholder="请输入顺序" @input="handleSortInput" /> <el-input
v-model="formData.sort"
:placeholder="t('DataCollection.Device.attributePlaceholderSort')"
@input="handleSortInput"
/>
</el-form-item> </el-form-item>
<el-form-item label="备注" prop="remark"> <el-form-item :label="t('DataCollection.Device.remark')" prop="remark">
<el-input <el-input
v-model="formData.remark" v-model="formData.remark"
placeholder="请输入备注" :placeholder="t('DataCollection.Device.placeholderRemark')"
maxlength="100" maxlength="100"
show-word-limit show-word-limit
/> />
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<el-button @click="dialogVisible = false"> </el-button> <el-button @click="dialogVisible = false">{{ t('common.cancel') }}</el-button>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button> <el-button @click="submitForm" type="primary" :disabled="formLoading">
{{ t('common.ok') }}
</el-button>
</template> </template>
</Dialog> </Dialog>
</template> </template>
@ -151,7 +173,7 @@ const handleAttributeTypeChange = (val: number | string) => {
const formRules = reactive({ const formRules = reactive({
attributeCode: [ attributeCode: [
{ required: true, message: '点位编码不能为空', trigger: 'blur' }, { required: true, message: t('DataCollection.Device.attributeValidatorCodeRequired'), trigger: 'blur' },
{ {
validator: (_rule: any, value: string, callback: any) => { validator: (_rule: any, value: string, callback: any) => {
if (!value) { if (!value) {
@ -159,7 +181,7 @@ const formRules = reactive({
return return
} }
if (/[\u4e00-\u9fa5]/.test(value)) { if (/[\u4e00-\u9fa5]/.test(value)) {
callback(new Error('点位编码不允许输入中文')) callback(new Error(t('DataCollection.Device.attributeValidatorCodeNoChinese')))
return return
} }
callback() callback()
@ -167,7 +189,7 @@ const formRules = reactive({
trigger: ['blur', 'change'] trigger: ['blur', 'change']
} }
], ],
attributeName: [{ required: true, message: '点位名称不能为空', trigger: 'blur' }], attributeName: [{ required: true, message: t('DataCollection.Device.attributeValidatorNameRequired'), trigger: 'blur' }],
sort: [ sort: [
{ {
validator: (_rule: any, value: string, callback: any) => { validator: (_rule: any, value: string, callback: any) => {
@ -176,7 +198,7 @@ const formRules = reactive({
return return
} }
if (!/^\d+$/.test(value)) { if (!/^\d+$/.test(value)) {
callback(new Error('顺序只能输入数字')) callback(new Error(t('DataCollection.Device.attributeValidatorSortNumber')))
return return
} }
callback() callback()
@ -188,7 +210,7 @@ const formRules = reactive({
{ {
validator: (_rule: any, value: string, callback: any) => { validator: (_rule: any, value: string, callback: any) => {
if (value && value.length > 100) { if (value && value.length > 100) {
callback(new Error('备注不能超过100字')) callback(new Error(t('DataCollection.Device.attributeValidatorRemarkTooLong')))
return return
} }
callback() callback()

@ -1,38 +1,72 @@
<template> <template>
<ContentWrap> <ContentWrap>
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" label-width="100px"> <el-form
<el-form-item label="点位编码" prop="attributeCode"> class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="120px"
>
<el-form-item :label="t('DataCollection.Device.attributeCode')" prop="attributeCode">
<el-input <el-input
v-model="queryParams.attributeCode" placeholder="请输入点位编码" clearable @keyup.enter="handleQuery" v-model="queryParams.attributeCode"
class="!w-240px" /> :placeholder="t('DataCollection.Device.placeholderAttributeCode')"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item> </el-form-item>
<el-form-item label="点位名称" prop="attributeName"> <el-form-item :label="t('DataCollection.Device.attributeName')" prop="attributeName">
<el-input <el-input
v-model="queryParams.attributeName" placeholder="请输入点位名称" clearable @keyup.enter="handleQuery" v-model="queryParams.attributeName"
class="!w-240px" /> :placeholder="t('DataCollection.Device.placeholderAttributeName')"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item> </el-form-item>
<el-form-item label="点位类型" prop="attributeType"> <el-form-item :label="t('DataCollection.Device.attributeType')" prop="attributeType">
<el-select v-model="queryParams.attributeType" clearable filterable placeholder="请选择" class="!w-240px"> <el-select
v-model="queryParams.attributeType"
clearable
filterable
:placeholder="t('DataCollection.Device.placeholderAttributeType')"
class="!w-240px"
>
<el-option v-for="item in typeList" :key="item.id" :label="item.name" :value="item.name" /> <el-option v-for="item in typeList" :key="item.id" :label="item.name" :value="item.name" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button @click="handleQuery"> <el-button @click="handleQuery">
<Icon icon="ep:search" class="mr-5px" /> 搜索 <Icon icon="ep:search" class="mr-5px" /> {{ t('DataCollection.Device.search') }}
</el-button> </el-button>
<el-button @click="resetQuery"> <el-button @click="resetQuery">
<Icon icon="ep:refresh" class="mr-5px" /> 重置 <Icon icon="ep:refresh" class="mr-5px" /> {{ t('DataCollection.Device.reset') }}
</el-button> </el-button>
<el-button type="primary" plain @click="openForm('create')" v-hasPermi="['iot:device:create']"> <el-button
<Icon icon="ep:plus" class="mr-5px" /> 新增 type="primary"
plain
@click="openForm('create')"
v-hasPermi="['iot:device:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> {{ t('DataCollection.Device.create') }}
</el-button> </el-button>
<el-button <el-button
type="success" plain @click="handleExport" :loading="exportLoading" type="success"
v-hasPermi="['iot:device:export']"> plain
<Icon icon="ep:download" class="mr-5px" /> 导出 @click="handleExport"
:loading="exportLoading"
v-hasPermi="['iot:device:export']"
>
<Icon icon="ep:download" class="mr-5px" /> {{ t('DataCollection.Device.export') }}
</el-button> </el-button>
<el-button type="danger" plain @click="handleBatchDelete" v-hasPermi="['iot:device:delete']"> <el-button
<Icon icon="ep:delete" class="mr-5px" /> 批量删除 type="danger"
plain
@click="handleBatchDelete"
v-hasPermi="['iot:device:delete']"
>
<Icon icon="ep:delete" class="mr-5px" /> {{ t('DataCollection.Device.batchDelete') }}
</el-button> </el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
@ -43,38 +77,107 @@ type="success" plain @click="handleExport" :loading="exportLoading"
ref="tableRef" v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" row-key="id" ref="tableRef" v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" row-key="id"
@selection-change="handleSelectionChange"> @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" reserve-selection /> <el-table-column type="selection" width="55" reserve-selection />
<el-table-column label="点位编码" align="left" prop="attributeCode" width="150px" /> <el-table-column
<el-table-column label="点位名称" align="left" prop="attributeName" width="150px" /> :label="t('DataCollection.Device.attributeCode')"
<el-table-column label="点位类型" align="center" prop="typeName" width="140px" /> align="left"
<el-table-column label="数据类型" align="center" prop="dataType" width="120px"> prop="attributeCode"
width="150px"
/>
<el-table-column
:label="t('DataCollection.Device.attributeName')"
align="left"
prop="attributeName"
width="150px"
/>
<el-table-column
:label="t('DataCollection.Device.attributeType')"
align="center"
prop="typeName"
width="140px"
/>
<el-table-column
:label="t('DataCollection.Device.dataType')"
align="center"
prop="dataType"
width="120px"
>
<template #default="scope"> <template #default="scope">
<dict-tag :type="DICT_TYPE.IOT_DEVICE_DATA_TYPE" :value="scope.row.dataType" /> <dict-tag :type="DICT_TYPE.IOT_DEVICE_DATA_TYPE" :value="scope.row.dataType" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="寄存器地址" align="center" prop="address" min-width="140px" /> <el-table-column
<el-table-column label="最新值" align="center" prop="addressValue" min-width="120px"> :label="t('DataCollection.Device.address')"
align="center"
prop="address"
min-width="140px"
/>
<el-table-column
:label="t('DataCollection.Device.attributeLatestValue')"
align="center"
prop="addressValue"
min-width="120px"
>
<template #default="scope"> <template #default="scope">
{{ formatAddressValue(scope.row.addressValue) }} {{ formatAddressValue(scope.row.addressValue) }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="单位" align="center" prop="dataUnit" width="80px" />
<el-table-column label="倍率" align="center" prop="ratio" width="80px" />
<el-table-column <el-table-column
label="最新采集时间" align="center" prop="latestCollectionTime" :formatter="dateFormatter" :label="t('DataCollection.Device.dataUnit')"
width="170px" /> align="center"
<el-table-column label="顺序" align="center" prop="sort" width="80px"> prop="dataUnit"
width="80px"
/>
<el-table-column
:label="t('DataCollection.Device.ratio')"
align="center"
prop="ratio"
width="80px"
/>
<el-table-column
:label="t('DataCollection.Device.attributeLatestCollectionTime')"
align="center"
prop="latestCollectionTime"
:formatter="dateFormatter"
width="170px"
/>
<el-table-column
:label="t('DataCollection.Device.attributeSort')"
align="center"
prop="sort"
width="80px"
>
<template #default="scope"> <template #default="scope">
{{ scope.row.sort ?? '-' }} {{ scope.row.sort ?? '-' }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="备注" align="center" prop="remark" min-width="160px" /> <el-table-column
<el-table-column label="操作" align="center" width="150px" fixed="right"> :label="t('DataCollection.Device.remark')"
align="center"
prop="remark"
min-width="160px"
/>
<el-table-column
:label="t('DataCollection.Device.operate')"
align="center"
width="150px"
fixed="right"
>
<template #default="scope"> <template #default="scope">
<el-button link type="primary" @click="openForm('update', scope.row.id)" v-hasPermi="['iot:device:update']"> <el-button
编辑 link
type="primary"
@click="openForm('update', scope.row.id)"
v-hasPermi="['iot:device:update']"
>
{{ t('action.edit') }}
</el-button> </el-button>
<el-button link type="danger" @click="handleDelete(scope.row.id)" v-hasPermi="['iot:device:delete']"> <el-button
删除 link
type="danger"
@click="handleDelete(scope.row.id)"
v-hasPermi="['iot:device:delete']"
>
{{ t('action.delete') }}
</el-button> </el-button>
</template> </template>
</el-table-column> </el-table-column>
@ -197,7 +300,7 @@ const resetQuery = () => {
const formRef = ref() const formRef = ref()
const openForm = (type: string, id?: number) => { const openForm = (type: string, id?: number) => {
if (!props.deviceId) { if (!props.deviceId) {
message.error('请选择一个物联设备') message.error(t('DataCollection.Device.messageSelectDeviceRequired'))
return return
} }
formRef.value.open(type, id, props.deviceId) formRef.value.open(type, id, props.deviceId)
@ -224,7 +327,7 @@ const handleDelete = async (ids: number | number[]) => {
const handleBatchDelete = async () => { const handleBatchDelete = async () => {
if (!selectedIds.value.length) { if (!selectedIds.value.length) {
message.error('请选择需要删除的数据') message.error(t('common.delNoData'))
return return
} }
await handleDelete(selectedIds.value) await handleDelete(selectedIds.value)
@ -240,7 +343,7 @@ const handleExport = async () => {
params.ids = selectedIds.value.join(',') params.ids = selectedIds.value.join(',')
} }
const data = await DeviceModelAttributeApi.exportDeviceModelAttribute(params) const data = await DeviceModelAttributeApi.exportDeviceModelAttribute(params)
download.excel(data, '采集设备-点位管理.xls') download.excel(data, t('DataCollection.Device.attributeExportFilename'))
} catch { } catch {
} finally { } finally {
exportLoading.value = false exportLoading.value = false

@ -1,15 +1,21 @@
<template> <template>
<ContentWrap> <ContentWrap>
<!-- 搜索工作栏 --> <!-- 搜索工作栏 -->
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" label-width="120px">
<el-form-item label="设备编号" prop="deviceCode"> <el-form-item :label="t('DataCollection.Device.deviceCode')" prop="deviceCode">
<el-input <el-input
v-model="queryParams.deviceCode" placeholder="请输入设备编号" clearable @keyup.enter="handleQuery" v-model="queryParams.deviceCode"
:placeholder="t('DataCollection.Device.placeholderDeviceCode')"
clearable
@keyup.enter="handleQuery"
class="!w-240px" /> class="!w-240px" />
</el-form-item> </el-form-item>
<el-form-item label="设备名称" prop="deviceName"> <el-form-item :label="t('DataCollection.Device.deviceName')" prop="deviceName">
<el-input <el-input
v-model="queryParams.deviceName" placeholder="请输入设备名称" clearable @keyup.enter="handleQuery" v-model="queryParams.deviceName"
:placeholder="t('DataCollection.Device.placeholderDeviceName')"
clearable
@keyup.enter="handleQuery"
class="!w-240px" /> class="!w-240px" />
</el-form-item> </el-form-item>
@ -42,21 +48,21 @@ v-model="queryParams.deviceName" placeholder="请输入设备名称" clearable @
</el-form-item> --> </el-form-item> -->
<el-form-item> <el-form-item>
<el-button @click="handleQuery"> <el-button @click="handleQuery">
<Icon icon="ep:search" class="mr-5px" /> 搜索 <Icon icon="ep:search" class="mr-5px" /> {{ t('DataCollection.Device.search') }}
</el-button> </el-button>
<el-button @click="resetQuery"> <el-button @click="resetQuery">
<Icon icon="ep:refresh" class="mr-5px" /> 重置 <Icon icon="ep:refresh" class="mr-5px" /> {{ t('DataCollection.Device.reset') }}
</el-button> </el-button>
<el-button type="primary" plain @click="openForm('create')" v-hasPermi="['iot:device:create']"> <el-button type="primary" plain @click="openForm('create')" v-hasPermi="['iot:device:create']">
<Icon icon="ep:plus" class="mr-5px" /> 新增 <Icon icon="ep:plus" class="mr-5px" /> {{ t('DataCollection.Device.create') }}
</el-button> </el-button>
<el-button <el-button
type="success" plain @click="handleExport" :loading="exportLoading" type="success" plain @click="handleExport" :loading="exportLoading"
v-hasPermi="['iot:device:export']"> v-hasPermi="['iot:device:export']">
<Icon icon="ep:download" class="mr-5px" /> 导出 <Icon icon="ep:download" class="mr-5px" /> {{ t('DataCollection.Device.export') }}
</el-button> </el-button>
<el-button type="danger" plain @click="handleBatchDelete" v-hasPermi="['iot:device:delete']"> <el-button type="danger" plain @click="handleBatchDelete" v-hasPermi="['iot:device:delete']">
<Icon icon="ep:delete" class="mr-5px" /> 批量删除 <Icon icon="ep:delete" class="mr-5px" /> {{ t('DataCollection.Device.batchDelete') }}
</el-button> </el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
@ -65,12 +71,12 @@ type="success" plain @click="handleExport" :loading="exportLoading"
<!-- 列表 --> <!-- 列表 -->
<ContentWrap> <ContentWrap>
<el-table <el-table
ref="tableRef" v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" row-key="id" ref="tableRef" v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" row-key="id"
@selection-change="handleSelectionChange"> @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" reserve-selection /> <el-table-column type="selection" width="55" reserve-selection />
<el-table-column label="设备编号" align="left" prop="deviceCode" /> <el-table-column :label="t('DataCollection.Device.deviceCode')" align="left" prop="deviceCode" />
<el-table-column label="设备名称" align="left" prop="deviceName" /> <el-table-column :label="t('DataCollection.Device.deviceName')" align="left" prop="deviceName" />
<el-table-column label="运行状态" align="center" prop="operatingStatus" width="120px"> <el-table-column :label="t('DataCollection.Device.operatingStatus')" align="center" prop="operatingStatus" width="120px">
<template #default="scope"> <template #default="scope">
<el-tag <el-tag
size="small" size="small"
@ -84,18 +90,18 @@ ref="tableRef" v-loading="loading" :data="list" :stripe="true" :show-overflow-to
<!-- <template #default="scope"> <!-- <template #default="scope">
<dict-tag :type="DICT_TYPE.IOT_DEVICE_TYPE" :value="scope.row.deviceType" /> <dict-tag :type="DICT_TYPE.IOT_DEVICE_TYPE" :value="scope.row.deviceType" />
</template> </template>
</el-table-column> --> </el-table-column> -->
<el-table-column label="采集协议" align="left" prop="protocol" width="120px"> <el-table-column :label="t('DataCollection.Device.protocol')" align="left" prop="protocol" width="120px">
<template #default="scope"> <template #default="scope">
<dict-tag :type="DICT_TYPE.IOT_PROTOCOL" :value="scope.row.protocol" /> <dict-tag :type="DICT_TYPE.IOT_PROTOCOL" :value="scope.row.protocol" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="连接状态" align="center" prop="status" width="120px"> <el-table-column :label="t('DataCollection.Device.status')" align="center" prop="status" width="120px">
<template #default="scope"> <template #default="scope">
<dict-tag :type="DICT_TYPE.IOT_GATEWAY_STATUS" :value="scope.row.status" /> <dict-tag :type="DICT_TYPE.IOT_GATEWAY_STATUS" :value="scope.row.status" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="采集周期(s)" align="center" prop="sampleCycle" width="120px" /> <el-table-column :label="t('DataCollection.Device.sampleCycle')" align="center" prop="sampleCycle" width="120px" />
<!-- <el-table-column label="读主题" align="center" prop="readTopic" /> <!-- <el-table-column label="读主题" align="center" prop="readTopic" />
<el-table-column label="写主题" align="center" prop="writeTopic" /> <el-table-column label="写主题" align="center" prop="writeTopic" />
@ -117,37 +123,57 @@ ref="tableRef" v-loading="loading" :data="list" :stripe="true" :show-overflow-to
:formatter="dateFormatter" :formatter="dateFormatter"
width="170px" width="170px"
/> --> /> -->
<el-table-column label="是否启用" align="center" prop="isEnable" width="120px"> <el-table-column :label="t('DataCollection.Device.isEnable')" align="center" prop="isEnable" width="120px">
<template #default="scope"> <template #default="scope">
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.isEnable" /> <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.isEnable" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
label="采集时间" :label="t('DataCollection.Device.collectionTime')"
align="center" align="center"
prop="collectionTime" prop="collectionTime"
width="180px" width="180px"
:formatter="dateFormatter" :formatter="dateFormatter"
/> />
<el-table-column label="操作" align="center" fixed="right" width="380px"> <el-table-column :label="t('DataCollection.Device.operate')" align="center" fixed="right" width="380px">
<template #default="scope"> <template #default="scope">
<el-button link type="primary" @click.stop="handleShowAttribute(scope.row)">点位</el-button> <el-button link type="primary" @click.stop="handleShowAttribute(scope.row)">{{ t('DataCollection.Device.attributeModuleName') }}</el-button>
<el-button link type="primary" @click="openForm('setting', scope.row.id)" v-hasPermi="['iot:device:update']"> <el-button
设置 link
type="primary"
@click="openForm('setting', scope.row.id)"
v-hasPermi="['iot:device:update']"
>
{{ t('DataCollection.Device.settingDialogTitle') }}
</el-button> </el-button>
<el-button link type="primary" @click="handleCopy(scope.row.id)" v-hasPermi="['iot:device:create']"> <el-button
复制 link
type="primary"
@click="handleCopy(scope.row.id)"
v-hasPermi="['iot:device:create']"
>
{{ t('action.copy') }}
</el-button> </el-button>
<el-button link type="primary" @click.stop="handleEdit(scope.row)" v-hasPermi="['iot:device:update']"> <el-button
编辑 link
type="primary"
@click.stop="handleEdit(scope.row)"
v-hasPermi="['iot:device:update']"
>
{{ t('action.edit') }}
</el-button> </el-button>
<el-button <el-button
link :type="isRowConnected(scope.row) ? 'warning' : 'success'" link :type="isRowConnected(scope.row) ? 'warning' : 'success'"
:loading="!!connectLoadingMap[scope.row.id]" @click.stop="handleToggleConnect(scope.row)"> :loading="!!connectLoadingMap[scope.row.id]" @click.stop="handleToggleConnect(scope.row)">
{{ isRowConnected(scope.row) ? '断开连接' : '连接' }} {{ isRowConnected(scope.row) ? t('DataCollection.Device.disconnect') : t('DataCollection.Device.connect') }}
</el-button> </el-button>
<el-button link type="danger" @click="handleDelete(scope.row.id)" v-hasPermi="['iot:device:delete']"> <el-button
删除 link
type="danger"
@click="handleDelete(scope.row.id)"
v-hasPermi="['iot:device:delete']"
>
{{ t('action.delete') }}
</el-button> </el-button>
</template> </template>
</el-table-column> </el-table-column>
@ -165,11 +191,11 @@ link :type="isRowConnected(scope.row) ? 'warning' : 'success'"
<template v-if="attributeDeviceId"> <template v-if="attributeDeviceId">
<div class="mb-10px flex items-center justify-between text-sm text-gray-500"> <div class="mb-10px flex items-center justify-between text-sm text-gray-500">
<div> <div>
当前设备<span class="font-medium text-gray-700">{{ attributeDeviceName || '-' }}</span> {{ t('DataCollection.Device.currentDeviceLabel') }}<span class="font-medium text-gray-700">{{ attributeDeviceName || '-' }}</span>
</div> </div>
<div> <div>
<el-button type="primary" link @click="handleShowDeviceAlarmHistory"> <el-button type="primary" link @click="handleShowDeviceAlarmHistory">
设备告警历史数据 {{ t('DataCollection.Device.alarmHistoryTitle') }}
</el-button> </el-button>
</div> </div>
</div> </div>
@ -183,30 +209,30 @@ link :type="isRowConnected(scope.row) ? 'warning' : 'success'"
:model="ruleQueryParams" :model="ruleQueryParams"
ref="ruleQueryFormRef" ref="ruleQueryFormRef"
:inline="true" :inline="true"
label-width="80px" label-width="120px"
> >
<el-form-item label="标识符" prop="identifier"> <el-form-item :label="t('DataCollection.DeviceModel.ruleIdentifier')" prop="identifier">
<el-input <el-input
v-model="ruleQueryParams.identifier" v-model="ruleQueryParams.identifier"
placeholder="请输入标识符" :placeholder="t('DataCollection.DeviceModel.ruleSearchIdentifierPlaceholder')"
clearable clearable
@keyup.enter="handleRuleQuery" @keyup.enter="handleRuleQuery"
class="!w-200px" class="!w-200px"
/> />
</el-form-item> </el-form-item>
<el-form-item label="名称" prop="fieldName"> <el-form-item :label="t('DataCollection.DeviceModel.ruleFieldName')" prop="fieldName">
<el-input <el-input
v-model="ruleQueryParams.fieldName" v-model="ruleQueryParams.fieldName"
placeholder="请输入名称" :placeholder="t('DataCollection.DeviceModel.ruleSearchFieldNamePlaceholder')"
clearable clearable
@keyup.enter="handleRuleQuery" @keyup.enter="handleRuleQuery"
class="!w-200px" class="!w-200px"
/> />
</el-form-item> </el-form-item>
<el-form-item label="默认值" prop="defaultValue"> <el-form-item :label="t('DataCollection.DeviceModel.ruleDefaultValue')" prop="defaultValue">
<el-input <el-input
v-model="ruleQueryParams.defaultValue" v-model="ruleQueryParams.defaultValue"
placeholder="请输入默认值" :placeholder="t('DataCollection.DeviceModel.ruleSearchDefaultValuePlaceholder')"
clearable clearable
@keyup.enter="handleRuleQuery" @keyup.enter="handleRuleQuery"
class="!w-200px" class="!w-200px"
@ -214,17 +240,17 @@ link :type="isRowConnected(scope.row) ? 'warning' : 'success'"
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button @click="handleRuleQuery"> <el-button @click="handleRuleQuery">
<Icon icon="ep:search" class="mr-5px" /> 搜索 <Icon icon="ep:search" class="mr-5px" /> {{ t('DataCollection.DeviceModel.ruleSearch') }}
</el-button> </el-button>
<el-button @click="resetRuleQuery"> <el-button @click="resetRuleQuery">
<Icon icon="ep:refresh" class="mr-5px" /> 重置 <Icon icon="ep:refresh" class="mr-5px" /> {{ t('DataCollection.DeviceModel.ruleReset') }}
</el-button> </el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<div class="mb-10px text-right"> <div class="mb-10px text-right">
<el-button type="primary" @click="openCreateRuleForm"> <el-button type="primary" @click="openCreateRuleForm">
新增告警规则 {{ t('DataCollection.DeviceModel.ruleCreateButton') }}
</el-button> </el-button>
</div> </div>
@ -235,21 +261,21 @@ link :type="isRowConnected(scope.row) ? 'warning' : 'success'"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
row-key="id" row-key="id"
> >
<el-table-column label="标识符" align="center" prop="identifier" /> <el-table-column :label="t('DataCollection.DeviceModel.ruleIdentifier')" align="center" prop="identifier" />
<el-table-column label="名称" align="center" prop="fieldName" /> <el-table-column :label="t('DataCollection.DeviceModel.ruleFieldName')" align="center" prop="fieldName" />
<el-table-column label="规则" align="center" prop="fieldRule" /> <el-table-column :label="t('DataCollection.DeviceModel.ruleFieldRule')" align="center" prop="fieldRule" />
<el-table-column label="默认值" align="center" prop="defaultValue" /> <el-table-column :label="t('DataCollection.DeviceModel.ruleDefaultValue')" align="center" prop="defaultValue" />
<el-table-column <el-table-column
label="创建时间" :label="t('DataCollection.DeviceModel.ruleCreateTime')"
align="center" align="center"
prop="createTime" prop="createTime"
:formatter="dateFormatter" :formatter="dateFormatter"
width="180px" width="180px"
/> />
<el-table-column label="操作" align="center" width="160px"> <el-table-column :label="t('DataCollection.DeviceModel.ruleOperate')" align="center" width="160px">
<template #default="scope"> <template #default="scope">
<el-button link type="primary" @click="openRuleForm(scope.row)"> <el-button link type="primary" @click="openRuleForm(scope.row)">
编辑 {{ t('DataCollection.DeviceModel.ruleEditRuleButton') }}
</el-button> </el-button>
<el-button <el-button
v-if="(scope.row.identifier || '').toString().toUpperCase() === 'ALARM'" v-if="(scope.row.identifier || '').toString().toUpperCase() === 'ALARM'"
@ -257,7 +283,7 @@ link :type="isRowConnected(scope.row) ? 'warning' : 'success'"
type="danger" type="danger"
@click="handleRuleDelete(scope.row.id)" @click="handleRuleDelete(scope.row.id)"
> >
删除 {{ t('DataCollection.DeviceModel.ruleDeleteRuleButton') }}
</el-button> </el-button>
</template> </template>
</el-table-column> </el-table-column>
@ -269,22 +295,25 @@ link :type="isRowConnected(scope.row) ? 'warning' : 'success'"
@pagination="getRuleList" @pagination="getRuleList"
/> />
<el-dialog v-model="ruleDialogVisible" title="编辑点位规则" width="880px" draggable> <el-dialog v-model="ruleDialogVisible" :title="t('DataCollection.DeviceModel.ruleDialogTitle')" width="880px" draggable>
<el-form :model="ruleForm" ref="ruleFormRef" label-width="90px"> <el-form :model="ruleForm" ref="ruleFormRef" label-width="120px">
<el-form-item label="标识符"> <el-form-item :label="t('DataCollection.DeviceModel.ruleDialogIdentifier')">
<el-input v-model="ruleForm.identifier" disabled /> <el-input v-model="ruleForm.identifier" disabled />
</el-form-item> </el-form-item>
<el-form-item label="名称"> <el-form-item :label="t('DataCollection.DeviceModel.ruleDialogFieldName')">
<el-input v-model="ruleForm.fieldName" disabled /> <el-input v-model="ruleForm.fieldName" disabled />
</el-form-item> </el-form-item>
<el-form-item label="默认值"> <el-form-item :label="t('DataCollection.DeviceModel.ruleDialogDefaultValue')">
<el-input v-model="ruleForm.defaultValue" disabled /> <el-input v-model="ruleForm.defaultValue" disabled />
</el-form-item> </el-form-item>
<el-form-item <el-form-item
v-if="(ruleForm.identifier || '').toString().toUpperCase() === 'ALARM'" v-if="(ruleForm.identifier || '').toString().toUpperCase() === 'ALARM'"
label="告警登记" :label="t('DataCollection.DeviceModel.ruleDialogAlarmLevel')"
> >
<el-select v-model="ruleForm.alarmLevel" placeholder="请选择告警登记"> <el-select
v-model="ruleForm.alarmLevel"
:placeholder="t('DataCollection.DeviceModel.ruleDialogAlarmLevelPlaceholder')"
>
<el-option <el-option
v-for="dict in getStrDictOptions(DICT_TYPE.IOT_ALARM_REGISTRATION)" v-for="dict in getStrDictOptions(DICT_TYPE.IOT_ALARM_REGISTRATION)"
:key="dict.value" :key="dict.value"
@ -296,10 +325,10 @@ link :type="isRowConnected(scope.row) ? 'warning' : 'success'"
<div class="flex flex-col w-full"> <div class="flex flex-col w-full">
<div class="border border-gray-200 dark:border-gray-600 rounded-md py-12px"> <div class="border border-gray-200 dark:border-gray-600 rounded-md py-12px">
<el-form-item label="点位规则"> <el-form-item :label="t('DataCollection.DeviceModel.ruleDialogFieldRule')">
<el-select <el-select
v-model="ruleForm.fieldRule" v-model="ruleForm.fieldRule"
placeholder="请选择点位规则" :placeholder="t('DataCollection.DeviceModel.ruleDialogFieldRulePlaceholder')"
class="!w-240px" class="!w-240px"
:disabled="!currentRuleOptions.length" :disabled="!currentRuleOptions.length"
> >
@ -312,11 +341,11 @@ link :type="isRowConnected(scope.row) ? 'warning' : 'success'"
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="规则"> <el-form-item :label="t('DataCollection.DeviceModel.ruleDialogRule')">
<div class="flex items-center gap-8px"> <div class="flex items-center gap-8px">
<el-select <el-select
v-model="ruleForm.ruleAttributeId" v-model="ruleForm.ruleAttributeId"
placeholder="请选择点位" :placeholder="t('DataCollection.DeviceModel.ruleDialogRuleAttributePlaceholder')"
class="!w-240px" class="!w-240px"
> >
<el-option <el-option
@ -328,7 +357,7 @@ link :type="isRowConnected(scope.row) ? 'warning' : 'success'"
</el-select> </el-select>
<el-select <el-select
v-model="ruleForm.ruleOperator" v-model="ruleForm.ruleOperator"
placeholder="请选择条件" :placeholder="t('DataCollection.DeviceModel.ruleDialogRuleOperatorPlaceholder')"
class="!w-160px" class="!w-160px"
> >
<el-option <el-option
@ -341,7 +370,7 @@ link :type="isRowConnected(scope.row) ? 'warning' : 'success'"
<el-input <el-input
v-if="ruleForm.ruleOperator !== 'TRUE' && ruleForm.ruleOperator !== 'FALSE'" v-if="ruleForm.ruleOperator !== 'TRUE' && ruleForm.ruleOperator !== 'FALSE'"
v-model="ruleForm.ruleValue" v-model="ruleForm.ruleValue"
placeholder="请输入符号值" :placeholder="t('DataCollection.DeviceModel.ruleDialogRuleValuePlaceholder')"
class="!w-200px" class="!w-200px"
/> />
</div> </div>
@ -353,10 +382,10 @@ link :type="isRowConnected(scope.row) ? 'warning' : 'success'"
:key="index" :key="index"
class="border border-gray-200 dark:border-gray-600 rounded-md px-16px py-12px" class="border border-gray-200 dark:border-gray-600 rounded-md px-16px py-12px"
> >
<el-form-item label="点位规则"> <el-form-item :label="t('DataCollection.DeviceModel.ruleDialogFieldRule')">
<el-select <el-select
v-model="item.rule" v-model="item.rule"
placeholder="请选择点位规则" :placeholder="t('DataCollection.DeviceModel.ruleDialogFieldRulePlaceholder')"
class="!w-240px" class="!w-240px"
> >
<el-option <el-option
@ -368,11 +397,11 @@ link :type="isRowConnected(scope.row) ? 'warning' : 'success'"
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="规则"> <el-form-item :label="t('DataCollection.DeviceModel.ruleDialogRule')">
<div class="flex items-center gap-8px"> <div class="flex items-center gap-8px">
<el-select <el-select
v-model="item.id" v-model="item.id"
placeholder="请选择点位" :placeholder="t('DataCollection.DeviceModel.ruleDialogRuleAttributePlaceholder')"
class="!w-240px" class="!w-240px"
> >
<el-option <el-option
@ -384,7 +413,7 @@ link :type="isRowConnected(scope.row) ? 'warning' : 'success'"
</el-select> </el-select>
<el-select <el-select
v-model="item.operator" v-model="item.operator"
placeholder="请选择条件" :placeholder="t('DataCollection.DeviceModel.ruleDialogRuleOperatorPlaceholder')"
class="!w-160px" class="!w-160px"
> >
<el-option <el-option
@ -397,11 +426,11 @@ link :type="isRowConnected(scope.row) ? 'warning' : 'success'"
<el-input <el-input
v-if="item.operator !== 'TRUE' && item.operator !== 'FALSE'" v-if="item.operator !== 'TRUE' && item.operator !== 'FALSE'"
v-model="item.operatorRule" v-model="item.operatorRule"
placeholder="请输入符号值" :placeholder="t('DataCollection.DeviceModel.ruleDialogRuleValuePlaceholder')"
class="!w-200px" class="!w-200px"
/> />
<el-button type="danger" link @click="handleRemovePointRule(index)"> <el-button type="danger" link @click="handleRemovePointRule(index)">
删除 {{ t('DataCollection.DeviceModel.ruleDeleteRuleButton') }}
</el-button> </el-button>
</div> </div>
</el-form-item> </el-form-item>
@ -409,32 +438,38 @@ link :type="isRowConnected(scope.row) ? 'warning' : 'success'"
<el-form-item v-if="isRunningIdentifier" label=" "> <el-form-item v-if="isRunningIdentifier" label=" ">
<el-button type="primary" link @click="handleAddPointRule"> <el-button type="primary" link @click="handleAddPointRule">
+ 添加规则 + {{ t('DataCollection.DeviceModel.ruleCreateButton') }}
</el-button> </el-button>
</el-form-item> </el-form-item>
</div> </div>
</el-form> </el-form>
<template #footer> <template #footer>
<span class="dialog-footer"> <span class="dialog-footer">
<el-button @click="ruleDialogVisible = false"> </el-button> <el-button @click="ruleDialogVisible = false">{{ t('DataCollection.DeviceModel.dialogCancel') }}</el-button>
<el-button type="primary" :loading="ruleFormLoading" @click="handleRuleSubmit"> </el-button> <el-button type="primary" :loading="ruleFormLoading" @click="handleRuleSubmit">{{ t('DataCollection.DeviceModel.dialogOk') }}</el-button>
</span> </span>
</template> </template>
</el-dialog> </el-dialog>
<el-dialog v-model="createRuleDialogVisible" title="新增告警规则" width="520px" draggable> <el-dialog v-model="createRuleDialogVisible" :title="t('DataCollection.DeviceModel.ruleCreateButton')" width="520px" draggable>
<el-form :model="createRuleForm" ref="createRuleFormRef" label-width="90px"> <el-form :model="createRuleForm" ref="createRuleFormRef" label-width="120px">
<el-form-item label="标识符"> <el-form-item :label="t('DataCollection.DeviceModel.ruleDialogIdentifier')">
<el-input v-model="createRuleForm.identifier" disabled /> <el-input v-model="createRuleForm.identifier" disabled />
</el-form-item> </el-form-item>
<el-form-item label="名称"> <el-form-item :label="t('DataCollection.DeviceModel.ruleDialogFieldName')">
<el-input v-model="createRuleForm.fieldName" placeholder="请输入名称" /> <el-input
v-model="createRuleForm.fieldName"
:placeholder="t('DataCollection.DeviceModel.ruleSearchFieldNamePlaceholder')"
/>
</el-form-item> </el-form-item>
<el-form-item label="默认值"> <el-form-item :label="t('DataCollection.DeviceModel.ruleDialogDefaultValue')">
<el-input v-model="createRuleForm.defaultValue" disabled /> <el-input v-model="createRuleForm.defaultValue" disabled />
</el-form-item> </el-form-item>
<el-form-item label="告警登记"> <el-form-item :label="t('DataCollection.DeviceModel.ruleDialogAlarmLevel')">
<el-select v-model="createRuleForm.alarmLevel" placeholder="请选择告警登记"> <el-select
v-model="createRuleForm.alarmLevel"
:placeholder="t('DataCollection.DeviceModel.ruleDialogAlarmLevelPlaceholder')"
>
<el-option <el-option
v-for="dict in getStrDictOptions(DICT_TYPE.IOT_ALARM_REGISTRATION)" v-for="dict in getStrDictOptions(DICT_TYPE.IOT_ALARM_REGISTRATION)"
:key="dict.value" :key="dict.value"
@ -446,9 +481,9 @@ link :type="isRowConnected(scope.row) ? 'warning' : 'success'"
</el-form> </el-form>
<template #footer> <template #footer>
<span class="dialog-footer"> <span class="dialog-footer">
<el-button @click="createRuleDialogVisible = false"> </el-button> <el-button @click="createRuleDialogVisible = false">{{ t('DataCollection.DeviceModel.dialogCancel') }}</el-button>
<el-button type="primary" :loading="createRuleFormLoading" @click="handleCreateRuleSubmit"> <el-button type="primary" :loading="createRuleFormLoading" @click="handleCreateRuleSubmit">
{{ t('DataCollection.DeviceModel.dialogOk') }}
</el-button> </el-button>
</span> </span>
</template> </template>
@ -456,10 +491,10 @@ link :type="isRowConnected(scope.row) ? 'warning' : 'success'"
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
</template> </template>
<el-empty v-else description="请点击设备列表的“点位”查看采集点和点位规则" /> <el-empty v-else :description="t('DataCollection.Device.emptyDescription')" />
</ContentWrap> </ContentWrap>
<Dialog title="设备告警历史数据" v-model="deviceAlarmDialogVisible" width="800px"> <Dialog :title="t('DataCollection.Device.alarmHistoryTitle')" v-model="deviceAlarmDialogVisible" width="800px">
<el-table <el-table
:data="deviceAlarmList" :data="deviceAlarmList"
v-loading="deviceAlarmLoading" v-loading="deviceAlarmLoading"
@ -467,10 +502,10 @@ link :type="isRowConnected(scope.row) ? 'warning' : 'success'"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
:max-height="700" :max-height="700"
> >
<el-table-column label="规则名称" align="center" prop="ruleName" /> <el-table-column :label="t('DataCollection.Device.alarmRuleName')" align="center" prop="ruleName" />
<el-table-column label="点位名称" align="center" prop="modelName" /> <el-table-column :label="t('DataCollection.Device.alarmPointName')" align="center" prop="modelName" />
<el-table-column label="点位值" align="center" prop="addressValue" /> <el-table-column :label="t('DataCollection.Device.alarmPointValue')" align="center" prop="addressValue" />
<el-table-column label="告警等级" align="center" prop="alarmLevel"> <el-table-column :label="t('DataCollection.Device.alarmLevel')" align="center" prop="alarmLevel">
<template #default="scope"> <template #default="scope">
<dict-tag :type="DICT_TYPE.IOT_ALARM_REGISTRATION" :value="scope.row.alarmLevel" /> <dict-tag :type="DICT_TYPE.IOT_ALARM_REGISTRATION" :value="scope.row.alarmLevel" />
</template> </template>
@ -601,7 +636,7 @@ const handleDelete = async (ids: number | number[]) => {
const handleBatchDelete = async () => { const handleBatchDelete = async () => {
if (!selectedIds.value.length) { if (!selectedIds.value.length) {
message.error('请选择需要删除的数据') message.error(t('common.delNoData'))
return return
} }
await handleDelete(selectedIds.value) await handleDelete(selectedIds.value)
@ -610,7 +645,7 @@ const handleBatchDelete = async () => {
const handleCopy = async (id: number) => { const handleCopy = async (id: number) => {
try { try {
await DeviceApi.copyDevice(id) await DeviceApi.copyDevice(id)
message.success('复制成功') message.success(t('common.copySuccess'))
await getList() await getList()
} catch { } } catch { }
} }
@ -627,7 +662,7 @@ const handleExport = async () => {
ids: selectedIds.value.length ? selectedIds.value.join(',') : undefined ids: selectedIds.value.length ? selectedIds.value.join(',') : undefined
} }
const data = await DeviceApi.exportDevice(params) const data = await DeviceApi.exportDevice(params)
download.excel(data, '物联设备.xls') download.excel(data, t('DataCollection.Device.exportFilename'))
} catch { } catch {
} finally { } finally {
exportLoading.value = false exportLoading.value = false
@ -653,11 +688,11 @@ const attributeDeviceName = ref('')
const deviceTabActive = ref('deviceAttribute') const deviceTabActive = ref('deviceAttribute')
const deviceAttributeTabLabel = computed(() => { const deviceAttributeTabLabel = computed(() => {
return '设备属性' return t('DataCollection.Device.deviceAttributeTabLabel')
}) })
const deviceRuleTabLabel = computed(() => { const deviceRuleTabLabel = computed(() => {
return '点位规则' return t('DataCollection.Device.deviceRuleTabLabel')
}) })
const handleShowAttribute = (row: any) => { const handleShowAttribute = (row: any) => {
@ -803,7 +838,7 @@ const isRunningIdentifier = computed(() => {
const openRuleForm = async (row: DevicePointRuleVO & { pointRulesVOList?: any[] }) => { const openRuleForm = async (row: DevicePointRuleVO & { pointRulesVOList?: any[] }) => {
const deviceId = row.deviceId || attributeDeviceId.value const deviceId = row.deviceId || attributeDeviceId.value
if (!deviceId) { if (!deviceId) {
message.error('设备信息缺失,无法加载点位规则') message.error(t('DataCollection.Device.messageDeviceInfoMissingForRules'))
return return
} }
@ -910,7 +945,7 @@ const handleRuleSubmit = async () => {
} }
await request.put({ url: '/iot/device-point-rules/update', data: payload }) await request.put({ url: '/iot/device-point-rules/update', data: payload })
message.success('保存成功') message.success(t('common.updateSuccess'))
ruleDialogVisible.value = false ruleDialogVisible.value = false
await getRuleList() await getRuleList()
} finally { } finally {
@ -1079,7 +1114,7 @@ const deviceAlarmList = ref<any[]>([])
const handleShowDeviceAlarmHistory = async () => { const handleShowDeviceAlarmHistory = async () => {
if (!attributeDeviceId.value) { if (!attributeDeviceId.value) {
message.error('请选择一个物联设备') message.error(t('DataCollection.Device.messageSelectDeviceRequired'))
return return
} }
deviceAlarmDialogVisible.value = true deviceAlarmDialogVisible.value = true

@ -2,7 +2,13 @@
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="6" :xs="24"> <el-col :span="6" :xs="24">
<ContentWrap class="h-1/1"> <ContentWrap class="h-1/1">
<el-input v-model="keyword" clearable placeholder="搜索设备或参数" class="!w-1/1" @input="handleKeywordChange" /> <el-input
v-model="keyword"
clearable
:placeholder="t('DataCollection.DeviceParamAnalysis.treeSearchPlaceholder')"
class="!w-1/1"
@input="handleKeywordChange"
/>
<div class="mt-12px"> <div class="mt-12px">
<el-tree <el-tree
ref="treeRef" v-loading="treeLoading" :data="treeData" :props="treeProps" node-key="id" ref="treeRef" v-loading="treeLoading" :data="treeData" :props="treeProps" node-key="id"
@ -13,19 +19,27 @@ ref="treeRef" v-loading="treeLoading" :data="treeData" :props="treeProps" node-k
<el-col :span="18" :xs="24"> <el-col :span="18" :xs="24">
<ContentWrap> <ContentWrap>
<el-form class="-mb-15px" :inline="true"> <el-form class="-mb-15px device-param-analysis-form" :inline="true" label-width="auto">
<el-form-item label="时间"> <el-form-item :label="t('DataCollection.DeviceParamAnalysis.formTimeLabel')">
<el-date-picker <el-date-picker
v-model="dateRange" type="daterange" start-placeholder="开始日期" end-placeholder="结束日期" v-model="dateRange"
value-format="YYYY-MM-DD" :shortcuts="dateShortcuts" type="daterange"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" class="!w-360px" /> :start-placeholder="t('DataCollection.DeviceParamAnalysis.formTimeStartPlaceholder')"
:end-placeholder="t('DataCollection.DeviceParamAnalysis.formTimeEndPlaceholder')"
value-format="YYYY-MM-DD"
:shortcuts="dateShortcuts"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-360px"
/>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button :disabled="!selectedParam" @click="handleQuery"> <el-button :disabled="!selectedParam" @click="handleQuery">
<Icon icon="ep:search" class="mr-5px" /> 搜索 <Icon icon="ep:search" class="mr-5px" />
{{ t('DataCollection.DeviceParamAnalysis.searchButtonText') }}
</el-button> </el-button>
<el-button :disabled="!selectedParam" @click="resetQuery"> <el-button :disabled="!selectedParam" @click="resetQuery">
<Icon icon="ep:refresh" class="mr-5px" /> 重置 <Icon icon="ep:refresh" class="mr-5px" />
{{ t('DataCollection.DeviceParamAnalysis.resetButtonText') }}
</el-button> </el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
@ -34,8 +48,14 @@ v-model="dateRange" type="daterange" start-placeholder="开始日期" end-placeh
<ContentWrap> <ContentWrap>
<el-alert v-if="selectedParam" :title="selectedParamTitle" type="info" :closable="false" class="mb-12px" /> <el-alert v-if="selectedParam" :title="selectedParamTitle" type="info" :closable="false" class="mb-12px" />
<div v-loading="chartLoading"> <div v-loading="chartLoading">
<el-empty v-if="chartState === 'empty'" description="暂无数据" /> <el-empty
<el-empty v-else-if="!selectedParam" description="请选择左侧节点" /> v-if="chartState === 'empty'"
:description="t('DataCollection.DeviceParamAnalysis.emptyDescription')"
/>
<el-empty
v-else-if="!selectedParam"
:description="t('DataCollection.DeviceParamAnalysis.emptySelectNodeDescription')"
/>
<EChart v-else-if="chartState === 'ready'" :key="chartRenderKey" :options="chartOption" height="70vh" /> <EChart v-else-if="chartState === 'ready'" :key="chartRenderKey" :options="chartOption" height="70vh" />
</div> </div>
</ContentWrap> </ContentWrap>
@ -53,6 +73,8 @@ import { handleTree } from '@/utils/tree'
defineOptions({ name: 'DeviceParamAnalysis' }) defineOptions({ name: 'DeviceParamAnalysis' })
const { t } = useI18n()
type TreeNodeType = 'device' | 'param' type TreeNodeType = 'device' | 'param'
type DeviceTreeNode = { type DeviceTreeNode = {
@ -117,7 +139,7 @@ const dateRange = ref<[string, string]>(buildDefaultDateRange())
const dateShortcuts = [ const dateShortcuts = [
{ {
text: '最近 7 天', text: t('DataCollection.DeviceParamAnalysis.shortcutLast7Days'),
value: () => { value: () => {
const end = dayjs().endOf('day').toDate() const end = dayjs().endOf('day').toDate()
const start = dayjs().subtract(6, 'day').startOf('day').toDate() const start = dayjs().subtract(6, 'day').startOf('day').toDate()
@ -125,7 +147,7 @@ const dateShortcuts = [
} }
}, },
{ {
text: '上周', text: t('DataCollection.DeviceParamAnalysis.shortcutLastWeek'),
value: () => { value: () => {
const start = dayjs().subtract(1, 'week').startOf('week').toDate() const start = dayjs().subtract(1, 'week').startOf('week').toDate()
const end = dayjs().subtract(1, 'week').endOf('week').toDate() const end = dayjs().subtract(1, 'week').endOf('week').toDate()
@ -133,7 +155,7 @@ const dateShortcuts = [
} }
}, },
{ {
text: '上个月', text: t('DataCollection.DeviceParamAnalysis.shortcutLastMonth'),
value: () => { value: () => {
const start = dayjs().subtract(1, 'month').startOf('month').toDate() const start = dayjs().subtract(1, 'month').startOf('month').toDate()
const end = dayjs().subtract(1, 'month').endOf('month').toDate() const end = dayjs().subtract(1, 'month').endOf('month').toDate()
@ -141,7 +163,7 @@ const dateShortcuts = [
} }
}, },
{ {
text: '三个月内', text: t('DataCollection.DeviceParamAnalysis.shortcutLast3Months'),
value: () => { value: () => {
const end = dayjs().endOf('day').toDate() const end = dayjs().endOf('day').toDate()
const start = dayjs().subtract(3, 'month').startOf('day').toDate() const start = dayjs().subtract(3, 'month').startOf('day').toDate()
@ -153,8 +175,11 @@ const dateShortcuts = [
const selectedParamTitle = computed(() => { const selectedParamTitle = computed(() => {
const param = selectedParam.value const param = selectedParam.value
if (!param) return '' if (!param) return ''
const unitText = param.unit ? `${param.unit}` : '' const unitText = param.unit ? ` (${param.unit})` : ''
return `节点:${param.label}${unitText}` return t('DataCollection.DeviceParamAnalysis.selectedParamTitle', {
label: param.label,
unit: unitText
})
}) })
const chartOption = computed<EChartsOption>(() => { const chartOption = computed<EChartsOption>(() => {
@ -281,7 +306,7 @@ const loadTree = async () => {
chartSeries.value = [] chartSeries.value = []
} }
} catch { } catch {
message.error('获取树数据失败') message.error(t('DataCollection.DeviceParamAnalysis.messageLoadTreeFailed'))
} finally { } finally {
treeLoading.value = false treeLoading.value = false
} }
@ -344,7 +369,7 @@ const fetchChart = async () => {
chartXAxis.value = [] chartXAxis.value = []
chartSeries.value = [] chartSeries.value = []
chartState.value = 'empty' chartState.value = 'empty'
message.warning('该节点下暂无参数') message.warning(t('DataCollection.DeviceParamAnalysis.messageNodeNoParams'))
return return
} }
@ -386,12 +411,14 @@ const fetchChart = async () => {
} else { } else {
chartXAxis.value = keys chartXAxis.value = keys
const values = keys.map((k) => toNumber(firstRow[k])) const values = keys.map((k) => toNumber(firstRow[k]))
chartSeries.value = [{ name: param?.label || '参数', data: values }] chartSeries.value = [
{ name: param?.label || t('DataCollection.DeviceParamAnalysis.defaultSeriesName'), data: values }
]
} }
chartState.value = chartXAxis.value.length && chartSeries.value.length ? 'ready' : 'empty' chartState.value = chartXAxis.value.length && chartSeries.value.length ? 'ready' : 'empty'
} catch { } catch {
message.error('获取图表数据失败') message.error(t('DataCollection.DeviceParamAnalysis.messageFetchChartFailed'))
chartState.value = 'idle' chartState.value = 'idle'
} finally { } finally {
chartLoading.value = false chartLoading.value = false
@ -418,7 +445,7 @@ const handleTreeNodeClick = async (data: DeviceTreeNode) => {
selectedModelId.value = undefined selectedModelId.value = undefined
chartState.value = 'empty' chartState.value = 'empty'
resetChartData() resetChartData()
message.warning('该设备下没有参数') message.warning(t('DataCollection.DeviceParamAnalysis.messageDeviceNoParams'))
return return
} }
@ -459,6 +486,10 @@ onMounted(async () => {
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.device-param-analysis-form :deep(.el-form-item__label) {
min-width: 100px;
}
:deep(.el-tree) { :deep(.el-tree) {
max-height: calc(100vh - 280px); max-height: calc(100vh - 280px);
overflow: auto; overflow: auto;

@ -4,25 +4,25 @@
ref="formRef" ref="formRef"
:model="formData" :model="formData"
:rules="formRules" :rules="formRules"
label-width="100px" label-width="120px"
v-loading="formLoading" v-loading="formLoading"
> >
<el-form-item label="分类编码" prop="code"> <el-form-item :label="t('DataCollection.DeviceAttributeType.code')" prop="code">
<el-input v-model="formData.code" placeholder="请输入分类编码" :disabled = "formType === 'update'"/> <el-input v-model="formData.code" :placeholder="t('DataCollection.DeviceAttributeType.placeholderCode')" :disabled = "formType === 'update'"/>
</el-form-item> </el-form-item>
<el-form-item label="分类名称" prop="name"> <el-form-item :label="t('DataCollection.DeviceAttributeType.name')" prop="name">
<el-input v-model="formData.name" placeholder="请输入分类名称" /> <el-input v-model="formData.name" :placeholder="t('DataCollection.DeviceAttributeType.placeholderName')" />
</el-form-item> </el-form-item>
<el-form-item label="分类顺序" prop="sort"> <el-form-item :label="t('DataCollection.DeviceAttributeType.sort')" prop="sort">
<el-input v-model="formData.sort" placeholder="请输入分类顺序" /> <el-input v-model="formData.sort" :placeholder="t('DataCollection.DeviceAttributeType.placeholderSort')" />
</el-form-item> </el-form-item>
<el-form-item label="备注" prop="remark"> <el-form-item :label="t('DataCollection.DeviceAttributeType.remark')" prop="remark">
<el-input v-model="formData.remark" placeholder="请输入备注" /> <el-input v-model="formData.remark" :placeholder="t('DataCollection.DeviceAttributeType.placeholderRemark')" />
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button> <el-button @click="submitForm" type="primary" :disabled="formLoading">{{ t('DataCollection.DeviceAttributeType.dialogOk') }}</el-button>
<el-button @click="dialogVisible = false"> </el-button> <el-button @click="dialogVisible = false">{{ t('DataCollection.DeviceAttributeType.dialogCancel') }}</el-button>
</template> </template>
</Dialog> </Dialog>
</template> </template>
@ -47,9 +47,9 @@ const formData = ref({
remark: undefined, remark: undefined,
}) })
const formRules = reactive({ const formRules = reactive({
code: [{ required: true, message: '分类编码不能为空', trigger: 'blur' }], code: [{ required: true, message: t('DataCollection.DeviceAttributeType.validatorCodeRequired'), trigger: 'blur' }],
name: [{ required: true, message: '分类名称不能为空', trigger: 'blur' }], name: [{ required: true, message: t('DataCollection.DeviceAttributeType.validatorNameRequired'), trigger: 'blur' }],
sort: [{ required: true, message: '分类顺序不能为空', trigger: 'blur' }], sort: [{ required: true, message: t('DataCollection.DeviceAttributeType.validatorSortRequired'), trigger: 'blur' }],
}) })
const formRef = ref() // Ref const formRef = ref() // Ref

@ -1,26 +1,25 @@
<template> <template>
<ContentWrap> <ContentWrap>
<!-- 搜索工作栏 -->
<el-form <el-form
class="-mb-15px" class="-mb-15px"
:model="queryParams" :model="queryParams"
ref="queryFormRef" ref="queryFormRef"
:inline="true" :inline="true"
label-width="68px" label-width="120px"
> >
<el-form-item label="分类编码" prop="code"> <el-form-item :label="t('DataCollection.DeviceAttributeType.code')" prop="code">
<el-input <el-input
v-model="queryParams.code" v-model="queryParams.code"
placeholder="请输入分类编码" :placeholder="t('DataCollection.DeviceAttributeType.placeholderCode')"
clearable clearable
@keyup.enter="handleQuery" @keyup.enter="handleQuery"
class="!w-240px" class="!w-240px"
/> />
</el-form-item> </el-form-item>
<el-form-item label="分类名称" prop="name"> <el-form-item :label="t('DataCollection.DeviceAttributeType.name')" prop="name">
<el-input <el-input
v-model="queryParams.name" v-model="queryParams.name"
placeholder="请输入分类名称" :placeholder="t('DataCollection.DeviceAttributeType.placeholderName')"
clearable clearable
@keyup.enter="handleQuery" @keyup.enter="handleQuery"
class="!w-240px" class="!w-240px"
@ -47,15 +46,15 @@
/> />
</el-form-item> --> </el-form-item> -->
<el-form-item> <el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> {{ t('DataCollection.DeviceAttributeType.search') }}</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> {{ t('DataCollection.DeviceAttributeType.reset') }}</el-button>
<el-button <el-button
type="primary" type="primary"
plain plain
@click="openForm('create')" @click="openForm('create')"
v-hasPermi="['iot:device-attribute-type:create']" v-hasPermi="['iot:device-attribute-type:create']"
> >
<Icon icon="ep:plus" class="mr-5px" /> 新增 <Icon icon="ep:plus" class="mr-5px" /> {{ t('DataCollection.DeviceAttributeType.create') }}
</el-button> </el-button>
<el-button <el-button
type="danger" type="danger"
@ -63,7 +62,7 @@
@click="handleBatchDelete" @click="handleBatchDelete"
v-hasPermi="['iot:device-attribute-type:delete']" v-hasPermi="['iot:device-attribute-type:delete']"
> >
<Icon icon="ep:delete" class="mr-5px" /> 批量删除 <Icon icon="ep:delete" class="mr-5px" /> {{ t('DataCollection.DeviceAttributeType.batchDelete') }}
</el-button> </el-button>
<el-button <el-button
type="success" type="success"
@ -72,7 +71,7 @@
:loading="exportLoading" :loading="exportLoading"
v-hasPermi="['iot:device-attribute-type:export']" v-hasPermi="['iot:device-attribute-type:export']"
> >
<Icon icon="ep:download" class="mr-5px" /> 导出 <Icon icon="ep:download" class="mr-5px" /> {{ t('DataCollection.DeviceAttributeType.export') }}
</el-button> </el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
@ -90,18 +89,18 @@
@selection-change="handleSelectionChange" @selection-change="handleSelectionChange"
> >
<el-table-column type="selection" width="55" reserve-selection /> <el-table-column type="selection" width="55" reserve-selection />
<el-table-column label="分类编码" align="center" prop="code" /> <el-table-column :label="t('DataCollection.DeviceAttributeType.code')" align="center" prop="code" />
<el-table-column label="分类名称" align="center" prop="name" /> <el-table-column :label="t('DataCollection.DeviceAttributeType.name')" align="center" prop="name" />
<el-table-column label="分类顺序" align="center" prop="sort" /> <el-table-column :label="t('DataCollection.DeviceAttributeType.sort')" align="center" prop="sort" />
<el-table-column label="备注" align="center" prop="remark" /> <el-table-column :label="t('DataCollection.DeviceAttributeType.remark')" align="center" prop="remark" />
<el-table-column <el-table-column
label="创建时间" :label="t('DataCollection.DeviceAttributeType.createTime')"
align="center" align="center"
prop="createTime" prop="createTime"
:formatter="dateFormatter" :formatter="dateFormatter"
width="180px" width="180px"
/> />
<el-table-column label="操作" align="center" width="150px" fixed="right"> <el-table-column :label="t('DataCollection.DeviceAttributeType.operate')" align="center" width="150px" fixed="right">
<template #default="scope"> <template #default="scope">
<el-button <el-button
link link
@ -109,7 +108,7 @@
@click="openForm('update', scope.row.id)" @click="openForm('update', scope.row.id)"
v-hasPermi="['iot:device-attribute-type:update']" v-hasPermi="['iot:device-attribute-type:update']"
> >
编辑 {{ t('action.edit') }}
</el-button> </el-button>
<el-button <el-button
link link
@ -117,7 +116,7 @@
@click="handleDelete(scope.row.id)" @click="handleDelete(scope.row.id)"
v-hasPermi="['iot:device-attribute-type:delete']" v-hasPermi="['iot:device-attribute-type:delete']"
> >
删除 {{ t('action.del') }}
</el-button> </el-button>
</template> </template>
</el-table-column> </el-table-column>
@ -235,7 +234,7 @@ const handleExport = async () => {
ids: selectedIds.value.length ? selectedIds.value.join(',') : undefined, ids: selectedIds.value.length ? selectedIds.value.join(',') : undefined,
} }
const data = await DeviceAttributeTypeApi.exportDeviceAttributeType(params) const data = await DeviceAttributeTypeApi.exportDeviceAttributeType(params)
download.excel(data, '采集点分类.xls') download.excel(data, t('DataCollection.DeviceAttributeType.exportFilename'))
} catch { } catch {
} finally { } finally {
exportLoading.value = false exportLoading.value = false

@ -4,17 +4,24 @@
ref="formRef" ref="formRef"
:model="formData" :model="formData"
:rules="formRules" :rules="formRules"
label-width="100px" label-width="120px"
v-loading="formLoading" v-loading="formLoading"
> >
<el-form-item label="模型编码" prop="code"> <el-form-item :label="t('DataCollection.DeviceModel.code')" prop="code">
<el-input v-model="formData.code" placeholder="请输入模型编码" :disabled = "formType === 'update'"/> <el-input
v-model="formData.code"
:placeholder="t('DataCollection.DeviceModel.placeholderCode')"
:disabled = "formType === 'update'"/>
</el-form-item> </el-form-item>
<el-form-item label="模型名称" prop="name"> <el-form-item :label="t('DataCollection.DeviceModel.name')" prop="name">
<el-input v-model="formData.name" placeholder="请输入模型名称" /> <el-input
v-model="formData.name"
:placeholder="t('DataCollection.DeviceModel.placeholderName')" />
</el-form-item> </el-form-item>
<el-form-item label="通讯协议" prop="protocol"> <el-form-item :label="t('DataCollection.DeviceModel.protocol')" prop="protocol">
<el-select v-model="formData.protocol" placeholder="请选择通讯协议"> <el-select
v-model="formData.protocol"
:placeholder="t('DataCollection.DeviceModel.placeholderProtocol')">
<el-option <el-option
v-for="dict in getStrDictOptions(DICT_TYPE.IOT_PROTOCOL)" v-for="dict in getStrDictOptions(DICT_TYPE.IOT_PROTOCOL)"
:key="dict.value" :key="dict.value"
@ -23,13 +30,15 @@
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="备注" prop="remark"> <el-form-item :label="t('DataCollection.DeviceModel.remark')" prop="remark">
<el-input v-model="formData.remark" placeholder="请输入备注" /> <el-input
v-model="formData.remark"
:placeholder="t('DataCollection.DeviceModel.placeholderRemark')" />
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button> <el-button @click="submitForm" type="primary" :disabled="formLoading">{{ t('DataCollection.DeviceModel.dialogOk') }}</el-button>
<el-button @click="dialogVisible = false"> </el-button> <el-button @click="dialogVisible = false">{{ t('DataCollection.DeviceModel.dialogCancel') }}</el-button>
</template> </template>
</Dialog> </Dialog>
</template> </template>
@ -55,9 +64,9 @@ const formData = ref({
remark: undefined, remark: undefined,
}) })
const formRules = reactive({ const formRules = reactive({
code: [{ required: true, message: '分类编码不能为空', trigger: 'blur' }], code: [{ required: true, message: t('DataCollection.DeviceModel.validatorCodeRequired'), trigger: 'blur' }],
name: [{ required: true, message: '分类名称不能为空', trigger: 'blur' }], name: [{ required: true, message: t('DataCollection.DeviceModel.validatorNameRequired'), trigger: 'blur' }],
protocol: [{ required: true, message: '通讯协议不能为空', trigger: 'blur' }], protocol: [{ required: true, message: t('DataCollection.DeviceModel.validatorProtocolRequired'), trigger: 'blur' }],
}) })
const formRef = ref() // Ref const formRef = ref() // Ref

@ -4,29 +4,36 @@
ref="formRef" ref="formRef"
:model="formData" :model="formData"
:rules="formRules" :rules="formRules"
label-width="100px" label-width="120px"
v-loading="formLoading" v-loading="formLoading"
> >
<el-form-item label="点位编码" prop="attributeCode"> <el-form-item :label="t('DataCollection.DeviceModel.attributeCode')" prop="attributeCode">
<el-input v-model="formData.attributeCode" placeholder="请输入点位编码" :disabled = "formType === 'update'"/> <el-input
v-model="formData.attributeCode"
:placeholder="t('DataCollection.DeviceModel.placeholderAttributeCode')"
:disabled = "formType === 'update'"/>
</el-form-item> </el-form-item>
<el-form-item label="点位名称" prop="attributeName"> <el-form-item :label="t('DataCollection.DeviceModel.attributeName')" prop="attributeName">
<el-input v-model="formData.attributeName" placeholder="请输入点位名称" /> <el-input
v-model="formData.attributeName"
:placeholder="t('DataCollection.DeviceModel.placeholderAttributeName')" />
</el-form-item> </el-form-item>
<el-form-item label="点位类型" prop="attributeType"> <el-form-item :label="t('DataCollection.DeviceModel.attributeType')" prop="attributeType">
<el-select <el-select
v-model="formData.attributeType" v-model="formData.attributeType"
clearable clearable
filterable filterable
placeholder="请选择点位类型" :placeholder="t('DataCollection.DeviceModel.placeholderAttributeType')"
class="!w-180px" class="!w-180px"
@change="handleAttributeTypeChange" @change="handleAttributeTypeChange"
> >
<el-option v-for="item in typeList" :key="item.id" :label="item.name" :value="item.id" /> <el-option v-for="item in typeList" :key="item.id" :label="item.name" :value="item.id" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="数据类型" prop="dataType"> <el-form-item :label="t('DataCollection.DeviceModel.dataType')" prop="dataType">
<el-select v-model="formData.dataType" placeholder="请选择数据类型"> <el-select
v-model="formData.dataType"
:placeholder="t('DataCollection.DeviceModel.placeholderDataType')">
<el-option <el-option
v-for="dict in getStrDictOptions(DICT_TYPE.IOT_DEVICE_DATA_TYPE)" v-for="dict in getStrDictOptions(DICT_TYPE.IOT_DEVICE_DATA_TYPE)"
:key="dict.value" :key="dict.value"
@ -35,12 +42,16 @@
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="寄存器地址" prop="address"> <el-form-item :label="t('DataCollection.DeviceModel.address')" prop="address">
<el-input v-model="formData.address" placeholder="请输入寄存器地址" /> <el-input
v-model="formData.address"
:placeholder="t('DataCollection.DeviceModel.placeholderAddress')" />
</el-form-item> </el-form-item>
<el-form-item label="单位" prop="dataUnit"> <el-form-item :label="t('DataCollection.DeviceModel.dataUnit')" prop="dataUnit">
<el-select v-model="formData.dataUnit" placeholder="请选择单位"> <el-select
v-model="formData.dataUnit"
:placeholder="t('DataCollection.DeviceModel.placeholderDataUnit')">
<el-option <el-option
v-for="dict in getStrDictOptions(DICT_TYPE.IOT_DEVICE_ATTRIBUTE_UNIT)" v-for="dict in getStrDictOptions(DICT_TYPE.IOT_DEVICE_ATTRIBUTE_UNIT)"
:key="dict.value" :key="dict.value"
@ -49,16 +60,20 @@
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="倍率" prop="ratio"> <el-form-item :label="t('DataCollection.DeviceModel.ratio')" prop="ratio">
<el-input v-model="formData.ratio" placeholder="请输入倍率" /> <el-input
v-model="formData.ratio"
:placeholder="t('DataCollection.DeviceModel.placeholderRatio')" />
</el-form-item> </el-form-item>
<el-form-item label="备注" prop="remark"> <el-form-item :label="t('DataCollection.DeviceModel.remark')" prop="remark">
<el-input v-model="formData.remark" placeholder="请输入备注" /> <el-input
v-model="formData.remark"
:placeholder="t('DataCollection.DeviceModel.placeholderAttributeRemark')" />
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button> <el-button @click="submitForm" type="primary" :disabled="formLoading">{{ t('DataCollection.DeviceModel.dialogOk') }}</el-button>
<el-button @click="dialogVisible = false"> </el-button> <el-button @click="dialogVisible = false">{{ t('DataCollection.DeviceModel.dialogCancel') }}</el-button>
</template> </template>
</Dialog> </Dialog>
</template> </template>
@ -106,8 +121,8 @@ const formData = ref({
deviceModelId: undefined, deviceModelId: undefined,
}) })
const formRules = reactive({ const formRules = reactive({
attributeCode: [{ required: true, message: '点位编码不能为空', trigger: 'blur' }], attributeCode: [{ required: true, message: t('DataCollection.DeviceModel.validatorAttributeCodeRequired'), trigger: 'blur' }],
attributeName: [{ required: true, message: '点位名称不能为空', trigger: 'blur' }], attributeName: [{ required: true, message: t('DataCollection.DeviceModel.validatorAttributeNameRequired'), trigger: 'blur' }],
}) })
const formRef = ref() // Ref const formRef = ref() // Ref

@ -1,19 +1,30 @@
<template> <template>
<ContentWrap> <ContentWrap>
<!-- 搜索工作栏 --> <!-- 搜索工作栏 -->
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" label-width="100px"> <el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" label-width="120px">
<el-form-item label="点位编码" prop="attributeCode"> <el-form-item :label="t('DataCollection.DeviceModel.attributeCode')" prop="attributeCode">
<el-input <el-input
v-model="queryParams.attributeCode" placeholder="请输入点位编码" clearable @keyup.enter="handleQuery" v-model="queryParams.attributeCode"
:placeholder="t('DataCollection.DeviceModel.placeholderAttributeCode')"
clearable
@keyup.enter="handleQuery"
class="!w-240px" /> class="!w-240px" />
</el-form-item> </el-form-item>
<el-form-item label="点位名称" prop="attributeName"> <el-form-item :label="t('DataCollection.DeviceModel.attributeName')" prop="attributeName">
<el-input <el-input
v-model="queryParams.attributeName" placeholder="请输入点位名称" clearable @keyup.enter="handleQuery" v-model="queryParams.attributeName"
:placeholder="t('DataCollection.DeviceModel.placeholderAttributeName')"
clearable
@keyup.enter="handleQuery"
class="!w-240px" /> class="!w-240px" />
</el-form-item> </el-form-item>
<el-form-item label="点位类型" prop="attributeType"> <el-form-item :label="t('DataCollection.DeviceModel.attributeType')" prop="attributeType">
<el-select v-model="queryParams.attributeType" clearable filterable placeholder="请选择" class="!w-240px"> <el-select
v-model="queryParams.attributeType"
clearable
filterable
:placeholder="t('DataCollection.DeviceModel.placeholderAttributeType')"
class="!w-240px">
<el-option v-for="item in typeList" :key="item.id" :label="item.name" :value="item.id" /> <el-option v-for="item in typeList" :key="item.id" :label="item.name" :value="item.id" />
</el-select> </el-select>
</el-form-item> </el-form-item>
@ -39,21 +50,21 @@ v-model="queryParams.attributeName" placeholder="请输入点位名称" clearabl
</el-form-item> --> </el-form-item> -->
<el-form-item> <el-form-item>
<el-button @click="handleQuery"> <el-button @click="handleQuery">
<Icon icon="ep:search" class="mr-5px" /> 搜索 <Icon icon="ep:search" class="mr-5px" /> {{ t('DataCollection.DeviceModel.search') }}
</el-button> </el-button>
<el-button @click="resetQuery"> <el-button @click="resetQuery">
<Icon icon="ep:refresh" class="mr-5px" /> 重置 <Icon icon="ep:refresh" class="mr-5px" /> {{ t('DataCollection.DeviceModel.reset') }}
</el-button> </el-button>
<el-button type="primary" plain @click="openForm('create')" v-hasPermi="['iot:device-model:create']"> <el-button type="primary" plain @click="openForm('create')" v-hasPermi="['iot:device-model:create']">
<Icon icon="ep:plus" class="mr-5px" /> 新增 <Icon icon="ep:plus" class="mr-5px" /> {{ t('DataCollection.DeviceModel.create') }}
</el-button> </el-button>
<el-button <el-button
type="success" plain @click="handleExport" :loading="exportLoading" type="success" plain @click="handleExport" :loading="exportLoading"
v-hasPermi="['iot:device-model:export']"> v-hasPermi="['iot:device-model:export']">
<Icon icon="ep:download" class="mr-5px" /> 导出 <Icon icon="ep:download" class="mr-5px" /> {{ t('DataCollection.DeviceModel.export') }}
</el-button> </el-button>
<el-button type="danger" plain @click="handleBatchDelete" v-hasPermi="['iot:device-model:delete']"> <el-button type="danger" plain @click="handleBatchDelete" v-hasPermi="['iot:device-model:delete']">
<Icon icon="ep:delete" class="mr-5px" /> 批量删除 <Icon icon="ep:delete" class="mr-5px" /> {{ t('DataCollection.DeviceModel.batchDelete') }}
</el-button> </el-button>
</el-form-item> </el-form-item>
@ -67,32 +78,32 @@ ref="tableRef" v-loading="loading" :data="list" :stripe="true" :show-overflow-to
@selection-change="handleSelectionChange"> @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" reserve-selection /> <el-table-column type="selection" width="55" reserve-selection />
<!-- <el-table-column label="ID" align="center" prop="id" /> --> <!-- <el-table-column label="ID" align="center" prop="id" /> -->
<el-table-column label="点位编码" align="center" prop="attributeCode" /> <el-table-column :label="t('DataCollection.DeviceModel.attributeCode')" align="center" prop="attributeCode" />
<el-table-column label="点位名称" align="center" prop="attributeName" /> <el-table-column :label="t('DataCollection.DeviceModel.attributeName')" align="center" prop="attributeName" />
<el-table-column label="点位类型" align="center" prop="typeName" /> <el-table-column :label="t('DataCollection.DeviceModel.attributeType')" align="center" prop="typeName" />
<el-table-column label="数据类型" align="center" prop="dataType" /> <el-table-column :label="t('DataCollection.DeviceModel.dataType')" align="center" prop="dataType" />
<el-table-column label="寄存器地址" align="center" prop="address" /> <el-table-column :label="t('DataCollection.DeviceModel.address')" align="center" prop="address" />
<el-table-column label="单位" align="center" prop="dataUnit" /> <el-table-column :label="t('DataCollection.DeviceModel.dataUnit')" align="center" prop="dataUnit" />
<el-table-column label="倍率" align="center" prop="ratio" /> <el-table-column :label="t('DataCollection.DeviceModel.ratio')" align="center" prop="ratio" />
<el-table-column label="备注" align="center" prop="remark" /> <el-table-column :label="t('DataCollection.DeviceModel.remark')" align="center" prop="remark" />
<!-- <el-table-column label="采集设备模型id" align="center" prop="deviceModelId" /> --> <!-- <el-table-column label="采集设备模型id" align="center" prop="deviceModelId" /> -->
<el-table-column label="创建时间" align="center" prop="createTime" :formatter="dateFormatter" width="180px" /> <el-table-column :label="t('DataCollection.DeviceModel.createTime')" align="center" prop="createTime" :formatter="dateFormatter" width="180px" />
<el-table-column label="操作" align="center" width="150px" fixed="right"> <el-table-column :label="t('DataCollection.DeviceModel.operate')" align="center" width="150px" fixed="right">
<template #default="scope"> <template #default="scope">
<el-button <el-button
link type="primary" @click="openForm('update', scope.row.id)" link type="primary" @click="openForm('update', scope.row.id)"
v-hasPermi="['iot:device-model:update']"> v-hasPermi="['iot:device-model:update']">
编辑 {{ t('action.edit') }}
</el-button> </el-button>
<el-button link type="danger" @click="handleDelete(scope.row.id)" v-hasPermi="['iot:device-model:delete']"> <el-button link type="danger" @click="handleDelete(scope.row.id)" v-hasPermi="['iot:device-model:delete']">
删除 {{ t('action.del') }}
</el-button> </el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<!-- 分页 --> <!-- 分页 -->
<Pagination <Pagination
:total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize" :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
@pagination="getList" /> @pagination="getList" />
</ContentWrap> </ContentWrap>

@ -1,20 +1,30 @@
<template> <template>
<ContentWrap> <ContentWrap>
<!-- 搜索工作栏 --> <!-- 搜索工作栏 -->
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" label-width="120px">
<el-form-item label="模型编码" prop="code"> <el-form-item :label="t('DataCollection.DeviceModel.code')" prop="code">
<el-input <el-input
v-model="queryParams.code" placeholder="请输入模型编码" clearable @keyup.enter="handleQuery" v-model="queryParams.code"
:placeholder="t('DataCollection.DeviceModel.placeholderCode')"
clearable
@keyup.enter="handleQuery"
class="!w-240px" /> class="!w-240px" />
</el-form-item> </el-form-item>
<el-form-item label="模型名称" prop="name"> <el-form-item :label="t('DataCollection.DeviceModel.name')" prop="name">
<el-input <el-input
v-model="queryParams.name" placeholder="请输入模型名称" clearable @keyup.enter="handleQuery" v-model="queryParams.name"
:placeholder="t('DataCollection.DeviceModel.placeholderName')"
clearable
@keyup.enter="handleQuery"
class="!w-240px" /> class="!w-240px" />
</el-form-item> </el-form-item>
<el-form-item label="通讯协议" prop="protocol"> <el-form-item :label="t('DataCollection.DeviceModel.protocol')" prop="protocol">
<el-select v-model="queryParams.protocol" placeholder="请选择通讯协议" clearable class="!w-240px"> <el-select
v-model="queryParams.protocol"
:placeholder="t('DataCollection.DeviceModel.placeholderProtocol')"
clearable
class="!w-240px">
<el-option <el-option
v-for="dict in getStrDictOptions(DICT_TYPE.IOT_PROTOCOL)" :key="dict.value" :label="dict.label" v-for="dict in getStrDictOptions(DICT_TYPE.IOT_PROTOCOL)" :key="dict.value" :label="dict.label"
:value="dict.value" /> :value="dict.value" />
@ -43,21 +53,21 @@ v-for="dict in getStrDictOptions(DICT_TYPE.IOT_PROTOCOL)" :key="dict.value" :lab
</el-form-item> --> </el-form-item> -->
<el-form-item> <el-form-item>
<el-button @click="handleQuery"> <el-button @click="handleQuery">
<Icon icon="ep:search" class="mr-5px" /> 搜索 <Icon icon="ep:search" class="mr-5px" /> {{ t('DataCollection.DeviceModel.search') }}
</el-button> </el-button>
<el-button @click="resetQuery"> <el-button @click="resetQuery">
<Icon icon="ep:refresh" class="mr-5px" /> 重置 <Icon icon="ep:refresh" class="mr-5px" /> {{ t('DataCollection.DeviceModel.reset') }}
</el-button> </el-button>
<el-button type="primary" plain @click="openForm('create')" v-hasPermi="['iot:device-model:create']"> <el-button type="primary" plain @click="openForm('create')" v-hasPermi="['iot:device-model:create']">
<Icon icon="ep:plus" class="mr-5px" /> 新增 <Icon icon="ep:plus" class="mr-5px" /> {{ t('DataCollection.DeviceModel.create') }}
</el-button> </el-button>
<el-button type="danger" plain @click="handleBatchDelete" v-hasPermi="['iot:device-model:delete']"> <el-button type="danger" plain @click="handleBatchDelete" v-hasPermi="['iot:device-model:delete']">
<Icon icon="ep:delete" class="mr-5px" /> 批量删除 <Icon icon="ep:delete" class="mr-5px" /> {{ t('DataCollection.DeviceModel.batchDelete') }}
</el-button> </el-button>
<el-button <el-button
type="success" plain @click="handleExport" :loading="exportLoading" type="success" plain @click="handleExport" :loading="exportLoading"
v-hasPermi="['iot:device-model:export']"> v-hasPermi="['iot:device-model:export']">
<Icon icon="ep:download" class="mr-5px" /> 导出 <Icon icon="ep:download" class="mr-5px" /> {{ t('DataCollection.DeviceModel.export') }}
</el-button> </el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
@ -66,74 +76,74 @@ type="success" plain @click="handleExport" :loading="exportLoading"
<!-- 列表 --> <!-- 列表 -->
<ContentWrap> <ContentWrap>
<el-table <el-table
ref="tableRef" v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" ref="tableRef" v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"
highlight-current-row row-key="id" @selection-change="handleSelectionChange"> highlight-current-row row-key="id" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" reserve-selection /> <el-table-column type="selection" width="55" reserve-selection />
<el-table-column label="模型编码" align="center" prop="code" /> <el-table-column :label="t('DataCollection.DeviceModel.code')" align="center" prop="code" />
<el-table-column label="模型名称" align="center" prop="name" /> <el-table-column :label="t('DataCollection.DeviceModel.name')" align="center" prop="name" />
<el-table-column align="center" label="通讯协议" prop="protocol" min-width="150"> <el-table-column align="center" :label="t('DataCollection.DeviceModel.protocol')" prop="protocol" min-width="150">
<template #default="scope"> <template #default="scope">
<dict-tag :type="DICT_TYPE.IOT_PROTOCOL" :value="scope.row.protocol" /> <dict-tag :type="DICT_TYPE.IOT_PROTOCOL" :value="scope.row.protocol" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="备注" align="center" prop="remark" /> <el-table-column :label="t('DataCollection.DeviceModel.remark')" align="center" prop="remark" />
<!-- <el-table-column label="创建时间" align="center" prop="createTime" :formatter="dateFormatter" width="180px" /> --> <!-- <el-table-column label="创建时间" align="center" prop="createTime" :formatter="dateFormatter" width="180px" /> -->
<el-table-column label="操作" align="center" min-width="120px"> <el-table-column :label="t('DataCollection.DeviceModel.operate')" align="center" min-width="120px">
<template #default="scope"> <template #default="scope">
<el-button link type="primary" @click.stop="handleShowAttribute(scope.row)">点位</el-button> <el-button link type="primary" @click.stop="handleShowAttribute(scope.row)">{{ t('DataCollection.DeviceModel.attributeModuleName') }}</el-button>
<el-button link type="primary" @click="handleCopy(scope.row.id)" v-hasPermi="['iot:device-model:create']"> <el-button link type="primary" @click="handleCopy(scope.row.id)" v-hasPermi="['iot:device-model:create']">
复制 {{ t('action.copy') }}
</el-button> </el-button>
<el-button <el-button
link type="primary" @click="openForm('update', scope.row.id)" link type="primary" @click="openForm('update', scope.row.id)"
v-hasPermi="['iot:device-model:update']"> v-hasPermi="['iot:device-model:update']">
编辑 {{ t('action.edit') }}
</el-button> </el-button>
<el-button link type="danger" @click="handleDelete(scope.row.id)" v-hasPermi="['iot:device-model:delete']"> <el-button link type="danger" @click="handleDelete(scope.row.id)" v-hasPermi="['iot:device-model:delete']">
删除 {{ t('action.del') }}
</el-button> </el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<!-- 分页 --> <!-- 分页 -->
<Pagination <Pagination
:total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize" :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
@pagination="getList" /> @pagination="getList" />
</ContentWrap> </ContentWrap>
<ContentWrap> <ContentWrap>
<template v-if="attributeModelId"> <template v-if="attributeModelId">
<div class="mb-10px text-sm text-gray-500"> <div class="mb-10px text-sm text-gray-500">
当前模型<span class="font-medium text-gray-700">{{ attributeModelName || '-' }}</span> {{ t('DataCollection.DeviceModel.moduleName') }}<span class="font-medium text-gray-700">{{ attributeModelName || '-' }}</span>
</div> </div>
<el-tabs model-value="modelAttribute"> <el-tabs model-value="modelAttribute">
<el-tab-pane :label="modelAttributeTabLabel" name="modelAttribute"> <el-tab-pane :label="t('DataCollection.DeviceModel.attributeModuleName')" name="modelAttribute">
<ModelAttributeList :id="attributeModelId" /> <ModelAttributeList :id="attributeModelId" />
</el-tab-pane> </el-tab-pane>
<el-tab-pane :label="modelRuleTabLabel" name="modelRule"> <el-tab-pane :label="t('DataCollection.DeviceModel.ruleTabLabel')" name="modelRule">
<el-form class="-mb-15px" :model="ruleQueryParams" ref="ruleQueryFormRef" :inline="true" label-width="80px"> <el-form class="-mb-15px" :model="ruleQueryParams" ref="ruleQueryFormRef" :inline="true" label-width="120px">
<el-form-item label="标识符" prop="identifier"> <el-form-item :label="t('DataCollection.DeviceModel.ruleIdentifier')" prop="identifier">
<el-input <el-input
v-model="ruleQueryParams.identifier" v-model="ruleQueryParams.identifier"
placeholder="请输入标识符" :placeholder="t('DataCollection.DeviceModel.ruleSearchIdentifierPlaceholder')"
clearable clearable
@keyup.enter="handleRuleQuery" @keyup.enter="handleRuleQuery"
class="!w-200px" class="!w-200px"
/> />
</el-form-item> </el-form-item>
<el-form-item label="名称" prop="fieldName"> <el-form-item :label="t('DataCollection.DeviceModel.ruleFieldName')" prop="fieldName">
<el-input <el-input
v-model="ruleQueryParams.fieldName" v-model="ruleQueryParams.fieldName"
placeholder="请输入名称" :placeholder="t('DataCollection.DeviceModel.ruleSearchFieldNamePlaceholder')"
clearable clearable
@keyup.enter="handleRuleQuery" @keyup.enter="handleRuleQuery"
class="!w-200px" class="!w-200px"
/> />
</el-form-item> </el-form-item>
<el-form-item label="默认值" prop="defaultValue"> <el-form-item :label="t('DataCollection.DeviceModel.ruleDefaultValue')" prop="defaultValue">
<el-input <el-input
v-model="ruleQueryParams.defaultValue" v-model="ruleQueryParams.defaultValue"
placeholder="请输入默认值" :placeholder="t('DataCollection.DeviceModel.ruleSearchDefaultValuePlaceholder')"
clearable clearable
@keyup.enter="handleRuleQuery" @keyup.enter="handleRuleQuery"
class="!w-200px" class="!w-200px"
@ -141,17 +151,17 @@ link type="primary" @click="openForm('update', scope.row.id)"
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button @click="handleRuleQuery"> <el-button @click="handleRuleQuery">
<Icon icon="ep:search" class="mr-5px" /> 搜索 <Icon icon="ep:search" class="mr-5px" /> {{ t('DataCollection.DeviceModel.ruleSearch') }}
</el-button> </el-button>
<el-button @click="resetRuleQuery"> <el-button @click="resetRuleQuery">
<Icon icon="ep:refresh" class="mr-5px" /> 重置 <Icon icon="ep:refresh" class="mr-5px" /> {{ t('DataCollection.DeviceModel.ruleReset') }}
</el-button> </el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<div class="mb-10px text-right"> <div class="mb-10px text-right">
<el-button type="primary" @click="openCreateRuleForm"> <el-button type="primary" @click="openCreateRuleForm">
新增告警规则 {{ t('DataCollection.DeviceModel.ruleCreateButton') }}
</el-button> </el-button>
</div> </div>
@ -162,15 +172,15 @@ link type="primary" @click="openForm('update', scope.row.id)"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
row-key="id" row-key="id"
> >
<el-table-column label="标识符" align="center" prop="identifier" /> <el-table-column :label="t('DataCollection.DeviceModel.ruleIdentifier')" align="center" prop="identifier" />
<el-table-column label="名称" align="center" prop="fieldName" /> <el-table-column :label="t('DataCollection.DeviceModel.ruleFieldName')" align="center" prop="fieldName" />
<el-table-column label="规则" align="center" prop="fieldRule" /> <el-table-column :label="t('DataCollection.DeviceModel.ruleFieldRule')" align="center" prop="fieldRule" />
<el-table-column label="默认值" align="center" prop="defaultValue" /> <el-table-column :label="t('DataCollection.DeviceModel.ruleDefaultValue')" align="center" prop="defaultValue" />
<el-table-column label="创建时间" align="center" prop="createTime" :formatter="dateFormatter" width="180px" /> <el-table-column :label="t('DataCollection.DeviceModel.ruleCreateTime')" align="center" prop="createTime" :formatter="dateFormatter" width="180px" />
<el-table-column label="操作" align="center" width="160px"> <el-table-column :label="t('DataCollection.DeviceModel.ruleOperate')" align="center" width="160px">
<template #default="scope"> <template #default="scope">
<el-button link type="primary" @click="openRuleForm(scope.row)"> <el-button link type="primary" @click="openRuleForm(scope.row)">
编辑 {{ t('DataCollection.DeviceModel.ruleEditRuleButton') }}
</el-button> </el-button>
<el-button <el-button
v-if="(scope.row.identifier || '').toString().toUpperCase() === 'ALARM'" v-if="(scope.row.identifier || '').toString().toUpperCase() === 'ALARM'"
@ -178,7 +188,7 @@ link type="primary" @click="openForm('update', scope.row.id)"
type="danger" type="danger"
@click="handleRuleDelete(scope.row.id)" @click="handleRuleDelete(scope.row.id)"
> >
删除 {{ t('DataCollection.DeviceModel.ruleDeleteRuleButton') }}
</el-button> </el-button>
</template> </template>
</el-table-column> </el-table-column>
@ -189,23 +199,25 @@ link type="primary" @click="openForm('update', scope.row.id)"
v-model:limit="ruleQueryParams.pageSize" v-model:limit="ruleQueryParams.pageSize"
@pagination="getRuleList" @pagination="getRuleList"
/> />
<el-dialog v-model="ruleDialogVisible" :title="t('DataCollection.DeviceModel.ruleDialogTitle')" width="880px" draggable>
<el-dialog v-model="ruleDialogVisible" title="编辑点位规则" width="880px" draggable> <el-form :model="ruleForm" ref="ruleFormRef" label-width="120px">
<el-form :model="ruleForm" ref="ruleFormRef" label-width="90px"> <el-form-item :label="t('DataCollection.DeviceModel.ruleDialogIdentifier')">
<el-form-item label="标识符">
<el-input v-model="ruleForm.identifier" disabled /> <el-input v-model="ruleForm.identifier" disabled />
</el-form-item> </el-form-item>
<el-form-item label="名称"> <el-form-item :label="t('DataCollection.DeviceModel.ruleDialogFieldName')">
<el-input v-model="ruleForm.fieldName" disabled /> <el-input v-model="ruleForm.fieldName" disabled />
</el-form-item> </el-form-item>
<el-form-item label="默认值"> <el-form-item :label="t('DataCollection.DeviceModel.ruleDialogDefaultValue')">
<el-input v-model="ruleForm.defaultValue" disabled /> <el-input v-model="ruleForm.defaultValue" disabled />
</el-form-item> </el-form-item>
<el-form-item <el-form-item
v-if="(ruleForm.identifier || '').toString().toUpperCase() === 'ALARM'" v-if="(ruleForm.identifier || '').toString().toUpperCase() === 'ALARM'"
label="告警登记" :label="t('DataCollection.DeviceModel.ruleDialogAlarmLevel')"
> >
<el-select v-model="ruleForm.alarmLevel" placeholder="请选择告警登记"> <el-select
v-model="ruleForm.alarmLevel"
:placeholder="t('DataCollection.DeviceModel.ruleDialogAlarmLevelPlaceholder')"
>
<el-option <el-option
v-for="dict in getStrDictOptions(DICT_TYPE.IOT_ALARM_REGISTRATION)" v-for="dict in getStrDictOptions(DICT_TYPE.IOT_ALARM_REGISTRATION)"
:key="dict.value" :key="dict.value"
@ -217,10 +229,10 @@ link type="primary" @click="openForm('update', scope.row.id)"
<div class="flex flex-col w-full"> <div class="flex flex-col w-full">
<div class="border border-gray-200 dark:border-gray-600 rounded-md py-12px"> <div class="border border-gray-200 dark:border-gray-600 rounded-md py-12px">
<el-form-item label="点位规则"> <el-form-item :label="t('DataCollection.DeviceModel.ruleDialogFieldRule')">
<el-select <el-select
v-model="ruleForm.fieldRule" v-model="ruleForm.fieldRule"
placeholder="请选择点位规则" :placeholder="t('DataCollection.DeviceModel.ruleDialogFieldRulePlaceholder')"
class="!w-240px" class="!w-240px"
:disabled="!currentRuleOptions.length" :disabled="!currentRuleOptions.length"
> >
@ -233,11 +245,11 @@ link type="primary" @click="openForm('update', scope.row.id)"
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="规则"> <el-form-item :label="t('DataCollection.DeviceModel.ruleDialogRule')">
<div class="flex items-center gap-8px"> <div class="flex items-center gap-8px">
<el-select <el-select
v-model="ruleForm.ruleAttributeId" v-model="ruleForm.ruleAttributeId"
placeholder="请选择点位" :placeholder="t('DataCollection.DeviceModel.ruleDialogRuleAttributePlaceholder')"
class="!w-240px" class="!w-240px"
> >
<el-option <el-option
@ -249,7 +261,7 @@ link type="primary" @click="openForm('update', scope.row.id)"
</el-select> </el-select>
<el-select <el-select
v-model="ruleForm.ruleOperator" v-model="ruleForm.ruleOperator"
placeholder="请选择条件" :placeholder="t('DataCollection.DeviceModel.ruleDialogRuleOperatorPlaceholder')"
class="!w-160px" class="!w-160px"
> >
<el-option <el-option
@ -262,7 +274,7 @@ link type="primary" @click="openForm('update', scope.row.id)"
<el-input <el-input
v-if="ruleForm.ruleOperator !== 'TRUE' && ruleForm.ruleOperator !== 'FALSE'" v-if="ruleForm.ruleOperator !== 'TRUE' && ruleForm.ruleOperator !== 'FALSE'"
v-model="ruleForm.ruleValue" v-model="ruleForm.ruleValue"
placeholder="请输入符号值" :placeholder="t('DataCollection.DeviceModel.ruleDialogRuleValuePlaceholder')"
class="!w-200px" class="!w-200px"
/> />
</div> </div>
@ -274,10 +286,10 @@ link type="primary" @click="openForm('update', scope.row.id)"
:key="index" :key="index"
class="border border-gray-200 dark:border-gray-600 rounded-md px-16px py-12px" class="border border-gray-200 dark:border-gray-600 rounded-md px-16px py-12px"
> >
<el-form-item label="点位规则"> <el-form-item :label="t('DataCollection.DeviceModel.ruleDialogFieldRule')">
<el-select <el-select
v-model="item.rule" v-model="item.rule"
placeholder="请选择点位规则" :placeholder="t('DataCollection.DeviceModel.ruleDialogFieldRulePlaceholder')"
class="!w-240px" class="!w-240px"
> >
<el-option <el-option
@ -289,11 +301,11 @@ link type="primary" @click="openForm('update', scope.row.id)"
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="规则"> <el-form-item :label="t('DataCollection.DeviceModel.ruleDialogRule')">
<div class="flex items-center gap-8px"> <div class="flex items-center gap-8px">
<el-select <el-select
v-model="item.id" v-model="item.id"
placeholder="请选择点位" :placeholder="t('DataCollection.DeviceModel.ruleDialogRuleAttributePlaceholder')"
class="!w-240px" class="!w-240px"
> >
<el-option <el-option
@ -305,7 +317,7 @@ link type="primary" @click="openForm('update', scope.row.id)"
</el-select> </el-select>
<el-select <el-select
v-model="item.operator" v-model="item.operator"
placeholder="请选择条件" :placeholder="t('DataCollection.DeviceModel.ruleDialogRuleOperatorPlaceholder')"
class="!w-160px" class="!w-160px"
> >
<el-option <el-option
@ -318,11 +330,11 @@ link type="primary" @click="openForm('update', scope.row.id)"
<el-input <el-input
v-if="item.operator !== 'TRUE' && item.operator !== 'FALSE'" v-if="item.operator !== 'TRUE' && item.operator !== 'FALSE'"
v-model="item.operatorRule" v-model="item.operatorRule"
placeholder="请输入符号值" :placeholder="t('DataCollection.DeviceModel.ruleDialogRuleValuePlaceholder')"
class="!w-200px" class="!w-200px"
/> />
<el-button type="danger" link @click="handleRemovePointRule(index)"> <el-button type="danger" link @click="handleRemovePointRule(index)">
删除 {{ t('DataCollection.DeviceModel.ruleDeleteRuleButton') }}
</el-button> </el-button>
</div> </div>
</el-form-item> </el-form-item>
@ -330,36 +342,39 @@ link type="primary" @click="openForm('update', scope.row.id)"
<el-form-item v-if="isRunningIdentifier" label=" "> <el-form-item v-if="isRunningIdentifier" label=" ">
<el-button type="primary" link @click="handleAddPointRule"> <el-button type="primary" link @click="handleAddPointRule">
+ 添加规则 + {{ t('DataCollection.DeviceModel.ruleCreateButton') }}
</el-button> </el-button>
</el-form-item> </el-form-item>
</div> </div>
</el-form> </el-form>
<template #footer> <template #footer>
<span class="dialog-footer"> <span class="dialog-footer">
<el-button @click="ruleDialogVisible = false"> </el-button> <el-button @click="ruleDialogVisible = false">{{ t('DataCollection.DeviceModel.dialogCancel') }}</el-button>
<el-button type="primary" :loading="ruleFormLoading" @click="handleRuleSubmit"> </el-button> <el-button type="primary" :loading="ruleFormLoading" @click="handleRuleSubmit">{{ t('DataCollection.DeviceModel.dialogOk') }}</el-button>
</span> </span>
</template> </template>
</el-dialog> </el-dialog>
<el-dialog v-model="createRuleDialogVisible" title="新增告警规则" width="520px" draggable> <el-dialog v-model="createRuleDialogVisible" :title="t('DataCollection.DeviceModel.ruleCreateButton')" width="520px" draggable>
<el-form :model="createRuleForm" ref="createRuleFormRef" label-width="90px"> <el-form :model="createRuleForm" ref="createRuleFormRef" label-width="120px">
<el-form-item label="标识符"> <el-form-item :label="t('DataCollection.DeviceModel.ruleDialogIdentifier')">
<el-input v-model="createRuleForm.identifier" disabled /> <el-input v-model="createRuleForm.identifier" disabled />
</el-form-item> </el-form-item>
<el-form-item label="名称"> <el-form-item :label="t('DataCollection.DeviceModel.ruleDialogFieldName')">
<el-input v-model="createRuleForm.fieldName" placeholder="请输入名称" /> <el-input
v-model="createRuleForm.fieldName"
:placeholder="t('DataCollection.DeviceModel.ruleSearchFieldNamePlaceholder')"
/>
</el-form-item> </el-form-item>
<el-form-item label="默认值"> <el-form-item :label="t('DataCollection.DeviceModel.ruleDialogDefaultValue')">
<el-input v-model="createRuleForm.defaultValue" disabled /> <el-input v-model="createRuleForm.defaultValue" disabled />
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<span class="dialog-footer"> <span class="dialog-footer">
<el-button @click="createRuleDialogVisible = false"> </el-button> <el-button @click="createRuleDialogVisible = false">{{ t('DataCollection.DeviceModel.dialogCancel') }}</el-button>
<el-button type="primary" :loading="createRuleFormLoading" @click="handleCreateRuleSubmit"> <el-button type="primary" :loading="createRuleFormLoading" @click="handleCreateRuleSubmit">
{{ t('DataCollection.DeviceModel.dialogOk') }}
</el-button> </el-button>
</span> </span>
</template> </template>
@ -367,7 +382,10 @@ link type="primary" @click="openForm('update', scope.row.id)"
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
</template> </template>
<el-empty v-else description="请点击设备模型列表的“点位”查看采集点和点位规则" /> <el-empty
v-else
:description="t('DataCollection.DeviceModel.moduleName') + t('common.colon') + t('DataCollection.DeviceModel.attributeModuleName') + t('DataCollection.DeviceModel.ruleTabLabel')"
/>
</ContentWrap> </ContentWrap>
<!-- 表单弹窗添加/修改 --> <!-- 表单弹窗添加/修改 -->
<DeviceModelForm ref="formRef" @success="getList" /> <DeviceModelForm ref="formRef" @success="getList" />

@ -1,23 +1,28 @@
<template> <template>
<Dialog v-model="dialogVisible" :title="dialogTitle" width="900" :scroll="true" max-height="80vh" align-center> <Dialog v-model="dialogVisible" :title="dialogTitle" width="900" :scroll="true" max-height="80vh" align-center>
<div class="single-device-dialog"> <div class="single-device-dialog">
<el-form class="-mb-15px" :inline="true" label-width="80px"> <el-form class="-mb-15px history-single-device-form" :inline="true" label-width="auto">
<el-form-item label="采集时间"> <el-form-item :label="t('DataCollection.HistoryData.dialogCollectionTimeLabel')">
<el-date-picker <el-date-picker
v-model="collectionTimeRange" value-format="YYYY-MM-DD HH:mm:ss" type="datetimerange" v-model="collectionTimeRange"
start-placeholder="开始时间" end-placeholder="结束时间" value-format="YYYY-MM-DD HH:mm:ss"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" class="!w-360px" /> type="datetimerange"
:start-placeholder="t('DataCollection.HistoryData.dialogCollectionTimeStartPlaceholder')"
:end-placeholder="t('DataCollection.HistoryData.dialogCollectionTimeEndPlaceholder')"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-360px"
/>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button @click="handleQuery"></el-button> <el-button @click="handleQuery">{{ t('DataCollection.HistoryData.dialogSearchButtonText') }}</el-button>
<el-button @click="resetQuery"></el-button> <el-button @click="resetQuery">{{ t('DataCollection.HistoryData.dialogResetButtonText') }}</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<ContentWrap v-loading="loading"> <ContentWrap v-loading="loading">
<div v-if="recordGroups.length" class="single-device-dialog__record-list"> <div v-if="recordGroups.length" class="single-device-dialog__record-list">
<div v-for="group in recordGroups" :key="group.key" class="single-device-dialog__record"> <div v-for="group in recordGroups" :key="group.key" class="single-device-dialog__record">
<div class="single-device-dialog__record-title"> <div class="single-device-dialog__record-title">
采集时间{{ group.collectTime || '-' }} {{ t('DataCollection.HistoryData.dialogRecordCollectionTimePrefix') }}{{ group.collectTime || '-' }}
</div> </div>
<div v-if="group.sections.length" class="single-device-dialog__table-grid"> <div v-if="group.sections.length" class="single-device-dialog__table-grid">
<div <div
@ -26,7 +31,10 @@ v-for="section in group.sections" :key="`${group.key}-${section.key}`"
<div class="single-device-dialog__section-title"> <div class="single-device-dialog__section-title">
{{ section.title }} {{ section.title }}
</div> </div>
<el-empty v-if="!section.columns.length" description="暂无数据" /> <el-empty
v-if="!section.columns.length"
:description="t('DataCollection.HistoryData.emptyDescription')"
/>
<el-table <el-table
v-else :data="section.rows" :border="true" :header-cell-style="headerCellStyle" v-else :data="section.rows" :border="true" :header-cell-style="headerCellStyle"
:cell-style="bodyCellStyle" size="small"> :cell-style="bodyCellStyle" size="small">
@ -40,10 +48,10 @@ v-for="col in section.columns" :key="col.prop" :prop="col.prop" :label="col.labe
</el-table> </el-table>
</div> </div>
</div> </div>
<el-empty v-else description="暂无数据" /> <el-empty v-else :description="t('DataCollection.HistoryData.emptyDescription')" />
</div> </div>
</div> </div>
<el-empty v-else description="暂无数据" /> <el-empty v-else :description="t('DataCollection.HistoryData.emptyDescription')" />
</ContentWrap> </ContentWrap>
</div> </div>
</Dialog> </Dialog>
@ -72,6 +80,8 @@ type RecordGroup = {
sections: Section[] sections: Section[]
} }
const { t } = useI18n()
const props = defineProps<{ const props = defineProps<{
modelValue: boolean modelValue: boolean
deviceId?: string | number deviceId?: string | number
@ -93,7 +103,7 @@ const dialogVisible = computed({
const dialogTitle = computed(() => { const dialogTitle = computed(() => {
const name = props.deviceName ? props.deviceName : '-' const name = props.deviceName ? props.deviceName : '-'
return `历史记录:${name}` return `${t('DataCollection.HistoryData.dialogTitlePrefix')}${name}`
}) })
@ -165,7 +175,8 @@ const buildSectionsFromGroups = (groups: Record<string, any[]>): Section[] => {
if (Array.isArray(list) && list.length) { if (Array.isArray(list) && list.length) {
list.forEach((item: any, index: number) => { list.forEach((item: any, index: number) => {
const prop = `col${index}` const prop = `col${index}`
const label = item?.attributeName ?? `字段${index + 1}` const label =
item?.attributeName ?? `${t('DataCollection.HistoryData.defaultFieldLabelPrefix')}${index + 1}`
columns.push({ prop, label }) columns.push({ prop, label })
row[prop] = item?.addressValue ?? '' row[prop] = item?.addressValue ?? ''
}) })

@ -1,43 +1,43 @@
<template> <template>
<ContentWrap> <ContentWrap>
<el-form <el-form
class="-mb-15px" class="-mb-15px history-data-form"
:model="queryParams" :model="queryParams"
ref="queryFormRef" ref="queryFormRef"
:inline="true" :inline="true"
label-width="80px" label-width="auto"
> >
<el-form-item label="产线编码" prop="lineNode"> <el-form-item :label="t('DataCollection.HistoryData.searchLineCodeLabel')" prop="lineNode">
<el-input <el-input
v-model="queryParams.lineNode" v-model="queryParams.lineNode"
placeholder="请输入产线编码" :placeholder="t('DataCollection.HistoryData.searchLineCodePlaceholder')"
clearable clearable
@keyup.enter="handleQuery" @keyup.enter="handleQuery"
class="!w-240px" class="!w-240px"
/> />
</el-form-item> </el-form-item>
<el-form-item label="产线名称" prop="lineName"> <el-form-item :label="t('DataCollection.HistoryData.searchLineNameLabel')" prop="lineName">
<el-input <el-input
v-model="queryParams.lineName" v-model="queryParams.lineName"
placeholder="请输入产线名称" :placeholder="t('DataCollection.HistoryData.searchLineNamePlaceholder')"
clearable clearable
@keyup.enter="handleQuery" @keyup.enter="handleQuery"
class="!w-240px" class="!w-240px"
/> />
</el-form-item> </el-form-item>
<el-form-item label="设备编码" prop="deviceCode"> <el-form-item :label="t('DataCollection.HistoryData.searchDeviceCodeLabel')" prop="deviceCode">
<el-input <el-input
v-model="queryParams.deviceCode" v-model="queryParams.deviceCode"
placeholder="请输入设备编码" :placeholder="t('DataCollection.HistoryData.searchDeviceCodePlaceholder')"
clearable clearable
@keyup.enter="handleQuery" @keyup.enter="handleQuery"
class="!w-240px" class="!w-240px"
/> />
</el-form-item> </el-form-item>
<el-form-item label="设备名称" prop="deviceName"> <el-form-item :label="t('DataCollection.HistoryData.searchDeviceNameLabel')" prop="deviceName">
<el-input <el-input
v-model="queryParams.deviceName" v-model="queryParams.deviceName"
placeholder="请输入设备名称" :placeholder="t('DataCollection.HistoryData.searchDeviceNamePlaceholder')"
clearable clearable
@keyup.enter="handleQuery" @keyup.enter="handleQuery"
class="!w-240px" class="!w-240px"
@ -45,13 +45,16 @@
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button @click="handleQuery"> <el-button @click="handleQuery">
<Icon icon="ep:search" class="mr-5px" /> 搜索 <Icon icon="ep:search" class="mr-5px" />
{{ t('DataCollection.HistoryData.searchButtonText') }}
</el-button> </el-button>
<el-button @click="resetQuery"> <el-button @click="resetQuery">
<Icon icon="ep:refresh" class="mr-5px" /> 重置 <Icon icon="ep:refresh" class="mr-5px" />
{{ t('DataCollection.HistoryData.resetButtonText') }}
</el-button> </el-button>
<el-button type="success" plain @click="handleExport" :loading="exportLoading"> <el-button type="success" plain @click="handleExport" :loading="exportLoading">
<Icon icon="ep:download" class="mr-5px" /> 导出 <Icon icon="ep:download" class="mr-5px" />
{{ t('DataCollection.HistoryData.exportButtonText') }}
</el-button> </el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
@ -67,21 +70,46 @@
@selection-change="handleSelectionChange" @selection-change="handleSelectionChange"
> >
<el-table-column type="selection" width="55" reserve-selection /> <el-table-column type="selection" width="55" reserve-selection />
<el-table-column label="产线编码" align="left" prop="lineNode" min-width="140px" />
<el-table-column label="产线名称" align="left" prop="lineName" min-width="160px" />
<el-table-column label="设备编码" align="left" prop="deviceCode" min-width="140px" />
<el-table-column label="设备名称" align="left" prop="deviceName" min-width="160px" />
<el-table-column <el-table-column
label="采集时间" :label="t('DataCollection.HistoryData.tableLineCodeColumn')"
align="left"
prop="lineNode"
min-width="140px"
/>
<el-table-column
:label="t('DataCollection.HistoryData.tableLineNameColumn')"
align="left"
prop="lineName"
min-width="160px"
/>
<el-table-column
:label="t('DataCollection.HistoryData.tableDeviceCodeColumn')"
align="left"
prop="deviceCode"
min-width="140px"
/>
<el-table-column
:label="t('DataCollection.HistoryData.tableDeviceNameColumn')"
align="left"
prop="deviceName"
min-width="160px"
/>
<el-table-column
:label="t('DataCollection.HistoryData.tableCollectionTimeColumn')"
align="center" align="center"
prop="collectionTime" prop="collectionTime"
:formatter="dateFormatter" :formatter="dateFormatter"
width="180px" width="180px"
/> />
<el-table-column label="操作" align="center" fixed="right" width="150px"> <el-table-column
:label="t('DataCollection.HistoryData.tableOperateColumn')"
align="center"
fixed="right"
width="150px"
>
<template #default="scope"> <template #default="scope">
<el-button link type="primary" @click="handleSingleView(scope.row)"> <el-button link type="primary" @click="handleSingleView(scope.row)">
历史记录 {{ t('DataCollection.HistoryData.tableActionHistoryLabel') }}
</el-button> </el-button>
</template> </template>
</el-table-column> </el-table-column>
@ -110,6 +138,7 @@ import HistorySingleDeviceDialog from './HistorySingleDeviceDialog.vue'
defineOptions({ name: 'HistoryData' }) defineOptions({ name: 'HistoryData' })
const message = useMessage() const message = useMessage()
const { t } = useI18n()
const loading = ref(true) const loading = ref(true)
const list = ref<LineDeviceVO[]>([]) const list = ref<LineDeviceVO[]>([])
@ -215,10 +244,16 @@ const handleExport = async () => {
ids: selectedIds.value.length ? selectedIds.value.join(',') : undefined ids: selectedIds.value.length ? selectedIds.value.join(',') : undefined
} }
const data = await DeviceApi.exportLineDevice(params) const data = await DeviceApi.exportLineDevice(params)
download.excel(data, '历史数据设备.xls') download.excel(data, t('DataCollection.HistoryData.exportFilename'))
} catch { } catch {
} finally { } finally {
exportLoading.value = false exportLoading.value = false
} }
} }
</script> </script>
<style scoped lang="scss">
.history-data-form :deep(.el-form-item__label) {
min-width: 100px;
}
</style>

@ -2,8 +2,8 @@
<Dialog v-model="dialogVisible" :title="dialogTitle" width="1200"> <Dialog v-model="dialogVisible" :title="dialogTitle" width="1200">
<div class="single-device-dialog"> <div class="single-device-dialog">
<div class="single-device-dialog__header"> <div class="single-device-dialog__header">
<div>设备名称{{ deviceName || '-' }}</div> <div>{{ t('DataCollection.RealTimeMonitoring.dialogDeviceNameLabel') }}{{ deviceName || '-' }}</div>
<div>采集时间{{ displayTime }}</div> <div>{{ t('DataCollection.RealTimeMonitoring.dialogCollectionTimeLabel') }}{{ displayTime }}</div>
</div> </div>
<ContentWrap v-loading="loading"> <ContentWrap v-loading="loading">
<div v-if="sections.length" class="single-device-dialog__table-grid"> <div v-if="sections.length" class="single-device-dialog__table-grid">
@ -11,7 +11,10 @@
<div class="single-device-dialog__section-title"> <div class="single-device-dialog__section-title">
{{ section.title }} {{ section.title }}
</div> </div>
<el-empty v-if="!section.columns.length" description="暂无数据" /> <el-empty
v-if="!section.columns.length"
:description="t('DataCollection.RealTimeMonitoring.emptyDescription')"
/>
<el-table <el-table
v-else :data="section.rows" :border="true" :header-cell-style="headerCellStyle" v-else :data="section.rows" :border="true" :header-cell-style="headerCellStyle"
:cell-style="bodyCellStyle" size="small"> :cell-style="bodyCellStyle" size="small">
@ -25,7 +28,10 @@ v-for="col in section.columns" :key="col.prop" :prop="col.prop" :label="col.labe
</el-table> </el-table>
</div> </div>
</div> </div>
<el-empty v-else description="暂无数据" /> <el-empty
v-else
:description="t('DataCollection.RealTimeMonitoring.emptyDescription')"
/>
</ContentWrap> </ContentWrap>
</div> </div>
</Dialog> </Dialog>
@ -49,6 +55,8 @@ type Section = {
rows: SectionRow[] rows: SectionRow[]
} }
const { t } = useI18n()
const props = defineProps<{ const props = defineProps<{
modelValue: boolean modelValue: boolean
deviceId?: string | number deviceId?: string | number
@ -70,7 +78,7 @@ const dialogVisible = computed({
}) })
const dialogTitle = computed(() => { const dialogTitle = computed(() => {
return '单设备监控' return t('DataCollection.RealTimeMonitoring.dialogTitle')
}) })
const loading = ref(false) const loading = ref(false)
@ -136,7 +144,8 @@ const buildSectionsFromGroups = (groups: Record<string, any[]>): Section[] => {
if (Array.isArray(list) && list.length) { if (Array.isArray(list) && list.length) {
list.forEach((item: any, index: number) => { list.forEach((item: any, index: number) => {
const prop = `col${index}` const prop = `col${index}`
const label = item?.attributeName ?? `字段${index + 1}` const label =
item?.attributeName ?? `${t('DataCollection.RealTimeMonitoring.defaultFieldLabelPrefix')}${index + 1}`
columns.push({ prop, label }) columns.push({ prop, label })
row[prop] = item?.addressValue ?? '' row[prop] = item?.addressValue ?? ''
}) })
@ -160,12 +169,12 @@ const fetchList = async () => {
try { try {
const res: any = await DeviceApi.getSingleDevice({ deviceId: props.deviceId }) const res: any = await DeviceApi.getSingleDevice({ deviceId: props.deviceId })
const groups = Array.isArray(res) const groups = Array.isArray(res)
? { 默认: res } ? { [t('DataCollection.RealTimeMonitoring.defaultGroupName')]: res }
: { : {
...toGroupMap(res?.data), ...toGroupMap(res?.data),
...toGroupMap(res?.data?.data), ...toGroupMap(res?.data?.data),
...toGroupMap(res) ...toGroupMap(res)
} }
sections.value = buildSectionsFromGroups(groups) sections.value = buildSectionsFromGroups(groups)
} finally { } finally {
loading.value = false loading.value = false

@ -1,35 +1,60 @@
<template> <template>
<ContentWrap> <ContentWrap>
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-form
<el-form-item label="产线编码" prop="lineNode"> class="-mb-15px real-time-monitoring-form"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="auto"
>
<el-form-item :label="t('DataCollection.RealTimeMonitoring.searchLineCodeLabel')" prop="lineNode">
<el-input <el-input
v-model="queryParams.lineNode" placeholder="请输入产线编码" clearable @keyup.enter="handleQuery" v-model="queryParams.lineNode"
class="!w-240px" /> :placeholder="t('DataCollection.RealTimeMonitoring.searchLineCodePlaceholder')"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item> </el-form-item>
<el-form-item label="产线名称" prop="lineName"> <el-form-item :label="t('DataCollection.RealTimeMonitoring.searchLineNameLabel')" prop="lineName">
<el-input <el-input
v-model="queryParams.lineName" placeholder="请输入产线名称" clearable @keyup.enter="handleQuery" v-model="queryParams.lineName"
class="!w-240px" /> :placeholder="t('DataCollection.RealTimeMonitoring.searchLineNamePlaceholder')"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item> </el-form-item>
<el-form-item label="设备编码" prop="deviceCode"> <el-form-item :label="t('DataCollection.RealTimeMonitoring.searchDeviceCodeLabel')" prop="deviceCode">
<el-input <el-input
v-model="queryParams.deviceCode" placeholder="请输入设备编码" clearable @keyup.enter="handleQuery" v-model="queryParams.deviceCode"
class="!w-240px" /> :placeholder="t('DataCollection.RealTimeMonitoring.searchDeviceCodePlaceholder')"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item> </el-form-item>
<el-form-item label="设备名称" prop="deviceName"> <el-form-item :label="t('DataCollection.RealTimeMonitoring.searchDeviceNameLabel')" prop="deviceName">
<el-input <el-input
v-model="queryParams.deviceName" placeholder="请输入设备名称" clearable @keyup.enter="handleQuery" v-model="queryParams.deviceName"
class="!w-240px" /> :placeholder="t('DataCollection.RealTimeMonitoring.searchDeviceNamePlaceholder')"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button @click="handleQuery"> <el-button @click="handleQuery">
<Icon icon="ep:search" class="mr-5px" /> 搜索 <Icon icon="ep:search" class="mr-5px" />
{{ t('DataCollection.RealTimeMonitoring.searchButtonText') }}
</el-button> </el-button>
<el-button @click="resetQuery"> <el-button @click="resetQuery">
<Icon icon="ep:refresh" class="mr-5px" /> 重置 <Icon icon="ep:refresh" class="mr-5px" />
{{ t('DataCollection.RealTimeMonitoring.resetButtonText') }}
</el-button> </el-button>
<el-button type="success" plain @click="handleExport" :loading="exportLoading"> <el-button type="success" plain @click="handleExport" :loading="exportLoading">
<Icon icon="ep:download" class="mr-5px" /> 导出 <Icon icon="ep:download" class="mr-5px" />
{{ t('DataCollection.RealTimeMonitoring.exportButtonText') }}
</el-button> </el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
@ -45,19 +70,57 @@ v-model="queryParams.deviceName" placeholder="请输入设备名称" clearable @
@selection-change="handleSelectionChange" @selection-change="handleSelectionChange"
> >
<el-table-column type="selection" width="55" reserve-selection /> <el-table-column type="selection" width="55" reserve-selection />
<el-table-column label="产线编码" align="left" prop="lineNode" min-width="140px" /> <el-table-column
<el-table-column label="产线名称" align="left" prop="lineName" min-width="160px" /> :label="t('DataCollection.RealTimeMonitoring.tableLineCodeColumn')"
<el-table-column label="设备编码" align="left" prop="deviceCode" min-width="140px" /> align="left"
<el-table-column label="设备名称" align="left" prop="deviceName" min-width="160px" /> prop="lineNode"
<el-table-column label="连接状态" align="center" prop="status" width="120px"> min-width="140px"
/>
<el-table-column
:label="t('DataCollection.RealTimeMonitoring.tableLineNameColumn')"
align="left"
prop="lineName"
min-width="160px"
/>
<el-table-column
:label="t('DataCollection.RealTimeMonitoring.tableDeviceCodeColumn')"
align="left"
prop="deviceCode"
min-width="140px"
/>
<el-table-column
:label="t('DataCollection.RealTimeMonitoring.tableDeviceNameColumn')"
align="left"
prop="deviceName"
min-width="160px"
/>
<el-table-column
:label="t('DataCollection.RealTimeMonitoring.tableStatusColumn')"
align="center"
prop="status"
width="120px"
>
<template #default="scope"> <template #default="scope">
<dict-tag :type="DICT_TYPE.IOT_GATEWAY_STATUS" :value="scope.row.status" /> <dict-tag :type="DICT_TYPE.IOT_GATEWAY_STATUS" :value="scope.row.status" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="最新采集时间" align="center" prop="collectionTime" :formatter="dateFormatter" width="180px" /> <el-table-column
<el-table-column label="操作" align="center" fixed="right" width="150px"> :label="t('DataCollection.RealTimeMonitoring.tableCollectionTimeColumn')"
align="center"
prop="collectionTime"
:formatter="dateFormatter"
width="180px"
/>
<el-table-column
:label="t('DataCollection.RealTimeMonitoring.tableOperateColumn')"
align="center"
fixed="right"
width="150px"
>
<template #default="scope"> <template #default="scope">
<el-button link type="primary" @click="handleSingleMonitor(scope.row)"></el-button> <el-button link type="primary" @click="handleSingleMonitor(scope.row)">
{{ t('DataCollection.RealTimeMonitoring.tableActionSingleMonitorLabel') }}
</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -84,6 +147,7 @@ import SingleDeviceMonitorDialog from './SingleDeviceMonitorDialog.vue'
defineOptions({ name: 'RealTimeMonitoring' }) defineOptions({ name: 'RealTimeMonitoring' })
const message = useMessage() const message = useMessage()
const { t } = useI18n()
const loading = ref(true) const loading = ref(true)
const list = ref<LineDeviceVO[]>([]) const list = ref<LineDeviceVO[]>([])
@ -155,7 +219,7 @@ const resetQuery = () => {
const handleSingleMonitor = (row: LineDeviceVO) => { const handleSingleMonitor = (row: LineDeviceVO) => {
const deviceId = (row as any)?.deviceId ?? row?.id const deviceId = (row as any)?.deviceId ?? row?.id
if (deviceId === undefined || deviceId === null || deviceId === '') { if (deviceId === undefined || deviceId === null || deviceId === '') {
message.error('设备信息不完整') message.error(t('DataCollection.RealTimeMonitoring.messageDeviceInfoIncomplete'))
return return
} }
monitorDeviceId.value = deviceId monitorDeviceId.value = deviceId
@ -173,7 +237,7 @@ const handleExport = async () => {
ids: selectedIds.value.length ? selectedIds.value.join(',') : undefined ids: selectedIds.value.length ? selectedIds.value.join(',') : undefined
} }
const data = await DeviceApi.exportLineDevice(params) const data = await DeviceApi.exportLineDevice(params)
download.excel(data, '实时监控设备.xls') download.excel(data, t('DataCollection.RealTimeMonitoring.exportFilename'))
} catch { } catch {
} finally { } finally {
exportLoading.value = false exportLoading.value = false
@ -184,3 +248,9 @@ onMounted(() => {
getList() getList()
}) })
</script> </script>
<style scoped lang="scss">
.real-time-monitoring-form :deep(.el-form-item__label) {
min-width: 100px;
}
</style>

@ -1,44 +1,53 @@
<template> <template>
<ContentWrap> <ContentWrap>
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" label-width="120px"> <el-form
<el-form-item label="设备编码" prop="deviceCode"> class="-mb-15px run-report-form"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="auto"
>
<el-form-item :label="t('DataCollection.RunReport.searchDeviceCodeLabel')" prop="deviceCode">
<el-input <el-input
v-model="queryParams.deviceCode" v-model="queryParams.deviceCode"
placeholder="请输入设备编码" :placeholder="t('DataCollection.RunReport.searchDeviceCodePlaceholder')"
clearable clearable
@keyup.enter="handleQuery" @keyup.enter="handleQuery"
class="!w-240px" class="!w-240px"
/> />
</el-form-item> </el-form-item>
<el-form-item label="设备名称" prop="deviceName"> <el-form-item :label="t('DataCollection.RunReport.searchDeviceNameLabel')" prop="deviceName">
<el-input <el-input
v-model="queryParams.deviceName" v-model="queryParams.deviceName"
placeholder="请输入设备名称" :placeholder="t('DataCollection.RunReport.searchDeviceNamePlaceholder')"
clearable clearable
@keyup.enter="handleQuery" @keyup.enter="handleQuery"
class="!w-240px" class="!w-240px"
/> />
</el-form-item> </el-form-item>
<el-form-item label="设备运行时间" prop="timeRange"> <el-form-item :label="t('DataCollection.RunReport.searchTimeRangeLabel')" prop="timeRange">
<el-date-picker <el-date-picker
v-model="queryParams.timeRange" v-model="queryParams.timeRange"
type="datetimerange" type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
start-placeholder="开始时间" :start-placeholder="t('DataCollection.RunReport.searchTimeRangeStartPlaceholder')"
end-placeholder="结束时间" :end-placeholder="t('DataCollection.RunReport.searchTimeRangeEndPlaceholder')"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-360px" class="!w-360px"
/> />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" @click="handleQuery"> <el-button type="primary" @click="handleQuery">
<Icon icon="ep:search" class="mr-5px" /> 搜索 <Icon icon="ep:search" class="mr-5px" />
{{ t('DataCollection.RunReport.searchButtonText') }}
</el-button> </el-button>
<el-button @click="resetQuery"> <el-button @click="resetQuery">
<Icon icon="ep:refresh" class="mr-5px" /> 重置 <Icon icon="ep:refresh" class="mr-5px" />
{{ t('DataCollection.RunReport.resetButtonText') }}
</el-button> </el-button>
<el-button type="success" plain @click="handleExport" :loading="exportLoading"> <el-button type="success" plain @click="handleExport" :loading="exportLoading">
<Icon icon="ep:download" class="mr-5px" /> 导出 <Icon icon="ep:download" class="mr-5px" />
{{ t('DataCollection.RunReport.exportButtonText') }}
</el-button> </el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
@ -46,15 +55,60 @@
<ContentWrap> <ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" row-key="id"> <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" row-key="id">
<el-table-column label="设备编码" align="left" prop="deviceCode" min-width="140px" /> <el-table-column
<el-table-column label="设备名称" align="left" prop="deviceName" min-width="160px" /> :label="t('DataCollection.RunReport.tableDeviceCodeColumn')"
<el-table-column label="运行时间(小时)" align="center" prop="totalRunningTime" min-width="140px" /> align="left"
<el-table-column label="待机时间(小时)" align="center" prop="totalStandbyTime" min-width="140px" /> prop="deviceCode"
<el-table-column label="故障时间(小时)" align="center" prop="totalFaultTime" min-width="140px" /> min-width="140px"
<el-table-column label="警告时间(小时)" align="center" prop="totalWarningTime" min-width="140px" /> />
<el-table-column label="稼动率" align="center" prop="utilizationRate" min-width="120px" /> <el-table-column
<el-table-column label="设备运行开始时间" align="center" prop="startTime" min-width="120px" /> :label="t('DataCollection.RunReport.tableDeviceNameColumn')"
<el-table-column label="设备运行结束时间" align="center" prop="endTime" min-width="120px" /> align="left"
prop="deviceName"
min-width="160px"
/>
<el-table-column
:label="t('DataCollection.RunReport.tableRunningTimeColumn')"
align="center"
prop="totalRunningTime"
min-width="140px"
/>
<el-table-column
:label="t('DataCollection.RunReport.tableStandbyTimeColumn')"
align="center"
prop="totalStandbyTime"
min-width="140px"
/>
<el-table-column
:label="t('DataCollection.RunReport.tableFaultTimeColumn')"
align="center"
prop="totalFaultTime"
min-width="140px"
/>
<el-table-column
:label="t('DataCollection.RunReport.tableWarningTimeColumn')"
align="center"
prop="totalWarningTime"
min-width="140px"
/>
<el-table-column
:label="t('DataCollection.RunReport.tableUtilizationRateColumn')"
align="center"
prop="utilizationRate"
min-width="120px"
/>
<el-table-column
:label="t('DataCollection.RunReport.tableStartTimeColumn')"
align="center"
prop="startTime"
min-width="120px"
/>
<el-table-column
:label="t('DataCollection.RunReport.tableEndTimeColumn')"
align="center"
prop="endTime"
min-width="120px"
/>
</el-table> </el-table>
<Pagination <Pagination
:total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize" :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
@ -68,6 +122,8 @@ import { DeviceOperationRecordApi, type DeviceOperationRecordVO, type DeviceOper
defineOptions({ name: 'IotRunReport' }) defineOptions({ name: 'IotRunReport' })
const { t } = useI18n()
const loading = ref(false) const loading = ref(false)
const list = ref<DeviceOperationRecordVO[]>([]) const list = ref<DeviceOperationRecordVO[]>([])
const total = ref(0) const total = ref(0)
@ -119,7 +175,7 @@ const handleExport = async () => {
queryParams.endTime = range[1] queryParams.endTime = range[1]
const params = queryParams as unknown as DeviceOperationRecordPageParams const params = queryParams as unknown as DeviceOperationRecordPageParams
const data = await DeviceOperationRecordApi.exportDeviceOperationReport(params) const data = await DeviceOperationRecordApi.exportDeviceOperationReport(params)
download.excel(data, '设备运行报表.xls') download.excel(data, t('DataCollection.RunReport.exportFilename'))
} catch { } catch {
} finally { } finally {
exportLoading.value = false exportLoading.value = false
@ -130,3 +186,9 @@ onMounted(() => {
getList() getList()
}) })
</script> </script>
<style scoped lang="scss">
.run-report-form :deep(.el-form-item__label) {
min-width: 120px;
}
</style>

Loading…
Cancel
Save