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