mirror of
https://github.com/yudaocode/yudao-ui-admin-vue3.git
synced 2026-04-19 13:28:39 +00:00
feat:【iot】modbus-tcp 协议接入 100%:完整实现,修复 todo
This commit is contained in:
@@ -4,14 +4,14 @@ import request from '@/config/axios'
|
|||||||
export interface DeviceModbusPointVO {
|
export interface DeviceModbusPointVO {
|
||||||
id?: number // 主键
|
id?: number // 主键
|
||||||
deviceId: number // 设备编号
|
deviceId: number // 设备编号
|
||||||
thingModelId: number // 物模型属性编号
|
thingModelId?: number // 物模型属性编号
|
||||||
identifier: string // 属性标识符
|
identifier: string // 属性标识符
|
||||||
name: string // 属性名称
|
name: string // 属性名称
|
||||||
functionCode: number // Modbus 功能码
|
functionCode?: number // Modbus 功能码
|
||||||
registerAddress: number // 寄存器起始地址
|
registerAddress?: number // 寄存器起始地址
|
||||||
registerCount: number // 寄存器数量
|
registerCount?: number // 寄存器数量
|
||||||
byteOrder: string // 字节序
|
byteOrder?: string // 字节序
|
||||||
rawDataType: string // 原始数据类型
|
rawDataType?: string // 原始数据类型
|
||||||
scale: number // 缩放因子
|
scale: number // 缩放因子
|
||||||
pollInterval: number // 轮询间隔,单位:毫秒
|
pollInterval: number // 轮询间隔,单位:毫秒
|
||||||
status: number // 状态
|
status: number // 状态
|
||||||
|
|||||||
@@ -5,7 +5,9 @@
|
|||||||
<ContentWrap>
|
<ContentWrap>
|
||||||
<div class="flex items-center justify-between mb-4">
|
<div class="flex items-center justify-between mb-4">
|
||||||
<span class="text-lg font-medium">连接配置</span>
|
<span class="text-lg font-medium">连接配置</span>
|
||||||
<el-button type="primary" @click="handleEditConfig">编辑</el-button>
|
<el-button type="primary" @click="handleEditConfig" v-hasPermi="['iot:device:create']">
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 详情展示 -->
|
<!-- 详情展示 -->
|
||||||
@@ -26,9 +28,7 @@
|
|||||||
{{ modbusConfig.retryInterval ? `${modbusConfig.retryInterval} ms` : '-' }}
|
{{ modbusConfig.retryInterval ? `${modbusConfig.retryInterval} ms` : '-' }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="状态">
|
<el-descriptions-item label="状态">
|
||||||
<el-tag :type="modbusConfig.status === 0 ? 'success' : 'danger'">
|
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="modbusConfig.status" />
|
||||||
{{ modbusConfig.status === 0 ? '启用' : '禁用' }}
|
|
||||||
</el-tag>
|
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
</ContentWrap>
|
</ContentWrap>
|
||||||
@@ -37,9 +37,8 @@
|
|||||||
<ContentWrap class="mt-4">
|
<ContentWrap class="mt-4">
|
||||||
<div class="flex items-center justify-between mb-4">
|
<div class="flex items-center justify-between mb-4">
|
||||||
<span class="text-lg font-medium">点位配置</span>
|
<span class="text-lg font-medium">点位配置</span>
|
||||||
<el-button type="primary" @click="handleAddPoint">
|
<el-button type="primary" @click="handleAddPoint" v-hasPermi="['iot:device:create']">
|
||||||
<Icon icon="ep:plus" class="mr-1" />
|
<Icon icon="ep:plus" class="mr-1" />
|
||||||
<!-- TODO @AI:权限,需要和后端接口对齐 -->
|
|
||||||
新增点位
|
新增点位
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
@@ -112,9 +111,20 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" align="center" fixed="right" width="120">
|
<el-table-column label="操作" align="center" fixed="right" width="120">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<!-- TODO @AI:权限,需要和后端接口对齐 -->
|
<el-button
|
||||||
<el-button link type="primary" @click="handleEditPoint(scope.row)">编辑</el-button>
|
link
|
||||||
<el-button link type="danger" @click="handleDeletePoint(scope.row.id, scope.row.name)">
|
type="primary"
|
||||||
|
@click="handleEditPoint(scope.row)"
|
||||||
|
v-hasPermi="['iot:device:update']"
|
||||||
|
>
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleDeletePoint(scope.row.id, scope.row.name)"
|
||||||
|
v-hasPermi="['iot:device:delete']"
|
||||||
|
>
|
||||||
删除
|
删除
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<!-- Modbus 连接配置弹窗 -->
|
<!-- Modbus 连接配置弹窗 -->
|
||||||
<template>
|
<template>
|
||||||
<Dialog :title="dialogTitle" v-model="dialogVisible" width="600px">
|
<Dialog title="编辑 Modbus 连接配置" v-model="dialogVisible" width="600px">
|
||||||
<el-form
|
<el-form
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
:model="formData"
|
:model="formData"
|
||||||
@@ -52,12 +52,15 @@
|
|||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="状态" prop="status">
|
<el-form-item label="状态" prop="status">
|
||||||
<!-- TODO @AI:参考别的模块,不要使用这个 -->
|
<el-radio-group v-model="formData.status">
|
||||||
<el-switch
|
<el-radio
|
||||||
v-model="formData.status"
|
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
|
||||||
:active-value="CommonStatusEnum.ENABLE"
|
:key="dict.value"
|
||||||
:inactive-value="CommonStatusEnum.DISABLE"
|
:label="dict.value"
|
||||||
/>
|
>
|
||||||
|
{{ dict.label }}
|
||||||
|
</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@@ -69,6 +72,7 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { DeviceModbusConfigApi, DeviceModbusConfigVO } from '@/api/iot/device/modbus/config'
|
import { DeviceModbusConfigApi, DeviceModbusConfigVO } from '@/api/iot/device/modbus/config'
|
||||||
|
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
|
||||||
import { CommonStatusEnum } from '@/utils/constants'
|
import { CommonStatusEnum } from '@/utils/constants'
|
||||||
|
|
||||||
defineOptions({ name: 'DeviceModbusConfigForm' })
|
defineOptions({ name: 'DeviceModbusConfigForm' })
|
||||||
@@ -82,12 +86,8 @@ const emit = defineEmits<{
|
|||||||
}>()
|
}>()
|
||||||
|
|
||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
|
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||||
const dialogVisible = ref(false)
|
const formLoading = ref(false) // 表单提交 loading 状态
|
||||||
const dialogTitle = ref('编辑 Modbus 连接配置')
|
|
||||||
const formLoading = ref(false)
|
|
||||||
const formRef = ref()
|
|
||||||
|
|
||||||
const formData = ref<DeviceModbusConfigVO>({
|
const formData = ref<DeviceModbusConfigVO>({
|
||||||
deviceId: props.deviceId,
|
deviceId: props.deviceId,
|
||||||
ip: '',
|
ip: '',
|
||||||
@@ -97,8 +97,6 @@ const formData = ref<DeviceModbusConfigVO>({
|
|||||||
retryInterval: 10000,
|
retryInterval: 10000,
|
||||||
status: CommonStatusEnum.ENABLE
|
status: CommonStatusEnum.ENABLE
|
||||||
})
|
})
|
||||||
|
|
||||||
/** 表单校验规则 */
|
|
||||||
const formRules = {
|
const formRules = {
|
||||||
ip: [{ required: true, message: '请输入 IP 地址', trigger: 'blur' }],
|
ip: [{ required: true, message: '请输入 IP 地址', trigger: 'blur' }],
|
||||||
port: [{ required: true, message: '请输入端口', trigger: 'blur' }],
|
port: [{ required: true, message: '请输入端口', trigger: 'blur' }],
|
||||||
@@ -106,6 +104,7 @@ const formRules = {
|
|||||||
timeout: [{ required: true, message: '请输入连接超时时间', trigger: 'blur' }],
|
timeout: [{ required: true, message: '请输入连接超时时间', trigger: 'blur' }],
|
||||||
retryInterval: [{ required: true, message: '请输入重试间隔', trigger: 'blur' }]
|
retryInterval: [{ required: true, message: '请输入重试间隔', trigger: 'blur' }]
|
||||||
}
|
}
|
||||||
|
const formRef = ref() // 表单 Ref
|
||||||
|
|
||||||
/** 打开弹窗 */
|
/** 打开弹窗 */
|
||||||
const open = async (data?: DeviceModbusConfigVO) => {
|
const open = async (data?: DeviceModbusConfigVO) => {
|
||||||
|
|||||||
@@ -35,14 +35,18 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="寄存器地址" prop="registerAddress">
|
<el-form-item label="寄存器地址" prop="registerAddress">
|
||||||
<el-input-number
|
<el-input
|
||||||
v-model="formData.registerAddress"
|
v-model.number="formData.registerAddress"
|
||||||
|
type="number"
|
||||||
:min="0"
|
:min="0"
|
||||||
:max="65535"
|
:max="65535"
|
||||||
controls-position="right"
|
|
||||||
placeholder="请输入寄存器地址"
|
placeholder="请输入寄存器地址"
|
||||||
class="!w-full"
|
class="!w-full"
|
||||||
/>
|
>
|
||||||
|
<template #suffix>
|
||||||
|
<span class="text-gray-400">{{ registerAddressHex }}</span>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="寄存器数量" prop="registerCount">
|
<el-form-item label="寄存器数量" prop="registerCount">
|
||||||
<el-input-number
|
<el-input-number
|
||||||
@@ -100,11 +104,15 @@
|
|||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="状态" prop="status">
|
<el-form-item label="状态" prop="status">
|
||||||
<el-switch
|
<el-radio-group v-model="formData.status">
|
||||||
v-model="formData.status"
|
<el-radio
|
||||||
:active-value="CommonStatusEnum.ENABLE"
|
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
|
||||||
:inactive-value="CommonStatusEnum.DISABLE"
|
:key="dict.value"
|
||||||
/>
|
:label="dict.value"
|
||||||
|
>
|
||||||
|
{{ dict.label }}
|
||||||
|
</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@@ -120,8 +128,10 @@ import { DeviceModbusPointApi, DeviceModbusPointVO } from '@/api/iot/device/modb
|
|||||||
import {
|
import {
|
||||||
ModbusFunctionCodeOptions,
|
ModbusFunctionCodeOptions,
|
||||||
ModbusRawDataTypeOptions,
|
ModbusRawDataTypeOptions,
|
||||||
getByteOrderOptions
|
getByteOrderOptions,
|
||||||
|
IoTThingModelTypeEnum
|
||||||
} from '@/views/iot/utils/constants'
|
} from '@/views/iot/utils/constants'
|
||||||
|
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
|
||||||
import { CommonStatusEnum } from '@/utils/constants'
|
import { CommonStatusEnum } from '@/utils/constants'
|
||||||
|
|
||||||
defineOptions({ name: 'DeviceModbusPointForm' })
|
defineOptions({ name: 'DeviceModbusPointForm' })
|
||||||
@@ -135,26 +145,27 @@ const emit = defineEmits<{
|
|||||||
(e: 'success'): void
|
(e: 'success'): void
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
|
|
||||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||||
const dialogTitle = ref('') // 弹窗的标题
|
const dialogTitle = ref('') // 弹窗的标题
|
||||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||||
const formData = ref<DeviceModbusPointVO>({
|
const formData = ref<DeviceModbusPointVO>({
|
||||||
deviceId: props.deviceId,
|
deviceId: props.deviceId,
|
||||||
thingModelId: 0, // TODO @AI:不要有默认值;
|
thingModelId: undefined,
|
||||||
identifier: '',
|
identifier: '',
|
||||||
name: '',
|
name: '',
|
||||||
functionCode: 3,
|
functionCode: undefined,
|
||||||
registerAddress: 0,
|
registerAddress: undefined,
|
||||||
registerCount: 1,
|
registerCount: undefined,
|
||||||
byteOrder: 'AB',
|
byteOrder: undefined,
|
||||||
rawDataType: 'INT16',
|
rawDataType: undefined,
|
||||||
scale: 1,
|
scale: 1,
|
||||||
pollInterval: 5000,
|
pollInterval: 5000,
|
||||||
status: CommonStatusEnum.ENABLE
|
status: CommonStatusEnum.ENABLE
|
||||||
}) // TODO @AI:registerAddress、registerAddress、registerCount、rawDataType 不要有默认值;
|
})
|
||||||
|
|
||||||
const formRules = {
|
const formRules = {
|
||||||
thingModelId: [{ required: true, message: '请选择物模型属性', trigger: 'change' }],
|
thingModelId: [{ required: true, message: '请选择物模型属性', trigger: 'change' }],
|
||||||
functionCode: [{ required: true, message: '请选择功能码', trigger: 'change' }],
|
functionCode: [{ required: true, message: '请选择功能码', trigger: 'change' }],
|
||||||
@@ -165,14 +176,24 @@ const formRules = {
|
|||||||
}
|
}
|
||||||
const formRef = ref() // 表单 Ref
|
const formRef = ref() // 表单 Ref
|
||||||
|
|
||||||
|
/** 寄存器地址十六进制显示 */
|
||||||
|
const registerAddressHex = computed(() => {
|
||||||
|
if (formData.value.registerAddress === undefined || formData.value.registerAddress === null) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
return '0x' + formData.value.registerAddress.toString(16).toUpperCase().padStart(4, '0')
|
||||||
|
})
|
||||||
|
|
||||||
/** 筛选属性类型的物模型 */
|
/** 筛选属性类型的物模型 */
|
||||||
const propertyList = computed(() => {
|
const propertyList = computed(() => {
|
||||||
// TODO @AI:1 使用枚举代替魔法数字
|
return props.thingModelList.filter((item) => item.type === IoTThingModelTypeEnum.PROPERTY)
|
||||||
return props.thingModelList.filter((item) => item.type === 1)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
/** 当前字节序选项(根据数据类型动态变化) */
|
/** 当前字节序选项(根据数据类型动态变化) */
|
||||||
const currentByteOrderOptions = computed(() => {
|
const currentByteOrderOptions = computed(() => {
|
||||||
|
if (!formData.value.rawDataType) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
return getByteOrderOptions(formData.value.rawDataType)
|
return getByteOrderOptions(formData.value.rawDataType)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -204,8 +225,7 @@ const handleRawDataTypeChange = (rawDataType: string) => {
|
|||||||
const open = async (type: 'create' | 'update', id?: number) => {
|
const open = async (type: 'create' | 'update', id?: number) => {
|
||||||
dialogVisible.value = true
|
dialogVisible.value = true
|
||||||
formType.value = type
|
formType.value = type
|
||||||
// TODO @AI:dialogTitle.value = t('action.' + type)
|
dialogTitle.value = t('action.' + type)
|
||||||
dialogTitle.value = type === 'create' ? '新增 Modbus 点位' : '编辑 Modbus 点位'
|
|
||||||
resetForm()
|
resetForm()
|
||||||
// 修改时,设置数据
|
// 修改时,设置数据
|
||||||
if (type === 'update' && id) {
|
if (type === 'update' && id) {
|
||||||
@@ -246,15 +266,14 @@ const submitForm = async () => {
|
|||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
formData.value = {
|
formData.value = {
|
||||||
deviceId: props.deviceId,
|
deviceId: props.deviceId,
|
||||||
thingModelId: 0,
|
thingModelId: undefined,
|
||||||
identifier: '',
|
identifier: '',
|
||||||
name: '',
|
name: '',
|
||||||
// TODO @AI:3、0、1、AB/INT16 使用枚举;
|
functionCode: undefined,
|
||||||
functionCode: 3,
|
registerAddress: undefined,
|
||||||
registerAddress: 0,
|
registerCount: undefined,
|
||||||
registerCount: 1,
|
byteOrder: undefined,
|
||||||
byteOrder: 'AB',
|
rawDataType: undefined,
|
||||||
rawDataType: 'INT16',
|
|
||||||
scale: 1,
|
scale: 1,
|
||||||
pollInterval: 5000,
|
pollInterval: 5000,
|
||||||
status: CommonStatusEnum.ENABLE
|
status: CommonStatusEnum.ENABLE
|
||||||
|
|||||||
Reference in New Issue
Block a user