feat(iot):【网关设备:80%】动态注册的初步实现(已测试)
This commit is contained in:
@@ -21,7 +21,6 @@ export namespace IotDeviceApi {
|
|||||||
offlineTime?: Date; // 最后离线时间
|
offlineTime?: Date; // 最后离线时间
|
||||||
activeTime?: Date; // 设备激活时间
|
activeTime?: Date; // 设备激活时间
|
||||||
deviceSecret?: string; // 设备密钥,用于设备认证
|
deviceSecret?: string; // 设备密钥,用于设备认证
|
||||||
authType?: string; // 认证类型(如一机一密、动态注册)
|
|
||||||
config?: string; // 设备配置
|
config?: string; // 设备配置
|
||||||
latitude?: number; // 设备位置的纬度
|
latitude?: number; // 设备位置的纬度
|
||||||
longitude?: number; // 设备位置的经度
|
longitude?: number; // 设备位置的经度
|
||||||
@@ -201,3 +200,35 @@ export function getDeviceMessagePairPage(params: PageParam) {
|
|||||||
export function sendDeviceMessage(params: IotDeviceApi.DeviceMessageSendReq) {
|
export function sendDeviceMessage(params: IotDeviceApi.DeviceMessageSendReq) {
|
||||||
return requestClient.post('/iot/device/message/send', params);
|
return requestClient.post('/iot/device/message/send', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 绑定子设备到网关设备 */
|
||||||
|
export function bindDeviceGateway(gatewayId: number, subIds: number[]) {
|
||||||
|
return requestClient.put<boolean>('/iot/device/bind-gateway', {
|
||||||
|
gatewayId,
|
||||||
|
subIds,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 解绑子设备与网关设备 */
|
||||||
|
export function unbindDeviceGateway(gatewayId: number, subIds: number[]) {
|
||||||
|
return requestClient.put<boolean>('/iot/device/unbind-gateway', {
|
||||||
|
gatewayId,
|
||||||
|
subIds,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取网关设备的子设备列表 */
|
||||||
|
export function getSubDeviceList(gatewayId: number) {
|
||||||
|
return requestClient.get<IotDeviceApi.Device[]>(
|
||||||
|
'/iot/device/sub-device-list',
|
||||||
|
{ params: { gatewayId } },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取未绑定的子设备分页 */
|
||||||
|
export function getUnboundSubDevicePage(params: PageParam) {
|
||||||
|
return requestClient.get<PageResult<IotDeviceApi.Device>>(
|
||||||
|
'/iot/device/unbound-sub-device-page',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ export namespace IotProductApi {
|
|||||||
id?: number; // 产品编号
|
id?: number; // 产品编号
|
||||||
name: string; // 产品名称
|
name: string; // 产品名称
|
||||||
productKey?: string; // 产品标识
|
productKey?: string; // 产品标识
|
||||||
|
productSecret?: string; // 产品密钥
|
||||||
protocolId?: number; // 协议编号
|
protocolId?: number; // 协议编号
|
||||||
protocolType?: number; // 接入协议类型
|
protocolType?: number; // 接入协议类型
|
||||||
categoryId?: number; // 产品所属品类标识符
|
categoryId?: number; // 产品所属品类标识符
|
||||||
@@ -21,6 +22,7 @@ export namespace IotProductApi {
|
|||||||
codecType?: string; // 数据格式(编解码器类型)
|
codecType?: string; // 数据格式(编解码器类型)
|
||||||
dataFormat?: number; // 数据格式
|
dataFormat?: number; // 数据格式
|
||||||
validateType?: number; // 认证方式
|
validateType?: number; // 认证方式
|
||||||
|
registerEnabled?: boolean; // 是否开启动态注册
|
||||||
deviceCount?: number; // 设备数量
|
deviceCount?: number; // 设备数量
|
||||||
createTime?: Date; // 创建时间
|
createTime?: Date; // 创建时间
|
||||||
}
|
}
|
||||||
@@ -67,8 +69,13 @@ export function updateProductStatus(id: number, status: number) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 查询产品(精简)列表 */
|
/** 查询产品(精简)列表 */
|
||||||
export function getSimpleProductList() {
|
export function getSimpleProductList(deviceType?: number) {
|
||||||
return requestClient.get<IotProductApi.Product[]>('/iot/product/simple-list');
|
return requestClient.get<IotProductApi.Product[]>(
|
||||||
|
'/iot/product/simple-list',
|
||||||
|
{
|
||||||
|
params: { deviceType },
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 根据 ProductKey 获取产品信息 */
|
/** 根据 ProductKey 获取产品信息 */
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import type { VbenFormSchema } from '#/adapter/form';
|
import type { VbenFormSchema } from '#/adapter/form';
|
||||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
|
|
||||||
import { DeviceTypeEnum, DICT_TYPE } from '@vben/constants';
|
import { DICT_TYPE } from '@vben/constants';
|
||||||
import { getDictOptions } from '@vben/hooks';
|
import { getDictOptions } from '@vben/hooks';
|
||||||
|
|
||||||
import { z } from '#/adapter/form';
|
import { z } from '#/adapter/form';
|
||||||
import { getSimpleDeviceList } from '#/api/iot/device/device';
|
|
||||||
import { getSimpleDeviceGroupList } from '#/api/iot/device/group';
|
import { getSimpleDeviceGroupList } from '#/api/iot/device/group';
|
||||||
import { getSimpleProductList } from '#/api/iot/product/product';
|
import { getSimpleProductList } from '#/api/iot/product/product';
|
||||||
|
|
||||||
@@ -64,21 +63,6 @@ export function useBasicFormSchema(): VbenFormSchema[] {
|
|||||||
'支持英文字母、数字、下划线(_)、中划线(-)、点号(.)、半角冒号(:)和特殊字符@',
|
'支持英文字母、数字、下划线(_)、中划线(-)、点号(.)、半角冒号(:)和特殊字符@',
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
fieldName: 'gatewayId',
|
|
||||||
label: '网关设备',
|
|
||||||
component: 'ApiSelect',
|
|
||||||
componentProps: {
|
|
||||||
api: () => getSimpleDeviceList(DeviceTypeEnum.GATEWAY),
|
|
||||||
labelField: 'deviceName',
|
|
||||||
valueField: 'id',
|
|
||||||
placeholder: '子设备可选择父设备',
|
|
||||||
},
|
|
||||||
dependencies: {
|
|
||||||
triggerFields: ['deviceType'],
|
|
||||||
show: (values) => values.deviceType === DeviceTypeEnum.GATEWAY_SUB,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,11 @@ import type { IotDeviceApi } from '#/api/iot/device/device';
|
|||||||
|
|
||||||
import { computed, ref, watchEffect } from 'vue';
|
import { computed, ref, watchEffect } from 'vue';
|
||||||
|
|
||||||
|
import { IotDeviceMessageMethodEnum } from '@vben/constants';
|
||||||
|
|
||||||
import { Alert, Button, message, Textarea } from 'ant-design-vue';
|
import { Alert, Button, message, Textarea } from 'ant-design-vue';
|
||||||
|
|
||||||
import { sendDeviceMessage, updateDevice } from '#/api/iot/device/device';
|
import { sendDeviceMessage, updateDevice } from '#/api/iot/device/device';
|
||||||
import { IotDeviceMessageMethodEnum } from '#/views/iot/utils/constants';
|
|
||||||
|
|
||||||
defineOptions({ name: 'DeviceDetailConfig' });
|
defineOptions({ name: 'DeviceDetailConfig' });
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
} from 'vue';
|
} from 'vue';
|
||||||
|
|
||||||
import { Page } from '@vben/common-ui';
|
import { Page } from '@vben/common-ui';
|
||||||
import { DICT_TYPE } from '@vben/constants';
|
import { DICT_TYPE, IotDeviceMessageMethodEnum } from '@vben/constants';
|
||||||
import { IconifyIcon } from '@vben/icons';
|
import { IconifyIcon } from '@vben/icons';
|
||||||
import { formatDateTime } from '@vben/utils';
|
import { formatDateTime } from '@vben/utils';
|
||||||
|
|
||||||
@@ -19,7 +19,6 @@ import { Button, Select, Space, Switch, Tag } from 'ant-design-vue';
|
|||||||
|
|
||||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
import { getDeviceMessagePage } from '#/api/iot/device/device';
|
import { getDeviceMessagePage } from '#/api/iot/device/device';
|
||||||
import { IotDeviceMessageMethodEnum } from '#/views/iot/utils/constants';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
deviceId: number;
|
deviceId: number;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import type { ThingModelData } from '#/api/iot/thingmodel';
|
|||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
import { ContentWrap } from '@vben/common-ui';
|
import { ContentWrap } from '@vben/common-ui';
|
||||||
|
import { IotDeviceMessageMethodEnum } from '@vben/constants';
|
||||||
import { IconifyIcon } from '@vben/icons';
|
import { IconifyIcon } from '@vben/icons';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -26,7 +27,6 @@ import {
|
|||||||
import { sendDeviceMessage } from '#/api/iot/device/device';
|
import { sendDeviceMessage } from '#/api/iot/device/device';
|
||||||
import {
|
import {
|
||||||
DeviceStateEnum,
|
DeviceStateEnum,
|
||||||
IotDeviceMessageMethodEnum,
|
|
||||||
IoTThingModelTypeEnum,
|
IoTThingModelTypeEnum,
|
||||||
} from '#/views/iot/utils/constants';
|
} from '#/views/iot/utils/constants';
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,23 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { PageParam } from '@vben/request';
|
import type { VbenFormSchema, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
|
|
||||||
import type { VbenFormSchema } from '#/adapter/form';
|
|
||||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
|
||||||
import type { IotDeviceApi } from '#/api/iot/device/device';
|
import type { IotDeviceApi } from '#/api/iot/device/device';
|
||||||
import type { IotProductApi } from '#/api/iot/product/product';
|
|
||||||
|
|
||||||
import { onMounted, ref, watch } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
import { Page } from '@vben/common-ui';
|
import { confirm, Page, useVbenModal } from '@vben/common-ui';
|
||||||
import { DeviceTypeEnum, DICT_TYPE } from '@vben/constants';
|
import { DeviceTypeEnum, DICT_TYPE } from '@vben/constants';
|
||||||
import { getDictOptions } from '@vben/hooks';
|
import { formatDateTime, isEmpty } from '@vben/utils';
|
||||||
|
|
||||||
|
import { message } from 'ant-design-vue';
|
||||||
|
|
||||||
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
import { getDevicePage } from '#/api/iot/device/device';
|
import {
|
||||||
|
bindDeviceGateway,
|
||||||
|
getSubDeviceList,
|
||||||
|
getUnboundSubDevicePage,
|
||||||
|
unbindDeviceGateway,
|
||||||
|
} from '#/api/iot/device/device';
|
||||||
import { getSimpleProductList } from '#/api/iot/product/product';
|
import { getSimpleProductList } from '#/api/iot/product/product';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -24,10 +27,10 @@ interface Props {
|
|||||||
const props = defineProps<Props>();
|
const props = defineProps<Props>();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const products = ref<IotProductApi.Product[]>([]); // 产品列表
|
/** 子设备列表表格列配置 */
|
||||||
|
|
||||||
function useGridColumns(): VxeTableGridOptions['columns'] {
|
function useGridColumns(): VxeTableGridOptions['columns'] {
|
||||||
return [
|
return [
|
||||||
|
{ type: 'checkbox', width: 40 },
|
||||||
{
|
{
|
||||||
field: 'deviceName',
|
field: 'deviceName',
|
||||||
title: 'DeviceName',
|
title: 'DeviceName',
|
||||||
@@ -39,10 +42,9 @@ function useGridColumns(): VxeTableGridOptions['columns'] {
|
|||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'productId',
|
field: 'productName',
|
||||||
title: '所属产品',
|
title: '产品名称',
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
slots: { default: 'product' },
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'state',
|
field: 'state',
|
||||||
@@ -56,21 +58,125 @@ function useGridColumns(): VxeTableGridOptions['columns'] {
|
|||||||
{
|
{
|
||||||
field: 'onlineTime',
|
field: 'onlineTime',
|
||||||
title: '最后上线时间',
|
title: '最后上线时间',
|
||||||
minWidth: 180,
|
minWidth: 160,
|
||||||
formatter: 'formatDateTime',
|
formatter: ({ cellValue }) => formatDateTime(cellValue),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
field: 'actions',
|
||||||
title: '操作',
|
title: '操作',
|
||||||
width: 150,
|
width: 120,
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
slots: { default: 'actions' },
|
slots: { default: 'actions' },
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 搜索表单 schema */
|
const [Grid, gridApi] = useVbenVxeGrid<IotDeviceApi.Device>({
|
||||||
function useGridFormSchema(): VbenFormSchema[] {
|
gridOptions: {
|
||||||
|
columns: useGridColumns(),
|
||||||
|
height: 'auto',
|
||||||
|
keepSource: true,
|
||||||
|
proxyConfig: {
|
||||||
|
ajax: {
|
||||||
|
query: async () => {
|
||||||
|
if (!props.deviceId) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return await getSubDeviceList(props.deviceId);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rowConfig: {
|
||||||
|
keyField: 'id',
|
||||||
|
isHover: true,
|
||||||
|
},
|
||||||
|
toolbarConfig: {
|
||||||
|
refresh: true,
|
||||||
|
},
|
||||||
|
pagerConfig: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
gridEvents: {
|
||||||
|
checkboxAll: handleRowCheckboxChange,
|
||||||
|
checkboxChange: handleRowCheckboxChange,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 获取子设备列表 */
|
||||||
|
function getList() {
|
||||||
|
gridApi.query();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 打开设备详情 */
|
||||||
|
function openDeviceDetail(id: number) {
|
||||||
|
router.push({ name: 'IoTDeviceDetail', params: { id } });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 多选框选中数据 */
|
||||||
|
const checkedIds = ref<number[]>([]);
|
||||||
|
function handleRowCheckboxChange({
|
||||||
|
records,
|
||||||
|
}: {
|
||||||
|
records: IotDeviceApi.Device[];
|
||||||
|
}) {
|
||||||
|
checkedIds.value = records.map((item) => item.id!);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 解绑单个设备 */
|
||||||
|
async function handleUnbind(row: IotDeviceApi.Device) {
|
||||||
|
await confirm({ content: `确定要解绑子设备【${row.deviceName}】吗?` });
|
||||||
|
const hideLoading = message.loading({
|
||||||
|
content: `正在解绑【${row.deviceName}】...`,
|
||||||
|
duration: 0,
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
await unbindDeviceGateway(props.deviceId, [row.id!]);
|
||||||
|
message.success('解绑成功');
|
||||||
|
getList();
|
||||||
|
} finally {
|
||||||
|
hideLoading();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 批量解绑 */
|
||||||
|
async function handleUnbindBatch() {
|
||||||
|
await confirm({
|
||||||
|
content: `确定要解绑选中的 ${checkedIds.value.length} 个子设备吗?`,
|
||||||
|
});
|
||||||
|
const hideLoading = message.loading({
|
||||||
|
content: '正在批量解绑...',
|
||||||
|
duration: 0,
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
await unbindDeviceGateway(props.deviceId, checkedIds.value);
|
||||||
|
checkedIds.value = [];
|
||||||
|
message.success('批量解绑成功');
|
||||||
|
getList();
|
||||||
|
} finally {
|
||||||
|
hideLoading();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===================== 添加子设备弹窗 =====================
|
||||||
|
|
||||||
|
const addSelectedRowKeys = ref<number[]>([]);
|
||||||
|
|
||||||
|
/** 添加弹窗搜索表单 schema */
|
||||||
|
function useAddGridFormSchema(): VbenFormSchema[] {
|
||||||
return [
|
return [
|
||||||
|
{
|
||||||
|
fieldName: 'productId',
|
||||||
|
label: '产品',
|
||||||
|
component: 'ApiSelect',
|
||||||
|
componentProps: {
|
||||||
|
api: () => getSimpleProductList(DeviceTypeEnum.GATEWAY_SUB),
|
||||||
|
labelField: 'name',
|
||||||
|
valueField: 'id',
|
||||||
|
placeholder: '请选择产品',
|
||||||
|
allowClear: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
fieldName: 'deviceName',
|
fieldName: 'deviceName',
|
||||||
label: 'DeviceName',
|
label: 'DeviceName',
|
||||||
@@ -80,116 +186,171 @@ function useGridFormSchema(): VbenFormSchema[] {
|
|||||||
allowClear: true,
|
allowClear: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function useAddGridColumns(): VxeTableGridOptions['columns'] {
|
||||||
|
return [
|
||||||
|
{ type: 'checkbox', width: 40 },
|
||||||
{
|
{
|
||||||
fieldName: 'status',
|
field: 'deviceName',
|
||||||
label: '设备状态',
|
title: 'DeviceName',
|
||||||
component: 'Select',
|
minWidth: 150,
|
||||||
componentProps: {
|
},
|
||||||
options: getDictOptions(DICT_TYPE.IOT_DEVICE_STATE, 'number'),
|
{
|
||||||
placeholder: '请选择设备状态',
|
field: 'nickname',
|
||||||
allowClear: true,
|
title: '备注名称',
|
||||||
|
minWidth: 120,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'productName',
|
||||||
|
title: '产品名称',
|
||||||
|
minWidth: 120,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'state',
|
||||||
|
title: '设备状态',
|
||||||
|
minWidth: 100,
|
||||||
|
cellRender: {
|
||||||
|
name: 'CellDict',
|
||||||
|
props: { type: DICT_TYPE.IOT_DEVICE_STATE },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
const [Grid, gridApi] = useVbenVxeGrid<IotDeviceApi.Device>({
|
const [AddGrid, addGridApi] = useVbenVxeGrid<IotDeviceApi.Device>({
|
||||||
formOptions: {
|
formOptions: {
|
||||||
schema: useGridFormSchema(),
|
schema: useAddGridFormSchema(),
|
||||||
|
submitOnChange: true,
|
||||||
},
|
},
|
||||||
gridOptions: {
|
gridOptions: {
|
||||||
columns: useGridColumns(),
|
columns: useAddGridColumns(),
|
||||||
height: 'auto',
|
height: 400,
|
||||||
|
keepSource: true,
|
||||||
|
proxyConfig: {
|
||||||
|
ajax: {
|
||||||
|
query: async ({ page }, formValues) => {
|
||||||
|
return await getUnboundSubDevicePage({
|
||||||
|
pageNo: page.currentPage,
|
||||||
|
pageSize: page.pageSize,
|
||||||
|
...formValues,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
rowConfig: {
|
rowConfig: {
|
||||||
keyField: 'id',
|
keyField: 'id',
|
||||||
isHover: true,
|
isHover: true,
|
||||||
},
|
},
|
||||||
proxyConfig: {
|
|
||||||
ajax: {
|
|
||||||
query: async (
|
|
||||||
{
|
|
||||||
page,
|
|
||||||
}: {
|
|
||||||
page: { currentPage: number; pageSize: number };
|
|
||||||
},
|
|
||||||
formValues?: { deviceName?: string; status?: number },
|
|
||||||
) => {
|
|
||||||
if (!props.deviceId) {
|
|
||||||
return { list: [], total: 0 };
|
|
||||||
}
|
|
||||||
return await getDevicePage({
|
|
||||||
pageNo: page.currentPage,
|
|
||||||
pageSize: page.pageSize,
|
|
||||||
gatewayId: props.deviceId,
|
|
||||||
deviceType: DeviceTypeEnum.GATEWAY_SUB,
|
|
||||||
deviceName: formValues?.deviceName || undefined,
|
|
||||||
status: formValues?.status,
|
|
||||||
} as PageParam);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
toolbarConfig: {
|
toolbarConfig: {
|
||||||
refresh: true,
|
refresh: true,
|
||||||
search: true,
|
search: true,
|
||||||
},
|
},
|
||||||
pagerConfig: {
|
},
|
||||||
enabled: true,
|
gridEvents: {
|
||||||
},
|
checkboxAll: handleAddSelectionChange,
|
||||||
|
checkboxChange: handleAddSelectionChange,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
/** 获取产品名称 */
|
/** 处理添加弹窗表格选择变化 */
|
||||||
function getProductName(productId: number) {
|
function handleAddSelectionChange() {
|
||||||
const product = products.value.find((p) => p.id === productId);
|
const records = addGridApi.grid?.getCheckboxRecords() || [];
|
||||||
return product?.name || '-';
|
addSelectedRowKeys.value = records.map(
|
||||||
|
(record: IotDeviceApi.Device) => record.id!,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 查看详情 */
|
const [AddModal, addModalApi] = useVbenModal({
|
||||||
function openDetail(id: number) {
|
async onConfirm() {
|
||||||
router.push({ name: 'IoTDeviceDetail', params: { id } });
|
if (addSelectedRowKeys.value.length === 0) {
|
||||||
}
|
message.warning('请先选择要添加的子设备');
|
||||||
|
return;
|
||||||
/** 监听设备ID变化 */
|
}
|
||||||
watch(
|
addModalApi.lock();
|
||||||
() => props.deviceId,
|
try {
|
||||||
(newValue) => {
|
await bindDeviceGateway(props.deviceId, addSelectedRowKeys.value);
|
||||||
if (newValue) {
|
message.success('绑定成功');
|
||||||
gridApi.query();
|
await addModalApi.close();
|
||||||
|
addSelectedRowKeys.value = [];
|
||||||
|
getList();
|
||||||
|
} finally {
|
||||||
|
addModalApi.unlock();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async onOpenChange(isOpen: boolean) {
|
||||||
|
if (isOpen) {
|
||||||
|
addSelectedRowKeys.value = [];
|
||||||
|
await addGridApi.formApi?.resetForm();
|
||||||
|
await addGridApi.query();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
|
||||||
|
|
||||||
/** 初始化 */
|
|
||||||
onMounted(async () => {
|
|
||||||
// 获取产品列表
|
|
||||||
products.value = await getSimpleProductList();
|
|
||||||
|
|
||||||
// 如果设备ID存在,则查询列表
|
|
||||||
if (props.deviceId) {
|
|
||||||
gridApi.query();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/** 打开添加子设备弹窗 */
|
||||||
|
function openAddModal() {
|
||||||
|
addModalApi.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 监听 deviceId 变化 */
|
||||||
|
watch(
|
||||||
|
() => props.deviceId,
|
||||||
|
(newVal) => {
|
||||||
|
if (newVal) {
|
||||||
|
getList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true },
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Page auto-content-height>
|
<Page auto-content-height>
|
||||||
<!-- 子设备列表 -->
|
<!-- 子设备列表 -->
|
||||||
<Grid>
|
<Grid table-title="子设备列表">
|
||||||
<template #product="{ row }">
|
<template #toolbar-tools>
|
||||||
{{ getProductName(row.productId) }}
|
<TableAction
|
||||||
|
:actions="[
|
||||||
|
{
|
||||||
|
label: '添加子设备',
|
||||||
|
type: 'primary',
|
||||||
|
icon: ACTION_ICON.ADD,
|
||||||
|
onClick: openAddModal,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '批量解绑',
|
||||||
|
type: 'primary',
|
||||||
|
danger: true,
|
||||||
|
icon: ACTION_ICON.DELETE,
|
||||||
|
disabled: isEmpty(checkedIds),
|
||||||
|
onClick: handleUnbindBatch,
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #actions="{ row }">
|
<template #actions="{ row }">
|
||||||
<TableAction
|
<TableAction
|
||||||
:actions="[
|
:actions="[
|
||||||
{
|
{
|
||||||
label: '查看详情',
|
label: '查看',
|
||||||
type: 'link',
|
type: 'link',
|
||||||
icon: ACTION_ICON.VIEW,
|
onClick: () => openDeviceDetail(row.id!),
|
||||||
onClick: openDetail.bind(null, row.id!),
|
},
|
||||||
|
{
|
||||||
|
label: '解绑',
|
||||||
|
type: 'link',
|
||||||
|
danger: true,
|
||||||
|
onClick: () => handleUnbind(row),
|
||||||
},
|
},
|
||||||
]"
|
]"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
<!-- 添加子设备弹窗 -->
|
||||||
|
<AddModal title="添加子设备" class="w-3/5">
|
||||||
|
<AddGrid />
|
||||||
|
</AddModal>
|
||||||
</Page>
|
</Page>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import type { ThingModelData } from '#/api/iot/thingmodel';
|
|||||||
import { computed, onMounted, reactive, watch } from 'vue';
|
import { computed, onMounted, reactive, watch } from 'vue';
|
||||||
|
|
||||||
import { Page } from '@vben/common-ui';
|
import { Page } from '@vben/common-ui';
|
||||||
|
import { IotDeviceMessageMethodEnum } from '@vben/constants';
|
||||||
import { IconifyIcon } from '@vben/icons';
|
import { IconifyIcon } from '@vben/icons';
|
||||||
import { formatDateTime } from '@vben/utils';
|
import { formatDateTime } from '@vben/utils';
|
||||||
|
|
||||||
@@ -15,7 +16,6 @@ import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
|||||||
import { getDeviceMessagePairPage } from '#/api/iot/device/device';
|
import { getDeviceMessagePairPage } from '#/api/iot/device/device';
|
||||||
import {
|
import {
|
||||||
getEventTypeLabel,
|
getEventTypeLabel,
|
||||||
IotDeviceMessageMethodEnum,
|
|
||||||
IoTThingModelTypeEnum,
|
IoTThingModelTypeEnum,
|
||||||
} from '#/views/iot/utils/constants';
|
} from '#/views/iot/utils/constants';
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import type { ThingModelData } from '#/api/iot/thingmodel';
|
|||||||
import { computed, onMounted, reactive, watch } from 'vue';
|
import { computed, onMounted, reactive, watch } from 'vue';
|
||||||
|
|
||||||
import { Page } from '@vben/common-ui';
|
import { Page } from '@vben/common-ui';
|
||||||
|
import { IotDeviceMessageMethodEnum } from '@vben/constants';
|
||||||
import { IconifyIcon } from '@vben/icons';
|
import { IconifyIcon } from '@vben/icons';
|
||||||
import { formatDateTime } from '@vben/utils';
|
import { formatDateTime } from '@vben/utils';
|
||||||
|
|
||||||
@@ -15,7 +16,6 @@ import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
|||||||
import { getDeviceMessagePairPage } from '#/api/iot/device/device';
|
import { getDeviceMessagePairPage } from '#/api/iot/device/device';
|
||||||
import {
|
import {
|
||||||
getThingModelServiceCallTypeLabel,
|
getThingModelServiceCallTypeLabel,
|
||||||
IotDeviceMessageMethodEnum,
|
|
||||||
IoTThingModelTypeEnum,
|
IoTThingModelTypeEnum,
|
||||||
} from '#/views/iot/utils/constants';
|
} from '#/views/iot/utils/constants';
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as ProductSelect } from './select.vue';
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import type { IotProductApi } from '#/api/iot/product/product';
|
||||||
|
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
|
||||||
|
import { Select } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { getSimpleProductList } from '#/api/iot/product/product';
|
||||||
|
|
||||||
|
/** 产品下拉选择器组件 */
|
||||||
|
defineOptions({ name: 'ProductSelect' });
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
deviceType?: number; // 设备类型过滤
|
||||||
|
modelValue?: number;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'update:modelValue', value?: number): void;
|
||||||
|
(e: 'change', value?: number): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
const productList = ref<IotProductApi.Product[]>([]);
|
||||||
|
|
||||||
|
/** 处理选择变化 */
|
||||||
|
function handleChange(value?: number) {
|
||||||
|
emit('update:modelValue', value);
|
||||||
|
emit('change', value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取产品列表 */
|
||||||
|
async function getProductList() {
|
||||||
|
try {
|
||||||
|
loading.value = true;
|
||||||
|
productList.value = (await getSimpleProductList(props.deviceType)) || [];
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getProductList();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Select
|
||||||
|
:value="modelValue"
|
||||||
|
:options="productList.map((p) => ({ label: p.name, value: p.id }))"
|
||||||
|
:loading="loading"
|
||||||
|
placeholder="请选择产品"
|
||||||
|
allow-clear
|
||||||
|
class="w-full"
|
||||||
|
@change="handleChange"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
@@ -153,9 +153,20 @@ export function useBasicFormSchema(
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 高级设置表单字段(图标、图片、产品描述) */
|
/** 高级设置表单字段(图标、图片、产品描述、动态注册) */
|
||||||
export function useAdvancedFormSchema(): VbenFormSchema[] {
|
export function useAdvancedFormSchema(): VbenFormSchema[] {
|
||||||
return [
|
return [
|
||||||
|
{
|
||||||
|
fieldName: 'registerEnabled',
|
||||||
|
label: '动态注册',
|
||||||
|
component: 'Switch',
|
||||||
|
componentProps: {
|
||||||
|
checkedChildren: '开',
|
||||||
|
unCheckedChildren: '关',
|
||||||
|
},
|
||||||
|
defaultValue: false,
|
||||||
|
help: '设备动态注册无需一一烧录设备证书(DeviceSecret),每台设备烧录相同的产品证书,即 ProductKey 和 ProductSecret ,云端鉴权通过后下发设备证书,您可以根据需要开启或关闭动态注册,保障安全性。',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
fieldName: 'icon',
|
fieldName: 'icon',
|
||||||
label: '产品图标',
|
label: '产品图标',
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { IotProductApi } from '#/api/iot/product/product';
|
import type { IotProductApi } from '#/api/iot/product/product';
|
||||||
|
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
import { DeviceTypeEnum, DICT_TYPE } from '@vben/constants';
|
import { DeviceTypeEnum, DICT_TYPE } from '@vben/constants';
|
||||||
|
|
||||||
import { Card, Descriptions } from 'ant-design-vue';
|
import { Button, Card, Descriptions, message } from 'ant-design-vue';
|
||||||
|
|
||||||
import { DictTag } from '#/components/dict-tag';
|
import { DictTag } from '#/components/dict-tag';
|
||||||
|
|
||||||
@@ -13,11 +15,28 @@ interface Props {
|
|||||||
|
|
||||||
defineProps<Props>();
|
defineProps<Props>();
|
||||||
|
|
||||||
|
const showProductSecret = ref(false); // 是否显示产品密钥
|
||||||
|
|
||||||
/** 格式化日期 */
|
/** 格式化日期 */
|
||||||
function formatDate(date?: Date | string) {
|
function formatDate(date?: Date | string) {
|
||||||
if (!date) return '-';
|
if (!date) return '-';
|
||||||
return new Date(date).toLocaleString('zh-CN');
|
return new Date(date).toLocaleString('zh-CN');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 切换产品密钥显示状态 */
|
||||||
|
function toggleProductSecretVisible() {
|
||||||
|
showProductSecret.value = !showProductSecret.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 复制到剪贴板 */
|
||||||
|
async function copyToClipboard(text: string) {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(text);
|
||||||
|
message.success('复制成功');
|
||||||
|
} catch {
|
||||||
|
message.error('复制失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -54,6 +73,23 @@ function formatDate(date?: Date | string) {
|
|||||||
>
|
>
|
||||||
<DictTag :type="DICT_TYPE.IOT_NET_TYPE" :value="product.netType" />
|
<DictTag :type="DICT_TYPE.IOT_NET_TYPE" :value="product.netType" />
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item v-if="product.productSecret" label="ProductSecret">
|
||||||
|
<span v-if="showProductSecret">{{ product.productSecret }}</span>
|
||||||
|
<span v-else>********</span>
|
||||||
|
<Button class="ml-2" size="small" @click="toggleProductSecretVisible">
|
||||||
|
{{ showProductSecret ? '隐藏' : '显示' }}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
class="ml-2"
|
||||||
|
size="small"
|
||||||
|
@click="copyToClipboard(product.productSecret || '')"
|
||||||
|
>
|
||||||
|
复制
|
||||||
|
</Button>
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="动态注册">
|
||||||
|
{{ product.registerEnabled ? '已开启' : '未开启' }}
|
||||||
|
</Descriptions.Item>
|
||||||
<Descriptions.Item :span="3" label="产品描述">
|
<Descriptions.Item :span="3" label="产品描述">
|
||||||
{{ product.description || '-' }}
|
{{ product.description || '-' }}
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ async function getAdvancedFormValues() {
|
|||||||
}
|
}
|
||||||
// 表单未挂载(折叠状态),从 formData 中获取
|
// 表单未挂载(折叠状态),从 formData 中获取
|
||||||
return {
|
return {
|
||||||
|
registerEnabled: formData.value?.registerEnabled,
|
||||||
icon: formData.value?.icon,
|
icon: formData.value?.icon,
|
||||||
picUrl: formData.value?.picUrl,
|
picUrl: formData.value?.picUrl,
|
||||||
description: formData.value?.description,
|
description: formData.value?.description,
|
||||||
@@ -120,6 +121,7 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
await formApi.setValues(formData.value);
|
await formApi.setValues(formData.value);
|
||||||
// 如果存在高级字段数据,自动展开 Collapse
|
// 如果存在高级字段数据,自动展开 Collapse
|
||||||
if (
|
if (
|
||||||
|
formData.value?.registerEnabled ||
|
||||||
formData.value?.icon ||
|
formData.value?.icon ||
|
||||||
formData.value?.picUrl ||
|
formData.value?.picUrl ||
|
||||||
formData.value?.description
|
formData.value?.description
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onMounted, reactive, ref } from 'vue';
|
import { computed, onMounted, reactive, ref } from 'vue';
|
||||||
|
|
||||||
|
import { IotDeviceMessageMethodEnum } from '@vben/constants';
|
||||||
import { IconifyIcon } from '@vben/icons';
|
import { IconifyIcon } from '@vben/icons';
|
||||||
|
|
||||||
import { Button, Form, Select, Table } from 'ant-design-vue';
|
import { Button, Form, Select, Table } from 'ant-design-vue';
|
||||||
@@ -8,10 +9,7 @@ import { Button, Form, Select, Table } from 'ant-design-vue';
|
|||||||
import { getSimpleDeviceList } from '#/api/iot/device/device';
|
import { getSimpleDeviceList } from '#/api/iot/device/device';
|
||||||
import { getSimpleProductList } from '#/api/iot/product/product';
|
import { getSimpleProductList } from '#/api/iot/product/product';
|
||||||
import { getThingModelListByProductId } from '#/api/iot/thingmodel';
|
import { getThingModelListByProductId } from '#/api/iot/thingmodel';
|
||||||
import {
|
import { IoTThingModelTypeEnum } from '#/views/iot/utils/constants';
|
||||||
IotDeviceMessageMethodEnum,
|
|
||||||
IoTThingModelTypeEnum,
|
|
||||||
} from '#/views/iot/utils/constants';
|
|
||||||
|
|
||||||
const formData = ref<any[]>([]);
|
const formData = ref<any[]>([]);
|
||||||
const productList = ref<any[]>([]); // 产品列表
|
const productList = ref<any[]>([]); // 产品列表
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
// TODO @AI:感觉这块,放到 biz-iot-enum 里好点。
|
||||||
|
|
||||||
/** 检查值是否为空 */
|
/** 检查值是否为空 */
|
||||||
const isEmpty = (value: any): boolean => {
|
const isEmpty = (value: any): boolean => {
|
||||||
return value === null || value === undefined || value === '';
|
return value === null || value === undefined || value === '';
|
||||||
@@ -22,49 +24,6 @@ export const IoTThingModelTypeEnum = {
|
|||||||
EVENT: 3, // 事件
|
EVENT: 3, // 事件
|
||||||
};
|
};
|
||||||
|
|
||||||
/** IoT 设备消息的方法枚举 */
|
|
||||||
export const IotDeviceMessageMethodEnum = {
|
|
||||||
// ========== 设备状态 ==========
|
|
||||||
STATE_UPDATE: {
|
|
||||||
method: 'thing.state.update',
|
|
||||||
name: '设备状态变更',
|
|
||||||
upstream: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
// ========== 设备属性 ==========
|
|
||||||
PROPERTY_POST: {
|
|
||||||
method: 'thing.property.post',
|
|
||||||
name: '属性上报',
|
|
||||||
upstream: true,
|
|
||||||
},
|
|
||||||
PROPERTY_SET: {
|
|
||||||
method: 'thing.property.set',
|
|
||||||
name: '属性设置',
|
|
||||||
upstream: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
// ========== 设备事件 ==========
|
|
||||||
EVENT_POST: {
|
|
||||||
method: 'thing.event.post',
|
|
||||||
name: '事件上报',
|
|
||||||
upstream: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
// ========== 服务调用 ==========
|
|
||||||
SERVICE_INVOKE: {
|
|
||||||
method: 'thing.service.invoke',
|
|
||||||
name: '服务调用',
|
|
||||||
upstream: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
// ========== 设备配置 ==========
|
|
||||||
CONFIG_PUSH: {
|
|
||||||
method: 'thing.config.push',
|
|
||||||
name: '配置推送',
|
|
||||||
upstream: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// IoT 产品物模型服务调用方式枚举
|
// IoT 产品物模型服务调用方式枚举
|
||||||
export const IoTThingModelServiceCallTypeEnum = {
|
export const IoTThingModelServiceCallTypeEnum = {
|
||||||
ASYNC: {
|
ASYNC: {
|
||||||
|
|||||||
@@ -26,18 +26,105 @@ export const ProductStatusEnum = {
|
|||||||
PUBLISHED: 1, // 已发布
|
PUBLISHED: 1, // 已发布
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
/**
|
|
||||||
* 产品定位类型枚举
|
|
||||||
*/
|
|
||||||
export const LocationTypeEnum = {
|
|
||||||
IP: 1, // IP 定位
|
|
||||||
MANUAL: 3, // 手动定位
|
|
||||||
MODULE: 2, // 设备定位
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据格式(编解码器类型)枚举
|
* 数据格式(编解码器类型)枚举
|
||||||
*/
|
*/
|
||||||
export const CodecTypeEnum = {
|
export const CodecTypeEnum = {
|
||||||
ALINK: 'Alink', // 阿里云 Alink 协议
|
ALINK: 'Alink', // 阿里云 Alink 协议
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IoT 设备消息的方法枚举
|
||||||
|
*/
|
||||||
|
export const IotDeviceMessageMethodEnum = {
|
||||||
|
// ========== 设备状态 ==========
|
||||||
|
STATE_UPDATE: {
|
||||||
|
method: 'thing.state.update',
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
|
||||||
|
// ========== 设备属性 ==========
|
||||||
|
PROPERTY_POST: {
|
||||||
|
method: 'thing.property.post',
|
||||||
|
name: '属性上报',
|
||||||
|
upstream: true,
|
||||||
|
},
|
||||||
|
PROPERTY_SET: {
|
||||||
|
method: 'thing.property.set',
|
||||||
|
name: '属性设置',
|
||||||
|
upstream: false,
|
||||||
|
},
|
||||||
|
PROPERTY_PACK_POST: {
|
||||||
|
method: 'thing.event.property.pack.post',
|
||||||
|
name: '批量上报(属性 + 事件 + 子设备)',
|
||||||
|
upstream: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
// ========== 设备事件 ==========
|
||||||
|
EVENT_POST: {
|
||||||
|
method: 'thing.event.post',
|
||||||
|
name: '事件上报',
|
||||||
|
upstream: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
// ========== 服务调用 ==========
|
||||||
|
SERVICE_INVOKE: {
|
||||||
|
method: 'thing.service.invoke',
|
||||||
|
name: '服务调用',
|
||||||
|
upstream: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
// ========== 设备配置 ==========
|
||||||
|
CONFIG_PUSH: {
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|||||||
Reference in New Issue
Block a user