From 2515caed35a015bdf1636c275f81e68d5d7f0ea4 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 22 Jan 2026 00:51:48 +0800 Subject: [PATCH 1/4] =?UTF-8?q?feat(iot):=E3=80=90=E7=BD=91=E5=85=B3?= =?UTF-8?q?=E8=AE=BE=E5=A4=87=EF=BC=9A20%=E3=80=91=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E7=BD=91=E5=85=B3=E8=AE=BE=E5=A4=87=E7=BB=91=E5=AE=9A=E8=83=BD?= =?UTF-8?q?=E5=8A=9B=EF=BC=88=E6=9C=AA=E5=AE=8C=E6=88=90=EF=BC=89=EF=BC=8C?= =?UTF-8?q?=E5=9F=BA=E4=BA=8E=20breezy-doodling-starlight.md=20=E8=A7=84?= =?UTF-8?q?=E5=88=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/iot/device/device/index.ts | 26 +++ .../device/detail/DeviceDetailsSubDevice.vue | 210 ++++++++++++++++++ src/views/iot/device/device/detail/index.vue | 9 +- 3 files changed, 244 insertions(+), 1 deletion(-) create mode 100644 src/views/iot/device/device/detail/DeviceDetailsSubDevice.vue diff --git a/src/api/iot/device/device/index.ts b/src/api/iot/device/device/index.ts index 1804628c5..be5193c6d 100644 --- a/src/api/iot/device/device/index.ts +++ b/src/api/iot/device/device/index.ts @@ -158,5 +158,31 @@ export const DeviceApi = { // 发送设备消息 sendDeviceMessage: async (params: IotDeviceMessageSendReqVO) => { return await request.post({ url: `/iot/device/message/send`, data: params }) + }, + + // 绑定子设备到网关 + bindDeviceGateway: async (data: { ids: number[]; gatewayId: number }) => { + return await request.put({ url: `/iot/device/bind-gateway`, data }) + }, + + // 解绑子设备与网关 + unbindDeviceGateway: async (data: { ids: number[] }) => { + return await request.put({ url: `/iot/device/unbind-gateway`, data }) + }, + + // 获取网关的子设备列表 + getSubDeviceList: async (gatewayId: number) => { + return await request.get({ + url: `/iot/device/sub-device-list`, + params: { gatewayId } + }) + }, + + // 获取可绑定到网关的子设备列表 + getBindableSubDeviceList: async (gatewayId?: number) => { + return await request.get({ + url: `/iot/device/bindable-sub-device-list`, + params: { gatewayId } + }) } } diff --git a/src/views/iot/device/device/detail/DeviceDetailsSubDevice.vue b/src/views/iot/device/device/detail/DeviceDetailsSubDevice.vue new file mode 100644 index 000000000..16a406c4f --- /dev/null +++ b/src/views/iot/device/device/detail/DeviceDetailsSubDevice.vue @@ -0,0 +1,210 @@ + + + + diff --git a/src/views/iot/device/device/detail/index.vue b/src/views/iot/device/device/detail/index.vue index 3ec756b5c..f8949ecd5 100644 --- a/src/views/iot/device/device/detail/index.vue +++ b/src/views/iot/device/device/detail/index.vue @@ -17,7 +17,13 @@ :thing-model-list="thingModelList" /> - + + + @@ -50,6 +56,7 @@ import DeviceDetailsThingModel from './DeviceDetailsThingModel.vue' import DeviceDetailsMessage from './DeviceDetailsMessage.vue' import DeviceDetailsSimulator from './DeviceDetailsSimulator.vue' import DeviceDetailConfig from './DeviceDetailConfig.vue' +import DeviceDetailsSubDevice from './DeviceDetailsSubDevice.vue' defineOptions({ name: 'IoTDeviceDetail' }) From 2076a27a262b9af9985e97c307c1679a9eb6a6e7 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 22 Jan 2026 09:53:01 +0800 Subject: [PATCH 2/4] =?UTF-8?q?feat(iot):=E3=80=90=E7=BD=91=E5=85=B3?= =?UTF-8?q?=E8=AE=BE=E5=A4=87=EF=BC=9A30%=E3=80=91=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E7=BD=91=E5=85=B3=E8=AE=BE=E5=A4=87=E7=BB=91=E5=AE=9A=E8=83=BD?= =?UTF-8?q?=E5=8A=9B=EF=BC=88=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81=EF=BC=89?= =?UTF-8?q?=EF=BC=8C=E5=9F=BA=E4=BA=8E=20optimized-pondering-dragon.md=20?= =?UTF-8?q?=E8=A7=84=E5=88=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/iot/device/device/index.ts | 9 +- src/api/iot/product/product/index.ts | 4 +- .../device/detail/DeviceDetailsSubDevice.vue | 136 ++++++++++++------ 3 files changed, 100 insertions(+), 49 deletions(-) diff --git a/src/api/iot/device/device/index.ts b/src/api/iot/device/device/index.ts index be5193c6d..d41429e0b 100644 --- a/src/api/iot/device/device/index.ts +++ b/src/api/iot/device/device/index.ts @@ -178,11 +178,8 @@ export const DeviceApi = { }) }, - // 获取可绑定到网关的子设备列表 - getBindableSubDeviceList: async (gatewayId?: number) => { - return await request.get({ - url: `/iot/device/bindable-sub-device-list`, - params: { gatewayId } - }) + // 获取未绑定网关的子设备分页 + getUnboundSubDevicePage: async (params: any) => { + return await request.get({ url: `/iot/device/unbound-sub-device-page`, params }) } } diff --git a/src/api/iot/product/product/index.ts b/src/api/iot/product/product/index.ts index a34efaeb9..d491d3b28 100644 --- a/src/api/iot/product/product/index.ts +++ b/src/api/iot/product/product/index.ts @@ -68,8 +68,8 @@ export const ProductApi = { }, // 查询产品(精简)列表 - getSimpleProductList() { - return request.get({ url: '/iot/product/simple-list' }) + getSimpleProductList(deviceType?: number) { + return request.get({ url: '/iot/product/simple-list', params: { deviceType } }) }, // 根据 ProductKey 获取产品信息 diff --git a/src/views/iot/device/device/detail/DeviceDetailsSubDevice.vue b/src/views/iot/device/device/detail/DeviceDetailsSubDevice.vue index 16a406c4f..d24ad2995 100644 --- a/src/views/iot/device/device/detail/DeviceDetailsSubDevice.vue +++ b/src/views/iot/device/device/detail/DeviceDetailsSubDevice.vue @@ -62,40 +62,82 @@ - - - - - - - - - - - + + + + + + + + + + + + + 搜索 + + + 重置 + + + + + + + + + + + + + + + + + + + + + From 69d8224305b9eb17274acf5b64fdb7362833eb73 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 22 Jan 2026 09:55:00 +0800 Subject: [PATCH 3/4] =?UTF-8?q?feat(iot):=E3=80=90=E7=BD=91=E5=85=B3?= =?UTF-8?q?=E8=AE=BE=E5=A4=87=EF=BC=9A30%=E3=80=91=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E7=BD=91=E5=85=B3=E8=AE=BE=E5=A4=87=E7=BB=91=E5=AE=9A=E8=83=BD?= =?UTF-8?q?=E5=8A=9B=EF=BC=88=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81=EF=BC=89?= =?UTF-8?q?=EF=BC=8C=E5=9F=BA=E4=BA=8E=20optimized-pondering-dragon.md=20?= =?UTF-8?q?=E8=A7=84=E5=88=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../product/components/ProductSelect.vue | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 src/views/iot/product/product/components/ProductSelect.vue diff --git a/src/views/iot/product/product/components/ProductSelect.vue b/src/views/iot/product/product/components/ProductSelect.vue new file mode 100644 index 000000000..fa288fa18 --- /dev/null +++ b/src/views/iot/product/product/components/ProductSelect.vue @@ -0,0 +1,65 @@ + + + + From 2a0ce1fb66d1e47e15ccb3b0fb72b31d0929a88f Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 25 Jan 2026 16:58:05 +0800 Subject: [PATCH 4/4] =?UTF-8?q?feat(iot):=E3=80=90=E7=BD=91=E5=85=B3?= =?UTF-8?q?=E8=AE=BE=E5=A4=87=EF=BC=9A80%=E3=80=91=E5=8A=A8=E6=80=81?= =?UTF-8?q?=E6=B3=A8=E5=86=8C=E7=9A=84=E5=88=9D=E6=AD=A5=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=EF=BC=88=E5=B7=B2=E6=B5=8B=E8=AF=95=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/iot/device/device/index.ts | 5 +- src/api/iot/product/product/index.ts | 2 + src/views/iot/device/device/DeviceForm.vue | 19 ------- .../device/detail/DeviceDetailsSubDevice.vue | 6 +-- src/views/iot/product/product/ProductForm.vue | 20 ++++++- .../product/detail/ProductDetailsInfo.vue | 35 ++++++++++++ src/views/iot/utils/constants.ts | 53 ++++++++++++++++++- 7 files changed, 112 insertions(+), 28 deletions(-) diff --git a/src/api/iot/device/device/index.ts b/src/api/iot/device/device/index.ts index d41429e0b..cf9f6d62d 100644 --- a/src/api/iot/device/device/index.ts +++ b/src/api/iot/device/device/index.ts @@ -21,7 +21,6 @@ export interface DeviceVO { mqttClientId: string // MQTT 客户端 ID mqttUsername: string // MQTT 用户名 mqttPassword: string // MQTT 密码 - authType: string // 认证类型 latitude?: number // 设备位置的纬度 longitude?: number // 设备位置的经度 areaId: number // 地区编码 @@ -161,12 +160,12 @@ export const DeviceApi = { }, // 绑定子设备到网关 - bindDeviceGateway: async (data: { ids: number[]; gatewayId: number }) => { + bindDeviceGateway: async (data: { subIds: number[]; gatewayId: number }) => { return await request.put({ url: `/iot/device/bind-gateway`, data }) }, // 解绑子设备与网关 - unbindDeviceGateway: async (data: { ids: number[] }) => { + unbindDeviceGateway: async (data: { subIds: number[]; gatewayId: number }) => { return await request.put({ url: `/iot/device/unbind-gateway`, data }) }, diff --git a/src/api/iot/product/product/index.ts b/src/api/iot/product/product/index.ts index d491d3b28..ba465efdc 100644 --- a/src/api/iot/product/product/index.ts +++ b/src/api/iot/product/product/index.ts @@ -5,6 +5,8 @@ export interface ProductVO { id: number // 产品编号 name: string // 产品名称 productKey: string // 产品标识 + productSecret?: string // 产品密钥 + registerEnabled?: boolean // 动态注册 protocolId: number // 协议编号 categoryId: number // 产品所属品类标识符 categoryName?: string // 产品所属品类名称 diff --git a/src/views/iot/device/device/DeviceForm.vue b/src/views/iot/device/device/DeviceForm.vue index 0ddbf2afb..fe75eed53 100644 --- a/src/views/iot/device/device/DeviceForm.vue +++ b/src/views/iot/device/device/DeviceForm.vue @@ -30,20 +30,6 @@ :disabled="formType === 'update'" /> - - - - - @@ -114,7 +100,6 @@ const formData = ref({ deviceName: undefined, nickname: undefined, picUrl: undefined, - gatewayId: undefined, deviceType: undefined as number | undefined, serialNumber: undefined, longitude: undefined as number | string | undefined, @@ -222,7 +207,6 @@ const formRules = reactive({ }) const formRef = ref() // 表单 Ref const products = ref([]) // 产品列表 -const gatewayDevices = ref([]) // 网关设备列表 const deviceGroups = ref([]) /** 打开弹窗 */ @@ -242,8 +226,6 @@ const open = async (type: string, id?: number) => { } } - // 加载网关设备列表 - gatewayDevices.value = await DeviceApi.getSimpleDeviceList(DeviceTypeEnum.GATEWAY) // 加载产品列表 products.value = await ProductApi.getSimpleProductList() // 加载设备分组列表 @@ -283,7 +265,6 @@ const resetForm = () => { deviceName: undefined, nickname: undefined, picUrl: undefined, - gatewayId: undefined, deviceType: undefined, serialNumber: undefined, longitude: undefined, diff --git a/src/views/iot/device/device/detail/DeviceDetailsSubDevice.vue b/src/views/iot/device/device/detail/DeviceDetailsSubDevice.vue index d24ad2995..290cd5cca 100644 --- a/src/views/iot/device/device/detail/DeviceDetailsSubDevice.vue +++ b/src/views/iot/device/device/detail/DeviceDetailsSubDevice.vue @@ -225,7 +225,7 @@ const handleBindSubmit = async () => { bindFormLoading.value = true try { await DeviceApi.bindDeviceGateway({ - ids: bindSelectedIds.value, + subIds: bindSelectedIds.value, gatewayId: props.gatewayId }) message.success('绑定成功') @@ -240,7 +240,7 @@ const handleBindSubmit = async () => { const handleUnbind = async (id: number) => { try { await message.confirm('确定要解绑该子设备吗?') - await DeviceApi.unbindDeviceGateway({ ids: [id] }) + await DeviceApi.unbindDeviceGateway({ subIds: [id], gatewayId: props.gatewayId }) message.success('解绑成功') await getSubDeviceList() } catch {} @@ -250,7 +250,7 @@ const handleUnbind = async (id: number) => { const handleUnbindBatch = async () => { try { await message.confirm(`确定要解绑选中的 ${selectedIds.value.length} 个子设备吗?`) - await DeviceApi.unbindDeviceGateway({ ids: selectedIds.value }) + await DeviceApi.unbindDeviceGateway({ subIds: selectedIds.value, gatewayId: props.gatewayId }) message.success('批量解绑成功') selectedIds.value = [] await getSubDeviceList() diff --git a/src/views/iot/product/product/ProductForm.vue b/src/views/iot/product/product/ProductForm.vue index 32a7b2221..9cb7c5099 100644 --- a/src/views/iot/product/product/ProductForm.vue +++ b/src/views/iot/product/product/ProductForm.vue @@ -75,6 +75,20 @@ + + + + @@ -120,7 +134,8 @@ const formData = ref({ description: undefined, deviceType: undefined, netType: undefined, - codecType: CodecTypeEnum.ALINK + codecType: CodecTypeEnum.ALINK, + registerEnabled: false }) const formRules = reactive({ productKey: [{ required: true, message: 'ProductKey 不能为空', trigger: 'blur' }], @@ -194,7 +209,8 @@ const resetForm = () => { description: undefined, deviceType: undefined, netType: undefined, - codecType: CodecTypeEnum.ALINK + codecType: CodecTypeEnum.ALINK, + registerEnabled: false } formRef.value?.resetFields() } diff --git a/src/views/iot/product/product/detail/ProductDetailsInfo.vue b/src/views/iot/product/product/detail/ProductDetailsInfo.vue index feb7eb5dc..38c523b6e 100644 --- a/src/views/iot/product/product/detail/ProductDetailsInfo.vue +++ b/src/views/iot/product/product/detail/ProductDetailsInfo.vue @@ -21,6 +21,28 @@ > + + + {{ product.registerEnabled ? '已开启' : '已关闭' }} + + + +
+ {{ secretVisible ? product.productSecret : '******' }} + + + + + + +
+
{{ product.description }} @@ -29,6 +51,19 @@ import { DICT_TYPE } from '@/utils/dict' import { DeviceTypeEnum, ProductVO } from '@/api/iot/product/product' import { formatDate } from '@/utils/formatTime' +import { useClipboard } from '@vueuse/core' const { product } = defineProps<{ product: ProductVO }>() + +const message = useMessage() +const secretVisible = ref(false) +const { copy } = useClipboard() + +/** 复制产品密钥 */ +const copySecret = async () => { + if (product.productSecret) { + await copy(product.productSecret) + message.success('复制成功') + } +} diff --git a/src/views/iot/utils/constants.ts b/src/views/iot/utils/constants.ts index f25bb7de5..8b9c6722a 100644 --- a/src/views/iot/utils/constants.ts +++ b/src/views/iot/utils/constants.ts @@ -24,7 +24,41 @@ export const IotDeviceMessageMethodEnum = { // ========== 设备状态 ========== STATE_UPDATE: { method: 'thing.state.update', - name: '设备状态变更', + name: '设备状态更新', + upstream: true + }, + + // ========== 拓扑管理 ========== + TOPO_ADD: { + method: 'thing.topo.add', + name: '添加拓扑关系', + upstream: true + }, + TOPO_DELETE: { + method: 'thing.topo.delete', + name: '删除拓扑关系', + upstream: true + }, + TOPO_GET: { + method: 'thing.topo.get', + name: '获取拓扑关系', + upstream: true + }, + TOPO_CHANGE: { + method: 'thing.topo.change', + name: '拓扑关系变更通知', + upstream: false + }, + + // ========== 设备注册 ========== + DEVICE_REGISTER: { + method: 'thing.auth.register', + name: '设备动态注册', + upstream: true + }, + SUB_DEVICE_REGISTER: { + method: 'thing.auth.register.sub', + name: '子设备动态注册', upstream: true }, @@ -39,6 +73,11 @@ export const IotDeviceMessageMethodEnum = { name: '属性设置', upstream: false }, + PROPERTY_PACK_POST: { + method: 'thing.event.property.pack.post', + name: '批量上报(属性 + 事件 + 子设备)', + upstream: true + }, // ========== 设备事件 ========== EVENT_POST: { @@ -59,6 +98,18 @@ export const IotDeviceMessageMethodEnum = { method: 'thing.config.push', name: '配置推送', upstream: false + }, + + // ========== OTA 固件 ========== + OTA_UPGRADE: { + method: 'thing.ota.upgrade', + name: 'OTA 固件信息推送', + upstream: false + }, + OTA_PROGRESS: { + method: 'thing.ota.progress', + name: 'OTA 升级进度上报', + upstream: true } }