设备数据流转配置完成

This commit is contained in:
ChenYi 2026-07-02 16:23:15 +08:00
parent cb3f0c1190
commit 6873c6aa97
6 changed files with 553 additions and 132 deletions

View File

@ -506,6 +506,32 @@ export const BatchCreateDeviceAggregationInputSchema = {
description: '批量创建设备信息'
} as const;
export const BatchSyncDeviceFromOneNETInputSchema = {
required: ['ioTPlatform', 'ioTPlatformProductId'],
type: 'object',
properties: {
ioTPlatform: {
'$ref': '#/components/schemas/IoTPlatformTypeEnum'
},
ioTPlatformProductId: {
minLength: 1,
type: 'string',
description: '物联网平台中对应的产品Id'
},
deviceAddresses: {
type: 'array',
items: {
type: 'string'
},
description: `要同步的设备地址列表(可选)。指定时只处理选中的设备,不按同步状态过滤;
IsPlatformPushSuccess == false `,
nullable: true
}
},
additionalProperties: false,
description: '从OneNET批量查询设备并同步本地'
} as const;
export const BatchUpdateStatusInputSchema = {
required: ['ids', 'upgradeStatus'],
type: 'object',
@ -1992,6 +2018,56 @@ export const DataEncryptionEnumSchema = {
description: '数据加密方式'
} as const;
export const DataPushDataTypeEnumSchema = {
enum: ['Property', 'Event', 'Status'],
type: 'string',
description: '数据推送的数据类型枚举(区分推送规则作用于哪一类上报数据)'
} as const;
export const DataPushRuleSchema = {
type: 'object',
properties: {
ruleName: {
type: 'string',
description: '规则备注名,便于管理(可空)',
nullable: true
},
enabled: {
type: 'boolean',
description: '是否启用该条规则'
},
dataType: {
'$ref': '#/components/schemas/DataPushDataTypeEnum'
},
includeNames: {
type: 'array',
items: {
type: 'string'
},
description: `【冗余保留,暂未启用】更细粒度的白名单(属性→字段名;事件/状态→事件名)。
"属性/事件/状态" = `,
nullable: true
},
pushType: {
'$ref': '#/components/schemas/DataPushTypeEnum'
},
pushServer: {
type: 'string',
description: '推送服务地址,例如 HTTPhttp://192.168.1.1:8080Redis192.168.1.1:6379',
nullable: true
},
pushInfo: {
type: 'string',
description: '推送信息HTTP 为推送路径Redis 为主题或 key',
nullable: true
}
},
additionalProperties: false,
description: `数据推送规则:一条规则 = "数据类型 + 目的地"。
//
DeviceCacheInfosApplication DTO JSON `
} as const;
export const DataPushTypeEnumSchema = {
enum: ['HttpPost', 'NormalReisSubscription', 'ReliableReisSubscription', 'RedisListPush'],
type: 'string',
@ -2209,6 +2285,35 @@ export const DeviceCommandForApiInputSchema = {
description: '设备命令'
} as const;
export const DeviceDataFlowConfigInputSchema = {
type: 'object',
properties: {
id: {
type: 'string',
description: '设备数据ID',
format: 'uuid'
},
dataPushOverride: {
type: 'boolean',
description: '是否覆盖产品级推送配置true 用本设备的推送配置false 继承所属产品配置'
},
isNeedPushData: {
type: 'boolean',
description: '是否需要推送数据(设备级开关,仅 JiShe.ServicePro.DeviceManagement.DeviceInfos.Dto.DeviceDataFlowConfigInput.DataPushOverride 为 true 时生效)'
},
dataPushRules: {
type: 'array',
items: {
'$ref': '#/components/schemas/DataPushRule'
},
description: '数据推送规则集合,一条规则 = "数据类型 + 白名单 + 目的地",仅 JiShe.ServicePro.DeviceManagement.DeviceInfos.Dto.DeviceDataFlowConfigInput.DataPushOverride 为 true 时生效',
nullable: true
}
},
additionalProperties: false,
description: '设备数据流转配置输入参数(设备级覆盖产品配置)'
} as const;
export const DeviceEncryptionMethodEnumSchema = {
enum: ['Unspecified', 'MD5', 'SHA1', 'SHA256'],
type: 'string',
@ -2665,6 +2770,22 @@ export const DeviceManagementInfoDtoSchema = {
type: 'string',
description: '最后主站地址',
nullable: true
},
dataPushOverride: {
type: 'boolean',
description: '是否覆盖产品级推送配置true 用本设备的推送配置false 继承所属产品配置'
},
isNeedPushData: {
type: 'boolean',
description: '是否需要推送数据(设备级开关,仅 JiShe.ServicePro.DeviceManagement.DeviceInfos.DeviceManagementInfoDto.DataPushOverride 为 true 时生效)'
},
dataPushRules: {
type: 'array',
items: {
'$ref': '#/components/schemas/DataPushRule'
},
description: '数据推送规则集合,仅 JiShe.ServicePro.DeviceManagement.DeviceInfos.DeviceManagementInfoDto.DataPushOverride 为 true 时生效',
nullable: true
}
},
additionalProperties: false
@ -7764,19 +7885,14 @@ export const OneNETDataFlowConfigInputSchema = {
},
isNeedPushData: {
type: 'boolean',
description: '是否需要推送数据'
description: '是否需要推送数据(产品级总开关)'
},
dataPushType: {
'$ref': '#/components/schemas/DataPushTypeEnum'
},
dataPushServer: {
type: 'string',
description: '数据推送服务地址信息例如HTTP地址http://192.168.1.1:8080Redis订阅地址192.168.1.1:6379',
nullable: true
},
dataPushInfo: {
type: 'string',
description: '数据推送信息,HTTP推送就是推送路径Redis推送主题或者key',
dataPushRules: {
type: 'array',
items: {
'$ref': '#/components/schemas/DataPushRule'
},
description: '数据推送规则集合,一条规则 = "数据类型 + 白名单 + 目的地",区分属性/事件/状态并支持分流',
nullable: true
}
},
@ -7986,19 +8102,14 @@ export const OneNETProductInfoDtoSchema = {
},
isNeedPushData: {
type: 'boolean',
description: '是否需要推送数据'
description: '是否需要推送数据(产品级总开关)'
},
dataPushType: {
'$ref': '#/components/schemas/DataPushTypeEnum'
},
dataPushServer: {
type: 'string',
description: '数据推送服务地址信息例如HTTP地址http://192.168.1.1:8080Redis订阅地址192.168.1.1:6379',
nullable: true
},
dataPushInfo: {
type: 'string',
description: '数据推送信息,HTTP推送就是推送路径Redis推送主题或者key',
dataPushRules: {
type: 'array',
items: {
'$ref': '#/components/schemas/DataPushRule'
},
description: '数据推送规则集合,区分属性/事件/状态 + 白名单 + 目的地(与实体同为 List<DataPushRule>Adapt 直接映射)',
nullable: true
}
},
@ -8464,6 +8575,11 @@ export const PageDeviceInputSchema = {
},
description: '设备地址列表(批量查询)',
nullable: true
},
isPlatformPushSuccess: {
type: 'boolean',
description: '平台推送是否成功null=不过滤true=已同步false=未同步)',
nullable: true
}
},
additionalProperties: false

