diff --git a/src/api/iot/device/device/index.ts b/src/api/iot/device/device/index.ts index 1804628c5..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 // 地区编码 @@ -158,5 +157,28 @@ export const DeviceApi = { // 发送设备消息 sendDeviceMessage: async (params: IotDeviceMessageSendReqVO) => { return await request.post({ url: `/iot/device/message/send`, data: params }) + }, + + // 绑定子设备到网关 + bindDeviceGateway: async (data: { subIds: number[]; gatewayId: number }) => { + return await request.put({ url: `/iot/device/bind-gateway`, data }) + }, + + // 解绑子设备与网关 + unbindDeviceGateway: async (data: { subIds: number[]; gatewayId: 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 } + }) + }, + + // 获取未绑定网关的子设备分页 + 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..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 // 产品所属品类名称 @@ -68,8 +70,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/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 new file mode 100644 index 000000000..290cd5cca --- /dev/null +++ b/src/views/iot/device/device/detail/DeviceDetailsSubDevice.vue @@ -0,0 +1,264 @@ + + + + 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' }) 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/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 @@ + + + + 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 } }