| | |
| | | <template> |
| | | <div> |
| | | <div |
| | | v-loading.fullscreen.lock="startActionLoading" |
| | | element-loading-text="正在启动实例,请稍候..." |
| | | > |
| | | <div v-if="loading" class="loading-container"> |
| | | <el-icon class="loading-icon" :size="40"><Loading /></el-icon> |
| | | <p>加载中...</p> |
| | |
| | | <el-switch |
| | | v-if="row.dataType === 'Bool'" |
| | | v-model="fieldEditValues[getFieldEditKey(row)]" |
| | | @change="markFieldDirty(row)" |
| | | :disabled="!isFieldWritable(row)" |
| | | /> |
| | | <el-input-number |
| | | v-else-if="row.dataType === 'Int' || row.dataType === 'DInt' || row.dataType === 'Byte'" |
| | | v-model="fieldEditValues[getFieldEditKey(row)]" |
| | | @change="markFieldDirty(row)" |
| | | :disabled="!isFieldWritable(row)" |
| | | :controls="false" |
| | | style="width: 100%" |
| | |
| | | <el-input |
| | | v-else |
| | | v-model="fieldEditValues[getFieldEditKey(row)]" |
| | | @input="markFieldDirty(row)" |
| | | :disabled="!isFieldWritable(row)" |
| | | /> |
| | | </template> |
| | |
| | | <el-switch |
| | | v-if="row.dataType === 'Bool'" |
| | | v-model="fieldEditValues[getFieldEditKey(row)]" |
| | | @change="markFieldDirty(row)" |
| | | :disabled="!isFieldWritable(row)" |
| | | /> |
| | | <el-input-number |
| | | v-else-if="row.dataType === 'Int' || row.dataType === 'DInt' || row.dataType === 'Byte'" |
| | | v-model="fieldEditValues[getFieldEditKey(row)]" |
| | | @change="markFieldDirty(row)" |
| | | :disabled="!isFieldWritable(row)" |
| | | :controls="false" |
| | | style="width: 100%" |
| | |
| | | <el-input |
| | | v-else |
| | | v-model="fieldEditValues[getFieldEditKey(row)]" |
| | | @input="markFieldDirty(row)" |
| | | :disabled="!isFieldWritable(row)" |
| | | /> |
| | | </template> |
| | |
| | | const protocolTemplate = ref<ProtocolTemplate | null>(null) |
| | | const loading = ref(true) |
| | | const errorMsg = ref('') |
| | | const startActionLoading = ref(false) |
| | | |
| | | const memoryLoading = ref(false) |
| | | const autoRefreshDb = ref(true) |
| | |
| | | const expandedDbViews = ref<Record<number, boolean>>({}) |
| | | const fieldEditValues = ref<Record<string, string | number | boolean>>({}) |
| | | const writingFieldKeys = ref<Record<string, boolean>>({}) |
| | | const dirtyFieldKeys = ref<Record<string, boolean>>({}) |
| | | |
| | | let refreshTimer: number | null = null |
| | | |
| | |
| | | }) |
| | | |
| | | watch(parsedFields, () => { |
| | | const next: Record<string, string | number | boolean> = {} |
| | | // 轮询刷新时,保留用户正在编辑的字段,避免输入值被覆盖。 |
| | | const next: Record<string, string | number | boolean> = { ...fieldEditValues.value } |
| | | const validKeys = new Set<string>() |
| | | for (const field of parsedFields.value) { |
| | | const key = buildFieldEditKey(field.templateDbNumber, field.fieldKey) |
| | | validKeys.add(key) |
| | | if (dirtyFieldKeys.value[key] === true) { |
| | | continue |
| | | } |
| | | next[key] = coerceEditValueByType(field.dataType, field.value) |
| | | } |
| | | |
| | | for (const key of Object.keys(next)) { |
| | | if (!validKeys.has(key)) { |
| | | delete next[key] |
| | | } |
| | | } |
| | | for (const key of Object.keys(dirtyFieldKeys.value)) { |
| | | if (!validKeys.has(key)) { |
| | | delete dirtyFieldKeys.value[key] |
| | | } |
| | | } |
| | | |
| | | fieldEditValues.value = next |
| | | }, { immediate: true }) |
| | | |
| | |
| | | cancelButtonText: '取消', |
| | | type: 'info' |
| | | }) |
| | | |
| | | startActionLoading.value = true |
| | | await api.startInstance(id) |
| | | await loadInstance() |
| | | await loadMemoryData(true) |
| | |
| | | console.error('启动实例失败:', err) |
| | | ElMessage.error('启动失败,请查看控制台') |
| | | } |
| | | } finally { |
| | | startActionLoading.value = false |
| | | } |
| | | } |
| | | |
| | |
| | | return buildFieldEditKey(row.templateDbNumber, row.fieldKey) |
| | | } |
| | | |
| | | function markFieldDirty(row: { templateDbNumber: number; fieldKey: string }): void { |
| | | dirtyFieldKeys.value[getFieldEditKey(row)] = true |
| | | } |
| | | |
| | | function clearFieldDirty(row: { templateDbNumber: number; fieldKey: string }): void { |
| | | delete dirtyFieldKeys.value[getFieldEditKey(row)] |
| | | } |
| | | |
| | | function isFieldWritable(row: { resolvedDbNumber: number | null }): boolean { |
| | | return row.resolvedDbNumber !== null |
| | | } |
| | |
| | | const dbBlockCount = instanceConfig.value?.memoryConfig.dbBlockCount || dbBlockNumbers.length || 1 |
| | | const dbBlockSize = instanceConfig.value?.memoryConfig.dbBlockSize || nextBytes.length |
| | | dbBlocks.value = splitDbBlocks(nextBytes, dbBlockCount, dbBlockSize, dbBlockNumbers) |
| | | clearFieldDirty(row) |
| | | ElMessage.success(`字段 ${row.fieldKey} 写入成功`) |
| | | } finally { |
| | | writingFieldKeys.value[editKey] = false |