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