File diff suppressed because one or more lines are too long

View File

@ -195,19 +195,17 @@ export type BatchCreateDeviceAggregationInput = {
};
/**
* OneNET批量查询设备信息并同步更新本地
* OneNET批量查询设备并同步本地
*/
export type BatchSyncDeviceFromOneNETInput = {
/**
*
*/
ioTPlatform: IoTPlatformTypeEnum;
/**
* Id
*/
ioTPlatformProductId: string;
/**
*
*
* IsPlatformPushSuccess == false
*/
deviceAddresses?: Array<(string)> | null;
};
@ -1128,6 +1126,42 @@ export type DataDictionaryDetailInfoDto = {
*/
export type DataEncryptionEnum = 'SM1' | 'SM2' | 'SM4' | 'DTLS' | 'Plaintext' | 'QuantumEncryption';
/**
*
*/
export type DataPushDataTypeEnum = 'Property' | 'Event' | 'Status';
/**
* = "数据类型 + 目的地"
* //
* DeviceCacheInfosApplication DTO JSON
*/
export type DataPushRule = {
/**
* 便
*/
ruleName?: (string) | null;
/**
*
*/
enabled?: boolean;
dataType?: DataPushDataTypeEnum;
/**
* /
* "属性/事件/状态" =
*/
includeNames?: Array<(string)> | null;
pushType?: DataPushTypeEnum;
/**
* HTTPhttp://192.168.1.1:8080Redis192.168.1.1:6379
*/
pushServer?: (string) | null;
/**
* HTTP Redis key
*/
pushInfo?: (string) | null;
};
/**
*
*/
@ -1253,6 +1287,28 @@ export type DeviceCommandForApiInput = {
};
};
/**
*
*/
export type DeviceDataFlowConfigInput = {
/**
* ID
*/
id?: string;
/**
* true false
*/
dataPushOverride?: boolean;
/**
* JiShe.ServicePro.DeviceManagement.DeviceInfos.Dto.DeviceDataFlowConfigInput.DataPushOverride true
*/
isNeedPushData?: boolean;
/**
* = "数据类型 + 白名单 + 目的地" JiShe.ServicePro.DeviceManagement.DeviceInfos.Dto.DeviceDataFlowConfigInput.DataPushOverride true
*/
dataPushRules?: Array<DataPushRule> | null;
};
/**
*
*/
@ -1537,6 +1593,18 @@ export type DeviceManagementInfoDto = {
*
*/
finalMainStation?: (string) | null;
/**
* true false
*/
dataPushOverride?: boolean;
/**
* JiShe.ServicePro.DeviceManagement.DeviceInfos.DeviceManagementInfoDto.DataPushOverride true
*/
isNeedPushData?: boolean;
/**
* JiShe.ServicePro.DeviceManagement.DeviceInfos.DeviceManagementInfoDto.DataPushOverride true
*/
dataPushRules?: Array<DataPushRule> | null;
};
/**
@ -4314,18 +4382,13 @@ export type OneNETDataFlowConfigInput = {
*/
id?: string;
/**
*
*
*/
isNeedPushData?: boolean;
dataPushType?: DataPushTypeEnum;
/**
* HTTP地址http://192.168.1.1:8080Redis订阅地址192.168.1.1:6379
* = "数据类型 + 白名单 + 目的地"//
*/
dataPushServer?: (string) | null;
/**
* ,HTTP推送就是推送路径Redis推送主题或者key
*/
dataPushInfo?: (string) | null;
dataPushRules?: Array<DataPushRule> | null;
};
export type OneNETProductInfoDto = {
@ -4462,18 +4525,13 @@ export type OneNETProductInfoDto = {
*/
thingModelInfos?: (string) | null;
/**
*
*
*/
isNeedPushData?: boolean;
dataPushType?: DataPushTypeEnum;
/**
* HTTP地址http://192.168.1.1:8080Redis订阅地址192.168.1.1:6379
* // + + List<DataPushRule>Adapt
*/
dataPushServer?: (string) | null;
/**
* ,HTTP推送就是推送路径Redis推送主题或者key
*/
dataPushInfo?: (string) | null;
dataPushRules?: Array<DataPushRule> | null;
};
export type OneNETProductInfoDtoPagedResultDto = {
@ -4737,7 +4795,7 @@ export type PageDeviceInput = {
/**
* null=true=false=
*/
isPlatformPushSuccess?: boolean;
isPlatformPushSuccess?: (boolean) | null;
};
/**
@ -7238,13 +7296,15 @@ export type PostAggregationDeviceBatchCreateAsyncResponse = (boolean);
export type PostAggregationDeviceBatchCreateAsyncError = unknown;
export type PostAggregationDeviceBatchSyncFromOneNETAsyncData = {
body?: BatchSyncDeviceFromOneNETInput;
export type PostAggregationDeviceBatchSyncDeviceFromOneNetAsyncData = {
query?: {
input?: BatchSyncDeviceFromOneNETInput;
};
};
export type PostAggregationDeviceBatchSyncFromOneNETAsyncResponse = (boolean);
export type PostAggregationDeviceBatchSyncDeviceFromOneNetAsyncResponse = (boolean);
export type PostAggregationDeviceBatchSyncFromOneNETAsyncError = unknown;
export type PostAggregationDeviceBatchSyncDeviceFromOneNetAsyncError = unknown;
export type PostAggregationDeviceDeleteAsyncData = {
query?: {
@ -7480,6 +7540,16 @@ export type PostDeviceInfoPageResponse = (DeviceManagementInfoDtoPagedResultDto)
export type PostDeviceInfoPageError = unknown;
export type PostDeviceInfoDeviceDataFlowConfigData = {
query?: {
input?: DeviceDataFlowConfigInput;
};
};
export type PostDeviceInfoDeviceDataFlowConfigResponse = (DeviceManagementInfoDto);
export type PostDeviceInfoDeviceDataFlowConfigError = unknown;
export type PostDeviceInfoCacheDeviceDataToRedisResponse = (boolean);
export type PostDeviceInfoCacheDeviceDataToRedisError = unknown;

View File

@ -0,0 +1,168 @@
<script setup lang="ts">
import type { DataPushRule } from '#/api-client';
import { ref, toRaw, watch } from 'vue';
import { Button, Input, Select, Switch } from 'ant-design-vue';
defineOptions({
name: 'DataPushRuleEditor',
});
const props = defineProps<{
// ""
disabled?: boolean;
rules?: DataPushRule[] | null;
}>();
const emit = defineEmits<{
(e: 'update:rules', value: DataPushRule[]): void;
}>();
// /
const dataTypeOptions = [
{ label: '属性', value: 'Property' },
{ label: '事件', value: 'Event' },
{ label: '状态', value: 'Status' },
];
const pushTypeOptions = [
{ label: 'HTTP POST', value: 'HttpPost' },
{ label: 'Redis 普通订阅', value: 'NormalReisSubscription' },
{ label: 'Redis 可靠订阅', value: 'ReliableReisSubscription' },
{ label: 'Redis List', value: 'RedisListPush' },
];
//
let seed = 0;
// v-model list
let lastEmitted: DataPushRule[] | null = null;
const list = ref<(DataPushRule & { _key: number })[]>([]);
watch(
() => props.rules,
(val) => {
// emit
// ref reactive toRaw
if (toRaw(val) === lastEmitted) {
return;
}
list.value = Array.isArray(val)
? val.map((r) => ({ ...r, _key: seed++ }))
: [];
},
{ immediate: true },
);
function emitChange() {
// _key
const out = list.value.map((r) => ({
ruleName: r.ruleName,
enabled: r.enabled,
dataType: r.dataType,
includeNames: r.includeNames ?? null,
pushType: r.pushType,
pushServer: r.pushServer,
pushInfo: r.pushInfo,
}));
lastEmitted = out;
emit('update:rules', out);
}
function addRule() {
list.value.push({
_key: seed++,
ruleName: '',
enabled: true,
dataType: 'Property',
includeNames: null,
pushType: 'HttpPost',
pushServer: '',
pushInfo: '',
});
emitChange();
}
function removeRule(index: number) {
list.value.splice(index, 1);
emitChange();
}
</script>
<template>
<div>
<!-- 表头 -->
<div class="mb-1 flex items-center gap-2 text-xs text-gray-500">
<span class="w-[44px] flex-shrink-0">启用</span>
<span class="w-[96px] flex-shrink-0">数据类型</span>
<span class="w-[150px] flex-shrink-0">推送方式</span>
<span class="flex-1">服务地址</span>
<span class="flex-1">路径 / 主题 / KEY</span>
<span class="w-[120px] flex-shrink-0">备注</span>
<span class="w-[48px] flex-shrink-0">操作</span>
</div>
<!-- 规则行用稳定 key v-for避免输入时被重建导致失焦 -->
<div
v-for="(item, index) in list"
:key="item._key"
class="mb-2 flex items-center gap-2"
>
<span class="w-[44px] flex-shrink-0">
<Switch
v-model:checked="item.enabled"
size="small"
:disabled="disabled"
@change="emitChange"
/>
</span>
<Select
v-model:value="item.dataType"
:options="dataTypeOptions"
:disabled="disabled"
class="w-[96px] flex-shrink-0"
@change="emitChange"
/>
<Select
v-model:value="item.pushType"
:options="pushTypeOptions"
:disabled="disabled"
class="w-[150px] flex-shrink-0"
@change="emitChange"
/>
<Input
v-model:value="item.pushServer"
:disabled="disabled"
class="flex-1"
placeholder="HTTPhttp://ip:portRedisip:port"
@change="emitChange"
/>
<Input
v-model:value="item.pushInfo"
:disabled="disabled"
class="flex-1"
placeholder="HTTP 为路径Redis 为主题或 key"
@change="emitChange"
/>
<Input
v-model:value="item.ruleName"
:disabled="disabled"
class="w-[120px] flex-shrink-0"
placeholder="可空"
@change="emitChange"
/>
<span class="w-[48px] flex-shrink-0">
<Button
type="link"
danger
size="small"
:disabled="disabled"
@click="removeRule(index)"
>
删除
</Button>
</span>
</div>
<Button type="dashed" block :disabled="disabled" @click="addRule">
+ 添加推送规则
</Button>
</div>
</template>

View File

@ -1,6 +1,7 @@
<script setup lang="ts">
import type { VbenFormProps } from '#/adapter/form';
import type { VxeGridProps } from '#/adapter/vxe-table';
import type { DataPushRule } from '#/api-client';
import type { Dayjs } from 'dayjs';
@ -18,6 +19,7 @@ import {
Modal,
Popover,
Select,
Switch,
Table,
Tag,
} from 'ant-design-vue';
@ -29,7 +31,7 @@ import { useVbenVxeGrid } from '#/adapter/vxe-table';
import {
getCommonGetSelectList,
postAggregationDeviceBatchCreateAsync,
postAggregationDeviceBatchSyncFromOneNETAsync,
postAggregationDeviceBatchSyncDeviceFromOneNetAsync,
postAggregationDeviceBindingDeviceThingModel,
postAggregationDeviceCallDeviceServiceForApiAsync,
postAggregationDeviceCreateAsync,
@ -42,6 +44,7 @@ import {
postAggregationDeviceRepushDeviceInfoToIoTplatform,
postAggregationDeviceUpdateDeviceSecKeyForApiAsync,
postDeviceInfoCacheDeviceDataToRedis,
postDeviceInfoDeviceDataFlowConfig,
postDeviceInfoPage,
postIoTplatformThingModelInfoGetIoTplatformThingModelService,
postIoTplatformThingModelInfoPageAsync,
@ -50,6 +53,7 @@ import { Icon } from '#/components/icon';
import { Loading } from '#/components/Loading';
import { TableAction } from '#/components/table-action';
import { $t } from '#/locales';
import DataPushRuleEditor from '#/views/components/DataPushRuleEditor.vue';
import {
addDeviceFormSchema,
@ -2714,7 +2718,7 @@ async function handleBatchSync() {
if (selectedAddresses.length > 0) {
body.deviceAddresses = selectedAddresses;
}
const resp = await postAggregationDeviceBatchSyncFromOneNETAsync({ body });
const resp = await postAggregationDeviceBatchSyncDeviceFromOneNetAsync({ body });
if (resp.data) {
//
Message.success(
@ -3047,6 +3051,82 @@ const toolbarActions = computed(() => [
},
},
]);
// ============ ============
const dataFlowRow = ref<Record<string, any>>({});
const dataFlowOverride = ref(false);
const dataFlowNeedPush = ref(false);
const dataFlowRules = ref<DataPushRule[]>([]);
//
function openDataFlowModal(record: any) {
if (!record.id) {
Message.error('设备数据ID不存在无法进行数据流转配置');
return;
}
dataFlowRow.value = record;
dataFlowOverride.value = record.dataPushOverride ?? false;
dataFlowNeedPush.value = record.isNeedPushData ?? false;
dataFlowRules.value = Array.isArray(record.dataPushRules)
? record.dataPushRules
: [];
deviceDataFlowModalApi.open();
}
//
async function submitDeviceDataFlowConfig() {
if (!dataFlowRow.value.id) {
Message.error('设备数据ID不存在无法进行数据流转配置');
return;
}
const override = dataFlowOverride.value;
const needPush = dataFlowNeedPush.value;
const rules = dataFlowRules.value ?? [];
//
if (override && needPush) {
if (rules.length === 0) {
Message.error('请至少添加一条推送规则');
return;
}
const invalid = rules.some(
(r) => r.enabled && (!r.pushServer || !r.pushInfo),
);
if (invalid) {
Message.error('启用的推送规则必须填写服务地址与推送路径/KEY');
return;
}
}
try {
const resp = await postDeviceInfoDeviceDataFlowConfig({
body: {
id: dataFlowRow.value.id,
dataPushOverride: override,
isNeedPushData: needPush,
//
dataPushRules: override ? rules : [],
},
});
if (resp.data) {
Message.success('数据流转配置成功');
deviceDataFlowModalApi.close();
gridApi.reload();
} else {
Message.error('数据流转配置失败');
}
} catch {
Message.error('数据流转配置失败');
}
}
const [DeviceDataFlowModal, deviceDataFlowModalApi] = useVbenModal({
draggable: true,
footer: true,
showCancelButton: true,
showConfirmButton: true,
onConfirm: submitDeviceDataFlowConfig,
});
</script>
<template>
@ -3163,6 +3243,10 @@ const toolbarActions = computed(() => [
style="padding: 4px 8px; text-align: left">
重推设备信息
</Button>
<Button type="text" size="small" @click="openDataFlowModal.bind(null, row)()"
style="padding: 4px 8px; text-align: left">
数据流转配置
</Button>
<Button type="text" size="small" @click="onDel.bind(null, row)()"
style="padding: 4px 8px; color: #ff4d4f; text-align: left">
{{ $t('common.delete') }}
@ -3181,6 +3265,37 @@ const toolbarActions = computed(() => [
<UserModal :title="editRow.id ? $t('common.edit') : $t('common.add')" class="w-[800px]">
<component :is="editRow.id ? EditForm : AddForm" />
</UserModal>
<DeviceDataFlowModal
:title="`数据流转配置 - ${dataFlowRow.deviceName || dataFlowRow.deviceAddress || ''}`"
class="w-[900px]"
>
<div class="space-y-3">
<div class="flex items-center gap-2">
<span class="w-24 flex-shrink-0 text-sm text-gray-600">覆盖产品配置</span>
<Switch
v-model:checked="dataFlowOverride"
checked-children="是"
un-checked-children="否"
/>
<span class="text-xs text-gray-400">
开启后使用本设备的推送配置关闭则继承所属产品配置
</span>
</div>
<div class="flex items-center gap-2">
<span class="w-24 flex-shrink-0 text-sm text-gray-600">是否推送数据</span>
<Switch
v-model:checked="dataFlowNeedPush"
:disabled="!dataFlowOverride"
checked-children="是"
un-checked-children="否"
/>
</div>
<DataPushRuleEditor
v-model:rules="dataFlowRules"
:disabled="!dataFlowOverride"
/>
</div>
</DeviceDataFlowModal>
<CommandModal
:title="`设备操作 - ${commandRow.deviceName || commandRow.deviceAddress || ''}`"
class="w-[1200px]"

View File

@ -1,6 +1,7 @@
<script setup lang="ts">
import type { VbenFormProps } from '#/adapter/form';
import type { VxeGridProps } from '#/adapter/vxe-table';
import type { DataPushRule } from '#/api-client';
import { h, nextTick, ref } from 'vue';
import { useRouter } from 'vue-router';
@ -12,7 +13,6 @@ import { message as Message, Tag } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import {
getCommonGetSelectList,
postAggregationIoTplatformUpdateIoTplatformProductThingModelInfoAsync,
postFilesDownload,
postFilesUpload,
@ -25,6 +25,7 @@ import {
} from '#/api-client';
import { TableAction } from '#/components/table-action';
import { $t } from '#/locales';
import DataPushRuleEditor from '#/views/components/DataPushRuleEditor.vue';
import {
addProductFormSchema,
@ -76,6 +77,8 @@ const gridOptions: VxeGridProps<any> = {
const [Grid, gridApi] = useVbenVxeGrid({ formOptions, gridOptions });
const editRow: Record<string, any> = ref({});
// v-model
const dataFlowRules = ref<DataPushRule[]>([]);
//
let selectedFile: File | null = null;
// loading 使 loading
@ -178,56 +181,6 @@ const [DataFlowForm, dataFlowFormApi] = useVbenForm({
class: 'ml-6',
},
},
{
component: 'ApiSelect',
fieldName: 'dataPushType',
label: '数据推送类型',
componentProps: {
api: getCommonGetSelectList,
params: {
query: {
typeName: 'DataPushTypeEnum',
},
},
labelField: 'value',
valueField: 'key',
optionsPropName: 'options',
immediate: true,
allowClear: true,
placeholder: '请选择推送类型',
afterFetch: (res: any) => {
let items = [];
if (Array.isArray(res)) {
items = res;
} else if (res && Array.isArray(res.items)) {
items = res.items;
} else if (res && Array.isArray(res.data)) {
items = res.data;
}
return items.map((item: any) => ({
...item,
key: String(item.key || item.value),
}));
},
},
},
{
component: 'Input',
fieldName: 'dataPushServer',
label: '推送服务器地址',
componentProps: {
placeholder:
'HTTP如 http://192.168.1.1:8080Redis如 192.168.1.1:6379',
},
},
{
component: 'Input',
fieldName: 'dataPushInfo',
label: '推送路径、KEY',
componentProps: {
placeholder: 'HTTP 为推送路径Redis 为主题或 key',
},
},
],
});
@ -411,13 +364,10 @@ async function onDataFlowConfig(record: any) {
editRow.value = record;
dataFlowFormApi.setValues({
isNeedPushData: record.isNeedPushData ?? false,
dataPushType:
record.dataPushType !== undefined && record.dataPushType !== null
? String(record.dataPushType)
: undefined,
dataPushServer: record.dataPushServer,
dataPushInfo: record.dataPushInfo,
});
dataFlowRules.value = Array.isArray(record.dataPushRules)
? record.dataPushRules
: [];
dataFlowModalApi.open();
}
@ -428,23 +378,19 @@ async function submitDataFlowConfig() {
}
const values = await dataFlowFormApi.getValues();
const rules = dataFlowRules.value ?? [];
//
//
if (values.isNeedPushData) {
if (
values.dataPushType === undefined ||
values.dataPushType === null ||
values.dataPushType === ''
) {
Message.error('请选择数据推送类型');
if (rules.length === 0) {
Message.error('请至少添加一条推送规则');
return;
}
if (!values.dataPushServer) {
Message.error('请输入推送服务器地址');
return;
}
if (!values.dataPushInfo) {
Message.error('请输入推送路径或 KEY');
const invalid = rules.some(
(r) => r.enabled && (!r.pushServer || !r.pushInfo),
);
if (invalid) {
Message.error('启用的推送规则必须填写服务地址与推送路径/KEY');
return;
}
}
@ -454,12 +400,7 @@ async function submitDataFlowConfig() {
body: {
id: editRow.value.id,
isNeedPushData: values.isNeedPushData ?? false,
dataPushType:
values.dataPushType !== undefined && values.dataPushType !== null
? Number(values.dataPushType)
: undefined,
dataPushServer: values.dataPushServer,
dataPushInfo: values.dataPushInfo,
dataPushRules: rules,
},
});
if (resp.data) {
@ -655,8 +596,9 @@ async function onThingModelUpdate(record: any) {
<component :is="editRow.id ? EditForm : AddForm" />
</UserModal>
<DataFlowModal title="数据流转配置" class="w-[800px]">
<DataFlowModal title="数据流转配置" class="w-[900px]">
<DataFlowForm />
<DataPushRuleEditor v-model:rules="dataFlowRules" />
</DataFlowModal>
</Page>
</template>