<template>
|
<div>
|
<div class="page-header">
|
<div>
|
<h2>协议模板管理</h2>
|
<p class="text-muted">维护字段与 DB 地址映射</p>
|
</div>
|
<el-button type="primary" @click="openNewTemplate">新建模板</el-button>
|
</div>
|
|
<el-table :data="templates" border>
|
<el-table-column prop="id" label="模板ID" width="240" />
|
<el-table-column prop="name" label="名称" min-width="240" />
|
<el-table-column prop="version" label="版本" width="140" />
|
<el-table-column label="字段数" width="100">
|
<template #default="{ row }">{{ row.fields.length }}</template>
|
</el-table-column>
|
<el-table-column label="操作" width="220" fixed="right">
|
<template #default="{ row }">
|
<el-button size="small" type="primary" @click="editTemplate(row)">编辑</el-button>
|
<el-button size="small" type="danger" @click="removeTemplate(row.id)">删除</el-button>
|
</template>
|
</el-table-column>
|
</el-table>
|
|
<el-dialog v-model="dialogVisible" title="协议模板" width="96vw" top="2vh" class="protocol-dialog">
|
<el-form :model="editing" label-width="100px">
|
<el-form-item label="模板ID">
|
<el-input v-model="editing.id" :disabled="editingMode === 'edit'" />
|
</el-form-item>
|
<el-form-item label="名称">
|
<el-input v-model="editing.name" />
|
</el-form-item>
|
<el-form-item label="版本">
|
<el-input v-model="editing.version" />
|
</el-form-item>
|
</el-form>
|
|
<el-divider>字段映射</el-divider>
|
<div class="mapping-table-wrap">
|
<el-table
|
:data="editing.fields"
|
border
|
size="small"
|
table-layout="fixed"
|
height="56vh"
|
class="mapping-table"
|
empty-text="暂无字段,请点击下方“新增字段”"
|
>
|
<el-table-column label="字段Key" min-width="240" show-overflow-tooltip>
|
<template #default="{ row }">
|
<el-input v-model="row.fieldKey" style="width: 100%" />
|
</template>
|
</el-table-column>
|
<el-table-column label="DB号" min-width="110">
|
<template #default="{ row }">
|
<el-input-number v-model="row.dbNumber" :min="1" style="width: 100%" />
|
</template>
|
</el-table-column>
|
<el-table-column label="偏移" min-width="110">
|
<template #default="{ row }">
|
<el-input-number v-model="row.offset" :min="0" style="width: 100%" />
|
</template>
|
</el-table-column>
|
<el-table-column label="Bit" min-width="100">
|
<template #default="{ row }">
|
<el-input-number v-model="row.bit" :min="0" :max="7" style="width: 100%" />
|
</template>
|
</el-table-column>
|
<el-table-column label="类型" min-width="150">
|
<template #default="{ row }">
|
<el-select v-model="row.dataType" style="width: 100%">
|
<el-option label="Byte" value="Byte" />
|
<el-option label="Int" value="Int" />
|
<el-option label="DInt" value="DInt" />
|
<el-option label="String" value="String" />
|
<el-option label="Bool" value="Bool" />
|
</el-select>
|
</template>
|
</el-table-column>
|
<el-table-column label="长度" min-width="120">
|
<template #default="{ row }">
|
<el-input-number v-model="row.length" :min="0" style="width: 100%" />
|
</template>
|
</el-table-column>
|
<el-table-column label="方向" min-width="180">
|
<template #default="{ row }">
|
<el-select v-model="row.direction" style="width: 100%">
|
<el-option label="WcsToPlc" value="WcsToPlc" />
|
<el-option label="PlcToWcs" value="PlcToWcs" />
|
<el-option label="Bidirectional" value="Bidirectional" />
|
</el-select>
|
</template>
|
</el-table-column>
|
<el-table-column label="操作" width="90" fixed="right">
|
<template #default="{ $index }">
|
<el-button size="small" type="danger" @click="editing.fields.splice($index, 1)">删</el-button>
|
</template>
|
</el-table-column>
|
</el-table>
|
</div>
|
|
<div class="mt-2">
|
<el-button @click="addField">新增字段</el-button>
|
</div>
|
|
<template #footer>
|
<el-button @click="dialogVisible = false">取消</el-button>
|
<el-button type="primary" @click="saveTemplate">保存</el-button>
|
</template>
|
</el-dialog>
|
</div>
|
</template>
|
|
<script setup lang="ts">
|
import { onMounted, ref } from 'vue'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
import * as api from '../api'
|
import type { ProtocolTemplate } from '../types'
|
|
const templates = ref<ProtocolTemplate[]>([])
|
const dialogVisible = ref(false)
|
const editingMode = ref<'new' | 'edit'>('new')
|
const editing = ref<ProtocolTemplate>({
|
id: '',
|
name: '',
|
version: '1.0',
|
fields: []
|
})
|
|
onMounted(loadTemplates)
|
|
async function loadTemplates() {
|
templates.value = await api.getProtocolTemplates()
|
}
|
|
function openNewTemplate() {
|
editingMode.value = 'new'
|
editing.value = { id: '', name: '', version: '1.0', fields: [] }
|
dialogVisible.value = true
|
}
|
|
function editTemplate(template: ProtocolTemplate) {
|
editingMode.value = 'edit'
|
editing.value = JSON.parse(JSON.stringify(template))
|
dialogVisible.value = true
|
}
|
|
function addField() {
|
editing.value.fields.push({
|
fieldKey: '',
|
dbNumber: 1,
|
offset: 0,
|
bit: 0,
|
dataType: 'Byte',
|
length: 0,
|
direction: 'Bidirectional'
|
})
|
}
|
|
async function saveTemplate() {
|
if (!editing.value.id || !editing.value.name) {
|
ElMessage.error('模板ID和名称不能为空')
|
return
|
}
|
|
try {
|
if (editingMode.value === 'new') {
|
await api.createProtocolTemplate(editing.value)
|
} else {
|
await api.updateProtocolTemplate(editing.value.id, editing.value)
|
}
|
ElMessage.success('模板保存成功')
|
dialogVisible.value = false
|
await loadTemplates()
|
} catch {
|
ElMessage.error('模板保存失败')
|
}
|
}
|
|
async function removeTemplate(id: string) {
|
await ElMessageBox.confirm(`确认删除模板 ${id} ?`, '提示')
|
await api.deleteProtocolTemplate(id)
|
ElMessage.success('删除成功')
|
await loadTemplates()
|
}
|
</script>
|
|
<style scoped>
|
.page-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: flex-start;
|
margin-bottom: 16px;
|
}
|
|
.text-muted {
|
color: #909399;
|
margin: 4px 0 0 0;
|
}
|
|
.mt-2 {
|
margin-top: 12px;
|
}
|
|
:deep(.protocol-dialog .el-dialog) {
|
max-width: 1680px;
|
}
|
|
.mapping-table-wrap {
|
width: 100%;
|
overflow-x: auto;
|
}
|
|
:deep(.mapping-table .el-table__body-wrapper) {
|
overflow-x: auto !important;
|
}
|
|
:deep(.mapping-table .el-input-number),
|
:deep(.mapping-table .el-select),
|
:deep(.mapping-table .el-input) {
|
width: 100%;
|
}
|
</style>
|