Commit 519cbd4dc1bf2378fa83704c3f23b0cbc051debf

Authored by ww
1 parent 239c2b3d

feat: 实现属性集和变换所有节点

Showing 55 changed files with 2186 additions and 256 deletions
1 -import { DeviceInfoItemType } from './model'; 1 +import { DeviceInfoItemType, DeviceTypeItem } from './model';
2 import { TBPaginationResult } from '/#/axios'; 2 import { TBPaginationResult } from '/#/axios';
3 import { defHttp } from '/@/utils/http/axios'; 3 import { defHttp } from '/@/utils/http/axios';
4 4
5 enum Api { 5 enum Api {
6 GET_DEVICE_INFOS = '/tenant/deviceInfos', 6 GET_DEVICE_INFOS = '/tenant/deviceInfos',
  7 + GET_DEVICE_TYPE = '/device/types',
7 } 8 }
8 9
9 export const getDeviceInfos = () => { 10 export const getDeviceInfos = () => {
@@ -14,3 +15,12 @@ export const getDeviceInfos = () => { @@ -14,3 +15,12 @@ export const getDeviceInfos = () => {
14 { joinPrefix: false } 15 { joinPrefix: false }
15 ); 16 );
16 }; 17 };
  18 +
  19 +export const getDeviceTypes = () => {
  20 + return defHttp.get<TBPaginationResult<DeviceTypeItem[]>>(
  21 + {
  22 + url: Api.GET_DEVICE_TYPE,
  23 + },
  24 + { joinPrefix: false }
  25 + );
  26 +};
