<template>
|
<el-dialog
|
v-model="dialogVisible"
|
:title="isEdit ? '编辑设备配置' : '创建设备配置'"
|
width="70%"
|
:close-on-click-modal="false"
|
:before-close="handleClose"
|
>
|
<el-form
|
ref="deviceFormRef"
|
:model="deviceForm"
|
:rules="deviceRules"
|
label-width="120px"
|
class="device-form"
|
>
|
<el-row :gutter="20">
|
<el-col :span="12">
|
<!-- 基本信息 -->
|
<el-card class="form-section" shadow="never">
|
<template #header>
|
<span class="section-title">基本信息</span>
|
</template>
|
|
<el-form-item label="设备名称" prop="deviceName">
|
<el-input
|
v-model="deviceForm.deviceName"
|
placeholder="请输入设备名称"
|
maxlength="50"
|
show-word-limit
|
/>
|
</el-form-item>
|
|
<el-form-item label="设备编码" prop="deviceCode">
|
<el-input
|
v-model="deviceForm.deviceCode"
|
placeholder="请输入设备编码"
|
maxlength="50"
|
:disabled="isEdit"
|
/>
|
</el-form-item>
|
|
<el-form-item label="设备类型" prop="deviceType">
|
<el-select v-model="deviceForm.deviceType" placeholder="选择设备类型" style="width: 100%;" :loading="deviceTypesLoading">
|
<el-option
|
v-for="type in deviceTypes"
|
:key="type"
|
:label="type"
|
:value="type"
|
/>
|
</el-select>
|
</el-form-item>
|
|
<el-form-item label="PLC类型" prop="plcType">
|
<el-select v-model="deviceForm.plcType" placeholder="选择PLC类型" style="width: 100%;" clearable>
|
<el-option label="西门子 S7-1200" value="S1200" />
|
<el-option label="西门子 S7-1500" value="S1500" />
|
<el-option label="Modbus 控制器" value="MODBUS" />
|
</el-select>
|
</el-form-item>
|
|
<el-form-item label="PLC IP" prop="plcIp">
|
<el-input
|
v-model="deviceForm.plcIp"
|
placeholder="请输入PLC IP地址"
|
/>
|
</el-form-item>
|
|
<el-form-item label="端口号" prop="plcPort">
|
<el-input-number
|
v-model="deviceForm.plcPort"
|
:min="1"
|
:max="65535"
|
placeholder="端口号"
|
style="width: 100%;"
|
/>
|
</el-form-item>
|
|
<el-form-item label="主控设备">
|
<el-switch v-model="deviceForm.isPrimary" />
|
<span class="form-tip">主控设备不可禁用或删除</span>
|
</el-form-item>
|
</el-card>
|
</el-col>
|
|
<el-col :span="12">
|
<!-- 连接配置 -->
|
<el-card class="form-section" shadow="never">
|
<template #header>
|
<span class="section-title">连接配置</span>
|
</template>
|
|
<el-form-item label="模块名称" prop="moduleName">
|
<el-input
|
v-model="deviceForm.moduleName"
|
placeholder="请输入模块名称"
|
maxlength="100"
|
/>
|
</el-form-item>
|
|
<el-form-item label="模块编号" prop="moduleCode">
|
<el-input
|
v-model="deviceForm.moduleCode"
|
placeholder="请输入模块编号"
|
maxlength="50"
|
/>
|
</el-form-item>
|
|
<el-form-item label="通讯协议" prop="protocolType">
|
<el-select
|
v-model="deviceForm.protocolType"
|
placeholder="选择通讯协议"
|
style="width: 100%;"
|
@change="handleProtocolTypeChange"
|
>
|
<el-option label="S7 Communication" value="S7 Communication" />
|
<el-option label="Modbus TCP" value="Modbus TCP" />
|
<el-option label="OPC UA" value="OPC UA" />
|
<el-option label="EtherNet/IP" value="EtherNet/IP" />
|
<el-option label="Profinet" value="Profinet" />
|
<el-option label="其他" value="其他" />
|
</el-select>
|
<span class="form-tip">S7系列PLC通常使用S7 Communication协议</span>
|
</el-form-item>
|
|
<el-form-item label="超时时间(秒)" prop="timeout">
|
<el-input-number
|
v-model="deviceForm.timeout"
|
:min="1"
|
:max="300"
|
:step="1"
|
style="width: 100%;"
|
/>
|
</el-form-item>
|
|
<el-form-item label="重试次数" prop="retryCount">
|
<el-input-number
|
v-model="deviceForm.retryCount"
|
:min="0"
|
:max="10"
|
:step="1"
|
style="width: 100%;"
|
/>
|
</el-form-item>
|
|
<el-form-item label="心跳间隔(秒)" prop="heartbeatInterval">
|
<el-input-number
|
v-model="deviceForm.heartbeatInterval"
|
:min="5"
|
:max="3600"
|
:step="5"
|
style="width: 100%;"
|
/>
|
</el-form-item>
|
</el-card>
|
|
<!-- PLC 地址配置 -->
|
<el-card class="form-section" shadow="never" style="margin-top: 20px;">
|
<template #header>
|
<span class="section-title">PLC 地址配置</span>
|
</template>
|
|
<el-form-item label="DB块" prop="dbArea">
|
<el-input
|
v-model="deviceForm.dbArea"
|
placeholder="如 DB1、DB38"
|
maxlength="20"
|
/>
|
</el-form-item>
|
|
<el-form-item label="起始索引" prop="beginIndex">
|
<el-input-number
|
v-model="deviceForm.beginIndex"
|
:min="0"
|
:max="65535"
|
:step="1"
|
style="width: 100%;"
|
/>
|
</el-form-item>
|
|
<el-form-item label="自动间隔(ms)" prop="autoModeInterval">
|
<el-input-number
|
v-model="deviceForm.autoModeInterval"
|
:min="100"
|
:max="600000"
|
:step="100"
|
style="width: 100%;"
|
/>
|
</el-form-item>
|
</el-card>
|
</el-col>
|
</el-row>
|
|
<!-- 配置参数 -->
|
<el-card class="form-section" shadow="never" style="margin-top: 20px;">
|
<template #header>
|
<div class="card-header">
|
<span class="section-title">配置参数</span>
|
<el-button type="primary" size="small" @click="addConfigParam">
|
添加参数
|
</el-button>
|
</div>
|
</template>
|
|
<div v-if="deviceForm.configParams.length === 0" class="empty-params">
|
<el-empty description="暂无配置参数" :image-size="60" />
|
</div>
|
|
<div v-else class="config-params">
|
<div
|
v-for="(param, index) in deviceForm.configParams"
|
:key="index"
|
class="config-param-item"
|
>
|
<el-row :gutter="12" style="width: 100%;">
|
<el-col :span="6">
|
<el-input
|
v-model="param.paramKey"
|
placeholder="参数键"
|
size="small"
|
/>
|
</el-col>
|
<el-col :span="6">
|
<el-input
|
v-model="param.paramValue"
|
placeholder="参数值"
|
size="small"
|
/>
|
</el-col>
|
<el-col :span="8">
|
<el-input
|
v-model="param.description"
|
placeholder="描述"
|
size="small"
|
/>
|
</el-col>
|
<el-col :span="4">
|
<el-button
|
type="danger"
|
size="small"
|
@click="removeConfigParam(index)"
|
>
|
删除
|
</el-button>
|
</el-col>
|
</el-row>
|
</div>
|
</div>
|
</el-card>
|
|
<!-- 设备逻辑配置 -->
|
<el-card class="form-section" shadow="never" style="margin-top: 20px;" v-if="deviceForm.deviceType">
|
<template #header>
|
<span class="section-title">设备逻辑配置</span>
|
<span class="form-tip">根据设备类型配置特定的业务逻辑参数</span>
|
</template>
|
|
<!-- 使用动态组件加载对应设备类型的配置组件 -->
|
<component
|
:is="deviceConfigComponent"
|
v-if="deviceConfigComponent"
|
v-model="deviceLogicParams"
|
/>
|
<div v-else class="no-config-tip">
|
<el-alert
|
:title="`设备类型「${deviceForm.deviceType}」暂无配置组件`"
|
type="info"
|
:closable="false"
|
show-icon
|
/>
|
</div>
|
</el-card>
|
|
<!-- 描述信息 -->
|
<el-card class="form-section" shadow="never" style="margin-top: 20px;">
|
<template #header>
|
<span class="section-title">描述信息</span>
|
</template>
|
|
<el-form-item label="设备描述" prop="description">
|
<el-input
|
v-model="deviceForm.description"
|
type="textarea"
|
:rows="3"
|
placeholder="请输入设备描述"
|
maxlength="500"
|
show-word-limit
|
/>
|
</el-form-item>
|
</el-card>
|
</el-form>
|
|
<!-- 连接测试 -->
|
<el-card class="connection-test" shadow="never" style="margin-top: 20px;">
|
<template #header>
|
<span class="section-title">连接测试</span>
|
</template>
|
|
<div class="test-content">
|
<el-button type="primary" @click="testConnection" :loading="testing">
|
{{ testing ? '测试中...' : '测试连接' }}
|
</el-button>
|
|
<div v-if="testResult" class="test-result">
|
<el-alert
|
:title="testResult.message"
|
:type="testResult.success ? 'success' : 'error'"
|
:closable="false"
|
show-icon
|
/>
|
</div>
|
</div>
|
</el-card>
|
|
<template #footer>
|
<el-button @click="handleClose">取消</el-button>
|
<el-button @click="resetForm" v-if="!isEdit">重置</el-button>
|
<el-button type="primary" @click="saveDevice" :loading="saving">
|
{{ saving ? '保存中...' : (isEdit ? '更新' : '创建') }}
|
</el-button>
|
</template>
|
</el-dialog>
|
</template>
|
|
<script setup>
|
import { ref, reactive, watch, computed } from 'vue'
|
import { ElMessage } from 'element-plus'
|
import { deviceConfigApi } from '@/api/device/deviceManagement'
|
import { getDeviceConfigComponent } from './components/DeviceLogicConfig'
|
|
// Props定义
|
const props = defineProps({
|
modelValue: {
|
type: Boolean,
|
default: false
|
},
|
deviceData: {
|
type: Object,
|
default: null
|
}
|
})
|
|
// Emits定义
|
const emit = defineEmits(['update:modelValue', 'success', 'close'])
|
|
// 响应式数据
|
const deviceFormRef = ref(null)
|
const dialogVisible = ref(false)
|
const saving = ref(false)
|
const testing = ref(false)
|
const testResult = ref(null)
|
|
// 设备类型列表
|
const deviceTypes = ref([])
|
const deviceTypesLoading = ref(false)
|
|
// 设备逻辑参数(根据设备类型动态显示)
|
const deviceLogicParams = reactive({})
|
|
const S7_PLC_TYPES = ['S1200', 'S1500']
|
const MODBUS_PLC_TYPES = ['MODBUS']
|
|
// 计算属性:根据设备类型获取对应的配置组件
|
const deviceConfigComponent = computed(() => {
|
if (!deviceForm.deviceType) {
|
return null
|
}
|
return getDeviceConfigComponent(deviceForm.deviceType)
|
})
|
|
// 设备表单数据
|
const getDefaultForm = () => ({
|
deviceName: '',
|
deviceCode: '',
|
deviceType: '',
|
plcType: '',
|
plcIp: '',
|
plcPort: 502,
|
moduleName: '',
|
moduleCode: '',
|
protocolType: '',
|
timeout: 30,
|
retryCount: 3,
|
heartbeatInterval: 30,
|
dbArea: 'DB1',
|
beginIndex: 0,
|
autoModeInterval: 5000,
|
configParams: [],
|
description: '',
|
isPrimary: false,
|
enabled: true,
|
extraParams: null
|
})
|
|
const deviceForm = reactive(getDefaultForm())
|
|
// 计算属性
|
const isEdit = computed(() => !!props.deviceData)
|
|
// 表单验证规则
|
const deviceRules = {
|
deviceName: [
|
{ required: true, message: '请输入设备名称', trigger: 'blur' },
|
{ min: 1, max: 50, message: '设备名称长度在 1 到 50 个字符', trigger: 'blur' }
|
],
|
deviceCode: [
|
{ required: true, message: '请输入设备编码', trigger: 'blur' },
|
{ pattern: /^[A-Z0-9_]+$/, message: '设备编码只能包含大写字母、数字和下划线', trigger: 'blur' }
|
],
|
deviceType: [
|
{ required: true, message: '请选择设备类型', trigger: 'change' }
|
],
|
plcType: [
|
{ required: true, message: '请选择PLC类型', trigger: 'change' }
|
],
|
plcIp: [
|
{ required: true, message: '请输入PLC IP地址', trigger: 'blur' },
|
{ pattern: /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/, message: '请输入有效的IP地址', trigger: 'blur' }
|
],
|
port: [
|
{ required: true, message: '请输入端口号', trigger: 'blur' },
|
{ type: 'number', min: 1, max: 65535, message: '端口号在 1 到 65535 之间', trigger: 'blur' }
|
],
|
moduleName: [
|
{ required: true, message: '请输入模块名称', trigger: 'blur' }
|
],
|
protocolType: [
|
{ required: true, message: '请选择通讯协议', trigger: 'change' }
|
],
|
timeout: [
|
{ required: true, message: '请输入超时时间', trigger: 'blur' },
|
{ type: 'number', min: 1, max: 300, message: '超时时间在 1 到 300 秒之间', trigger: 'blur' }
|
],
|
retryCount: [
|
{ required: true, message: '请输入重试次数', trigger: 'blur' },
|
{ type: 'number', min: 0, max: 10, message: '重试次数在 0 到 10 次之间', trigger: 'blur' }
|
],
|
heartbeatInterval: [
|
{ required: true, message: '请输入心跳间隔', trigger: 'blur' },
|
{ type: 'number', min: 5, max: 3600, message: '心跳间隔在 5 到 3600 秒之间', trigger: 'blur' }
|
],
|
dbArea: [
|
{ required: true, message: '请输入DB块', trigger: 'blur' }
|
],
|
beginIndex: [
|
{ type: 'number', min: 0, max: 65535, message: '起始索引在 0 到 65535 之间', trigger: 'blur' }
|
],
|
autoModeInterval: [
|
{ type: 'number', min: 100, max: 600000, message: '自动间隔在 100 到 600000 之间', trigger: 'blur' }
|
]
|
}
|
|
// 监听对话框显示状态
|
watch(() => props.modelValue, (newVal) => {
|
dialogVisible.value = newVal
|
if (newVal) {
|
// 加载设备类型列表
|
loadDeviceTypes()
|
if (isEdit.value && props.deviceData) {
|
loadDeviceData(props.deviceData)
|
} else {
|
// 创建模式,重置表单
|
resetForm()
|
}
|
// 清除测试结果
|
testResult.value = null
|
}
|
})
|
|
// 监听对话框关闭
|
watch(dialogVisible, (newVal) => {
|
emit('update:modelValue', newVal)
|
})
|
|
// 监听PLC类型变化,自动设置通讯协议
|
watch(() => deviceForm.plcType, (newPlcType) => {
|
if (!newPlcType) {
|
return
|
}
|
|
if (S7_PLC_TYPES.includes(newPlcType)) {
|
if (!deviceForm.protocolType || deviceForm.protocolType === '其他' || deviceForm.protocolType === 'Modbus TCP') {
|
deviceForm.protocolType = 'S7 Communication'
|
}
|
return
|
}
|
|
if (MODBUS_PLC_TYPES.includes(newPlcType)) {
|
if (!deviceForm.protocolType || deviceForm.protocolType === '其他' || deviceForm.protocolType === 'S7 Communication') {
|
deviceForm.protocolType = 'Modbus TCP'
|
}
|
}
|
})
|
|
// 处理通讯协议变化
|
const handleProtocolTypeChange = (value) => {
|
if (!deviceForm.plcType || !value) {
|
return
|
}
|
|
if (value !== 'S7 Communication' && S7_PLC_TYPES.includes(deviceForm.plcType)) {
|
ElMessage.warning('S7系列PLC通常使用S7 Communication协议,请确认协议选择是否正确')
|
return
|
}
|
|
if (value !== 'Modbus TCP' && MODBUS_PLC_TYPES.includes(deviceForm.plcType)) {
|
ElMessage.warning('Modbus 类型PLC通常使用 Modbus TCP 协议,请确认协议选择是否正确')
|
}
|
}
|
|
// 方法定义
|
// 加载设备类型列表
|
const loadDeviceTypes = async () => {
|
if (deviceTypes.value.length > 0) {
|
// 如果已经加载过,不再重复加载
|
return
|
}
|
// 所有支持的设备类型(确保包含所有有配置组件的类型)
|
const supportedTypes = ['大车设备', '大理片笼', '卧转立扫码', '卧转立']
|
|
try {
|
deviceTypesLoading.value = true
|
const res = await deviceConfigApi.getDeviceTypes()
|
if (res?.data && Array.isArray(res.data)) {
|
// 合并数据库中的类型和支持的类型,去重并排序
|
const dbTypes = res.data
|
const allTypes = [...new Set([...supportedTypes, ...dbTypes])].sort()
|
deviceTypes.value = allTypes
|
} else {
|
// 如果API返回失败,使用默认类型
|
deviceTypes.value = supportedTypes
|
console.warn('获取设备类型列表失败,使用默认类型')
|
}
|
} catch (error) {
|
console.error('加载设备类型列表失败:', error)
|
// 失败时使用默认类型
|
deviceTypes.value = supportedTypes
|
ElMessage.warning('加载设备类型列表失败,使用默认类型')
|
} finally {
|
deviceTypesLoading.value = false
|
}
|
}
|
|
const parseJsonSafe = (str, defaultValue = null) => {
|
if (!str) return defaultValue
|
try {
|
return JSON.parse(str)
|
} catch (error) {
|
console.warn('JSON解析失败:', error)
|
return defaultValue
|
}
|
}
|
|
const loadDeviceData = (data) => {
|
resetForm()
|
Object.assign(deviceForm, getDefaultForm(), {
|
...data,
|
plcPort: data?.plcPort ?? 502
|
})
|
|
deviceForm.configParams = parseJsonSafe(data?.configJson, []) || []
|
deviceForm.extraParams = data?.extraParams || null
|
|
const extraObj = parseJsonSafe(deviceForm.extraParams, {}) || {}
|
const connection = extraObj.connectionConfig || {}
|
deviceForm.moduleCode = connection.moduleCode || ''
|
deviceForm.protocolType = connection.protocolType || ''
|
deviceForm.timeout = connection.timeout ?? 30
|
deviceForm.retryCount = connection.retryCount ?? 3
|
deviceForm.heartbeatInterval = connection.heartbeatInterval ?? 30
|
|
const plcConfig = extraObj.plcConfig || {}
|
deviceForm.dbArea = plcConfig.dbArea || 'DB1'
|
deviceForm.beginIndex = plcConfig.beginIndex ?? 0
|
deviceForm.autoModeInterval = plcConfig.autoModeInterval ?? 5000
|
|
// 加载配置参数(从 configJson)
|
// 兼容两种格式:
|
// 1. 数组格式:[{ paramKey, paramValue, description }]
|
// 2. 对象格式(旧格式):{ fieldName: offset } - 自动转换为数组格式
|
loadConfigParams(data?.configJson)
|
|
// 加载设备逻辑参数
|
const deviceLogic = extraObj.deviceLogic || {}
|
loadDeviceLogicParams(deviceLogic, data?.deviceType)
|
}
|
|
// 加载配置参数(兼容旧的对象格式)
|
const loadConfigParams = (configJson) => {
|
if (!configJson) {
|
deviceForm.configParams = []
|
return
|
}
|
|
try {
|
const parsed = typeof configJson === 'string' ? JSON.parse(configJson) : configJson
|
|
// 如果是数组格式,直接使用
|
if (Array.isArray(parsed)) {
|
deviceForm.configParams = parsed
|
}
|
// 如果是对象格式(字段名 → 偏移量),转换为数组格式
|
else if (typeof parsed === 'object' && parsed !== null) {
|
// 字段名到中文描述的映射
|
const fieldDescriptionMap = {
|
'plcRequest': 'PLC请求字',
|
'inPosition': '进片位置',
|
'plcGlassId1': '玻璃id1',
|
'plcGlassId2': '玻璃id2',
|
'plcGlassId3': '玻璃id3',
|
'plcGlassId4': '玻璃id4',
|
'plcGlassId5': '玻璃id5',
|
'plcGlassId6': '玻璃id6',
|
'plcGlassCount': '玻璃数量',
|
'onlineState': '联机状态',
|
'plcReport': 'PLC汇报',
|
'state1': '状态1',
|
'state2': '状态2',
|
'state3': '状态3',
|
'state4': '状态4',
|
'state5': '状态5',
|
'state6': '状态6',
|
'mesSend': 'MES发送',
|
'mesConfirm': 'MES确认',
|
'trainInfo': '列车信息',
|
'start1': '起始1',
|
'start2': '起始2',
|
'start3': '起始3',
|
'start4': '起始4',
|
'start5': '起始5',
|
'start6': '起始6',
|
'target1': '目标1',
|
'target2': '目标2',
|
'target3': '目标3',
|
'target4': '目标4',
|
'target5': '目标5',
|
'target6': '目标6',
|
'mesWidth1': 'MES宽度1',
|
'mesWidth2': 'MES宽度2',
|
'mesWidth3': 'MES宽度3',
|
'mesWidth4': 'MES宽度4',
|
'mesWidth5': 'MES宽度5',
|
'mesWidth6': 'MES宽度6',
|
'mesHeight1': 'MES高度1',
|
'mesHeight2': 'MES高度2',
|
'mesHeight3': 'MES高度3',
|
'mesHeight4': 'MES高度4',
|
'mesHeight5': 'MES高度5',
|
'mesHeight6': 'MES高度6',
|
'mesThickness1': 'MES厚度1',
|
'mesThickness2': 'MES厚度2',
|
'mesThickness3': 'MES厚度3',
|
'mesThickness4': 'MES厚度4',
|
'mesThickness5': 'MES厚度5',
|
'mesThickness6': 'MES厚度6',
|
'edgeDistance1': '边缘距离1',
|
'edgeDistance2': '边缘距离2',
|
'edgeDistance3': '边缘距离3',
|
'edgeDistance4': '边缘距离4',
|
'edgeDistance5': '边缘距离5',
|
'edgeDistance6': '边缘距离6',
|
'targetEdgeDistance1': '目标边缘距离1',
|
'targetEdgeDistance2': '目标边缘距离2',
|
'targetEdgeDistance3': '目标边缘距离3',
|
'targetEdgeDistance4': '目标边缘距离4',
|
'targetEdgeDistance5': '目标边缘距离5',
|
'targetEdgeDistance6': '目标边缘距离6',
|
'alarmInfo': '报警信息'
|
}
|
|
// 转换为数组格式
|
deviceForm.configParams = Object.keys(parsed).map(fieldName => ({
|
paramKey: fieldName,
|
paramValue: String(parsed[fieldName]),
|
description: fieldDescriptionMap[fieldName] || fieldName
|
}))
|
} else {
|
deviceForm.configParams = []
|
}
|
} catch (error) {
|
console.warn('解析configJson失败', error)
|
deviceForm.configParams = []
|
}
|
}
|
|
// 加载设备逻辑参数
|
const loadDeviceLogicParams = (deviceLogic, deviceType) => {
|
// 清空现有参数
|
Object.keys(deviceLogicParams).forEach(key => {
|
delete deviceLogicParams[key]
|
})
|
|
// 根据设备类型加载对应的参数
|
if (deviceLogic && Object.keys(deviceLogic).length > 0) {
|
Object.assign(deviceLogicParams, deviceLogic)
|
}
|
}
|
|
|
const resetForm = () => {
|
Object.assign(deviceForm, getDefaultForm())
|
deviceFormRef.value?.clearValidate()
|
|
// 重置设备逻辑参数
|
Object.keys(deviceLogicParams).forEach(key => {
|
delete deviceLogicParams[key]
|
})
|
}
|
|
const addConfigParam = () => {
|
deviceForm.configParams.push({
|
paramKey: '',
|
paramValue: '',
|
description: ''
|
})
|
}
|
|
const removeConfigParam = (index) => {
|
deviceForm.configParams.splice(index, 1)
|
}
|
|
const testConnection = async () => {
|
try {
|
testing.value = true
|
testResult.value = null
|
|
const testData = {
|
plcIp: deviceForm.plcIp,
|
plcPort: deviceForm.plcPort,
|
timeout: deviceForm.timeout
|
}
|
|
const response = await deviceConfigApi.testConnection(testData)
|
if (response.success) {
|
testResult.value = {
|
success: true,
|
message: response.data || '连接测试成功!设备可以正常通讯。'
|
}
|
} else {
|
testResult.value = {
|
success: false,
|
message: response.message || '连接测试失败,请检查网络连接和设备配置。'
|
}
|
}
|
} catch (error) {
|
console.error('连接测试失败:', error)
|
testResult.value = {
|
success: false,
|
message: '连接测试失败,请检查网络连接和设备配置。'
|
}
|
} finally {
|
testing.value = false
|
}
|
}
|
|
const saveDevice = async () => {
|
try {
|
// 表单验证
|
await deviceFormRef.value.validate()
|
|
saving.value = true
|
|
// 构建保存数据
|
const connectionConfig = {
|
moduleCode: deviceForm.moduleCode,
|
protocolType: deviceForm.protocolType,
|
timeout: deviceForm.timeout,
|
retryCount: deviceForm.retryCount,
|
heartbeatInterval: deviceForm.heartbeatInterval
|
}
|
|
const extraObj = parseJsonSafe(deviceForm.extraParams, {}) || {}
|
extraObj.connectionConfig = connectionConfig
|
extraObj.plcConfig = {
|
dbArea: deviceForm.dbArea,
|
beginIndex: deviceForm.beginIndex,
|
autoModeInterval: deviceForm.autoModeInterval,
|
plcType: deviceForm.plcType
|
}
|
|
// 保存设备逻辑参数(直接使用deviceLogicParams,由各个配置组件管理)
|
if (deviceLogicParams && Object.keys(deviceLogicParams).length > 0) {
|
extraObj.deviceLogic = { ...deviceLogicParams }
|
}
|
|
// 构建 configJson:将 configParams 数组转换为 JSON 字符串
|
// configParams 结构: [{ paramKey: '', paramValue: '', description: '' }]
|
let configJsonValue = null
|
if (deviceForm.configParams && deviceForm.configParams.length > 0) {
|
// 过滤掉空参数
|
const validParams = deviceForm.configParams.filter(
|
param => param.paramKey && param.paramKey.trim() !== ''
|
)
|
if (validParams.length > 0) {
|
configJsonValue = JSON.stringify(validParams)
|
}
|
}
|
|
const saveData = {
|
deviceName: deviceForm.deviceName,
|
deviceCode: deviceForm.deviceCode,
|
deviceType: deviceForm.deviceType,
|
plcType: deviceForm.plcType,
|
plcIp: deviceForm.plcIp,
|
plcPort: deviceForm.plcPort,
|
moduleName: deviceForm.moduleName,
|
isPrimary: deviceForm.isPrimary,
|
enabled: deviceForm.enabled,
|
description: deviceForm.description,
|
configJson: configJsonValue, // 保存配置参数JSON
|
extraParams: JSON.stringify(extraObj)
|
}
|
|
if (isEdit.value) {
|
// 更新设备
|
await deviceConfigApi.update(props.deviceData.id, saveData)
|
ElMessage.success('设备配置更新成功')
|
} else {
|
// 创建设备
|
await deviceConfigApi.create(saveData)
|
ElMessage.success('设备配置创建成功')
|
}
|
|
emit('success')
|
handleClose()
|
} catch (error) {
|
console.error('保存设备配置失败:', error)
|
// 如果是表单验证错误,显示更详细的错误信息
|
if (error && typeof error === 'object' && !error.response) {
|
const errorFields = Object.keys(error)
|
if (errorFields.length > 0) {
|
const firstError = error[errorFields[0]]
|
const errorMessage = Array.isArray(firstError)
|
? firstError[0]?.message || firstError[0]
|
: firstError?.message || firstError
|
ElMessage.error(`表单验证失败: ${errorMessage}`)
|
return
|
}
|
}
|
ElMessage.error(isEdit.value ? '更新设备配置失败' : '创建设备配置失败')
|
} finally {
|
saving.value = false
|
}
|
}
|
|
const handleClose = () => {
|
dialogVisible.value = false
|
testResult.value = null
|
emit('close')
|
}
|
</script>
|
|
<style scoped>
|
.device-form {
|
max-height: 60vh;
|
overflow-y: auto;
|
padding-right: 10px;
|
}
|
|
.form-section {
|
margin-bottom: 20px;
|
}
|
|
.section-title {
|
font-weight: bold;
|
color: #303133;
|
}
|
|
.card-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
}
|
|
.form-tip {
|
margin-left: 10px;
|
font-size: 12px;
|
color: #909399;
|
}
|
|
.empty-params {
|
padding: 20px;
|
}
|
|
.config-params {
|
max-height: 200px;
|
overflow-y: auto;
|
}
|
|
.config-param-item {
|
margin-bottom: 12px;
|
padding: 12px;
|
border: 1px solid #ebeef5;
|
border-radius: 6px;
|
background-color: #fafafa;
|
}
|
|
.connection-test {
|
margin-top: 20px;
|
}
|
|
.test-content {
|
display: flex;
|
align-items: center;
|
gap: 20px;
|
}
|
|
.test-result {
|
flex: 1;
|
}
|
|
:deep(.el-card__header) {
|
padding: 12px 20px;
|
background-color: #fafafa;
|
border-bottom: 1px solid #ebeef5;
|
}
|
|
:deep(.el-form-item__label) {
|
font-weight: 500;
|
}
|
|
:deep(.el-card__body) {
|
padding: 20px;
|
}
|
|
.position-mapping {
|
width: 100%;
|
}
|
|
.mapping-item {
|
display: flex;
|
align-items: center;
|
margin-bottom: 12px;
|
padding: 12px;
|
border: 1px solid #ebeef5;
|
border-radius: 6px;
|
background-color: #fafafa;
|
}
|
|
.no-config-tip {
|
padding: 20px;
|
}
|
</style>
|