feat(iot):【网关设备:80%】动态注册的初步实现(已测试)
parent
69d8224305
commit
2a0ce1fb66
|
|
@ -21,7 +21,6 @@ export interface DeviceVO {
|
||||||
mqttClientId: string // MQTT 客户端 ID
|
mqttClientId: string // MQTT 客户端 ID
|
||||||
mqttUsername: string // MQTT 用户名
|
mqttUsername: string // MQTT 用户名
|
||||||
mqttPassword: string // MQTT 密码
|
mqttPassword: string // MQTT 密码
|
||||||
authType: string // 认证类型
|
|
||||||
latitude?: number // 设备位置的纬度
|
latitude?: number // 设备位置的纬度
|
||||||
longitude?: number // 设备位置的经度
|
longitude?: number // 设备位置的经度
|
||||||
areaId: 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 })
|
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 })
|
return await request.put({ url: `/iot/device/unbind-gateway`, data })
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ export interface ProductVO {
|
||||||
id: number // 产品编号
|
id: number // 产品编号
|
||||||
name: string // 产品名称
|
name: string // 产品名称
|
||||||
productKey: string // 产品标识
|
productKey: string // 产品标识
|
||||||
|
productSecret?: string // 产品密钥
|
||||||
|
registerEnabled?: boolean // 动态注册
|
||||||
protocolId: number // 协议编号
|
protocolId: number // 协议编号
|
||||||
categoryId: number // 产品所属品类标识符
|
categoryId: number // 产品所属品类标识符
|
||||||
categoryName?: string // 产品所属品类名称
|
categoryName?: string // 产品所属品类名称
|
||||||
|
|
|
||||||
|
|
@ -30,20 +30,6 @@
|
||||||
:disabled="formType === 'update'"
|
:disabled="formType === 'update'"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item
|
|
||||||
v-if="formData.deviceType === DeviceTypeEnum.GATEWAY_SUB"
|
|
||||||
label="网关设备"
|
|
||||||
prop="gatewayId"
|
|
||||||
>
|
|
||||||
<el-select v-model="formData.gatewayId" placeholder="子设备可选择父设备" clearable>
|
|
||||||
<el-option
|
|
||||||
v-for="gateway in gatewayDevices"
|
|
||||||
:key="gateway.id"
|
|
||||||
:label="gateway.nickname || gateway.deviceName"
|
|
||||||
:value="gateway.id"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-collapse>
|
<el-collapse>
|
||||||
<el-collapse-item title="更多配置">
|
<el-collapse-item title="更多配置">
|
||||||
|
|
@ -114,7 +100,6 @@ const formData = ref({
|
||||||
deviceName: undefined,
|
deviceName: undefined,
|
||||||
nickname: undefined,
|
nickname: undefined,
|
||||||
picUrl: undefined,
|
picUrl: undefined,
|
||||||
gatewayId: undefined,
|
|
||||||
deviceType: undefined as number | undefined,
|
deviceType: undefined as number | undefined,
|
||||||
serialNumber: undefined,
|
serialNumber: undefined,
|
||||||
longitude: undefined as number | string | undefined,
|
longitude: undefined as number | string | undefined,
|
||||||
|
|
@ -222,7 +207,6 @@ const formRules = reactive({
|
||||||
})
|
})
|
||||||
const formRef = ref() // 表单 Ref
|
const formRef = ref() // 表单 Ref
|
||||||
const products = ref<ProductVO[]>([]) // 产品列表
|
const products = ref<ProductVO[]>([]) // 产品列表
|
||||||
const gatewayDevices = ref<DeviceVO[]>([]) // 网关设备列表
|
|
||||||
const deviceGroups = ref<any[]>([])
|
const deviceGroups = ref<any[]>([])
|
||||||
|
|
||||||
/** 打开弹窗 */
|
/** 打开弹窗 */
|
||||||
|
|
@ -242,8 +226,6 @@ const open = async (type: string, id?: number) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载网关设备列表
|
|
||||||
gatewayDevices.value = await DeviceApi.getSimpleDeviceList(DeviceTypeEnum.GATEWAY)
|
|
||||||
// 加载产品列表
|
// 加载产品列表
|
||||||
products.value = await ProductApi.getSimpleProductList()
|
products.value = await ProductApi.getSimpleProductList()
|
||||||
// 加载设备分组列表
|
// 加载设备分组列表
|
||||||
|
|
@ -283,7 +265,6 @@ const resetForm = () => {
|
||||||
deviceName: undefined,
|
deviceName: undefined,
|
||||||
nickname: undefined,
|
nickname: undefined,
|
||||||
picUrl: undefined,
|
picUrl: undefined,
|
||||||
gatewayId: undefined,
|
|
||||||
deviceType: undefined,
|
deviceType: undefined,
|
||||||
serialNumber: undefined,
|
serialNumber: undefined,
|
||||||
longitude: undefined,
|
longitude: undefined,
|
||||||
|
|
|
||||||
|
|
@ -225,7 +225,7 @@ const handleBindSubmit = async () => {
|
||||||
bindFormLoading.value = true
|
bindFormLoading.value = true
|
||||||
try {
|
try {
|
||||||
await DeviceApi.bindDeviceGateway({
|
await DeviceApi.bindDeviceGateway({
|
||||||
ids: bindSelectedIds.value,
|
subIds: bindSelectedIds.value,
|
||||||
gatewayId: props.gatewayId
|
gatewayId: props.gatewayId
|
||||||
})
|
})
|
||||||
message.success('绑定成功')
|
message.success('绑定成功')
|
||||||
|
|
@ -240,7 +240,7 @@ const handleBindSubmit = async () => {
|
||||||
const handleUnbind = async (id: number) => {
|
const handleUnbind = async (id: number) => {
|
||||||
try {
|
try {
|
||||||
await message.confirm('确定要解绑该子设备吗?')
|
await message.confirm('确定要解绑该子设备吗?')
|
||||||
await DeviceApi.unbindDeviceGateway({ ids: [id] })
|
await DeviceApi.unbindDeviceGateway({ subIds: [id], gatewayId: props.gatewayId })
|
||||||
message.success('解绑成功')
|
message.success('解绑成功')
|
||||||
await getSubDeviceList()
|
await getSubDeviceList()
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
@ -250,7 +250,7 @@ const handleUnbind = async (id: number) => {
|
||||||
const handleUnbindBatch = async () => {
|
const handleUnbindBatch = async () => {
|
||||||
try {
|
try {
|
||||||
await message.confirm(`确定要解绑选中的 ${selectedIds.value.length} 个子设备吗?`)
|
await message.confirm(`确定要解绑选中的 ${selectedIds.value.length} 个子设备吗?`)
|
||||||
await DeviceApi.unbindDeviceGateway({ ids: selectedIds.value })
|
await DeviceApi.unbindDeviceGateway({ subIds: selectedIds.value, gatewayId: props.gatewayId })
|
||||||
message.success('批量解绑成功')
|
message.success('批量解绑成功')
|
||||||
selectedIds.value = []
|
selectedIds.value = []
|
||||||
await getSubDeviceList()
|
await getSubDeviceList()
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,20 @@
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-collapse>
|
<el-collapse>
|
||||||
<el-collapse-item title="更多配置">
|
<el-collapse-item title="更多配置">
|
||||||
|
<el-form-item label="动态注册" prop="registerEnabled">
|
||||||
|
<template #label>
|
||||||
|
<el-tooltip
|
||||||
|
content="设备动态注册无需一一烧录设备证书(DeviceSecret),每台设备烧录相同的产品证书,即 ProductKey 和 ProductSecret ,云端鉴权通过后下发设备证书,您可以根据需要开启或关闭动态注册,保障安全性。"
|
||||||
|
placement="top"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
动态注册
|
||||||
|
<Icon icon="ep:question-filled" class="ml-2px" />
|
||||||
|
</span>
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
<el-switch v-model="formData.registerEnabled" />
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="产品图标" prop="icon">
|
<el-form-item label="产品图标" prop="icon">
|
||||||
<UploadImg v-model="formData.icon" :height="'80px'" :width="'80px'" />
|
<UploadImg v-model="formData.icon" :height="'80px'" :width="'80px'" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
@ -120,7 +134,8 @@ const formData = ref({
|
||||||
description: undefined,
|
description: undefined,
|
||||||
deviceType: undefined,
|
deviceType: undefined,
|
||||||
netType: undefined,
|
netType: undefined,
|
||||||
codecType: CodecTypeEnum.ALINK
|
codecType: CodecTypeEnum.ALINK,
|
||||||
|
registerEnabled: false
|
||||||
})
|
})
|
||||||
const formRules = reactive({
|
const formRules = reactive({
|
||||||
productKey: [{ required: true, message: 'ProductKey 不能为空', trigger: 'blur' }],
|
productKey: [{ required: true, message: 'ProductKey 不能为空', trigger: 'blur' }],
|
||||||
|
|
@ -194,7 +209,8 @@ const resetForm = () => {
|
||||||
description: undefined,
|
description: undefined,
|
||||||
deviceType: undefined,
|
deviceType: undefined,
|
||||||
netType: undefined,
|
netType: undefined,
|
||||||
codecType: CodecTypeEnum.ALINK
|
codecType: CodecTypeEnum.ALINK,
|
||||||
|
registerEnabled: false
|
||||||
}
|
}
|
||||||
formRef.value?.resetFields()
|
formRef.value?.resetFields()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,28 @@
|
||||||
>
|
>
|
||||||
<dict-tag :type="DICT_TYPE.IOT_NET_TYPE" :value="product.netType" />
|
<dict-tag :type="DICT_TYPE.IOT_NET_TYPE" :value="product.netType" />
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="动态注册">
|
||||||
|
<el-tag :type="product.registerEnabled ? 'success' : 'info'">
|
||||||
|
{{ product.registerEnabled ? '已开启' : '已关闭' }}
|
||||||
|
</el-tag>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="产品密钥">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<span>{{ secretVisible ? product.productSecret : '******' }}</span>
|
||||||
|
<el-button link type="primary" class="ml-2" @click="secretVisible = !secretVisible">
|
||||||
|
<Icon :icon="secretVisible ? 'ep:hide' : 'ep:view'" />
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-if="secretVisible && product.productSecret"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
class="ml-1"
|
||||||
|
@click="copySecret"
|
||||||
|
>
|
||||||
|
<Icon icon="ep:document-copy" />
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="产品描述">{{ product.description }}</el-descriptions-item>
|
<el-descriptions-item label="产品描述">{{ product.description }}</el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
</ContentWrap>
|
</ContentWrap>
|
||||||
|
|
@ -29,6 +51,19 @@
|
||||||
import { DICT_TYPE } from '@/utils/dict'
|
import { DICT_TYPE } from '@/utils/dict'
|
||||||
import { DeviceTypeEnum, ProductVO } from '@/api/iot/product/product'
|
import { DeviceTypeEnum, ProductVO } from '@/api/iot/product/product'
|
||||||
import { formatDate } from '@/utils/formatTime'
|
import { formatDate } from '@/utils/formatTime'
|
||||||
|
import { useClipboard } from '@vueuse/core'
|
||||||
|
|
||||||
const { product } = defineProps<{ product: ProductVO }>()
|
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('复制成功')
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,41 @@ export const IotDeviceMessageMethodEnum = {
|
||||||
// ========== 设备状态 ==========
|
// ========== 设备状态 ==========
|
||||||
STATE_UPDATE: {
|
STATE_UPDATE: {
|
||||||
method: 'thing.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
|
upstream: true
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -39,6 +73,11 @@ export const IotDeviceMessageMethodEnum = {
|
||||||
name: '属性设置',
|
name: '属性设置',
|
||||||
upstream: false
|
upstream: false
|
||||||
},
|
},
|
||||||
|
PROPERTY_PACK_POST: {
|
||||||
|
method: 'thing.event.property.pack.post',
|
||||||
|
name: '批量上报(属性 + 事件 + 子设备)',
|
||||||
|
upstream: true
|
||||||
|
},
|
||||||
|
|
||||||
// ========== 设备事件 ==========
|
// ========== 设备事件 ==========
|
||||||
EVENT_POST: {
|
EVENT_POST: {
|
||||||
|
|
@ -59,6 +98,18 @@ export const IotDeviceMessageMethodEnum = {
|
||||||
method: 'thing.config.push',
|
method: 'thing.config.push',
|
||||||
name: '配置推送',
|
name: '配置推送',
|
||||||
upstream: false
|
upstream: false
|
||||||
|
},
|
||||||
|
|
||||||
|
// ========== OTA 固件 ==========
|
||||||
|
OTA_UPGRADE: {
|
||||||
|
method: 'thing.ota.upgrade',
|
||||||
|
name: 'OTA 固件信息推送',
|
||||||
|
upstream: false
|
||||||
|
},
|
||||||
|
OTA_PROGRESS: {
|
||||||
|
method: 'thing.ota.progress',
|
||||||
|
name: 'OTA 升级进度上报',
|
||||||
|
upstream: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue