From e76f0739e647fe8a7e0e2618914e2faff554b1b7 Mon Sep 17 00:00:00 2001
From: huang <1532065656@qq.com>
Date: 星期一, 17 十一月 2025 17:33:23 +0800
Subject: [PATCH] 解决冲突
---
mes-web/src/views/device/DeviceEditDialog.vue | 650 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 650 insertions(+), 0 deletions(-)
diff --git a/mes-web/src/views/device/DeviceEditDialog.vue b/mes-web/src/views/device/DeviceEditDialog.vue
new file mode 100644
index 0000000..773dd73
--- /dev/null
+++ b/mes-web/src/views/device/DeviceEditDialog.vue
@@ -0,0 +1,650 @@
+<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%;">
+ <el-option label="涓婂ぇ杞�" value="涓婂ぇ杞�" />
+ <el-option label="澶х悊鐗�" value="澶х悊鐗�" />
+ <el-option label="鐜荤拑瀛樺偍" value="鐜荤拑瀛樺偍" />
+ </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="瑗块棬瀛� S7-400" value="S400" />
+ <el-option label="瑗块棬瀛� S7-300" value="S300" />
+ <el-option label="瑗块棬瀛� S7-200" value="S200" />
+ <el-option label="瑗块棬瀛� S7-200 SMART" value="S200_SMART" />
+ </el-select>
+ </el-form-item>
+
+ <el-form-item label="PLC IP" prop="plcIp">
+ <el-input
+ v-model="deviceForm.plcIp"
+ placeholder="璇疯緭鍏LC 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%;">
+ <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>
+ </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銆丏B38"
+ 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;">
+ <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'
+
+// 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 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: '璇疯緭鍏LC 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: '璇疯緭鍏ョ鍙e彿', 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: '璇疯緭鍏B鍧�', 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) {
+ if (isEdit.value && props.deviceData) {
+ loadDeviceData(props.deviceData)
+ } else {
+ // 鍒涘缓妯″紡锛岄噸缃〃鍗�
+ resetForm()
+ }
+ // 娓呴櫎娴嬭瘯缁撴灉
+ testResult.value = null
+ }
+})
+
+// 鐩戝惉瀵硅瘽妗嗗叧闂�
+watch(dialogVisible, (newVal) => {
+ emit('update:modelValue', newVal)
+})
+
+// 鏂规硶瀹氫箟
+const parseJsonSafe = (str, defaultValue = null) => {
+ if (!str) return defaultValue
+ try {
+ return JSON.parse(str)
+ } catch (error) {
+ console.warn('JSON瑙f瀽澶辫触:', 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
+}
+
+const resetForm = () => {
+ Object.assign(deviceForm, getDefaultForm())
+ deviceFormRef.value?.clearValidate()
+}
+
+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
+ }
+
+ 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: deviceForm.configParams.length > 0
+ ? JSON.stringify(deviceForm.configParams)
+ : null,
+ 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)
+ 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;
+}
+</style>
\ No newline at end of file
--
Gitblit v1.8.0