@@ -38,3 +38,9 @@ export interface Configuration { @@ -38,3 +38,9 @@ export interface Configuration {
38 export interface TransportConfiguration { 38 export interface TransportConfiguration {
39 type: string; 39 type: string;
40 } 40 }
  41 +
  42 +export interface DeviceTypeItem {
  43 + tenantId: Id;
  44 + entityType: string;
  45 + type: string;
  46 +}
@@ -132,4 +132,7 @@ export type ComponentType = @@ -132,4 +132,7 @@ export type ComponentType =
132 | 'OrgTreeSelect' 132 | 'OrgTreeSelect'
133 | 'ExtendDesc' 133 | 'ExtendDesc'
134 | 'JavaScriptFunctionEditor' 134 | 'JavaScriptFunctionEditor'
135 - | 'JavaScriptFilterFunctionModal'; 135 + | 'JavascriptEditorWithTestModal'
  136 + | 'AttributeConfiguration'
  137 + | 'CorrelationFilters'
  138 + | 'RelationsQuery';
@@ -14,8 +14,13 @@ export enum DirectionNameEnum { @@ -14,8 +14,13 @@ export enum DirectionNameEnum {
14 * @description 关联类型 14 * @description 关联类型
15 */ 15 */
16 export enum RelationTypeEnum { 16 export enum RelationTypeEnum {
17 - CONTAINS = 'contains',  
18 - MANAGES = 'manages', 17 + CONTAINS = 'CONTAINS',
  18 + MANAGES = 'MANAGES',
  19 +}
  20 +
  21 +export enum RelationTypeNameEnum {
  22 + CONTAINS = 'Contains',
  23 + MANAGES = 'Manages',
19 } 24 }
20 25
21 /** 26 /**
@@ -119,3 +124,107 @@ export enum MessageTypesFilterNameEnum { @@ -119,3 +124,107 @@ export enum MessageTypesFilterNameEnum {
119 RPC_TIMEOUT = 'RPC Timeout', 124 RPC_TIMEOUT = 'RPC Timeout',
120 RPC_FAILED = 'RPC Failed', 125 RPC_FAILED = 'RPC Failed',
121 } 126 }
  127 +
  128 +// Enrichment Customer details
  129 +export enum DetailsListEnum {
  130 + TITLE = 'TITLE',
  131 + COUNTRY = 'COUNTRY',
  132 + STATE = 'STATE',
  133 + CITY = 'CITY',
  134 + ZIP = 'ZIP',
  135 + ADDRESS = 'ADDRESS',
  136 + ADDRESS_2 = 'ADDRESS_2',
  137 + PHONE = 'PHONE',
  138 + EMAIL = 'EMAIL',
  139 + ADDITIONAL_INFO = 'ADDITIONAL_INFO',
  140 +}
  141 +
  142 +export enum DetailsListNameEnum {
  143 + TITLE = 'Title',
  144 + COUNTRY = 'Country',
  145 + STATE = 'State',
  146 + CITY = 'City',
  147 + ZIP = 'Zip',
  148 + ADDRESS = 'Address',
  149 + ADDRESS_2 = 'Address2',
  150 + PHONE = 'Phone',
  151 + EMAIL = 'Email',
  152 + ADDITIONAL_INFO = 'Additional Info',
  153 +}
  154 +
  155 +// Enrichment Originator Telemetry FetchMode
  156 +export enum FetchModeEnum {
  157 + FIRST = 'FIRST',
  158 + LAST = 'LAST',
  159 + ALL = 'ALL',
  160 +}
  161 +
  162 +// Enrichment Originator Telemetry Time interval unit
  163 +export enum TimeIntervalUnitEnum {
  164 + MILLISECONDS = 'MILLISECONDS',
  165 + SECONDS = 'SECONDS',
  166 + MINUTES = 'MINUTES',
  167 + HOURS = 'HOURS',
  168 + DAYS = 'DAYS',
  169 +}
  170 +
  171 +export enum TimeIntervalUnitNameEnum {
  172 + MILLISECONDS = 'Milliseconds',
  173 + SECONDS = 'Seconds',
  174 + MINUTES = 'Minutes',
  175 + HOURS = 'Hours',
  176 + DAYS = 'Days',
  177 +}
  178 +
  179 +// Enrichment Originator Telemetry OrderBy
  180 +export enum OrderByEnum {
  181 + DESC = 'DESC',
  182 + ASC = 'ASC',
  183 +}
  184 +
  185 +// Enrichment Originator Telemetry Aggregation
  186 +export enum AggregationEnum {
  187 + MIN = 'MIN',
  188 + MAX = 'MAX',
  189 + AVG = 'AVG',
  190 + SUM = 'SUM',
  191 + COUNT = 'COUNT',
  192 + NONE = 'NONE',
  193 +}
  194 +
  195 +export enum AggregationNameEnum {
  196 + MIN = '最少值',
  197 + MAX = '最大值',
  198 + AVG = '平均值',
  199 + SUM = '求和',
  200 + COUNT = '计数',
  201 + NONE = '空',
  202 +}
  203 +
  204 +// Originator source
  205 +export enum OriginatorSourceEnum {
  206 + CUSTOMER = 'CUSTOMER',
  207 + TENANT = 'TENANT',
  208 + RELATED = 'RELATED',
  209 + ALARM_ORIGINATOR = 'ALARM_ORIGINATOR',
  210 +}
  211 +
  212 +export enum OriginatorSourceNameEnum {
  213 + CUSTOMER = 'Customer',
  214 + TENANT = 'Tenant',
  215 + RELATED = 'Related',
  216 + ALARM_ORIGINATOR = 'Alarm Originator',
  217 +}
  218 +
  219 +// Transformation To Email
  220 +export enum MailBodyTypeEnum {
  221 + DYNAMIC = 'dynamic',
  222 + HTML = 'true',
  223 + PLAIN_TEXT = 'false',
  224 +}
  225 +
  226 +export enum MailBodyTypeNameEnum {
  227 + DYNAMIC = 'Dynamic',
  228 + HTML = 'HTML',
  229 + PLAIN_TEXT = 'Plain Text',
  230 +}
  1 +// Assign to customer
  2 +export enum AssignToCustomerFieldsEnum {
  3 + CUSTOMER_NAME_PATTERN = 'customerNamePattern',
  4 + CREATE_CUSTOMER_IF_NOT_EXISTS = 'createCustomerIfNotExists',
  5 + CUSTOMER_CACHE_EXPIRATION = 'customerCacheExpiration',
  6 +}
  7 +
  8 +export enum AssignToCustomerFieldsNameEnum {
  9 + CUSTOMER_NAME_PATTERN = 'Customer name pattern',
  10 + CREATE_CUSTOMER_IF_NOT_EXISTS = 'Create new customer if not exists',
  11 + CUSTOMER_CACHE_EXPIRATION = 'Customers cache expiration time(sec)',
  12 +}
  13 +
  14 +// clear alarm
  15 +export enum ClearAlarmFieldsEnum {
  16 + ALARM_TYPE = 'alarmType',
  17 + ALARM_DETAILS_BUILD_JS = 'alarmDetailsBuildJs',
  18 +}
  19 +
  20 +export enum ClearAlarmFieldsNameEnum {
  21 + ALARM_TYPE = 'Alarm type',
  22 + ALARM_DETAILS_BUILD_JS = 'Alarm details builder',
  23 +}
  24 +
  25 +// Create alarm
  26 +export enum CreateAlarmFieldsEnum {
  27 + ALARM_DETAILS_BUILD_JS = 'alarmDetailsBuildJs',
  28 + USE_MESSAGE_ALARM_DATA = 'useMessageAlarmData',
  29 + OVERWRITE_ALARM_DETAILS = 'overwriteAlarmDetails',
  30 + ALARM_TYPE = 'alarmType',
  31 + SEVERITY = 'severity',
  32 + PROPAGATE = 'propagate',
  33 + RELATION_TYPES = 'relationTypes',
  34 + PROPAGATE_TO_OWNER = 'propagateToOwner',
  35 + PROPAGATE_TO_TENANT = 'propagateToTenant',
  36 + DYNAMIC_SEVERITY = 'dynamicSeverity',
  37 +}
  38 +
  39 +export enum CreateAlarmFieldsNameEnum {
  40 + ALARM_DETAILS_BUILD_JS = 'Alarm details builder',
  41 + USE_MESSAGE_ALARM_DATA = 'Use message alarm data',
  42 + OVERWRITE_ALARM_DETAILS = 'overwriteAlarmDetails',
  43 + ALARM_TYPE = 'Alarm type',
  44 + SEVERITY = 'Alarm severity pattern',
  45 + PROPAGATE = 'Propagate alarm to related entities',
  46 + RELATION_TYPES = 'Relation types to propagate',
  47 + PROPAGATE_TO_OWNER = 'Propagate alarm to entity owner (Customer or Tenant)',
  48 + PROPAGATE_TO_TENANT = 'Propagate alarm to Tenant',
  49 + DYNAMIC_SEVERITY = 'dynamicSeverity',
  50 +}
  1 +export enum CommonFieldsEnum {
  2 + NAME = 'name',
  3 + DESCRIPTION = 'description',
  4 + DEBUG_MODE = 'debugMode',
  5 +}
  6 +
  7 +export enum CommonFieldsNameEnum {
  8 + NAME = '名称',
  9 + DESCRIPTION = '说明',
  10 + DEBUG_MODE = '调试模式',
  11 +}
  12 +
  13 +export const CommonFields = { ...CommonFieldsEnum };
  14 +export const CommonFieldsName = { ...CommonFieldsNameEnum };
  15 +export type CommonFieldsType = typeof CommonFields;
  1 +// Enrichment Calculate delta
  2 +export enum CalculateDeltaFieldsEnum {
  3 + INPUT_VALUE_KEY = 'inputValueKey',
  4 + OUTPUT_VALUE_KEY = 'outputValueKey',
  5 + ROUND = 'round',
  6 + USE_CACHE = 'useCache',
  7 + TELL_FAILURE_IF_DELTA_IS_NEGATIVE = 'tellFailureIfDeltaIsNegative',
  8 + ADD_PERIOD_BETWEEN_MSGS = 'addPeriodBetweenMsgs',
  9 + PERIOD_VALUE_KEY = 'periodValueKey',
  10 +}
  11 +
  12 +export enum CalculateDeltaFieldsNameEnum {
  13 + INPUT_VALUE_KEY = 'Input value key',
  14 + OUTPUT_VALUE_KEY = 'Output value key',
  15 + ROUND = 'Decimals',
  16 + USE_CACHE = 'Use cache for latest value',
  17 + TELL_FAILURE_IF_DELTA_IS_NEGATIVE = 'Tell Failure if delta is negative',
  18 + ADD_PERIOD_BETWEEN_MSGS = 'Add period between messages',
  19 + PERIOD_VALUE_KEY = 'Period value key',
  20 +}
  21 +
  22 +// Enrichment Customer Attributes
  23 +export enum CustomerAttributesFieldsEnum {
  24 + ATTR_MAPING = 'attrMapping',
  25 + TELEMETRY = 'telemetry',
  26 +}
  27 +
  28 +export enum CustomerAttributesFieldsNameEnum {
  29 + ATTR_MAPING = 'Attributes mapping',
  30 + TELEMETRY = 'Latest telemetry',
  31 +}
  32 +
  33 +// Enrichment Customer details
  34 +export enum CustomerDetailsFieldsEnum {
  35 + DETAILS_LIST = 'detailsList',
  36 + ADD_TO_METADATA = 'addToMetadata',
  37 +}
  38 +
  39 +export enum CustomerDetailsFieldsNameEnum {
  40 + DETAILS_LIST = 'Select entity details',
  41 + ADD_TO_METADATA = 'Add selected details to message metadata',
  42 +}
  43 +
  44 +// Enrichment Originator attributes
  45 +export enum OriginatorAttributesEnum {
  46 + TELL_FAILURE_IF_ABSENT = 'tellFailureIfAbsent',
  47 + CLIENT_ATTRIBUTE_NAMES = 'clientAttributeNames',
  48 + SHARED_ATTRIBUTE_NAMES = 'sharedAttributeNames',
  49 + SERVER_ATTRIBUTE_NAMES = 'serverAttributeNames',
  50 + LATEST_TS_KEY_NAMES = 'latestTsKeyNames',
  51 + GET_LATEST_VALUE_WITH_TS = 'getLatestValueWithTs',
  52 +}
  53 +
  54 +export enum OriginatorAttributesNameEnum {
  55 + TELL_FAILURE_IF_ABSENT = 'Tell Failure',
  56 + CLIENT_ATTRIBUTE_NAMES = 'Client attributes',
  57 + SHARED_ATTRIBUTE_NAMES = 'Shared attributes',
  58 + SERVER_ATTRIBUTE_NAMES = 'Server attributes',
  59 + LATEST_TS_KEY_NAMES = 'Latest timeseries',
  60 + GET_LATEST_VALUE_WITH_TS = 'Fetch Latest telemetry with Timestamp',
  61 +}
  62 +
  63 +// Enrichment Originator Fields
  64 +export enum OriginatorFieldsEnum {
  65 + FIELDS_MAPPING = 'fieldsMapping',
  66 +}
  67 +
  68 +export enum OriginatorFieldsNameEnum {
  69 + FIELDS_MAPPING = 'Fields mapping',
  70 +}
  71 +
  72 +// Enrichment originator telemetry
  73 +export enum OriginatorTelemetryFieldsEnum {
  74 + AGGREGATION = 'aggregation',
  75 + FETCH_MODE = 'fetchMode',
  76 + ORDER_BY = 'orderBy',
  77 + LIMIT = 'limit',
  78 + USE_METADATA_INTERVAL_PATTERNS = 'useMetadataIntervalPatterns',
  79 + START_INTERVAL = 'startInterval',
  80 + START_INTERVAL_TIME_UNIT = 'startIntervalTimeUnit',
  81 + END_INTERVAL = 'endInterval',
  82 + END_INTERVAL_TIME_UNIT = 'endIntervalTimeUnit',
  83 + START_INTERVAL_PATTERN = 'startIntervalPattern',
  84 + END_INTERVAL_PATTERN = 'endIntervalPattern',
  85 +}
  86 +
  87 +export enum OriginatorTelemetryFieldsNameEnum {
  88 + AGGREGATION = '数据聚合功能',
  89 + FETCH_MODE = 'Fetch Mode',
  90 + ORDER_BY = 'Order by',
  91 + LIMIT = 'Limit',
  92 + USE_METADATA_INTERVAL_PATTERNS = 'useMetadataIntervalPatterns',
  93 + START_INTERVAL = 'Start Interval',
  94 + START_INTERVAL_TIME_UNIT = 'Start Interval Time Unit',
  95 + END_INTERVAL = 'End Interval',
  96 + END_INTERVAL_TIME_UNIT = 'End Interval Time Unit',
  97 + START_INTERVAL_PATTERN = 'startIntervalPattern',
  98 + END_INTERVAL_PATTERN = 'endIntervalPattern',
  99 +}
  100 +
  101 +// Enrichment Related attributes
  102 +export enum RelatedAttributesFieldsEnum {
  103 + RELATIONS_QUERY = 'relationsQuery',
  104 + ATTR_MAPPING = 'attrMapping',
  105 +}
  106 +
  107 +export enum RelatedAttributesFieldNameEnum {
  108 + RELATIONS_QUERY = 'Relations query',
  109 + ATTR_MAPPING = 'Attributes mapping',
  110 +}
  111 +
  112 +// Enrichment Related device Attributes
  113 +export enum RelatedDeviceAttributeFieldsEnum {
  114 + DEVICE_RELATIONS_QUERY = 'deviceRelationsQuery',
  115 + TELL_FAILURE_IF_ABSENT = 'tellFailureIfAbsent',
  116 + CLIENT_ATTRIBUTE_NAMES = 'clientAttributeNames',
  117 + SHARED_ATTRIBUTE_NAMES = 'sharedAttributeNames',
  118 + SERVER_ATTRIBUTE_NAMES = 'serverAttributeNames',
  119 + LATEST_TS_KEY_NAMES = 'latestTsKeyNames',
  120 + GET_LATEST_VALUE_WITH_TS = 'getLatestValueWithTs',
  121 + FETCH_LAST_LEVEL_ONLY = 'fetchLastLevelOnly',
  122 +
  123 + // DEVICE_RELATIONS_QUERY
  124 + DIRECTION = 'direction',
  125 + MAX_LEVEL = 'maxLevel',
  126 + RELATION_TYPE = 'relationType',
  127 + DEVICE_TYPES = 'deviceTypes',
  128 +}
  129 +
  130 +export enum RelatedDeviceAttributeFieldsNameEnum {
  131 + DEVICE_RELATIONS_QUERY = 'deviceRelationsQuery',
  132 + TELL_FAILURE_IF_ABSENT = 'Tell Failure',
  133 + CLIENT_ATTRIBUTE_NAMES = 'Client attributes',
  134 + SHARED_ATTRIBUTE_NAMES = 'Shared attributes',
  135 + SERVER_ATTRIBUTE_NAMES = 'Server attributes',
  136 + LATEST_TS_KEY_NAMES = 'Latest timeseries',
  137 + GET_LATEST_VALUE_WITH_TS = 'Fetch Latest telemetry with Timestamp',
  138 + FETCH_LAST_LEVEL_ONLY = '仅获取最后一级关联',
  139 +
  140 + // DEVICE_RELATIONS_QUERY
  141 + DIRECTION = '方向',
  142 + MAX_LEVEL = 'Max relation level',
  143 + RELATION_TYPE = '关联类型',
  144 + DEVICE_TYPES = '设备类型',
  145 +}
  146 +
  147 +// Enrichment Tenant details
  148 +export enum TenantDetailsFieldsEnum {
  149 + DETAILS_LIST = 'detailsList',
  150 + ADD_TO_METADATA = 'addToMetadata',
  151 +}
  152 +
  153 +export enum TenantDetailsFieldsNameEnum {
  154 + DETAILS_LIST = 'Add selected details to message metadata',
  155 + ADD_TO_METADATA = 'Select entity details',
  156 +}
  157 +
  158 +export const EnrichmentCategoryFormFields = {
  159 + ...CalculateDeltaFieldsEnum,
  160 + ...CustomerAttributesFieldsEnum,
  161 + ...CustomerDetailsFieldsEnum,
  162 + ...OriginatorAttributesEnum,
  163 + ...OriginatorFieldsEnum,
  164 + ...OriginatorTelemetryFieldsEnum,
  165 + ...RelatedAttributesFieldsEnum,
  166 + ...RelatedDeviceAttributeFieldsEnum,
  167 + ...TenantDetailsFieldsEnum,
  168 +};
  169 +
  170 +export const EnrichmentCategoryFormFieldsName = {
  171 + ...CalculateDeltaFieldsNameEnum,
  172 + ...CustomerAttributesFieldsNameEnum,
  173 + ...CustomerDetailsFieldsNameEnum,
  174 + ...OriginatorAttributesNameEnum,
  175 + ...OriginatorFieldsNameEnum,
  176 + ...OriginatorTelemetryFieldsNameEnum,
  177 + ...RelatedAttributesFieldNameEnum,
  178 + ...RelatedDeviceAttributeFieldsNameEnum,
  179 + ...TenantDetailsFieldsNameEnum,
  180 +};
  181 +
  182 +export type EnrichmentCategoryType = typeof EnrichmentCategoryFormFields;
  1 +// Filter Check Alarm Status Fields
  2 +export enum FilterCheckAlarmStatusFieldEnum {
  3 + ALARM_STATUS_LIST = 'alarmStatusList',
  4 +}
  5 +
  6 +export enum FilterCheckAlarmStatusFieldNameEnum {
  7 + ALARM_STATUS_LIST = 'Alarm status filter',
  8 +}
  9 +
  10 +// Filter CHeck Existence Fields
  11 +export enum CheckExistenceFieldsEnum {
  12 + MESSAGE_NAMES = 'messageNames',
  13 + METADATA_NAMES = 'metadataNames',
  14 + CHECK_ALL_KEYS = 'checkAllKeys',
  15 +}
  16 +
  17 +export enum CheckExistenceFieldsNameEnum {
  18 + MESSAGE_NAMES = '消息数据',
  19 + METADATA_NAMES = '消息元数据',
  20 + CHECK_ALL_KEYS = '检查所有选择的键是否都存在',
  21 +}
  22 +
  23 +// Filter Check Relation Fields
  24 +export enum CheckRelationFieldsEnum {
  25 + DIRECTION = 'direction',
  26 + CHECK_FOR_SINGLE_ENTITY = 'checkForSingleEntity',
  27 + ENTITY_TYPE = 'entityType',
  28 + RELEATION_TYPE = 'relationType',
  29 + ENTITY_ID = 'entityId',
  30 +}
  31 +
  32 +export enum CheckRelationFieldsNameEnum {
  33 + DIRECTION = '方向',
  34 + CHECK_FOR_SINGLE_ENTITY = 'Check relation to specific entity',
  35 + ENTITY_TYPE = '类型',
  36 + RELEATION_TYPE = '关联类型',
  37 +}
  38 +
  39 +// Filter Gps geofencing filter
  40 +export enum GpsGeofencingFilterFieldsEnum {
  41 + LATITUDE_KEY_NAME = 'latitudeKeyName',
  42 + LONGITUDE_KEY_NAME = 'longitudeKeyName',
  43 + PERIMETER_TYPE = 'perimeterType',
  44 + FETCH_PERIMETER_INFO_FROM_MESSAGE_METADATA = 'fetchPerimeterInfoFromMessageMetadata',
  45 + PERIMETER_KEY_NAME = 'perimeterKeyName',
  46 + CENTER_LATITUDE = 'centerLatitude',
  47 + CENTER_LONGITUDE = 'centerLongitude',
  48 + RANGE = 'range',
  49 + RANGE_UNIT = 'rangeUnit',
  50 + POLYGONS_DEFINITION = 'polygonsDefinition',
  51 +}
  52 +
  53 +export enum GpsGeofencingFilterFieldsNameEnum {
  54 + LATITUDE_KEY_NAME = 'Latitude Key Name',
  55 + LONGITUDE_KEY_NAME = 'Longitude Key Name',
  56 + PERIMETER_TYPE = 'Perimeter Type',
  57 + FETCH_PERIMETER_INFO_FROM_MESSAGE_METADATA = 'Fetch perimeter information from message metadata',
  58 + CENTER_LATITUDE = 'Center latitude',
  59 + CENTER_LONGITUDE = 'Center longitude',
  60 + RANGE = 'Range',
  61 + RANGE_UNIT = 'Range unit',
  62 + PERIMETER_KEY_NAME = 'Perimeter key name',
  63 + POLYGONS_DEFINITION = 'Polygons definition',
  64 +}
  65 +
  66 +// Filter Message Type
  67 +export enum MessageTypeFieldsEnum {
  68 + MESSAGE_TYPES = 'messageTypes',
  69 +}
  70 +
  71 +export enum MessageTypeFieldsNameEnum {
  72 + MESSAGE_TYPES = 'Message Types Filter',
  73 +}
  74 +
  75 +// Filter Originator Type
  76 +export enum OriginatorTypeFieldsEnum {
  77 + ORIGINATOR_TYPES = 'originatorTypes',
  78 +}
  79 +
  80 +export enum OriginatorTypeFieldsNameEnum {
  81 + ORIGINATOR_TYPES = 'Originator types filter',
  82 +}
  83 +
  84 +// Filter Script
  85 +export enum ScriptFieldsEnum {
  86 + JS_SCRIPT = 'jsScript',
  87 +}
  88 +
  89 +export enum ScriptFieldsNameEnum {
  90 + JS_SCRIPT = 'Filter',
  91 +}
  92 +
  93 +export const FilterCategoryFormFields = {
  94 + ...FilterCheckAlarmStatusFieldEnum,
  95 + ...CheckExistenceFieldsEnum,
  96 + ...CheckRelationFieldsEnum,
  97 + ...GpsGeofencingFilterFieldsEnum,
  98 + ...MessageTypeFieldsEnum,
  99 + ...OriginatorTypeFieldsEnum,
  100 + ...ScriptFieldsEnum,
  101 +};
  102 +
  103 +export const FilterCategoryFormFieldsName = {
  104 + ...FilterCheckAlarmStatusFieldNameEnum,
  105 + ...CheckExistenceFieldsNameEnum,
  106 + ...CheckRelationFieldsNameEnum,
  107 + ...GpsGeofencingFilterFieldsNameEnum,
  108 + ...MessageTypeFieldsNameEnum,
  109 + ...OriginatorTypeFieldsNameEnum,
  110 + ...ScriptFieldsNameEnum,
  111 +};
  112 +
  113 +export type FilterCategoryFormType = typeof FilterCategoryFormFields;
  1 +// Change originator
  2 +export enum ChangeOriginatorFieldsEnum {
  3 + ORIGINATOR_SOURCE = 'originatorSource',
  4 + RELATIONS_QUERY = 'relationsQuery',
  5 +}
  6 +
  7 +export enum ChangeOriginatorFieldsNameEnum {
  8 + ORIGINATOR_SOURCE = 'Originator source',
  9 + RELATIONS_QUERY = 'Relations Query',
  10 +}
  11 +
  12 +export enum ScriptFieldsEnum {
  13 + JS_SCRIPT = 'jsScript',
  14 +}
  15 +
  16 +export enum ScriptFieldsNameEnum {
  17 + JS_SCRIPT = 'Transform',
  18 +}
  19 +
  20 +export enum ToEmailFieldsEnum {
  21 + FROM_TEMPLATE = 'fromTemplate',
  22 + TO_TEMPLATE = 'toTemplate',
  23 + CC_TEMPLATE = 'ccTemplate',
  24 + BCC_TEMPLATE = 'bccTemplate',
  25 + SUBJECT_TEMPLATE = 'subjectTemplate',
  26 + MAIL_BODY_TYPE = 'mailBodyType',
  27 + IS_HTML_TEMPLATE = 'isHtmlTemplate',
  28 + BODY_TEMPLATE = 'bodyTemplate',
  29 +}
  30 +
  31 +export enum ToEmailFieldsNameEnum {
  32 + FROM_TEMPLATE = 'From Template',
  33 + TO_TEMPLATE = 'To Template',
  34 + CC_TEMPLATE = 'Cc Template',
  35 + BCC_TEMPLATE = 'Bcc Template',
  36 + SUBJECT_TEMPLATE = 'Subject Template',
  37 + MAIL_BODY_TYPE = 'Mail body type',
  38 + IS_HTML_TEMPLATE = 'Dynamic mail body type',
  39 + BODY_TEMPLATE = 'Body Template',
  40 +}
  1 +import { CommonFields, CommonFieldsName } from './formField/common';
  2 +import {
  3 + EnrichmentCategoryFormFields,
  4 + EnrichmentCategoryFormFieldsName,
  5 +} from './formField/enrichment';
  6 +import { FilterCategoryFormFields, FilterCategoryFormFieldsName } from './formField/filter';
  7 +
1 export enum FetchNodeComFlagTypeENum { 8 export enum FetchNodeComFlagTypeENum {
2 CONNECTION_MODAL = 'CONNECTION_MODAL', 9 CONNECTION_MODAL = 'CONNECTION_MODAL',
3 CREATE_MODAL = 'CREATE_MODAL', 10 CREATE_MODAL = 'CREATE_MODAL',
4 } 11 }
5 12
6 -export enum NodeBindDataFieldEnum {  
7 - NAME = 'name',  
8 - DESCRIPTION = 'description',  
9 - DEBUG_MODE = 'debugMode',  
10 -  
11 - // Filter Check Alarm status  
12 - ALARM_STATUS_LIST = 'alarmStatusList',  
13 -  
14 - // Filter Check Existence Fields  
15 - MESSAGE_NAMES = 'messageNames',  
16 - METADATA_NAMES = 'metadataNames',  
17 - CHECK_ALL_KEYS = 'checkAllKeys',  
18 -  
19 - // Filter Check Relation  
20 - DIRECTION = 'direction',  
21 - CHECK_FOR_SINGLE_ENTITY = 'checkForSingleEntity',  
22 - ENTITY_TYPE = 'entityType',  
23 - RELEATION_TYPE = 'relationType',  
24 - ENTITY_ID = 'entityId',  
25 -  
26 - // Filter Gps geofencing filter  
27 - LATITUDE_KEY_NAME = 'latitudeKeyName',  
28 - LONGITUDE_KEY_NAME = 'longitudeKeyName',  
29 - PERIMETER_TYPE = 'perimeterType',  
30 - FETCH_PERIMETER_INFO_FROM_MESSAGE_METADATA = 'fetchPerimeterInfoFromMessageMetadata',  
31 - PERIMETER_KEY_NAME = 'perimeterKeyName',  
32 - CENTER_LATITUDE = 'centerLatitude',  
33 - CENTER_LONGITUDE = 'centerLongitude',  
34 - RANGE = 'range',  
35 - RANGE_UNIT = 'rangeUnit',  
36 - POLYGONS_DEFINITION = 'polygonsDefinition',  
37 -  
38 - // Filter Message Type  
39 - MESSAGE_TYPES = 'messageTypes',  
40 -  
41 - // Filter Originator Type  
42 - ORIGINATOR_TYPES = 'originatorTypes',  
43 -  
44 - // Filter Script  
45 - JS_SCRIPT = 'jsScript',  
46 -}  
47 -  
48 -export enum NodeBindDataFieldNameEnum {  
49 - NAME = '名称',  
50 - DESCRIPTION = '说明',  
51 - DEBUG_MODE = '调试模式',  
52 -  
53 - // Filter Check Alarm status  
54 - ALARM_STATUS_LIST = 'Alarm status filter',  
55 -  
56 - // Filter Check Existence Fields  
57 - MESSAGE_NAMES = '消息数据',  
58 - METADATA_NAMES = '消息元数据',  
59 - CHECK_ALL_KEYS = '检查所有选择的键是否都存在',  
60 -  
61 - // Filter Check Relation  
62 - DIRECTION = '方向',  
63 - CHECK_FOR_SINGLE_ENTITY = 'Check relation to specific entity',  
64 - ENTITY_TYPE = '类型',  
65 - RELEATION_TYPE = '关联类型',  
66 -  
67 - // Filter Gps geofencing filter  
68 - LATITUDE_KEY_NAME = 'Latitude Key Name',  
69 - LONGITUDE_KEY_NAME = 'Longitude Key Name',  
70 - PERIMETER_TYPE = 'Perimeter Type',  
71 - FETCH_PERIMETER_INFO_FROM_MESSAGE_METADATA = 'Fetch perimeter information from message metadata',  
72 - CENTER_LATITUDE = 'Center latitude',  
73 - CENTER_LONGITUDE = 'Center longitude',  
74 - RANGE = 'Range',  
75 - RANGE_UNIT = 'Range unit',  
76 - PERIMETER_KEY_NAME = 'Perimeter key name',  
77 - POLYGONS_DEFINITION = 'Polygons definition',  
78 -  
79 - // Filter Message Type  
80 - MESSAGE_TYPES = 'Message Types Filter',  
81 -  
82 - // Filter Originator Type  
83 - ORIGINATOR_TYPES = 'Originator types filter',  
84 -  
85 - // Filter Script  
86 - JS_SCRIPT = 'Filter',  
87 -}  
88 -  
89 export enum EdgeBindDataFieldEnum { 13 export enum EdgeBindDataFieldEnum {
90 TYPE = 'type', 14 TYPE = 'type',
91 } 15 }
@@ -93,3 +17,15 @@ export enum EdgeBindDataFieldEnum { @@ -93,3 +17,15 @@ export enum EdgeBindDataFieldEnum {
93 export enum EdgeBindDataFieldNameEnum { 17 export enum EdgeBindDataFieldNameEnum {
94 TYPE = '链接标签', 18 TYPE = '链接标签',
95 } 19 }
  20 +
  21 +export const NodeBindDataFieldEnum = {
  22 + ...CommonFields,
  23 + ...FilterCategoryFormFields,
  24 + ...EnrichmentCategoryFormFields,
  25 +};
  26 +
  27 +export const NodeBindDataFieldNameEnum = {
  28 + ...CommonFieldsName,
  29 + ...FilterCategoryFormFieldsName,
  30 + ...EnrichmentCategoryFormFieldsName,
  31 +};
@@ -133,7 +133,6 @@ export function useRuleFlow(options: UseRuleFlowOptionsType) { @@ -133,7 +133,6 @@ export function useRuleFlow(options: UseRuleFlowOptionsType) {
133 if (!flag) return; 133 if (!flag) return;
134 134
135 const currentNode = findNode(node.id); 135 const currentNode = findNode(node.id);
136 -  
137 (currentNode!.data as NodeData).data = data; 136 (currentNode!.data as NodeData).data = data;
138 }); 137 });
139 138
1 -import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node'; 1 +import {
  2 + AssignToCustomerFieldsEnum,
  3 + AssignToCustomerFieldsNameEnum,
  4 +} from '../../../enum/formField/action';
2 import { FormSchema } from '/@/components/Form'; 5 import { FormSchema } from '/@/components/Form';
3 6
4 export const formSchemas: FormSchema[] = [ 7 export const formSchemas: FormSchema[] = [
5 { 8 {
6 - field: NodeBindDataFieldEnum.NAME, 9 + field: AssignToCustomerFieldsEnum.CUSTOMER_NAME_PATTERN,
7 component: 'Input', 10 component: 'Input',
8 - label: NodeBindDataFieldNameEnum.NAME, 11 + label: AssignToCustomerFieldsNameEnum.CUSTOMER_NAME_PATTERN,
  12 + helpMessage:
  13 + 'Hint: use ${metadataKey} for value from metadata, $[messageKey] for value from message body',
  14 + componentProps: {
  15 + placeholder: `请输入${AssignToCustomerFieldsNameEnum.CUSTOMER_NAME_PATTERN}`,
  16 + },
  17 + },
  18 + {
  19 + field: AssignToCustomerFieldsEnum.CREATE_CUSTOMER_IF_NOT_EXISTS,
  20 + component: 'Checkbox',
  21 + label: '',
  22 + renderComponentContent: () => ({
  23 + default: () => AssignToCustomerFieldsNameEnum.CREATE_CUSTOMER_IF_NOT_EXISTS,
  24 + }),
  25 + },
  26 + {
  27 + field: AssignToCustomerFieldsEnum.CUSTOMER_CACHE_EXPIRATION,
  28 + component: 'InputNumber',
  29 + label: AssignToCustomerFieldsNameEnum.CUSTOMER_CACHE_EXPIRATION,
  30 + required: true,
  31 + helpMessage:
  32 + 'Specifies maximum time interval allowed to store found customer records. 0 value means that records will never expire.',
  33 + componentProps: {
  34 + placeholder: `请输入${AssignToCustomerFieldsNameEnum.CUSTOMER_CACHE_EXPIRATION}`,
  35 + min: 0,
  36 + },
9 }, 37 },
10 ]; 38 ];
1 -import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node';  
2 -import { FormSchema } from '/@/components/Form'; 1 +import { ClearAlarmFieldsEnum, ClearAlarmFieldsNameEnum } from '../../../enum/formField/action';
  2 +import { JavascriptEditorWithTestModal } from '../../../src/components/JavaScriptFilterModal';
  3 +import { FormSchema, useComponentRegister } from '/@/components/Form';
  4 +
  5 +useComponentRegister('JavascriptEditorWithTestModal', JavascriptEditorWithTestModal);
3 6
4 export const formSchemas: FormSchema[] = [ 7 export const formSchemas: FormSchema[] = [
5 { 8 {
6 - field: NodeBindDataFieldEnum.NAME, 9 + field: ClearAlarmFieldsEnum.ALARM_DETAILS_BUILD_JS,
  10 + component: 'JavascriptEditorWithTestModal',
  11 + label: ClearAlarmFieldsNameEnum.ALARM_DETAILS_BUILD_JS,
  12 + componentProps: {
  13 + javaScriptEditorProps: {
  14 + functionName: 'Details',
  15 + paramsName: ['msg', 'metadata', 'msgType'],
  16 + },
  17 + buttonName: 'Test details function',
  18 + },
  19 + },
  20 + {
  21 + field: ClearAlarmFieldsEnum.ALARM_TYPE,
7 component: 'Input', 22 component: 'Input',
8 - label: NodeBindDataFieldNameEnum.NAME, 23 + label: ClearAlarmFieldsNameEnum.ALARM_TYPE,
  24 + required: true,
  25 + componentProps: {
  26 + placeholder: `请输入${ClearAlarmFieldsNameEnum.ALARM_TYPE}`,
  27 + },
9 }, 28 },
10 ]; 29 ];
1 -import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node';  
2 import { FormSchema } from '/@/components/Form'; 1 import { FormSchema } from '/@/components/Form';
3 2
4 -export const formSchemas: FormSchema[] = [  
5 - {  
6 - field: NodeBindDataFieldEnum.NAME,  
7 - component: 'Input',  
8 - label: NodeBindDataFieldNameEnum.NAME,  
9 - },  
10 -]; 3 +export const formSchemas: FormSchema[] = [];
1 -import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node';  
2 -import { FormSchema } from '/@/components/Form'; 1 +import { CreateAlarmFieldsEnum, CreateAlarmFieldsNameEnum } from '../../../enum/formField/action';
  2 +import { JavascriptEditorWithTestModal } from '../../../src/components/JavaScriptFilterModal';
  3 +import { FormSchema, useComponentRegister } from '/@/components/Form';
  4 +
  5 +useComponentRegister('JavascriptEditorWithTestModal', JavascriptEditorWithTestModal);
3 6
4 export const formSchemas: FormSchema[] = [ 7 export const formSchemas: FormSchema[] = [
5 { 8 {
6 - field: NodeBindDataFieldEnum.NAME,  
7 - component: 'Input',  
8 - label: NodeBindDataFieldNameEnum.NAME, 9 + field: CreateAlarmFieldsEnum.USE_MESSAGE_ALARM_DATA,
  10 + component: 'Checkbox',
  11 + label: '',
  12 + renderComponentContent: () => ({
  13 + default: () => CreateAlarmFieldsNameEnum.USE_MESSAGE_ALARM_DATA,
  14 + }),
  15 + },
  16 + {
  17 + field: CreateAlarmFieldsEnum.ALARM_DETAILS_BUILD_JS,
  18 + component: 'JavascriptEditorWithTestModal',
  19 + label: CreateAlarmFieldsNameEnum.ALARM_DETAILS_BUILD_JS,
9 }, 20 },
10 ]; 21 ];
@@ -3,8 +3,64 @@ import { FormSchema } from '/@/components/Form'; @@ -3,8 +3,64 @@ import { FormSchema } from '/@/components/Form';
3 3
4 export const formSchemas: FormSchema[] = [ 4 export const formSchemas: FormSchema[] = [
5 { 5 {
6 - field: NodeBindDataFieldEnum.NAME, 6 + field: NodeBindDataFieldEnum.INPUT_VALUE_KEY,
7 component: 'Input', 7 component: 'Input',
8 - label: NodeBindDataFieldNameEnum.NAME, 8 + label: NodeBindDataFieldNameEnum.INPUT_VALUE_KEY,
  9 + required: true,
  10 + colProps: { span: 8 },
  11 + },
  12 + {
  13 + field: NodeBindDataFieldEnum.OUTPUT_VALUE_KEY,
  14 + component: 'Input',
  15 + label: NodeBindDataFieldNameEnum.OUTPUT_VALUE_KEY,
  16 + required: true,
  17 + colProps: { span: 8 },
  18 + },
  19 + {
  20 + field: NodeBindDataFieldEnum.ROUND,
  21 + component: 'InputNumber',
  22 + label: NodeBindDataFieldNameEnum.ROUND,
  23 + colProps: { span: 8 },
  24 + componentProps: {
  25 + step: 1,
  26 + max: 15,
  27 + },
  28 + },
  29 + {
  30 + field: NodeBindDataFieldEnum.USE_CACHE,
  31 + component: 'Checkbox',
  32 + label: '',
  33 + renderComponentContent: () => {
  34 + return {
  35 + default: () => NodeBindDataFieldNameEnum.USE_CACHE,
  36 + };
  37 + },
  38 + },
  39 + {
  40 + field: NodeBindDataFieldEnum.TELL_FAILURE_IF_DELTA_IS_NEGATIVE,
  41 + component: 'Checkbox',
  42 + label: '',
  43 + renderComponentContent: () => {
  44 + return {
  45 + default: () => NodeBindDataFieldNameEnum.TELL_FAILURE_IF_DELTA_IS_NEGATIVE,
  46 + };
  47 + },
  48 + },
  49 + {
  50 + field: NodeBindDataFieldEnum.ADD_PERIOD_BETWEEN_MSGS,
  51 + component: 'Checkbox',
  52 + label: '',
  53 + renderComponentContent: () => {
  54 + return {
  55 + default: () => NodeBindDataFieldNameEnum.ADD_PERIOD_BETWEEN_MSGS,
  56 + };
  57 + },
  58 + },
  59 + {
  60 + field: NodeBindDataFieldEnum.PERIOD_VALUE_KEY,
  61 + component: 'Input',
  62 + label: NodeBindDataFieldNameEnum.PERIOD_VALUE_KEY,
  63 + required: true,
  64 + ifShow: ({ model }) => model[NodeBindDataFieldEnum.ADD_PERIOD_BETWEEN_MSGS],
9 }, 65 },
10 ]; 66 ];
1 import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node'; 1 import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node';
2 -import { FormSchema } from '/@/components/Form'; 2 +import { FormSchema, useComponentRegister } from '/@/components/Form';
  3 +import { AttributeConfiguration } from '/@/views/rule/designer/src/components/AttributeConfiguration';
  4 +
  5 +useComponentRegister('AttributeConfiguration', AttributeConfiguration);
3 6
4 export const formSchemas: FormSchema[] = [ 7 export const formSchemas: FormSchema[] = [
5 { 8 {
6 - field: NodeBindDataFieldEnum.NAME,  
7 - component: 'Input',  
8 - label: NodeBindDataFieldNameEnum.NAME, 9 + field: NodeBindDataFieldEnum.TELEMETRY,
  10 + component: 'Checkbox',
  11 + label: '',
  12 + renderComponentContent: () => {
  13 + return {
  14 + default: () => NodeBindDataFieldNameEnum.TELEMETRY,
  15 + };
  16 + },
  17 + },
  18 + {
  19 + field: NodeBindDataFieldEnum.ATTR_MAPING,
  20 + component: 'AttributeConfiguration',
  21 + label: NodeBindDataFieldNameEnum.ATTR_MAPING,
  22 + slot: NodeBindDataFieldEnum.ATTR_MAPING,
  23 + valueField: 'value',
  24 + changeEvent: 'update:value',
9 }, 25 },
10 ]; 26 ];
@@ -3,25 +3,33 @@ @@ -3,25 +3,33 @@
3 import { BasicForm, useForm } from '/@/components/Form'; 3 import { BasicForm, useForm } from '/@/components/Form';
4 import { formSchemas } from './create.config'; 4 import { formSchemas } from './create.config';
5 import { NodeData } from '../../../types/node'; 5 import { NodeData } from '../../../types/node';
  6 + import { AttributeConfiguration } from '/@/views/rule/designer/src/components/AttributeConfiguration';
  7 + import { ref, unref } from 'vue';
  8 + import { NodeBindDataFieldEnum } from '../../../enum/node';
6 9
7 defineProps<{ 10 defineProps<{
8 config: NodeData; 11 config: NodeData;
9 }>(); 12 }>();
10 13
  14 + const attributeControlElRef = ref<Nullable<InstanceType<typeof AttributeConfiguration>>>();
  15 +
11 const [register, { validate, getFieldsValue, setFieldsValue, resetFields }] = useForm({ 16 const [register, { validate, getFieldsValue, setFieldsValue, resetFields }] = useForm({
12 schemas: formSchemas, 17 schemas: formSchemas,
13 showActionButtonGroup: false, 18 showActionButtonGroup: false,
14 }); 19 });
15 20
16 const getValue: CreateModalDefineExposeType['getFieldsValue'] = async () => { 21 const getValue: CreateModalDefineExposeType['getFieldsValue'] = async () => {
  22 + await unref(attributeControlElRef)?.validate();
17 await validate(); 23 await validate();
18 const value = getFieldsValue() || {}; 24 const value = getFieldsValue() || {};
19 - return value; 25 + const attrMapping = unref(attributeControlElRef)?.getFieldsValue();
  26 + return { ...value, [NodeBindDataFieldEnum.ATTR_MAPING]: attrMapping };
20 }; 27 };
21 28
22 const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => { 29 const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => {
23 resetFields(); 30 resetFields();
24 setFieldsValue(value); 31 setFieldsValue(value);
  32 + unref(attributeControlElRef)?.setFieldsValue(value?.[NodeBindDataFieldEnum.ATTR_MAPING]);
25 }; 33 };
26 34
27 defineExpose({ 35 defineExpose({
@@ -31,5 +39,32 @@ @@ -31,5 +39,32 @@
31 </script> 39 </script>
32 40
33 <template> 41 <template>
34 - <BasicForm @register="register" /> 42 + <BasicForm @register="register">
  43 + <template #attrMapping="{ field, model }">
  44 + <AttributeConfiguration
  45 + v-model:value="model[field]"
  46 + ref="attributeControlElRef"
  47 + :keyLabel="`Source ${model[NodeBindDataFieldEnum.TELEMETRY] ? 'telemetry' : 'attribute'}`"
  48 + valueLabel="Target attribute"
  49 + >
  50 + <template #afterForm>
  51 + <div class="text-xs text-gray-500">
  52 + Hint: use
  53 + <code>
  54 + <span class="text-dark-500">${</span>
  55 + metadataKey
  56 + <span class="text-dark-500">}</span>
  57 + </code>
  58 + for value from metadata,
  59 + <code>
  60 + <span class="text-dark-500"> $[</span>
  61 + messageKey
  62 + <span class="text-dark-500">] </span>
  63 + </code>
  64 + for value from message body to substitute "Source" and "Target" key names
  65 + </div>
  66 + </template>
  67 + </AttributeConfiguration>
  68 + </template>
  69 + </BasicForm>
35 </template> 70 </template>
  1 +import { DetailsListEnum, DetailsListNameEnum } from '../../../enum/form';
1 import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node'; 2 import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node';
2 import { FormSchema } from '/@/components/Form'; 3 import { FormSchema } from '/@/components/Form';
3 4
4 export const formSchemas: FormSchema[] = [ 5 export const formSchemas: FormSchema[] = [
5 { 6 {
6 - field: NodeBindDataFieldEnum.NAME,  
7 - component: 'Input',  
8 - label: NodeBindDataFieldNameEnum.NAME, 7 + field: NodeBindDataFieldEnum.DETAILS_LIST,
  8 + component: 'Select',
  9 + label: NodeBindDataFieldNameEnum.DETAILS_LIST,
  10 + componentProps: {
  11 + mode: 'multiple',
  12 + options: Object.keys(DetailsListEnum).map((item) => ({
  13 + label: DetailsListNameEnum[item],
  14 + value: item,
  15 + getPopupContainer: () => document.body,
  16 + })),
  17 + },
  18 + },
  19 + {
  20 + field: NodeBindDataFieldEnum.ADD_TO_METADATA,
  21 + component: 'Checkbox',
  22 + label: '',
  23 + renderComponentContent: () => {
  24 + return {
  25 + default: () => NodeBindDataFieldNameEnum.ADD_TO_METADATA,
  26 + };
  27 + },
9 }, 28 },
10 ]; 29 ];
@@ -3,8 +3,73 @@ import { FormSchema } from '/@/components/Form'; @@ -3,8 +3,73 @@ import { FormSchema } from '/@/components/Form';
3 3
4 export const formSchemas: FormSchema[] = [ 4 export const formSchemas: FormSchema[] = [
5 { 5 {
6 - field: NodeBindDataFieldEnum.NAME,  
7 - component: 'Input',  
8 - label: NodeBindDataFieldNameEnum.NAME, 6 + field: NodeBindDataFieldEnum.TELL_FAILURE_IF_ABSENT,
  7 + component: 'Checkbox',
  8 + label: '',
  9 + // helpMessage: [
  10 + // 'If at least one selected key doesn\'t exist the outbound message will report "Failure".',
  11 + // ],
  12 + renderComponentContent: () => ({
  13 + default: () => NodeBindDataFieldNameEnum.TELL_FAILURE_IF_ABSENT,
  14 + }),
  15 + },
  16 + {
  17 + field: NodeBindDataFieldEnum.CLIENT_ATTRIBUTE_NAMES,
  18 + component: 'Select',
  19 + label: NodeBindDataFieldNameEnum.CLIENT_ATTRIBUTE_NAMES,
  20 + helpMessage: [
  21 + `Hint: use \${metadataKey} for value from metadata, $[messageKey] for value from message body`,
  22 + ],
  23 + componentProps: {
  24 + mode: 'tags',
  25 + open: false,
  26 + },
  27 + },
  28 + {
  29 + field: NodeBindDataFieldEnum.SHARED_ATTRIBUTE_NAMES,
  30 + component: 'Select',
  31 + label: NodeBindDataFieldNameEnum.SHARED_ATTRIBUTE_NAMES,
  32 + helpMessage: [
  33 + `Hint: use \${metadataKey} for value from metadata, $[messageKey] for value from message body`,
  34 + ],
  35 + componentProps: {
  36 + mode: 'tags',
  37 + open: false,
  38 + },
  39 + },
  40 + {
  41 + field: NodeBindDataFieldEnum.SERVER_ATTRIBUTE_NAMES,
  42 + component: 'Select',
  43 + label: NodeBindDataFieldNameEnum.SERVER_ATTRIBUTE_NAMES,
  44 + helpMessage: [
  45 + `Hint: use \${metadataKey} for value from metadata, $[messageKey] for value from message body`,
  46 + ],
  47 + componentProps: {
  48 + mode: 'tags',
  49 + open: false,
  50 + },
  51 + },
  52 + {
  53 + field: NodeBindDataFieldEnum.LATEST_TS_KEY_NAMES,
  54 + component: 'Select',
  55 + label: NodeBindDataFieldNameEnum.LATEST_TS_KEY_NAMES,
  56 + helpMessage: [
  57 + `Hint: use \${metadataKey} for value from metadata, $[messageKey] for value from message body`,
  58 + ],
  59 + componentProps: {
  60 + mode: 'tags',
  61 + open: false,
  62 + },
  63 + },
  64 + {
  65 + field: NodeBindDataFieldEnum.GET_LATEST_VALUE_WITH_TS,
  66 + component: 'Checkbox',
  67 + label: '',
  68 + // helpMessage: [
  69 + // 'If selected, latest telemetry values will be added to the outbound message metadata with timestamp, e.g: "temp": "{"ts":1574329385897, "value":42}"',
  70 + // ],
  71 + renderComponentContent: () => ({
  72 + default: () => NodeBindDataFieldNameEnum.GET_LATEST_VALUE_WITH_TS,
  73 + }),
9 }, 74 },
10 ]; 75 ];
1 import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node'; 1 import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node';
2 -import { FormSchema } from '/@/components/Form'; 2 +import { FormSchema, useComponentRegister } from '/@/components/Form';
  3 +import { AttributeConfiguration } from '/@/views/rule/designer/src/components/AttributeConfiguration';
  4 +
  5 +useComponentRegister('AttributeConfiguration', AttributeConfiguration);
3 6
4 export const formSchemas: FormSchema[] = [ 7 export const formSchemas: FormSchema[] = [
5 { 8 {
6 - field: NodeBindDataFieldEnum.NAME,  
7 - component: 'Input',  
8 - label: NodeBindDataFieldNameEnum.NAME, 9 + field: NodeBindDataFieldEnum.FIELDS_MAPPING,
  10 + component: 'AttributeConfiguration',
  11 + label: NodeBindDataFieldNameEnum.FIELDS_MAPPING,
  12 + slot: NodeBindDataFieldEnum.FIELDS_MAPPING,
  13 + valueField: 'value',
  14 + changeEvent: 'update:value',
9 }, 15 },
10 ]; 16 ];
@@ -3,25 +3,33 @@ @@ -3,25 +3,33 @@
3 import { BasicForm, useForm } from '/@/components/Form'; 3 import { BasicForm, useForm } from '/@/components/Form';
4 import { formSchemas } from './create.config'; 4 import { formSchemas } from './create.config';
5 import { NodeData } from '../../../types/node'; 5 import { NodeData } from '../../../types/node';
  6 + import { AttributeConfiguration } from '/@/views/rule/designer/src/components/AttributeConfiguration';
  7 + import { ref, unref } from 'vue';
  8 + import { NodeBindDataFieldEnum } from '../../../enum/node';
6 9
7 defineProps<{ 10 defineProps<{
8 config: NodeData; 11 config: NodeData;
9 }>(); 12 }>();
10 13
  14 + const fieldControlElRef = ref<Nullable<InstanceType<typeof AttributeConfiguration>>>();
  15 +
11 const [register, { validate, getFieldsValue, setFieldsValue, resetFields }] = useForm({ 16 const [register, { validate, getFieldsValue, setFieldsValue, resetFields }] = useForm({
12 schemas: formSchemas, 17 schemas: formSchemas,
13 showActionButtonGroup: false, 18 showActionButtonGroup: false,
14 }); 19 });
15 20
16 const getValue: CreateModalDefineExposeType['getFieldsValue'] = async () => { 21 const getValue: CreateModalDefineExposeType['getFieldsValue'] = async () => {
  22 + await unref(fieldControlElRef)?.validate();
17 await validate(); 23 await validate();
18 const value = getFieldsValue() || {}; 24 const value = getFieldsValue() || {};
19 - return value; 25 + const fieldsMapping = unref(fieldControlElRef)?.getFieldsValue();
  26 + return { ...value, [NodeBindDataFieldEnum.FIELDS_MAPPING]: fieldsMapping };
20 }; 27 };
21 28
22 const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => { 29 const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => {
23 resetFields(); 30 resetFields();
24 setFieldsValue(value); 31 setFieldsValue(value);
  32 + unref(fieldControlElRef)?.setFieldsValue(value?.[NodeBindDataFieldEnum.FIELDS_MAPPING]);
25 }; 33 };
26 34
27 defineExpose({ 35 defineExpose({
@@ -31,5 +39,14 @@ @@ -31,5 +39,14 @@
31 </script> 39 </script>
32 40
33 <template> 41 <template>
34 - <BasicForm @register="register" /> 42 + <BasicForm @register="register">
  43 + <template #fieldsMapping="{ field, model }">
  44 + <AttributeConfiguration
  45 + v-model:value="model[field]"
  46 + ref="fieldControlElRef"
  47 + keyLabel="Source Field"
  48 + valueLabel="Target attribute"
  49 + />
  50 + </template>
  51 + </BasicForm>
35 </template> 52 </template>
  1 +import {
  2 + AggregationEnum,
  3 + AggregationNameEnum,
  4 + FetchModeEnum,
  5 + OrderByEnum,
  6 + TimeIntervalUnitEnum,
  7 + TimeIntervalUnitNameEnum,
  8 +} from '../../../enum/form';
1 import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node'; 9 import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node';
2 import { FormSchema } from '/@/components/Form'; 10 import { FormSchema } from '/@/components/Form';
3 11
4 export const formSchemas: FormSchema[] = [ 12 export const formSchemas: FormSchema[] = [
5 { 13 {
6 - field: NodeBindDataFieldEnum.NAME, 14 + field: NodeBindDataFieldEnum.LATEST_TS_KEY_NAMES,
  15 + component: 'Select',
  16 + label: NodeBindDataFieldNameEnum.LATEST_TS_KEY_NAMES,
  17 + componentProps: {
  18 + mode: 'tags',
  19 + open: false,
  20 + },
  21 + },
  22 + {
  23 + field: NodeBindDataFieldEnum.FETCH_MODE,
  24 + component: 'Select',
  25 + label: NodeBindDataFieldNameEnum.FETCH_MODE,
  26 + required: true,
  27 + componentProps: {
  28 + options: Object.keys(FetchModeEnum).map((value) => ({ label: value, value })),
  29 + },
  30 + },
  31 + {
  32 + field: NodeBindDataFieldEnum.AGGREGATION,
  33 + component: 'Select',
  34 + label: NodeBindDataFieldNameEnum.AGGREGATION,
  35 + required: true,
  36 + show: ({ model }) => model[NodeBindDataFieldEnum.FETCH_MODE] === FetchModeEnum.ALL,
  37 + componentProps: {
  38 + options: Object.keys(AggregationEnum).map((value) => ({
  39 + label: AggregationNameEnum[value],
  40 + value,
  41 + })),
  42 + },
  43 + },
  44 + {
  45 + field: NodeBindDataFieldEnum.ORDER_BY,
  46 + component: 'Select',
  47 + label: NodeBindDataFieldNameEnum.ORDER_BY,
  48 + required: true,
  49 + show: ({ model }) => model[NodeBindDataFieldEnum.FETCH_MODE] === FetchModeEnum.ALL,
  50 + componentProps: {
  51 + options: Object.keys(OrderByEnum).map((value) => ({ label: value, value })),
  52 + },
  53 + },
  54 + {
  55 + field: NodeBindDataFieldEnum.ORDER_BY,
  56 + component: 'Select',
  57 + label: NodeBindDataFieldNameEnum.ORDER_BY,
  58 + required: true,
  59 + helpMessage: ['Select to choose telemetry sampling order.'],
  60 + show: ({ model }) => model[NodeBindDataFieldEnum.FETCH_MODE] === FetchModeEnum.ALL,
  61 + componentProps: {
  62 + options: Object.keys(OrderByEnum).map((value) => ({ label: value, value })),
  63 + },
  64 + },
  65 + {
  66 + field: NodeBindDataFieldEnum.LIMIT,
  67 + component: 'InputNumber',
  68 + label: NodeBindDataFieldNameEnum.LIMIT,
  69 + required: true,
  70 + helpMessage: [
  71 + "Min limit value is 2, max - 1000. In case you want to fetch a single entry, select fetch mode 'FIRST' or 'LAST'.",
  72 + ],
  73 + show: ({ model }) => model[NodeBindDataFieldEnum.FETCH_MODE] === FetchModeEnum.ALL,
  74 + componentProps: {
  75 + min: 2,
  76 + max: 1000,
  77 + step: 1,
  78 + },
  79 + },
  80 + {
  81 + field: NodeBindDataFieldEnum.USE_METADATA_INTERVAL_PATTERNS,
  82 + component: 'Checkbox',
  83 + label: '',
  84 + renderComponentContent: () => ({
  85 + default: () => NodeBindDataFieldNameEnum.USE_METADATA_INTERVAL_PATTERNS,
  86 + }),
  87 + },
  88 + {
  89 + field: NodeBindDataFieldEnum.START_INTERVAL,
  90 + component: 'InputNumber',
  91 + label: NodeBindDataFieldNameEnum.START_INTERVAL,
  92 + colProps: { span: 12 },
  93 + required: true,
  94 + show: ({ model }) => !model[NodeBindDataFieldEnum.USE_METADATA_INTERVAL_PATTERNS],
  95 + componentProps: {
  96 + step: 1,
  97 + min: 0,
  98 + },
  99 + },
  100 + {
  101 + field: NodeBindDataFieldEnum.START_INTERVAL_TIME_UNIT,
  102 + component: 'Select',
  103 + label: NodeBindDataFieldNameEnum.START_INTERVAL_TIME_UNIT,
  104 + colProps: { span: 12 },
  105 + required: true,
  106 + show: ({ model }) => !model[NodeBindDataFieldEnum.USE_METADATA_INTERVAL_PATTERNS],
  107 + componentProps: {
  108 + options: Object.keys(TimeIntervalUnitEnum).map((value) => ({
  109 + label: TimeIntervalUnitNameEnum[value],
  110 + value,
  111 + })),
  112 + },
  113 + },
  114 + {
  115 + field: NodeBindDataFieldEnum.END_INTERVAL,
  116 + component: 'InputNumber',
  117 + label: NodeBindDataFieldNameEnum.END_INTERVAL,
  118 + colProps: { span: 12 },
  119 + required: true,
  120 + show: ({ model }) => !model[NodeBindDataFieldEnum.USE_METADATA_INTERVAL_PATTERNS],
  121 + componentProps: {
  122 + step: 1,
  123 + min: 0,
  124 + },
  125 + },
  126 + {
  127 + field: NodeBindDataFieldEnum.END_INTERVAL_TIME_UNIT,
  128 + component: 'Select',
  129 + label: NodeBindDataFieldNameEnum.END_INTERVAL_TIME_UNIT,
  130 + colProps: { span: 12 },
  131 + required: true,
  132 + show: ({ model }) => !model[NodeBindDataFieldEnum.USE_METADATA_INTERVAL_PATTERNS],
  133 + componentProps: {
  134 + options: Object.keys(TimeIntervalUnitEnum).map((value) => ({
  135 + label: TimeIntervalUnitNameEnum[value],
  136 + value,
  137 + })),
  138 + },
  139 + },
  140 + {
  141 + field: NodeBindDataFieldEnum.START_INTERVAL_PATTERN,
  142 + component: 'Input',
  143 + label: NodeBindDataFieldNameEnum.START_INTERVAL_PATTERN,
  144 + required: true,
  145 + helpMessage: [
  146 + 'Hint: use ${metadataKey} for value from metadata, $[messageKey] for value from message body',
  147 + ],
  148 + show: ({ model }) => model[NodeBindDataFieldEnum.USE_METADATA_INTERVAL_PATTERNS],
  149 + },
  150 + {
  151 + field: NodeBindDataFieldEnum.END_INTERVAL_PATTERN,
7 component: 'Input', 152 component: 'Input',
8 - label: NodeBindDataFieldNameEnum.NAME, 153 + label: NodeBindDataFieldNameEnum.END_INTERVAL_PATTERN,
  154 + required: true,
  155 + helpMessage: [
  156 + 'Hint: use ${metadataKey} for value from metadata, $[messageKey] for value from message body',
  157 + ],
  158 + show: ({ model }) => model[NodeBindDataFieldEnum.USE_METADATA_INTERVAL_PATTERNS],
9 }, 159 },
10 ]; 160 ];
1 import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node'; 1 import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node';
2 -import { FormSchema } from '/@/components/Form'; 2 +import { AttributeConfiguration } from '/@/views/rule/designer/src/components/AttributeConfiguration';
  3 +import { FormSchema, useComponentRegister } from '/@/components/Form';
  4 +import { RelationsQuery } from '/@/views/rule/designer/src/components/RelationsQuery';
  5 +
  6 +useComponentRegister('RelationsQuery', RelationsQuery);
  7 +useComponentRegister('AttributeConfiguration', AttributeConfiguration);
3 8
4 export const formSchemas: FormSchema[] = [ 9 export const formSchemas: FormSchema[] = [
5 { 10 {
6 - field: NodeBindDataFieldEnum.NAME,  
7 - component: 'Input',  
8 - label: NodeBindDataFieldNameEnum.NAME, 11 + field: NodeBindDataFieldEnum.RELATIONS_QUERY,
  12 + component: 'RelationsQuery',
  13 + label: NodeBindDataFieldNameEnum.RELATIONS_QUERY,
  14 + changeEvent: 'update:value',
  15 + valueField: 'value',
  16 + slot: NodeBindDataFieldEnum.RELATIONS_QUERY,
  17 + },
  18 + {
  19 + field: NodeBindDataFieldEnum.TELEMETRY,
  20 + component: 'Checkbox',
  21 + label: '',
  22 + renderComponentContent: () => ({
  23 + default: () => NodeBindDataFieldNameEnum.TELEMETRY,
  24 + }),
  25 + },
  26 + {
  27 + field: NodeBindDataFieldEnum.ATTR_MAPING,
  28 + component: 'AttributeConfiguration',
  29 + label: '',
  30 + slot: NodeBindDataFieldEnum.ATTR_MAPING,
9 }, 31 },
10 ]; 32 ];
@@ -3,11 +3,19 @@ @@ -3,11 +3,19 @@
3 import { BasicForm, useForm } from '/@/components/Form'; 3 import { BasicForm, useForm } from '/@/components/Form';
4 import { formSchemas } from './create.config'; 4 import { formSchemas } from './create.config';
5 import { NodeData } from '../../../types/node'; 5 import { NodeData } from '../../../types/node';
  6 + import { RelationsQuery } from '/@/views/rule/designer/src/components/RelationsQuery';
  7 + import { ref, unref } from 'vue';
  8 + import { NodeBindDataFieldEnum } from '../../../enum/node';
  9 + import { AttributeConfiguration } from '/@/views/rule/designer/src/components/AttributeConfiguration';
6 10
7 defineProps<{ 11 defineProps<{
8 config: NodeData; 12 config: NodeData;
9 }>(); 13 }>();
10 14
  15 + const relationsQueryElRef = ref<Nullable<InstanceType<typeof RelationsQuery>>>();
  16 +
  17 + const attributeControlElRef = ref<Nullable<InstanceType<typeof AttributeConfiguration>>>();
  18 +
11 const [register, { validate, getFieldsValue, setFieldsValue, resetFields }] = useForm({ 19 const [register, { validate, getFieldsValue, setFieldsValue, resetFields }] = useForm({
12 schemas: formSchemas, 20 schemas: formSchemas,
13 showActionButtonGroup: false, 21 showActionButtonGroup: false,
@@ -15,13 +23,25 @@ @@ -15,13 +23,25 @@
15 23
16 const getValue: CreateModalDefineExposeType['getFieldsValue'] = async () => { 24 const getValue: CreateModalDefineExposeType['getFieldsValue'] = async () => {
17 await validate(); 25 await validate();
  26 + await unref(relationsQueryElRef)?.validate();
  27 + await unref(attributeControlElRef)?.validate();
18 const value = getFieldsValue() || {}; 28 const value = getFieldsValue() || {};
19 - return value; 29 + const queryValue = unref(relationsQueryElRef)?.getFieldsValue();
  30 + const attrMapping = unref(attributeControlElRef)?.getFieldsValue();
  31 + return {
  32 + ...value,
  33 + [NodeBindDataFieldEnum.RELATIONS_QUERY]: queryValue,
  34 + [NodeBindDataFieldEnum.ATTR_MAPING]: attrMapping,
  35 + };
20 }; 36 };
21 37
22 const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => { 38 const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => {
23 resetFields(); 39 resetFields();
24 setFieldsValue(value); 40 setFieldsValue(value);
  41 + unref(relationsQueryElRef)?.setFieldsValue(
  42 + value?.[NodeBindDataFieldEnum.RELATIONS_QUERY] as Recordable
  43 + );
  44 + unref(attributeControlElRef)?.setFieldsValue(value?.[NodeBindDataFieldEnum.ATTR_MAPING]);
25 }; 45 };
26 46
27 defineExpose({ 47 defineExpose({
@@ -31,5 +51,35 @@ @@ -31,5 +51,35 @@
31 </script> 51 </script>
32 52
33 <template> 53 <template>
34 - <BasicForm @register="register" /> 54 + <BasicForm @register="register">
  55 + <template #relationsQuery="{ field, model }">
  56 + <RelationsQuery ref="relationsQueryElRef" v-model:value="model[field]" />
  57 + </template>
  58 + <template #attrMapping="{ field, model }">
  59 + <AttributeConfiguration
  60 + v-model:value="model[field]"
  61 + ref="attributeControlElRef"
  62 + :keyLabel="`Source ${model[NodeBindDataFieldEnum.TELEMETRY] ? 'telemetry' : 'attribute'}`"
  63 + valueLabel="Target attribute"
  64 + >
  65 + <template #afterForm>
  66 + <div class="text-xs text-gray-500">
  67 + Hint: use
  68 + <code>
  69 + <span class="text-dark-500">${</span>
  70 + metadataKey
  71 + <span class="text-dark-500">}</span>
  72 + </code>
  73 + for value from metadata,
  74 + <code>
  75 + <span class="text-dark-500"> $[</span>
  76 + messageKey
  77 + <span class="text-dark-500">] </span>
  78 + </code>
  79 + for value from message body to substitute "Source" and "Target" key names
  80 + </div>
  81 + </template>
  82 + </AttributeConfiguration>
  83 + </template>
  84 + </BasicForm>
35 </template> 85 </template>
  1 +import {
  2 + DirectionEnum,
  3 + DirectionNameEnum,
  4 + RelationTypeEnum,
  5 + RelationTypeNameEnum,
  6 +} from '../../../enum/form';
1 import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node'; 7 import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node';
  8 +import { getDeviceTypes } from '/@/api/ruleChainDesigner';
2 import { FormSchema } from '/@/components/Form'; 9 import { FormSchema } from '/@/components/Form';
3 10
  11 +export interface ValueType {
  12 + deviceRelationsQuery: DeviceRelationsQuery;
  13 + tellFailureIfAbsent: boolean;
  14 + clientAttributeNames: string[];
  15 + sharedAttributeNames: string[];
  16 + serverAttributeNames: string[];
  17 + latestTsKeyNames: string[];
  18 + getLatestValueWithTs: boolean;
  19 +}
  20 +
  21 +export interface DeviceRelationsQuery {
  22 + fetchLastLevelOnly: boolean;
  23 + direction: string;
  24 + maxLevel: number;
  25 + relationType: string;
  26 + deviceTypes: string[];
  27 +}
  28 +
4 export const formSchemas: FormSchema[] = [ 29 export const formSchemas: FormSchema[] = [
5 { 30 {
6 - field: NodeBindDataFieldEnum.NAME,  
7 - component: 'Input',  
8 - label: NodeBindDataFieldNameEnum.NAME, 31 + field: NodeBindDataFieldEnum.FETCH_LAST_LEVEL_ONLY,
  32 + component: 'Checkbox',
  33 + label: '',
  34 + renderComponentContent: () => ({
  35 + default: () => NodeBindDataFieldNameEnum.FETCH_LAST_LEVEL_ONLY,
  36 + }),
  37 + },
  38 + {
  39 + field: NodeBindDataFieldEnum.DIRECTION,
  40 + component: 'Select',
  41 + label: NodeBindDataFieldNameEnum.DIRECTION,
  42 + required: true,
  43 + colProps: { span: 12 },
  44 + componentProps: {
  45 + options: Object.keys(DirectionEnum).map((value) => ({
  46 + label: DirectionNameEnum[value],
  47 + value,
  48 + })),
  49 + placeholder: `请选择${NodeBindDataFieldNameEnum.DIRECTION}`,
  50 + getPopupContainer: () => document.body,
  51 + },
  52 + },
  53 + {
  54 + field: NodeBindDataFieldEnum.MAX_LEVEL,
  55 + component: 'InputNumber',
  56 + label: NodeBindDataFieldNameEnum.MAX_LEVEL,
  57 + colProps: { span: 12 },
  58 + componentProps: {
  59 + min: 1,
  60 + },
  61 + },
  62 + {
  63 + field: NodeBindDataFieldEnum.RELATION_TYPE,
  64 + component: 'Select',
  65 + label: NodeBindDataFieldNameEnum.RELATION_TYPE,
  66 + componentProps: {
  67 + options: Object.keys(RelationTypeEnum).map((value) => ({
  68 + label: RelationTypeNameEnum[value],
  69 + value,
  70 + })),
  71 + placeholder: `请选择${NodeBindDataFieldNameEnum.RELATION_TYPE}`,
  72 + getPopupContainer: () => document.body,
  73 + },
  74 + },
  75 + {
  76 + field: NodeBindDataFieldEnum.DEVICE_TYPES,
  77 + component: 'ApiSelect',
  78 + label: NodeBindDataFieldNameEnum.DEVICE_TYPES,
  79 + componentProps: {
  80 + mode: 'multiple',
  81 + api: getDeviceTypes,
  82 + labelField: 'type',
  83 + valueField: 'type',
  84 + getPopupContainer: () => document.body,
  85 + placeholder: `请选择${NodeBindDataFieldNameEnum.DEVICE_TYPES}`,
  86 + },
  87 + },
  88 + {
  89 + field: NodeBindDataFieldEnum.TELL_FAILURE_IF_ABSENT,
  90 + component: 'Checkbox',
  91 + label: '',
  92 + renderComponentContent: () => ({
  93 + default: () => NodeBindDataFieldNameEnum.TELL_FAILURE_IF_ABSENT,
  94 + }),
  95 + },
  96 + {
  97 + field: NodeBindDataFieldEnum.CLIENT_ATTRIBUTE_NAMES,
  98 + component: 'Select',
  99 + label: NodeBindDataFieldNameEnum.CLIENT_ATTRIBUTE_NAMES,
  100 + componentProps: {
  101 + open: false,
  102 + mode: 'tags',
  103 + placeholder: NodeBindDataFieldNameEnum.CLIENT_ATTRIBUTE_NAMES,
  104 + },
  105 + },
  106 + {
  107 + field: NodeBindDataFieldEnum.SHARED_ATTRIBUTE_NAMES,
  108 + component: 'Select',
  109 + label: NodeBindDataFieldNameEnum.SHARED_ATTRIBUTE_NAMES,
  110 + componentProps: {
  111 + open: false,
  112 + mode: 'tags',
  113 + placeholder: NodeBindDataFieldNameEnum.SHARED_ATTRIBUTE_NAMES,
  114 + },
  115 + },
  116 + {
  117 + field: NodeBindDataFieldEnum.SERVER_ATTRIBUTE_NAMES,
  118 + component: 'Select',
  119 + label: NodeBindDataFieldNameEnum.SERVER_ATTRIBUTE_NAMES,
  120 + componentProps: {
  121 + open: false,
  122 + mode: 'tags',
  123 + placeholder: NodeBindDataFieldNameEnum.SERVER_ATTRIBUTE_NAMES,
  124 + },
  125 + },
  126 + {
  127 + field: NodeBindDataFieldEnum.LATEST_TS_KEY_NAMES,
  128 + component: 'Select',
  129 + label: NodeBindDataFieldNameEnum.LATEST_TS_KEY_NAMES,
  130 + componentProps: {
  131 + open: false,
  132 + mode: 'tags',
  133 + placeholder: NodeBindDataFieldNameEnum.LATEST_TS_KEY_NAMES,
  134 + },
  135 + },
  136 + {
  137 + field: NodeBindDataFieldEnum.GET_LATEST_VALUE_WITH_TS,
  138 + component: 'Checkbox',
  139 + label: '',
  140 + renderComponentContent: () => ({
  141 + default: () => NodeBindDataFieldNameEnum.GET_LATEST_VALUE_WITH_TS,
  142 + }),
9 }, 143 },
10 ]; 144 ];
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 import type { CreateModalDefineExposeType } from '../../../types'; 2 import type { CreateModalDefineExposeType } from '../../../types';
3 import { BasicForm, useForm } from '/@/components/Form'; 3 import { BasicForm, useForm } from '/@/components/Form';
4 - import { formSchemas } from './create.config'; 4 + import { DeviceRelationsQuery, formSchemas, ValueType } from './create.config';
5 import { NodeData } from '../../../types/node'; 5 import { NodeData } from '../../../types/node';
  6 + import { NodeBindDataFieldEnum } from '../../../enum/node';
6 7
7 defineProps<{ 8 defineProps<{
8 config: NodeData; 9 config: NodeData;
@@ -16,12 +17,33 @@ @@ -16,12 +17,33 @@
16 const getValue: CreateModalDefineExposeType['getFieldsValue'] = async () => { 17 const getValue: CreateModalDefineExposeType['getFieldsValue'] = async () => {
17 await validate(); 18 await validate();
18 const value = getFieldsValue() || {}; 19 const value = getFieldsValue() || {};
19 - return value; 20 + return {
  21 + [NodeBindDataFieldEnum.DEVICE_RELATIONS_QUERY]: {
  22 + [NodeBindDataFieldEnum.FETCH_LAST_LEVEL_ONLY]:
  23 + value[NodeBindDataFieldEnum.FETCH_LAST_LEVEL_ONLY],
  24 + [NodeBindDataFieldEnum.DEVICE_TYPES]: value[NodeBindDataFieldEnum.DEVICE_TYPES],
  25 + [NodeBindDataFieldEnum.DIRECTION]: value[NodeBindDataFieldEnum.DIRECTION],
  26 + [NodeBindDataFieldEnum.MAX_LEVEL]: value[NodeBindDataFieldEnum.MAX_LEVEL],
  27 + [NodeBindDataFieldEnum.RELATION_TYPE]: value[NodeBindDataFieldEnum.RELATION_TYPE],
  28 + } as DeviceRelationsQuery,
  29 + [NodeBindDataFieldEnum.CLIENT_ATTRIBUTE_NAMES]:
  30 + value[NodeBindDataFieldEnum.CLIENT_ATTRIBUTE_NAMES],
  31 + [NodeBindDataFieldEnum.GET_LATEST_VALUE_WITH_TS]:
  32 + value[NodeBindDataFieldEnum.GET_LATEST_VALUE_WITH_TS],
  33 + [NodeBindDataFieldEnum.LATEST_TS_KEY_NAMES]: value[NodeBindDataFieldEnum.LATEST_TS_KEY_NAMES],
  34 + [NodeBindDataFieldEnum.SERVER_ATTRIBUTE_NAMES]:
  35 + value[NodeBindDataFieldEnum.SERVER_ATTRIBUTE_NAMES],
  36 + [NodeBindDataFieldEnum.SHARED_ATTRIBUTE_NAMES]:
  37 + value[NodeBindDataFieldEnum.SHARED_ATTRIBUTE_NAMES],
  38 + [NodeBindDataFieldEnum.TELL_FAILURE_IF_ABSENT]:
  39 + value[NodeBindDataFieldEnum.TELL_FAILURE_IF_ABSENT],
  40 + } as ValueType;
20 }; 41 };
21 42
22 const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => { 43 const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => {
23 resetFields(); 44 resetFields();
24 - setFieldsValue(value); 45 + console.log(value);
  46 + setFieldsValue({ ...value, ...(value?.[NodeBindDataFieldEnum.DEVICE_RELATIONS_QUERY] || {}) });
25 }; 47 };
26 48
27 defineExpose({ 49 defineExpose({
1 import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node'; 1 import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node';
2 -import { FormSchema } from '/@/components/Form'; 2 +import { FormSchema, useComponentRegister } from '/@/components/Form';
  3 +import { AttributeConfiguration } from '/@/views/rule/designer/src/components/AttributeConfiguration';
  4 +
  5 +useComponentRegister('AttributeConfiguration', AttributeConfiguration);
3 6
4 export const formSchemas: FormSchema[] = [ 7 export const formSchemas: FormSchema[] = [
5 { 8 {
6 - field: NodeBindDataFieldEnum.NAME,  
7 - component: 'Input',  
8 - label: NodeBindDataFieldNameEnum.NAME, 9 + field: NodeBindDataFieldEnum.TELEMETRY,
  10 + component: 'Checkbox',
  11 + label: '',
  12 + renderComponentContent: () => {
  13 + return {
  14 + default: () => NodeBindDataFieldNameEnum.TELEMETRY,
  15 + };
  16 + },
  17 + },
  18 + {
  19 + field: NodeBindDataFieldEnum.ATTR_MAPING,
  20 + component: 'AttributeConfiguration',
  21 + label: NodeBindDataFieldNameEnum.ATTR_MAPING,
  22 + slot: NodeBindDataFieldEnum.ATTR_MAPING,
  23 + valueField: 'value',
  24 + changeEvent: 'update:value',
9 }, 25 },
10 ]; 26 ];
@@ -3,25 +3,33 @@ @@ -3,25 +3,33 @@
3 import { BasicForm, useForm } from '/@/components/Form'; 3 import { BasicForm, useForm } from '/@/components/Form';
4 import { formSchemas } from './create.config'; 4 import { formSchemas } from './create.config';
5 import { NodeData } from '../../../types/node'; 5 import { NodeData } from '../../../types/node';
  6 + import { AttributeConfiguration } from '/@/views/rule/designer/src/components/AttributeConfiguration';
  7 + import { ref, unref } from 'vue';
  8 + import { NodeBindDataFieldEnum } from '../../../enum/node';
6 9
7 defineProps<{ 10 defineProps<{
8 config: NodeData; 11 config: NodeData;
9 }>(); 12 }>();
10 13
  14 + const attributeControlElRef = ref<Nullable<InstanceType<typeof AttributeConfiguration>>>();
  15 +
11 const [register, { validate, getFieldsValue, setFieldsValue, resetFields }] = useForm({ 16 const [register, { validate, getFieldsValue, setFieldsValue, resetFields }] = useForm({
12 schemas: formSchemas, 17 schemas: formSchemas,
13 showActionButtonGroup: false, 18 showActionButtonGroup: false,
14 }); 19 });
15 20
16 const getValue: CreateModalDefineExposeType['getFieldsValue'] = async () => { 21 const getValue: CreateModalDefineExposeType['getFieldsValue'] = async () => {
  22 + await unref(attributeControlElRef)?.validate();
17 await validate(); 23 await validate();
18 const value = getFieldsValue() || {}; 24 const value = getFieldsValue() || {};
19 - return value; 25 + const attrMapping = unref(attributeControlElRef)?.getFieldsValue();
  26 + return { ...value, [NodeBindDataFieldEnum.ATTR_MAPING]: attrMapping };
20 }; 27 };
21 28
22 const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => { 29 const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => {
23 resetFields(); 30 resetFields();
24 setFieldsValue(value); 31 setFieldsValue(value);
  32 + unref(attributeControlElRef)?.setFieldsValue(value?.[NodeBindDataFieldEnum.ATTR_MAPING]);
25 }; 33 };
26 34
27 defineExpose({ 35 defineExpose({
@@ -31,5 +39,32 @@ @@ -31,5 +39,32 @@
31 </script> 39 </script>
32 40
33 <template> 41 <template>
34 - <BasicForm @register="register" /> 42 + <BasicForm @register="register">
  43 + <template #attrMapping="{ field, model }">
  44 + <AttributeConfiguration
  45 + v-model:value="model[field]"
  46 + ref="attributeControlElRef"
  47 + :keyLabel="`Source ${model[NodeBindDataFieldEnum.TELEMETRY] ? 'telemetry' : 'attribute'}`"
  48 + valueLabel="Target attribute"
  49 + >
  50 + <template #afterForm>
  51 + <div class="text-xs text-gray-500">
  52 + Hint: use
  53 + <code>
  54 + <span class="text-dark-500">${</span>
  55 + metadataKey
  56 + <span class="text-dark-500">}</span>
  57 + </code>
  58 + for value from metadata,
  59 + <code>
  60 + <span class="text-dark-500"> $[</span>
  61 + messageKey
  62 + <span class="text-dark-500">] </span>
  63 + </code>
  64 + for value from message body to substitute "Source" and "Target" key names
  65 + </div>
  66 + </template>
  67 + </AttributeConfiguration>
  68 + </template>
  69 + </BasicForm>
35 </template> 70 </template>
1 -import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node'; 1 +import { DetailsListEnum, DetailsListNameEnum } from '../../../enum/form';
  2 +import {
  3 + TenantDetailsFieldsEnum,
  4 + TenantDetailsFieldsNameEnum,
  5 +} from '../../../enum/formField/enrichment';
2 import { FormSchema } from '/@/components/Form'; 6 import { FormSchema } from '/@/components/Form';
3 7
4 export const formSchemas: FormSchema[] = [ 8 export const formSchemas: FormSchema[] = [
5 { 9 {
6 - field: NodeBindDataFieldEnum.NAME,  
7 - component: 'Input',  
8 - label: NodeBindDataFieldNameEnum.NAME, 10 + field: TenantDetailsFieldsEnum.DETAILS_LIST,
  11 + component: 'Select',
  12 + label: TenantDetailsFieldsNameEnum.DETAILS_LIST,
  13 + required: true,
  14 + componentProps: {
  15 + mode: 'multiple',
  16 + options: Object.keys(DetailsListEnum).map((value) => ({
  17 + label: DetailsListNameEnum[value],
  18 + value,
  19 + })),
  20 + getPopupContainer: () => document.body,
  21 + placeholder: `请选择${TenantDetailsFieldsNameEnum.DETAILS_LIST}`,
  22 + },
  23 + },
  24 + {
  25 + field: TenantDetailsFieldsEnum.ADD_TO_METADATA,
  26 + component: 'Checkbox',
  27 + label: '',
  28 + renderComponentContent: () => ({
  29 + default: () => TenantDetailsFieldsNameEnum.DETAILS_LIST,
  30 + }),
9 }, 31 },
10 ]; 32 ];
@@ -8,6 +8,7 @@ export const formSchemas: FormSchema[] = [ @@ -8,6 +8,7 @@ export const formSchemas: FormSchema[] = [
8 label: NodeBindDataFieldNameEnum.MESSAGE_NAMES, 8 label: NodeBindDataFieldNameEnum.MESSAGE_NAMES,
9 componentProps: { 9 componentProps: {
10 mode: 'tags', 10 mode: 'tags',
  11 + open: false,
11 getPopupContainer: () => document.body, 12 getPopupContainer: () => document.body,
12 }, 13 },
13 }, 14 },
@@ -17,6 +18,7 @@ export const formSchemas: FormSchema[] = [ @@ -17,6 +18,7 @@ export const formSchemas: FormSchema[] = [
17 label: NodeBindDataFieldNameEnum.METADATA_NAMES, 18 label: NodeBindDataFieldNameEnum.METADATA_NAMES,
18 componentProps: { 19 componentProps: {
19 mode: 'tags', 20 mode: 'tags',
  21 + open: false,
20 getPopupContainer: () => document.body, 22 getPopupContainer: () => document.body,
21 }, 23 },
22 }, 24 },
@@ -4,6 +4,7 @@ import { @@ -4,6 +4,7 @@ import {
4 EntityTypeEnum, 4 EntityTypeEnum,
5 EntityTypeNameEnum, 5 EntityTypeNameEnum,
6 RelationTypeEnum, 6 RelationTypeEnum,
  7 + RelationTypeNameEnum,
7 } from '../../../enum/form'; 8 } from '../../../enum/form';
8 import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node'; 9 import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node';
9 import { deviceProfilePage } from '/@/api/device/deviceManager'; 10 import { deviceProfilePage } from '/@/api/device/deviceManager';
@@ -18,8 +19,10 @@ export const formSchemas: FormSchema[] = [ @@ -18,8 +19,10 @@ export const formSchemas: FormSchema[] = [
18 { 19 {
19 field: NodeBindDataFieldEnum.CHECK_FOR_SINGLE_ENTITY, 20 field: NodeBindDataFieldEnum.CHECK_FOR_SINGLE_ENTITY,
20 component: 'Checkbox', 21 component: 'Checkbox',
21 - label: NodeBindDataFieldNameEnum.CHECK_FOR_SINGLE_ENTITY,  
22 - labelWidth: 220, 22 + label: '',
  23 + renderComponentContent: () => ({
  24 + default: () => NodeBindDataFieldNameEnum.CHECK_FOR_SINGLE_ENTITY,
  25 + }),
23 }, 26 },
24 { 27 {
25 field: NodeBindDataFieldEnum.DIRECTION, 28 field: NodeBindDataFieldEnum.DIRECTION,
@@ -69,10 +72,10 @@ export const formSchemas: FormSchema[] = [ @@ -69,10 +72,10 @@ export const formSchemas: FormSchema[] = [
69 label: NodeBindDataFieldNameEnum.RELEATION_TYPE, 72 label: NodeBindDataFieldNameEnum.RELEATION_TYPE,
70 defaultValue: RelationTypeEnum.CONTAINS, 73 defaultValue: RelationTypeEnum.CONTAINS,
71 componentProps: { 74 componentProps: {
72 - options: [  
73 - { label: RelationTypeEnum.CONTAINS, value: RelationTypeEnum.CONTAINS },  
74 - { label: RelationTypeEnum.MANAGES, value: RelationTypeEnum.MANAGES },  
75 - ], 75 + options: Object.keys(RelationTypeEnum).map((value) => ({
  76 + label: RelationTypeNameEnum[value],
  77 + value,
  78 + })),
76 }, 79 },
77 }, 80 },
78 ]; 81 ];
@@ -24,8 +24,10 @@ export const formSchemas: FormSchema[] = [ @@ -24,8 +24,10 @@ export const formSchemas: FormSchema[] = [
24 { 24 {
25 field: NodeBindDataFieldEnum.FETCH_PERIMETER_INFO_FROM_MESSAGE_METADATA, 25 field: NodeBindDataFieldEnum.FETCH_PERIMETER_INFO_FROM_MESSAGE_METADATA,
26 component: 'Checkbox', 26 component: 'Checkbox',
27 - label: NodeBindDataFieldNameEnum.FETCH_PERIMETER_INFO_FROM_MESSAGE_METADATA,  
28 - labelWidth: 350, 27 + label: '',
  28 + renderComponentContent: () => ({
  29 + default: () => NodeBindDataFieldNameEnum.FETCH_PERIMETER_INFO_FROM_MESSAGE_METADATA,
  30 + }),
29 }, 31 },
30 { 32 {
31 field: NodeBindDataFieldEnum.PERIMETER_KEY_NAME, 33 field: NodeBindDataFieldEnum.PERIMETER_KEY_NAME,
@@ -2,14 +2,14 @@ import { h } from 'vue'; @@ -2,14 +2,14 @@ import { h } from 'vue';
2 import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node'; 2 import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node';
3 import { FormSchema, useComponentRegister } from '/@/components/Form'; 3 import { FormSchema, useComponentRegister } from '/@/components/Form';
4 import HelpMessage from './HelpMessage.vue'; 4 import HelpMessage from './HelpMessage.vue';
5 -import JavaScriptFilterFunctionModal from './JavaScriptFilterFunctionModal.vue'; 5 +import { JavascriptEditorWithTestModal } from '/@/views/rule/designer/src/components/JavaScriptFilterModal';
6 6
7 -useComponentRegister('JavaScriptFilterFunctionModal', JavaScriptFilterFunctionModal); 7 +useComponentRegister('JavascriptEditorWithTestModal', JavascriptEditorWithTestModal);
8 8
9 export const formSchemas: FormSchema[] = [ 9 export const formSchemas: FormSchema[] = [
10 { 10 {
11 field: NodeBindDataFieldEnum.JS_SCRIPT, 11 field: NodeBindDataFieldEnum.JS_SCRIPT,
12 - component: 'JavaScriptFilterFunctionModal', 12 + component: 'JavascriptEditorWithTestModal',
13 label: NodeBindDataFieldNameEnum.JS_SCRIPT, 13 label: NodeBindDataFieldNameEnum.JS_SCRIPT,
14 changeEvent: 'update:value', 14 changeEvent: 'update:value',
15 valueField: 'value', 15 valueField: 'value',
1 import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node'; 1 import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node';
2 import { FormSchema, useComponentRegister } from '/@/components/Form'; 2 import { FormSchema, useComponentRegister } from '/@/components/Form';
3 -import JavaScriptFilterFunctionModal from '../Script/JavaScriptFilterFunctionModal.vue'; 3 +import { JavascriptEditorWithTestModal } from '/@/views/rule/designer/src/components/JavaScriptFilterModal';
  4 +
  5 +useComponentRegister('JavascriptEditorWithTestModal', JavascriptEditorWithTestModal);
4 6
5 -useComponentRegister('JavaScriptFilterFunctionModal', JavaScriptFilterFunctionModal);  
6 export const formSchemas: FormSchema[] = [ 7 export const formSchemas: FormSchema[] = [
7 { 8 {
8 field: NodeBindDataFieldEnum.JS_SCRIPT, 9 field: NodeBindDataFieldEnum.JS_SCRIPT,
9 - component: 'JavaScriptFilterFunctionModal', 10 + component: 'JavascriptEditorWithTestModal',
10 label: NodeBindDataFieldNameEnum.JS_SCRIPT, 11 label: NodeBindDataFieldNameEnum.JS_SCRIPT,
11 changeEvent: 'update:value', 12 changeEvent: 'update:value',
12 valueField: 'value', 13 valueField: 'value',
13 componentProps: { 14 componentProps: {
14 buttonName: 'Test Switch Function', 15 buttonName: 'Test Switch Function',
15 javaScriptEditorProps: { 16 javaScriptEditorProps: {
  17 + height: 230,
16 functionName: 'Switch', 18 functionName: 'Switch',
17 paramsName: ['msg', 'metadata', 'mstType'], 19 paramsName: ['msg', 'metadata', 'mstType'],
18 }, 20 },
1 -import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node';  
2 -import { FormSchema } from '/@/components/Form'; 1 +import { OriginatorSourceEnum, OriginatorSourceNameEnum } from '../../../enum/form';
  2 +import {
  3 + ChangeOriginatorFieldsEnum,
  4 + ChangeOriginatorFieldsNameEnum,
  5 +} from '../../../enum/formField/transformation';
  6 +import { RelationsQuery } from '../../../src/components/RelationsQuery';
  7 +import { FormSchema, useComponentRegister } from '/@/components/Form';
  8 +
  9 +useComponentRegister('RelationsQuery', RelationsQuery);
3 10
4 export const formSchemas: FormSchema[] = [ 11 export const formSchemas: FormSchema[] = [
5 { 12 {
6 - field: NodeBindDataFieldEnum.NAME,  
7 - component: 'Input',  
8 - label: NodeBindDataFieldNameEnum.NAME, 13 + field: ChangeOriginatorFieldsEnum.ORIGINATOR_SOURCE,
  14 + component: 'Select',
  15 + label: ChangeOriginatorFieldsNameEnum.ORIGINATOR_SOURCE,
  16 + required: true,
  17 + componentProps: {
  18 + options: Object.keys(OriginatorSourceEnum).map((value) => ({
  19 + label: OriginatorSourceNameEnum[value],
  20 + value,
  21 + })),
  22 + getPopupContainer: () => document.body,
  23 + placeholder: `请选择${ChangeOriginatorFieldsNameEnum.ORIGINATOR_SOURCE}`,
  24 + },
  25 + },
  26 + {
  27 + field: ChangeOriginatorFieldsEnum.RELATIONS_QUERY,
  28 + component: 'RelationsQuery',
  29 + label: ChangeOriginatorFieldsNameEnum.RELATIONS_QUERY,
  30 + slot: ChangeOriginatorFieldsEnum.RELATIONS_QUERY,
9 }, 31 },
10 ]; 32 ];
@@ -3,11 +3,16 @@ @@ -3,11 +3,16 @@
3 import { BasicForm, useForm } from '/@/components/Form'; 3 import { BasicForm, useForm } from '/@/components/Form';
4 import { formSchemas } from './create.config'; 4 import { formSchemas } from './create.config';
5 import { NodeData } from '../../../types/node'; 5 import { NodeData } from '../../../types/node';
  6 + import { RelationsQuery } from '../../../src/components/RelationsQuery';
  7 + import { ref, unref } from 'vue';
  8 + import { ChangeOriginatorFieldsEnum } from '../../../enum/formField/transformation';
6 9
7 defineProps<{ 10 defineProps<{
8 config: NodeData; 11 config: NodeData;
9 }>(); 12 }>();
10 13
  14 + const relationsQueryElRef = ref<Nullable<InstanceType<typeof RelationsQuery>>>();
  15 +
11 const [register, { validate, getFieldsValue, setFieldsValue, resetFields }] = useForm({ 16 const [register, { validate, getFieldsValue, setFieldsValue, resetFields }] = useForm({
12 schemas: formSchemas, 17 schemas: formSchemas,
13 showActionButtonGroup: false, 18 showActionButtonGroup: false,
@@ -16,12 +21,14 @@ @@ -16,12 +21,14 @@
16 const getValue: CreateModalDefineExposeType['getFieldsValue'] = async () => { 21 const getValue: CreateModalDefineExposeType['getFieldsValue'] = async () => {
17 await validate(); 22 await validate();
18 const value = getFieldsValue() || {}; 23 const value = getFieldsValue() || {};
19 - return value; 24 + const relationsQuery = unref(relationsQueryElRef)?.getFieldsValue();
  25 + return { ...value, [ChangeOriginatorFieldsEnum.RELATIONS_QUERY]: relationsQuery };
20 }; 26 };
21 27
22 const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => { 28 const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => {
23 resetFields(); 29 resetFields();
24 setFieldsValue(value); 30 setFieldsValue(value);
  31 + unref(relationsQueryElRef)?.setFieldsValue(value?.[ChangeOriginatorFieldsEnum.RELATIONS_QUERY]);
25 }; 32 };
26 33
27 defineExpose({ 34 defineExpose({
@@ -31,5 +38,9 @@ @@ -31,5 +38,9 @@
31 </script> 38 </script>
32 39
33 <template> 40 <template>
34 - <BasicForm @register="register" /> 41 + <BasicForm @register="register">
  42 + <template #relationsQuery="{ field, model }">
  43 + <RelationsQuery ref="relationsQueryElRef" v-model:value="model[field]" />
  44 + </template>
  45 + </BasicForm>
35 </template> 46 </template>
1 -import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node';  
2 -import { FormSchema } from '/@/components/Form'; 1 +import { ScriptFieldsEnum, ScriptFieldsNameEnum } from '../../../enum/formField/transformation';
  2 +import { JavascriptEditorWithTestModal } from '../../../src/components/JavaScriptFilterModal';
  3 +import { FormSchema, useComponentRegister } from '/@/components/Form';
  4 +
  5 +useComponentRegister('JavascriptEditorWithTestModal', JavascriptEditorWithTestModal);
3 6
4 export const formSchemas: FormSchema[] = [ 7 export const formSchemas: FormSchema[] = [
5 { 8 {
6 - field: NodeBindDataFieldEnum.NAME,  
7 - component: 'Input',  
8 - label: NodeBindDataFieldNameEnum.NAME, 9 + field: ScriptFieldsEnum.JS_SCRIPT,
  10 + component: 'JavascriptEditorWithTestModal',
  11 + label: ScriptFieldsNameEnum.JS_SCRIPT,
  12 + changeEvent: 'update:value',
  13 + valueField: 'value',
  14 + componentProps: {
  15 + javaScriptEditorProps: {
  16 + functionName: 'Transform',
  17 + paramsName: ['msg', 'metadata', 'msgType'],
  18 + },
  19 + buttonName: 'Test transformer function',
  20 + },
9 }, 21 },
10 ]; 22 ];
1 -import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node'; 1 +import { MailBodyTypeEnum, MailBodyTypeNameEnum } from '../../../enum/form';
  2 +import { ToEmailFieldsEnum, ToEmailFieldsNameEnum } from '../../../enum/formField/transformation';
2 import { FormSchema } from '/@/components/Form'; 3 import { FormSchema } from '/@/components/Form';
3 4
4 export const formSchemas: FormSchema[] = [ 5 export const formSchemas: FormSchema[] = [
5 { 6 {
6 - field: NodeBindDataFieldEnum.NAME, 7 + field: ToEmailFieldsEnum.FROM_TEMPLATE,
7 component: 'Input', 8 component: 'Input',
8 - label: NodeBindDataFieldNameEnum.NAME, 9 + label: ToEmailFieldsNameEnum.FROM_TEMPLATE,
  10 + helpMessage: [
  11 + 'Hint: use ${metadataKey} for value from metadata, $[messageKey] for value from message body',
  12 + ],
  13 + componentProps: {
  14 + placeholder: `请输入${ToEmailFieldsNameEnum.FROM_TEMPLATE}`,
  15 + },
  16 + },
  17 + {
  18 + field: ToEmailFieldsEnum.TO_TEMPLATE,
  19 + component: 'Input',
  20 + label: ToEmailFieldsNameEnum.TO_TEMPLATE,
  21 + helpMessage: [
  22 + 'Comma separated address list, use ${metadataKey} for value from metadata, $[messageKey] for value from message body',
  23 + ],
  24 + componentProps: {
  25 + placeholder: `请输入${ToEmailFieldsNameEnum.FROM_TEMPLATE}`,
  26 + },
  27 + },
  28 + {
  29 + field: ToEmailFieldsEnum.CC_TEMPLATE,
  30 + component: 'Input',
  31 + label: ToEmailFieldsNameEnum.CC_TEMPLATE,
  32 + helpMessage: [
  33 + 'Comma separated address list, use ${metadataKey} for value from metadata, $[messageKey] for value from message body',
  34 + ],
  35 + componentProps: {
  36 + placeholder: `请输入${ToEmailFieldsNameEnum.FROM_TEMPLATE}`,
  37 + },
  38 + },
  39 + {
  40 + field: ToEmailFieldsEnum.BCC_TEMPLATE,
  41 + component: 'Input',
  42 + label: ToEmailFieldsNameEnum.BCC_TEMPLATE,
  43 + helpMessage: [
  44 + 'Comma separated address list, use ${metadataKey} for value from metadata, $[messageKey] for value from message body',
  45 + ],
  46 + componentProps: {
  47 + placeholder: `请输入${ToEmailFieldsNameEnum.FROM_TEMPLATE}`,
  48 + },
  49 + },
  50 + {
  51 + field: ToEmailFieldsEnum.SUBJECT_TEMPLATE,
  52 + component: 'Input',
  53 + label: ToEmailFieldsNameEnum.SUBJECT_TEMPLATE,
  54 + helpMessage: [
  55 + 'Hint: use ${metadataKey} for value from metadata, $[messageKey] for value from message body',
  56 + ],
  57 + componentProps: {
  58 + placeholder: `请输入${ToEmailFieldsNameEnum.FROM_TEMPLATE}`,
  59 + },
  60 + },
  61 + {
  62 + field: ToEmailFieldsEnum.MAIL_BODY_TYPE,
  63 + component: 'Select',
  64 + label: ToEmailFieldsNameEnum.MAIL_BODY_TYPE,
  65 + componentProps: ({ formActionType }) => {
  66 + const { setFieldsValue } = formActionType;
  67 + return {
  68 + options: [
  69 + { label: MailBodyTypeNameEnum.PLAIN_TEXT, value: MailBodyTypeEnum.PLAIN_TEXT },
  70 + { label: MailBodyTypeNameEnum.HTML, value: MailBodyTypeEnum.HTML },
  71 + { label: MailBodyTypeNameEnum.DYNAMIC, value: MailBodyTypeEnum.DYNAMIC },
  72 + ],
  73 + placeholder: `请选择${ToEmailFieldsNameEnum.MAIL_BODY_TYPE}`,
  74 + getPopupContainer: () => document.body,
  75 + onChange: () => {
  76 + setFieldsValue({ [ToEmailFieldsEnum.IS_HTML_TEMPLATE]: null });
  77 + },
  78 + };
  79 + },
  80 + },
  81 + {
  82 + field: ToEmailFieldsEnum.IS_HTML_TEMPLATE,
  83 + component: 'Input',
  84 + label: ToEmailFieldsNameEnum.IS_HTML_TEMPLATE,
  85 + show: ({ model }) => model[ToEmailFieldsEnum.MAIL_BODY_TYPE] === MailBodyTypeEnum.DYNAMIC,
  86 + helpMessage: [
  87 + 'Hint: use ${metadataKey} for value from metadata, $[messageKey] for value from message body',
  88 + ],
  89 + componentProps: {
  90 + placeholder: `请输入${ToEmailFieldsNameEnum.IS_HTML_TEMPLATE}`,
  91 + },
  92 + },
  93 + {
  94 + field: ToEmailFieldsEnum.BODY_TEMPLATE,
  95 + component: 'InputTextArea',
  96 + label: ToEmailFieldsEnum.BODY_TEMPLATE,
  97 + componentProps: {
  98 + placeholder: `请输入${ToEmailFieldsNameEnum.BODY_TEMPLATE}`,
  99 + autoSize: { minRows: 3 },
  100 + },
9 }, 101 },
10 ]; 102 ];
@@ -19,13 +19,17 @@ const BasicConnectionComponent = { @@ -19,13 +19,17 @@ const BasicConnectionComponent = {
19 import('../src/components/CreateEdgeModal/CustomRelations.vue'), 19 import('../src/components/CreateEdgeModal/CustomRelations.vue'),
20 }; 20 };
21 21
22 -export const fetchNodeComponent = (key: string, flag: FetchNodeComFlagTypeENum) => { 22 +export const fetchNodeComponent = (config: NodeItemConfigType, flag: FetchNodeComFlagTypeENum) => {
  23 + const { key, categoryType } = config;
23 const modules = 24 const modules =
24 flag === FetchNodeComFlagTypeENum.CONNECTION_MODAL ? connectionModules : createModules; 25 flag === FetchNodeComFlagTypeENum.CONNECTION_MODAL ? connectionModules : createModules;
  26 +
25 const modulesName = Object.keys(modules); 27 const modulesName = Object.keys(modules);
26 const path = modulesName.find((item) => { 28 const path = modulesName.find((item) => {
27 const temp = item.split('/'); 29 const temp = item.split('/');
28 - return temp[temp.length - 2] === key; 30 + const categoryName = temp[temp.length - 3];
  31 + const fileName = temp[temp.length - 2];
  32 + return fileName === key && categoryType.toUpperCase() === categoryName.toUpperCase();
29 }); 33 });
30 return path ? modules[path] : null; 34 return path ? modules[path] : null;
31 }; 35 };
@@ -40,8 +44,7 @@ export const fetchConnectionComponent = async (config: NodeItemConfigType) => { @@ -40,8 +44,7 @@ export const fetchConnectionComponent = async (config: NodeItemConfigType) => {
40 }; 44 };
41 45
42 export const fetchCreateComponent = async (config: NodeItemConfigType) => { 46 export const fetchCreateComponent = async (config: NodeItemConfigType) => {
43 - const { key } = config;  
44 - return fetchNodeComponent(key, FetchNodeComFlagTypeENum.CREATE_MODAL); 47 + return fetchNodeComponent(config, FetchNodeComFlagTypeENum.CREATE_MODAL);
45 }; 48 };
46 49
47 export const allComponents: Record< 50 export const allComponents: Record<
@@ -56,14 +59,14 @@ export const allComponents: Record< @@ -56,14 +59,14 @@ export const allComponents: Record<
56 category: EnrichmentCategoryConfig, 59 category: EnrichmentCategoryConfig,
57 components: EnrichmentComponents, 60 components: EnrichmentComponents,
58 }, 61 },
59 - [RuleNodeTypeEnum.ACTION]: {  
60 - category: ActionCategoryConfig,  
61 - components: ActionComponents,  
62 - },  
63 [RuleNodeTypeEnum.TRANSFORMATION]: { 62 [RuleNodeTypeEnum.TRANSFORMATION]: {
64 category: TransformationCategoryConfig, 63 category: TransformationCategoryConfig,
65 components: TransformationComponents, 64 components: TransformationComponents,
66 }, 65 },
  66 + [RuleNodeTypeEnum.ACTION]: {
  67 + category: ActionCategoryConfig,
  68 + components: ActionComponents,
  69 + },
67 [RuleNodeTypeEnum.EXTERNAL]: { 70 [RuleNodeTypeEnum.EXTERNAL]: {
68 category: ExternalCategoryConfig, 71 category: ExternalCategoryConfig,
69 components: ExternalComponents, 72 components: ExternalComponents,
  1 +export { default as AttributeConfiguration } from './index.vue';
  1 +<script setup lang="ts">
  2 + import { Button, Tooltip, Row, Col } from 'ant-design-vue';
  3 + import { Icon } from '/@/components/Icon';
  4 + import { BasicForm, FormActionType, FormProps, FormSchema } from '/@/components/Form';
  5 + import { computed, ref, unref, watch } from 'vue';
  6 + import { buildShortUUID } from '/@/utils/uuid';
  7 + import { isNullOrUnDef } from '/@/utils/is';
  8 + import { ComponentType } from '/@/components/Form/src/types';
  9 +
  10 + enum FormFieldEnum {
  11 + KEY = 'key',
  12 + VALUE = 'value',
  13 + }
  14 +
  15 + interface ListItemType {
  16 + [FormFieldEnum.KEY]?: string;
  17 + [FormFieldEnum.VALUE]?: string;
  18 + uuid: string;
  19 + }
  20 +
  21 + const props = withDefaults(
  22 + defineProps<{
  23 + value?: Recordable;
  24 + formProps?: FormProps;
  25 + hiddenInputBorder?: boolean;
  26 + showLabel?: boolean;
  27 + keyLabel?: string;
  28 + valueLabel?: string;
  29 + keyComponent?: ComponentType;
  30 + valueComponent?: ComponentType;
  31 + }>(),
  32 + {
  33 + keyComponent: 'Input',
  34 + valueComponent: 'Input',
  35 + value: () => ({}),
  36 + formProps: () => ({ layout: 'vertical' }),
  37 + showLabel: true,
  38 + hiddenInputBorder: true,
  39 + }
  40 + );
  41 +
  42 + const emit = defineEmits(['update:value']);
  43 +
  44 + const getPlaceholder = (label = '') => `请输入${label}`;
  45 +
  46 + const getRules = (label = '') => {
  47 + return [{ required: true, message: getPlaceholder(label), trigger: ['change', 'blur'] }];
  48 + };
  49 +
  50 + const mergeComponentProps = (label = '') => {
  51 + return {
  52 + placeholder: getPlaceholder(label),
  53 + onBlur: handleOnBlur,
  54 + };
  55 + };
  56 +
  57 + const getSchemas = computed<FormSchema[]>(() => {
  58 + const { keyComponent, valueComponent, keyLabel, valueLabel } = props;
  59 + return [
  60 + {
  61 + field: FormFieldEnum.KEY,
  62 + label: '',
  63 + colProps: { span: 11 },
  64 + component: keyComponent,
  65 + rules: getRules(keyLabel),
  66 + componentProps: mergeComponentProps(keyLabel),
  67 + },
  68 + {
  69 + field: FormFieldEnum.VALUE,
  70 + label: '',
  71 + colProps: { span: 11, offset: 1 },
  72 + component: valueComponent,
  73 + rules: getRules(valueLabel),
  74 + componentProps: mergeComponentProps(valueLabel),
  75 + },
  76 + ] as FormSchema[];
  77 + });
  78 +
  79 + const handleOnBlur = () => {
  80 + const value = getFieldsValue();
  81 +
  82 + if (
  83 + unref(list).length === Object.keys(value).length &&
  84 + Object.values(value).some((item) => isNullOrUnDef(item))
  85 + )
  86 + return;
  87 + emit('update:value', value);
  88 + };
  89 +
  90 + const list = ref<ListItemType[]>([{ uuid: buildShortUUID() }]);
  91 +
  92 + const formListElRef = ref<Nullable<FormActionType>[]>([]);
  93 +
  94 + const handleAdd = () => {
  95 + list.value.push({ uuid: buildShortUUID() });
  96 + };
  97 +
  98 + const handleDelete = (string: string) => {
  99 + const index = unref(list).findIndex((item) => item.uuid === string);
  100 + ~index && unref(list).splice(index, 1);
  101 + };
  102 +
  103 + const validate = async () => {
  104 + for (const item of unref(formListElRef)) {
  105 + await item?.validate();
  106 + }
  107 + };
  108 +
  109 + const getFieldsValue = (): Recordable => {
  110 + return unref(formListElRef)
  111 + .map((item) => item?.getFieldsValue() as Record<FormFieldEnum, string>)
  112 + .reduce(
  113 + (prev, next) => ({ ...prev, ...(next.key ? { [next.key]: next.value } : {}) }),
  114 + {} as Recordable
  115 + );
  116 + };
  117 +
  118 + const setFieldsValue = (record: Recordable) => {
  119 + const keys = Object.keys(record);
  120 +
  121 + const valueList: ListItemType[] = [];
  122 +
  123 + for (const key of keys) {
  124 + const value = record[key];
  125 + const itemValue: ListItemType = {
  126 + uuid: buildShortUUID(),
  127 + [FormFieldEnum.KEY]: key,
  128 + [FormFieldEnum.VALUE]: value,
  129 + };
  130 + valueList.push(itemValue);
  131 + }
  132 +
  133 + list.value = valueList;
  134 +
  135 + if (!list.value.length) list.value = [{ uuid: buildShortUUID() }];
  136 + };
  137 +
  138 + watch(
  139 + () => props.value,
  140 + (value) => {
  141 + setFieldsValue(value);
  142 + },
  143 + {
  144 + immediate: true,
  145 + }
  146 + );
  147 +
  148 + defineExpose({
  149 + validate,
  150 + getFieldsValue,
  151 + setFieldsValue,
  152 + });
  153 +</script>
  154 +
  155 +<template>
  156 + <section
  157 + :class="[hiddenInputBorder && 'hidden-input-border', 'attribute-configuration-form']"
  158 + class="h-full flex flex-col"
  159 + >
  160 + <slot name="beforeForm"></slot>
  161 + <Row v-if="showLabel" type="flex" class="w-full h-6 font-medium">
  162 + <Col :span="11">{{ keyLabel }}</Col>
  163 + <Col :span="11" :offset="1">{{ valueLabel }}</Col>
  164 + <Col :span="1" />
  165 + </Row>
  166 + <section class="pr-2 overflow-x-hidden overflow-y-auto">
  167 + <main class="flex items-center justify-between" v-for="item in list" :key="item.uuid">
  168 + <BasicForm
  169 + ref="formListElRef"
  170 + :model="item"
  171 + class="w-full"
  172 + :schemas="getSchemas"
  173 + :showActionButtonGroup="false"
  174 + />
  175 + <Tooltip title="删除">
  176 + <Icon
  177 + icon="material-symbols:close"
  178 + class="cursor-pointer svg:text-2xl"
  179 + @click="handleDelete(item.uuid)"
  180 + />
  181 + </Tooltip>
  182 + </main>
  183 + </section>
  184 +
  185 + <slot name="afterForm"></slot>
  186 +
  187 + <footer>
  188 + <Button type="primary" class="text-light-50 mt-4 !flex items-center" @click="handleAdd">
  189 + <Icon icon="material-symbols:add" class="svg:text-xl" />
  190 + <span>添加</span>
  191 + </Button>
  192 + </footer>
  193 + </section>
  194 +</template>
  195 +
  196 +<style lang="less" scoped>
  197 + .attribute-configuration-form.hidden-input-border {
  198 + :deep(.ant-input-affix-wrapper) {
  199 + @apply border-t-transparent border-l-transparent border-r-transparent;
  200 + }
  201 +
  202 + :deep(.ant-input-affix-wrapper-focused) {
  203 + @apply shadow-none;
  204 + }
  205 + }
  206 +</style>
  1 +import { EntityTypeEnum, RelationTypeEnum } from '../../../enum/form';
  2 +
  3 +export enum FormFieldsEnum {
  4 + RELATION_TYPE = 'relationType',
  5 + ENTITY_TYPES = 'entityTypes',
  6 + UUID = 'uuid',
  7 +}
  8 +
  9 +export interface ListItemType {
  10 + uuid?: string;
  11 + [FormFieldsEnum.RELATION_TYPE]: RelationTypeEnum;
  12 + [FormFieldsEnum.ENTITY_TYPES]: EntityTypeEnum[];
  13 +}
  1 +export { default as CorrelationFilters } from './index.vue';
  1 +<script lang="ts" setup>
  2 + import { Card, Row, Button, Tooltip, Col, Empty } from 'ant-design-vue';
  3 + import { computed, nextTick, ref, toRaw, unref, watch } from 'vue';
  4 + import {
  5 + EntityTypeEnum,
  6 + EntityTypeNameEnum,
  7 + RelationTypeEnum,
  8 + RelationTypeNameEnum,
  9 + } from '../../../enum/form';
  10 + import { ListItemType, FormFieldsEnum } from './config';
  11 + import { BasicForm, FormActionType, FormSchema } from '/@/components/Form';
  12 + import { Icon } from '/@/components/Icon';
  13 + import { isArray } from '/@/utils/is';
  14 + import { buildShortUUID } from '/@/utils/uuid';
  15 +
  16 + const props = withDefaults(
  17 + defineProps<{
  18 + value?: ListItemType[];
  19 + allowAddFilters?: boolean;
  20 + required?: boolean;
  21 + }>(),
  22 + {
  23 + value: () => [],
  24 + allowAddFilters: true,
  25 + }
  26 + );
  27 +
  28 + const emit = defineEmits(['update:value', 'change']);
  29 +
  30 + const list = ref<ListItemType[]>([]);
  31 +
  32 + const listFormElRef = ref<Nullable<FormActionType>[]>([]);
  33 +
  34 + const focusFlag = ref(false);
  35 +
  36 + const getFormSchemas = computed<FormSchema[]>(() => {
  37 + const { required, allowAddFilters } = props;
  38 + return [
  39 + {
  40 + field: FormFieldsEnum.RELATION_TYPE,
  41 + component: 'Select',
  42 + label: allowAddFilters ? '' : '关联类型',
  43 + colProps: allowAddFilters ? { span: 11 } : undefined,
  44 + required,
  45 + componentProps: {
  46 + options: Object.keys(RelationTypeEnum).map((value) => ({
  47 + label: RelationTypeNameEnum[value],
  48 + value,
  49 + })),
  50 + placeholder: '请选择类型',
  51 + onFocus: () => (focusFlag.value = true),
  52 + onBlur: () => (focusFlag.value = false),
  53 + onChange: handleSelectChange,
  54 + getPopupContainer: () => document.body,
  55 + },
  56 + },
  57 + {
  58 + field: FormFieldsEnum.ENTITY_TYPES,
  59 + component: 'Select',
  60 + label: allowAddFilters ? '' : '设备类型',
  61 + colProps: allowAddFilters ? { span: 11, offset: 1 } : undefined,
  62 + required,
  63 + componentProps: {
  64 + mode: 'multiple',
  65 + options: Object.keys(EntityTypeEnum).map((value) => ({
  66 + label: EntityTypeNameEnum[value],
  67 + value,
  68 + })),
  69 + onFocus: () => (focusFlag.value = true),
  70 + onBlur: () => (focusFlag.value = false),
  71 + onChange: handleSelectChange,
  72 + placeholder: '请选择实体类型',
  73 + getPopupContainer: () => document.body,
  74 + },
  75 + },
  76 + ];
  77 + });
  78 +
  79 + function getDefaultItem(): ListItemType {
  80 + return {
  81 + uuid: buildShortUUID(),
  82 + entityTypes: [],
  83 + relationType: RelationTypeEnum.CONTAINS,
  84 + };
  85 + }
  86 +
  87 + function setFieldsValue(value: ListItemType[]) {
  88 + const res: ListItemType[] = [];
  89 + value = isArray(value) ? value : [];
  90 +
  91 + for (const item of value) {
  92 + res.push({
  93 + ...item,
  94 + uuid: item.uuid ? item.uuid : buildShortUUID(),
  95 + });
  96 + }
  97 +
  98 + if (!res.length) res.push(getDefaultItem());
  99 +
  100 + list.value = res;
  101 + }
  102 +
  103 + const handleAdd = () => {
  104 + emit('update:value', [...getFieldsValue(), getDefaultItem()]);
  105 + };
  106 +
  107 + const handleDelete = (item: ListItemType) => {
  108 + const index = unref(list).findIndex((temp) => temp.uuid === item.uuid);
  109 + ~index && unref(list).splice(index, 1);
  110 + ~index && unref(listFormElRef).splice(index, 1);
  111 + emit('update:value', getFieldsValue());
  112 + };
  113 +
  114 + const validate = async () => {
  115 + for (const item of unref(listFormElRef)) {
  116 + await item?.validate();
  117 + }
  118 +
  119 + return getFieldsValue();
  120 + };
  121 +
  122 + const getFieldsValue = (): ListItemType[] => {
  123 + return unref(listFormElRef).map((item) => ({
  124 + ...((item?.getFieldsValue() || {}) as ListItemType),
  125 + uuid: (item as any)?.model?.uuid,
  126 + }));
  127 + };
  128 +
  129 + const handleSelectChange = async () => {
  130 + await nextTick();
  131 + const value = getFieldsValue();
  132 + emit('change', value);
  133 + emit('update:value', value);
  134 + };
  135 +
  136 + watch(
  137 + () => props.value,
  138 + (target) => {
  139 + if (unref(focusFlag)) return;
  140 + setFieldsValue(toRaw(unref(target)));
  141 + },
  142 + {
  143 + immediate: true,
  144 + }
  145 + );
  146 +
  147 + defineExpose({
  148 + validate,
  149 + setFieldsValue,
  150 + getFieldsValue,
  151 + });
  152 +</script>
  153 +
  154 +<template>
  155 + <section class="correlation-filters-form">
  156 + <section v-if="allowAddFilters">
  157 + <Row v-if="list.length" class="w-full px-4 py-2 font-semibold">
  158 + <Col :span="11"> 类型 </Col>
  159 + <Col :span="11" :offset="1"> 实体类型 </Col>
  160 + </Row>
  161 + <Card v-for="item in list" :key="item.uuid" class="shadow-lg">
  162 + <main class="flex justify-between items-top">
  163 + <BasicForm
  164 + ref="listFormElRef"
  165 + class="w-full"
  166 + :schemas="getFormSchemas"
  167 + :model="item"
  168 + :showActionButtonGroup="false"
  169 + />
  170 + <Tooltip title="删除">
  171 + <Icon
  172 + icon="material-symbols:close"
  173 + class="svg:text-2xl cursor-pointer"
  174 + @click="handleDelete(item)"
  175 + />
  176 + </Tooltip>
  177 + </main>
  178 + </Card>
  179 +
  180 + <Empty
  181 + v-if="!list.length"
  182 + description="任意关联"
  183 + :image="Empty.PRESENTED_IMAGE_SIMPLE"
  184 + class="text-gray-600"
  185 + />
  186 + </section>
  187 +
  188 + <BasicForm
  189 + v-if="!allowAddFilters"
  190 + :schemas="getFormSchemas"
  191 + :model="list[0]"
  192 + :showActionButtonGroup="false"
  193 + />
  194 +
  195 + <Button
  196 + v-if="allowAddFilters"
  197 + class="mt-4 !flex items-center"
  198 + @click="handleAdd"
  199 + type="primary"
  200 + >
  201 + <Icon icon="material-symbols:add" />
  202 + <span>添加</span>
  203 + </Button>
  204 + </section>
  205 +</template>
  206 +
  207 +<style lang="less" scoped>
  208 + .correlation-filters-form {
  209 + :deep(.ant-card) {
  210 + & ~ .ant-card {
  211 + @apply mt-2;
  212 + }
  213 +
  214 + .ant-card-body {
  215 + @apply p-4;
  216 + }
  217 + }
  218 + }
  219 +</style>
@@ -17,18 +17,19 @@ export const TopFormSchemas: FormSchema[] = [ @@ -17,18 +17,19 @@ export const TopFormSchemas: FormSchema[] = [
17 { 17 {
18 field: NodeBindDataFieldEnum.DEBUG_MODE, 18 field: NodeBindDataFieldEnum.DEBUG_MODE,
19 component: 'Checkbox', 19 component: 'Checkbox',
20 - label: NodeBindDataFieldNameEnum.DEBUG_MODE, 20 + label: '',
21 colProps: { 21 colProps: {
22 offset: 2, 22 offset: 2,
23 span: 6, 23 span: 6,
24 }, 24 },
25 - itemProps: {  
26 - labelCol: { span: 14 },  
27 - wrapperCol: { span: 10 },  
28 - },  
29 componentProps: { 25 componentProps: {
30 placeholder: `请输入${NodeBindDataFieldNameEnum.NAME}`, 26 placeholder: `请输入${NodeBindDataFieldNameEnum.NAME}`,
31 }, 27 },
  28 + renderComponentContent: () => {
  29 + return {
  30 + default: () => NodeBindDataFieldNameEnum.DEBUG_MODE,
  31 + };
  32 + },
32 }, 33 },
33 ]; 34 ];
34 35
src/views/rule/designer/src/components/JavaScriptFilterModal/JavaScriptFilterTest.vue renamed from src/views/rule/designer/packages/Filter/Script/ScriptFilterTest.vue
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 - import { Card, Select, Tag, Form, Button, Input } from 'ant-design-vue';  
3 - import { ref, unref, watch } from 'vue'; 2 + import { Card, Select, Tag, Form, Button } from 'ant-design-vue';
  3 + import { ref, toRaw, unref, watch } from 'vue';
4 import { MessageTypesFilterEnum, MessageTypesFilterNameEnum } from '../../../enum/form'; 4 import { MessageTypesFilterEnum, MessageTypesFilterNameEnum } from '../../../enum/form';
5 import { JSONEditor } from '/@/components/CodeEditor'; 5 import { JSONEditor } from '/@/components/CodeEditor';
6 - import { buildShortUUID } from '/@/utils/uuid';  
7 import { Icon } from '/@/components/Icon'; 6 import { Icon } from '/@/components/Icon';
8 import { JavaScriptFunctionEditor } from '/@/components/Form'; 7 import { JavaScriptFunctionEditor } from '/@/components/Form';
9 import { useJsonParse } from '/@/hooks/business/useJsonParse'; 8 import { useJsonParse } from '/@/hooks/business/useJsonParse';
10 import { useMessage } from '/@/hooks/web/useMessage'; 9 import { useMessage } from '/@/hooks/web/useMessage';
11 - import { isNullOrUnDef } from '/@/utils/is';  
12 -  
13 - interface MetaDataItemType {  
14 - label: string;  
15 - value: string;  
16 - uuid: string;  
17 - } 10 + import { AttributeConfiguration } from '../AttributeConfiguration';
18 11
19 interface Value { 12 interface Value {
20 msg: Recordable; 13 msg: Recordable;
@@ -53,29 +46,17 @@ @@ -53,29 +46,17 @@
53 humidity: 78, 46 humidity: 78,
54 }); 47 });
55 48
  49 + const attributeConfigurationElRef = ref<Nullable<InstanceType<typeof AttributeConfiguration>>>();
  50 +
56 const scriptContent = ref('return msg.temperature > 20;'); 51 const scriptContent = ref('return msg.temperature > 20;');
57 52
58 const outputContent = ref(''); 53 const outputContent = ref('');
59 54
60 - const metaData = ref<MetaDataItemType[]>([  
61 - { uuid: buildShortUUID(), label: 'deviceName', value: 'Test Device' },  
62 - { uuid: buildShortUUID(), label: 'deviceType', value: 'default' },  
63 - { uuid: buildShortUUID(), label: 'ts', value: Date.now().toString() },  
64 - ]);  
65 -  
66 - const handleAddMetadata = () => {  
67 - metaData.value.push({ uuid: buildShortUUID(), label: '', value: '' });  
68 - };  
69 -  
70 - const handleRemove = (data: MetaDataItemType) => {  
71 - const index = unref(metaData).findIndex((item) => item.uuid === data.uuid);  
72 - ~index && unref(metaData).splice(index, 1);  
73 - }; 55 + const metadata = ref({ deviceName: 'Test Device', deviceType: 'default', ts: Date.now() });
74 56
75 const { createMessage } = useMessage(); 57 const { createMessage } = useMessage();
76 - const handleValidate = () => { 58 + const handleValidate = async () => {
77 const message = unref(messageContent); 59 const message = unref(messageContent);
78 - const metadata = unref(metaData);  
79 const { flag } = useJsonParse(message); 60 const { flag } = useJsonParse(message);
80 61
81 if (!flag) { 62 if (!flag) {
@@ -83,35 +64,26 @@ @@ -83,35 +64,26 @@
83 return false; 64 return false;
84 } 65 }
85 66
86 - for (const item of metadata) {  
87 - const { label, value } = item;  
88 - if (isNullOrUnDef(label) || isNullOrUnDef(value)) {  
89 - createMessage.warning('元数据的键名和键值为必填项');  
90 - return false;  
91 - }  
92 - } 67 + await unref(attributeConfigurationElRef)?.validate();
  68 +
93 return true; 69 return true;
94 }; 70 };
95 71
96 const getValue = (): Value => { 72 const getValue = (): Value => {
97 const msg = unref(messageContent); 73 const msg = unref(messageContent);
98 const msgType = unref(messageType); 74 const msgType = unref(messageType);
99 - const metadata = unref(metaData).reduce(  
100 - (prev, next) => ({ ...prev, [next.label]: next.value }),  
101 - {} as Recordable  
102 - );  
103 const javascriptFunction = unref(scriptContent); 75 const javascriptFunction = unref(scriptContent);
104 - return { msg, msgType, metadata, javascriptFunction };  
105 - };  
106 76
107 - const handleTestScript = () => {  
108 - const flag = handleValidate(); 77 + return { msg, msgType, metadata: toRaw(unref(metadata)), javascriptFunction };
  78 + };
109 79
  80 + const handleTestScript = async () => {
  81 + const flag = await handleValidate();
110 flag && emit('test', getValue()); 82 flag && emit('test', getValue());
111 }; 83 };
112 84
113 - const handleSave = () => {  
114 - const flag = handleValidate(); 85 + const handleSave = async () => {
  86 + const flag = await handleValidate();
115 flag && emit('save', getValue()); 87 flag && emit('save', getValue());
116 }; 88 };
117 89
@@ -148,24 +120,19 @@ @@ -148,24 +120,19 @@
148 </main> 120 </main>
149 <main> 121 <main>
150 <Card class="w-full h-full"> 122 <Card class="w-full h-full">
151 - <div class="flex flex-col"> 123 + <div class="flex flex-col h-full">
152 <div class="flex justify-between"> 124 <div class="flex justify-between">
153 <span class="text-gray-400">元数据</span> 125 <span class="text-gray-400">元数据</span>
154 <Tag color="blue">元数据</Tag> 126 <Tag color="blue">元数据</Tag>
155 </div> 127 </div>
156 - <div class="mt-4 overflow-y-auto">  
157 - <div v-for="item in metaData" :key="item.uuid" class="flex mt-2 gap-2 items-end">  
158 - <Input v-model:value="item.label" placeholder="请输入键名" />  
159 - <Input v-model:value="item.value" placeholder="请输入键值" />  
160 - <Icon  
161 - icon="material-symbols:close"  
162 - class="svg:text-2xl cursor-pointer"  
163 - @click="handleRemove(item)"  
164 - />  
165 - </div>  
166 - </div>  
167 - <div class="mt-4">  
168 - <Button type="primary" @click="handleAddMetadata">添加</Button> 128 + <div class="pt-4 flex-auto">
  129 + <AttributeConfiguration
  130 + ref="attributeConfigurationElRef"
  131 + v-model:value="metadata"
  132 + keyLabel="键名"
  133 + valueLabel="键值"
  134 + :showLabel="false"
  135 + />
169 </div> 136 </div>
170 </div> 137 </div>
171 </Card> 138 </Card>
src/views/rule/designer/src/components/JavaScriptFilterModal/JavaScriptTestModal.vue renamed from src/views/rule/designer/packages/Filter/Script/FilterModal.vue
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 import { BasicModal, useModalInner } from '/@/components/Modal'; 2 import { BasicModal, useModalInner } from '/@/components/Modal';
3 - import ScriptFilterTest from './ScriptFilterTest.vue'; 3 + import JavaScriptFilterTest from './JavaScriptFilterTest.vue';
4 import { MessageTypesFilterEnum } from '../../../enum/form'; 4 import { MessageTypesFilterEnum } from '../../../enum/form';
5 import { ref } from 'vue'; 5 import { ref } from 'vue';
6 import { JavaScriptFunctionEditor } from '/@/components/Form'; 6 import { JavaScriptFunctionEditor } from '/@/components/Form';
@@ -42,7 +42,7 @@ @@ -42,7 +42,7 @@
42 :showOkBtn="false" 42 :showOkBtn="false"
43 :footer="null" 43 :footer="null"
44 > 44 >
45 - <ScriptFilterTest 45 + <JavaScriptFilterTest
46 :javaScriptEditorProps="javaScriptEditorProps" 46 :javaScriptEditorProps="javaScriptEditorProps"
47 :value="value" 47 :value="value"
48 @cancel="closeModal()" 48 @cancel="closeModal()"
src/views/rule/designer/src/components/JavaScriptFilterModal/JavascriptEditorWithTestModal.vue renamed from src/views/rule/designer/packages/Filter/Script/JavaScriptFilterFunctionModal.vue
1 <script setup lang="ts"> 1 <script setup lang="ts">
2 import { JavaScriptFunctionEditor } from '/@/components/Form'; 2 import { JavaScriptFunctionEditor } from '/@/components/Form';
3 import { Button } from 'ant-design-vue'; 3 import { Button } from 'ant-design-vue';
4 - import FilterModal from './FilterModal.vue'; 4 + import JavaScriptTestModal from './JavaScriptTestModal.vue';
5 import { useModal } from '/@/components/Modal'; 5 import { useModal } from '/@/components/Modal';
6 import { DataActionModeEnum } from '/@/enums/toolEnum'; 6 import { DataActionModeEnum } from '/@/enums/toolEnum';
7 import { computed } from 'vue'; 7 import { computed } from 'vue';
@@ -49,7 +49,7 @@ @@ -49,7 +49,7 @@
49 <section> 49 <section>
50 <JavaScriptFunctionEditor v-model:value="getValue" v-bind="javaScriptEditorProps" /> 50 <JavaScriptFunctionEditor v-model:value="getValue" v-bind="javaScriptEditorProps" />
51 <Button class="mt-4" type="primary" @click="handleOpen">{{ buttonName }}</Button> 51 <Button class="mt-4" type="primary" @click="handleOpen">{{ buttonName }}</Button>
52 - <FilterModal 52 + <JavaScriptTestModal
53 :javaScriptEditorProps="javaScriptEditorProps" 53 :javaScriptEditorProps="javaScriptEditorProps"
54 @register="register" 54 @register="register"
55 @save="handleSave" 55 @save="handleSave"
  1 +export { default as JavaScriptTestModal } from './JavaScriptTestModal.vue';
  2 +export { default as JavascriptEditorWithTestModal } from './JavascriptEditorWithTestModal.vue';
  3 +export { default as JavaScriptFilterTest } from './JavaScriptFilterTest.vue';
  1 +import { DirectionEnum, DirectionNameEnum } from '../../../enum/form';
  2 +import { FormSchema, useComponentRegister } from '/@/components/Form';
  3 +import { CorrelationFilters } from '../CorrelationFilters';
  4 +import { ListItemType } from '../CorrelationFilters/config';
  5 +
  6 +useComponentRegister('CorrelationFilters', CorrelationFilters);
  7 +
  8 +export enum RelationsQueryFormFieldsEnum {
  9 + FETCH_LAST_LEVEL_ONLY = 'fetchLastLevelOnly',
  10 + DIRECTION = 'direction',
  11 + MAX_LEVEL = 'maxLevel',
  12 + FILTERS = 'filters',
  13 +}
  14 +
  15 +export enum RelationsQueryFormFieldsNameEnum {
  16 + FETCH_LAST_LEVEL_ONLY = '仅获取最后一级关联',
  17 + DIRECTION = '方向',
  18 + MAX_LEVEL = 'Max relation level',
  19 + FILTERS = '关联筛选器',
  20 +}
  21 +
  22 +export interface ValueType {
  23 + [RelationsQueryFormFieldsEnum.DIRECTION]?: DirectionEnum;
  24 + [RelationsQueryFormFieldsEnum.FILTERS]?: ListItemType[];
  25 + [RelationsQueryFormFieldsEnum.FETCH_LAST_LEVEL_ONLY]?: boolean;
  26 + [RelationsQueryFormFieldsEnum.MAX_LEVEL]?: number;
  27 +}
  28 +
  29 +export const getFormSchemas = (allowAddFilters: boolean): FormSchema[] => {
  30 + return [
  31 + {
  32 + field: RelationsQueryFormFieldsEnum.FETCH_LAST_LEVEL_ONLY,
  33 + component: 'Checkbox',
  34 + label: '',
  35 + required: true,
  36 + renderComponentContent: () => ({
  37 + default: () => RelationsQueryFormFieldsNameEnum.FETCH_LAST_LEVEL_ONLY,
  38 + }),
  39 + },
  40 + {
  41 + field: RelationsQueryFormFieldsEnum.DIRECTION,
  42 + component: 'Select',
  43 + label: RelationsQueryFormFieldsNameEnum.DIRECTION,
  44 + colProps: { span: 12 },
  45 + required: true,
  46 + componentProps: {
  47 + options: Object.keys(DirectionEnum).map((value) => ({
  48 + label: DirectionNameEnum[value],
  49 + value,
  50 + })),
  51 + getPopupContainer: () => document.body,
  52 + placeholder: `请选择${RelationsQueryFormFieldsNameEnum.DIRECTION}`,
  53 + componentProps: {},
  54 + },
  55 + },
  56 + {
  57 + field: RelationsQueryFormFieldsEnum.MAX_LEVEL,
  58 + label: RelationsQueryFormFieldsNameEnum.MAX_LEVEL,
  59 + component: 'InputNumber',
  60 + colProps: { span: 12 },
  61 + required: true,
  62 + componentProps: {
  63 + min: 1,
  64 + },
  65 + },
  66 + {
  67 + field: RelationsQueryFormFieldsEnum.FILTERS,
  68 + label: allowAddFilters ? RelationsQueryFormFieldsNameEnum.FILTERS : '',
  69 + component: 'CorrelationFilters',
  70 + valueField: 'value',
  71 + changeEvent: 'update:value',
  72 + slot: RelationsQueryFormFieldsEnum.FILTERS,
  73 + },
  74 + ];
  75 +};
  1 +export { default as RelationsQuery } from './index.vue';
  1 +<script lang="ts" setup>
  2 + import { onMounted, ref, toRaw, unref, watch } from 'vue';
  3 + import { getFormSchemas, RelationsQueryFormFieldsEnum, ValueType } from './config';
  4 + import { BasicForm, useForm } from '/@/components/Form';
  5 + import { CorrelationFilters } from '../CorrelationFilters';
  6 + import { FormFieldsEnum as CorrelationFiltersFormFieldsEnum } from '../CorrelationFilters/config';
  7 +
  8 + const props = withDefaults(
  9 + defineProps<{
  10 + value?: ValueType;
  11 + allowAddFilters?: boolean;
  12 + }>(),
  13 + {
  14 + value: () => ({}),
  15 + allowAddFilters: true,
  16 + }
  17 + );
  18 +
  19 + const correlationFiltersElRef = ref<Nullable<InstanceType<typeof CorrelationFilters>>>();
  20 +
  21 + const [register, formactionType] = useForm({
  22 + showActionButtonGroup: false,
  23 + schemas: getFormSchemas(props.allowAddFilters),
  24 + });
  25 +
  26 + watch(
  27 + () => props.value,
  28 + (target) => {
  29 + setFieldsValue(toRaw(unref(target)));
  30 + }
  31 + );
  32 +
  33 + onMounted(() => {
  34 + setFieldsValue(toRaw(unref(props.value)));
  35 + });
  36 +
  37 + const getFieldsValue = () => {
  38 + const value = formactionType.getFieldsValue();
  39 + const correlationValue = unref(correlationFiltersElRef)?.getFieldsValue();
  40 + return {
  41 + ...value,
  42 + [RelationsQueryFormFieldsEnum.FILTERS]: correlationValue?.map((item) => ({
  43 + [CorrelationFiltersFormFieldsEnum.ENTITY_TYPES]:
  44 + item[CorrelationFiltersFormFieldsEnum.ENTITY_TYPES],
  45 + [CorrelationFiltersFormFieldsEnum.RELATION_TYPE]:
  46 + item[CorrelationFiltersFormFieldsEnum.RELATION_TYPE],
  47 + })),
  48 + };
  49 + };
  50 +
  51 + const setFieldsValue = (value?: ValueType) => {
  52 + formactionType.setFieldsValue(value);
  53 + unref(correlationFiltersElRef)?.setFieldsValue(
  54 + value?.[RelationsQueryFormFieldsEnum.FILTERS] || []
  55 + );
  56 + };
  57 +
  58 + const validate = async () => {
  59 + await formactionType.validate();
  60 + await unref(correlationFiltersElRef)?.validate();
  61 +
  62 + return getFieldsValue();
  63 + };
  64 +
  65 + defineExpose({
  66 + getFieldsValue,
  67 + setFieldsValue,
  68 + validate,
  69 + });
  70 +</script>
  71 +
  72 +<template>
  73 + <BasicForm @register="register">
  74 + <template #filters="{ field, model }">
  75 + <CorrelationFilters
  76 + ref="correlationFiltersElRef"
  77 + v-model:value="model[field]"
  78 + :allowAddFilters="allowAddFilters"
  79 + />
  80 + </template>
  81 + </BasicForm>
  82 +</template>
@@ -84,6 +84,7 @@ @@ -84,6 +84,7 @@
84 showOkBtn 84 showOkBtn
85 @ok="handleModalOk" 85 @ok="handleModalOk"
86 @close="handleCancel" 86 @close="handleCancel"
  87 + wrapper-class-name="rule-node-update-drawer"
87 > 88 >
88 <template #title> 89 <template #title>
89 <h2 class="font-bold text-2xl truncate">{{ nodeData?.data?.name }}</h2> 90 <h2 class="font-bold text-2xl truncate">{{ nodeData?.data?.name }}</h2>
@@ -96,7 +97,7 @@ @@ -96,7 +97,7 @@
96 <Tabs> 97 <Tabs>
97 <Tabs.TabPane :tab="TabsPanelNameEnum[TabsPanelEnum.DETAIL]" :key="TabsPanelEnum.DETAIL"> 98 <Tabs.TabPane :tab="TabsPanelNameEnum[TabsPanelEnum.DETAIL]" :key="TabsPanelEnum.DETAIL">
98 <Spin :spinning="spinning"> 99 <Spin :spinning="spinning">
99 - <section v-if="shadowComponent" class="w-full h-full"> 100 + <section v-if="shadowComponent" class="w-full h-full overflow-y-auto">
100 <BasicForm @register="topFormRegister" @vue:mounted="handleTopFormMounted" /> 101 <BasicForm @register="topFormRegister" @vue:mounted="handleTopFormMounted" />
101 <component 102 <component
102 class="rule-node-form" 103 class="rule-node-form"
@@ -129,3 +130,35 @@ @@ -129,3 +130,35 @@
129 } 130 }
130 } 131 }
131 </style> 132 </style>
  133 +
  134 +<style lang="less">
  135 + .rule-node-update-drawer {
  136 + .ant-drawer-wrapper-body {
  137 + @apply flex flex-col;
  138 +
  139 + .ant-drawer-body {
  140 + @apply flex flex-auto overflow-hidden;
  141 +
  142 + .ant-tabs-bar {
  143 + box-shadow: 6px -1px 9px 3px #00000040;
  144 + }
  145 +
  146 + .scroll-container {
  147 + .scrollbar__wrap {
  148 + .scrollbar__view {
  149 + @apply h-full;
  150 +
  151 + .ant-tabs {
  152 + @apply h-full flex flex-col;
  153 +
  154 + .ant-tabs-content {
  155 + @apply flex-auto overflow-x-hidden overflow-y-auto pr-4;
  156 + }
  157 + }
  158 + }
  159 + }
  160 + }
  161 + }
  162 + }
  163 + }
  164 +</style>