feat: 网关管理页面 + 设备表单网关下拉联动
- 新增 src/api/gateway.js(网关 CRUD + 测试连接 + options API + 枚举映射) - 新增 src/views/config/gateway/Index.vue(网关管理页面:列表/新增/编辑/测试连接/详情抽屉+下挂设备) - 修改 src/views/config/device/Index.vue(GatewayCode 文本输入改为 GatewayId 下拉选择,列表列显示 GatewayName) - 新增 src/api/product.js、src/api/thingPoint.js(产品/物模型点 API) - 新增 src/components/thing/(PointSendForm、ThingPointTable、ThingAlarmPanel 共享组件)
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
<template>
|
||||
<div class="point-send-form">
|
||||
<div class="ps-toolbar">
|
||||
<span class="ps-count">
|
||||
共 {{ points.length }} 个点,可下发 {{ writablePoints.length }} 个
|
||||
<span v-if="points.length" class="ps-tip">(只读点不支持下发;Int32/Float 从地址起连写 2 个寄存器)</span>
|
||||
</span>
|
||||
<el-button link type="primary" size="small" :loading="loading" @click="fetchPoints">刷新</el-button>
|
||||
</div>
|
||||
|
||||
<el-table :data="points" v-loading="loading" size="small" border stripe>
|
||||
<el-table-column prop="Code" label="编码" width="120" />
|
||||
<el-table-column prop="Name" label="名称" min-width="110" show-overflow-tooltip />
|
||||
<el-table-column label="寄存器" width="90">
|
||||
<template #default="{ row }">{{ registerLabel(row.RegisterType) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="地址" width="70" align="center">
|
||||
<template #default="{ row }">0x{{ dec2hex(row.Address) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="读写" width="60" align="center">
|
||||
<template #default="{ row }">{{ rwLabel(row.Rw) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="84" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row.Rw === 1" size="small" type="info">只读</el-tag>
|
||||
<el-tag v-else size="small" type="success">可下发</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="下发值" width="150">
|
||||
<template #default="{ row }">
|
||||
<el-switch
|
||||
v-if="row.DataType === 1"
|
||||
:model-value="row._sendValue === 1"
|
||||
active-text="开"
|
||||
@change="row._sendValue = $event ? 1 : 0"
|
||||
/>
|
||||
<el-input-number
|
||||
v-else
|
||||
v-model="row._sendValue"
|
||||
:precision="row.DataType === 5 ? 1 : 0"
|
||||
:step="1"
|
||||
:controls="false"
|
||||
size="small"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单位" width="60" align="center">
|
||||
<template #default="{ row }">{{ row.Unit || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="70" fixed="right" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
link type="primary"
|
||||
:disabled="row.Rw === 1"
|
||||
@click="onSend(row)"
|
||||
>下发</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 空状态区分:无点 / 点均不可下发 -->
|
||||
<el-empty
|
||||
v-if="!loading && points.length === 0"
|
||||
description="该设备暂无物模型点,请先在「物模型点」标签页为该设备添加数据点"
|
||||
:image-size="60"
|
||||
/>
|
||||
<el-alert
|
||||
v-else-if="!loading && writablePoints.length === 0"
|
||||
title="已添加的点均为只读,暂无可下发点(可将点读写属性改为「只写/读写」)"
|
||||
type="info"
|
||||
:closable="false"
|
||||
class="ps-feedback"
|
||||
/>
|
||||
|
||||
<!-- 下发反馈 -->
|
||||
<el-alert v-if="feedback.text" :title="feedback.text" :type="feedback.type" :closable="false" class="ps-feedback" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, watch, onMounted } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { getThingPoints, registerLabel, rwLabel } from '@/api/thingPoint'
|
||||
import { sendDeviceCommand } from '@/api/device'
|
||||
|
||||
const props = defineProps({
|
||||
deviceId: { type: String, required: true }
|
||||
})
|
||||
|
||||
const loading = ref(false)
|
||||
const points = ref([])
|
||||
const feedback = ref({ text: '', type: 'success' })
|
||||
|
||||
// 只读(1)的点不能下发;只写/读写都可以(Int32/Float 走 FC16 连写 2 个寄存器,已支持)
|
||||
const writablePoints = computed(() => points.value.filter(p => p.Rw !== 1))
|
||||
|
||||
function dec2hex(n) {
|
||||
return (Number(n) || 0).toString(16).toUpperCase().padStart(4, '0')
|
||||
}
|
||||
|
||||
async function fetchPoints() {
|
||||
loading.value = true
|
||||
try {
|
||||
const pts = (await getThingPoints(2, props.deviceId)) || []
|
||||
// 显示设备全部点(含只读),便于与「物模型点」标签页对照
|
||||
points.value = pts.map(p => ({ ...p, _sendValue: p.DataType === 1 ? 1 : 0 }))
|
||||
} catch (e) {
|
||||
points.value = []
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function onSend(row) {
|
||||
try {
|
||||
await sendDeviceCommand(props.deviceId, {
|
||||
Id: props.deviceId,
|
||||
PointId: row.Id,
|
||||
Value: row._sendValue ?? 0,
|
||||
IsSimulated: false
|
||||
})
|
||||
// res 为后端返回的 Data(无数据时返回 undefined),消息提示走统一处理
|
||||
feedback.value = { text: `已下发至「${row.Name || row.Code}」`, type: 'success' }
|
||||
ElMessage.success(`指令已下发:${row.Name || row.Code}`)
|
||||
} catch (e) {
|
||||
feedback.value = { text: e?.message || '下发失败', type: 'error' }
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => props.deviceId, (val) => { if (val) fetchPoints() })
|
||||
onMounted(() => { if (props.deviceId) fetchPoints() })
|
||||
|
||||
defineExpose({ refresh: fetchPoints })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.ps-toolbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.ps-count {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
.ps-tip {
|
||||
color: #c0c4cc;
|
||||
}
|
||||
.ps-feedback {
|
||||
margin-top: 12px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user