Commit 1a84167fb93024bb3a698f28b063a150e9adeaa3

Authored by ww
1 parent ba5016e3

feat: 完成Flow类所有组件

Showing 48 changed files with 2495 additions and 91 deletions
@@ -5,6 +5,7 @@ @@ -5,6 +5,7 @@
5 "public/resource/tinymce/langs" 5 "public/resource/tinymce/langs"
6 ], 6 ],
7 "cSpell.words": [ 7 "cSpell.words": [
  8 + "ACKS",
8 "clazz", 9 "clazz",
9 "Cmds", 10 "Cmds",
10 "COAP", 11 "COAP",
@@ -19,9 +20,12 @@ @@ -19,9 +20,12 @@
19 "noconflict", 20 "noconflict",
20 "notif", 21 "notif",
21 "PROTOBUF", 22 "PROTOBUF",
  23 + "Rabbitmq",
22 "rtsp", 24 "rtsp",
23 "SCADA", 25 "SCADA",
  26 + "SMTPS",
24 "SNMP", 27 "SNMP",
  28 + "TSLV",
25 "UNACK", 29 "UNACK",
26 "unref", 30 "unref",
27 "vben", 31 "vben",
@@ -5,6 +5,7 @@ import { defHttp } from '/@/utils/http/axios'; @@ -5,6 +5,7 @@ import { defHttp } from '/@/utils/http/axios';
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 GET_DEVICE_TYPE = '/device/types',
  8 + TENANT_QUEUE = '/tenant/queues',
8 } 9 }
9 10
10 export const getDeviceInfos = () => { 11 export const getDeviceInfos = () => {
@@ -24,3 +25,13 @@ export const getDeviceTypes = () => { @@ -24,3 +25,13 @@ export const getDeviceTypes = () => {
24 { joinPrefix: false } 25 { joinPrefix: false }
25 ); 26 );
26 }; 27 };
  28 +
  29 +export const getTenantQueue = (params: Recordable) => {
  30 + return defHttp.get<string[]>(
  31 + {
  32 + url: Api.TENANT_QUEUE,
  33 + params,
  34 + },
  35 + { joinPrefix: false }
  36 + );
  37 +};
@@ -3,6 +3,7 @@ import { RuleChainType } from '/@/views/rule/designer/types/ruleNode'; @@ -3,6 +3,7 @@ import { RuleChainType } from '/@/views/rule/designer/types/ruleNode';
3 3
4 enum Api { 4 enum Api {
5 SAVE = '/ruleChain/metadata', 5 SAVE = '/ruleChain/metadata',
  6 + GET_RULE_CHAINES = '/ruleChains',
6 } 7 }
7 8
8 export const getRuleChainData = (id: string) => { 9 export const getRuleChainData = (id: string) => {
@@ -23,3 +24,15 @@ export const saveRuleChainData = (data: RuleChainType) => { @@ -23,3 +24,15 @@ export const saveRuleChainData = (data: RuleChainType) => {
23 { joinPrefix: false } 24 { joinPrefix: false }
24 ); 25 );
25 }; 26 };
  27 +
  28 +export const getRuleChains = (params: Recordable) => {
  29 + return defHttp.get(
  30 + {
  31 + url: Api.GET_RULE_CHAINES,
  32 + params,
  33 + },
  34 + {
  35 + joinPrefix: false,
  36 + }
  37 + );
  38 +};
@@ -14,6 +14,7 @@ @@ -14,6 +14,7 @@
14 import { get, omit } from 'lodash-es'; 14 import { get, omit } from 'lodash-es';
15 import { LoadingOutlined } from '@ant-design/icons-vue'; 15 import { LoadingOutlined } from '@ant-design/icons-vue';
16 import { useI18n } from '/@/hooks/web/useI18n'; 16 import { useI18n } from '/@/hooks/web/useI18n';
  17 + import { useDebounceFn } from '@vueuse/shared';
17 18
18 const emit = defineEmits(['options-change', 'change']); 19 const emit = defineEmits(['options-change', 'change']);
19 const props = withDefaults( 20 const props = withDefaults(
@@ -53,7 +54,7 @@ @@ -53,7 +54,7 @@
53 const { labelField, valueField = 'value', numberToString } = props; 54 const { labelField, valueField = 'value', numberToString } = props;
54 return unref(options).reduce((prev, next: Recordable) => { 55 return unref(options).reduce((prev, next: Recordable) => {
55 if (next) { 56 if (next) {
56 - const value = next[valueField]; 57 + const value = get(next, valueField);
57 prev.push({ 58 prev.push({
58 label: next[labelField], 59 label: next[labelField],
59 value: numberToString ? `${value}` : value, 60 value: numberToString ? `${value}` : value,
@@ -122,6 +123,7 @@ @@ -122,6 +123,7 @@
122 onChangeHook({ options }); 123 onChangeHook({ options });
123 } 124 }
124 125
  126 + const debounceSearchFunction = useDebounceFn(handleSearch, 300);
125 async function handleSearch(params?: string) { 127 async function handleSearch(params?: string) {
126 let { searchApi, api } = props; 128 let { searchApi, api } = props;
127 if (!searchApi || !isFunction(searchApi)) { 129 if (!searchApi || !isFunction(searchApi)) {
@@ -156,7 +158,7 @@ @@ -156,7 +158,7 @@
156 show-search 158 show-search
157 @change="handleChange" 159 @change="handleChange"
158 :options="getOptions" 160 :options="getOptions"
159 - @search="handleSearch" 161 + @search="debounceSearchFunction"
160 v-model:value="state" 162 v-model:value="state"
161 > 163 >
162 <template #[item]="data" v-for="item in Object.keys($slots)"> 164 <template #[item]="data" v-for="item in Object.keys($slots)">
@@ -40,16 +40,19 @@ @@ -40,16 +40,19 @@
40 accept?: string; 40 accept?: string;
41 maxSize?: number; 41 maxSize?: number;
42 disabled?: boolean; 42 disabled?: boolean;
43 - listType?: string; 43 + listType?: 'text' | 'picture-card' | 'picture';
44 multiple?: boolean; 44 multiple?: boolean;
45 maxFileLimit?: number; 45 maxFileLimit?: number;
46 - showUploadList?: boolean | { showPreviewIcon?: boolean; showRemoveIcon?: boolean }; 46 + showUploadList?: InstanceType<typeof Upload>['$props']['showUploadList'];
47 transformFile?: (file: File) => string | Blob | Promise<string | Blob | File>; 47 transformFile?: (file: File) => string | Blob | Promise<string | Blob | File>;
48 api: (file: string | Blob | Promise<string | Blob | File>) => Promise<FileItem>; 48 api: (file: string | Blob | Promise<string | Blob | File>) => Promise<FileItem>;
  49 + overFileLimitHiddenUploadEntry?: boolean;
49 }>(), 50 }>(),
50 { 51 {
51 fileList: () => [], 52 fileList: () => [],
52 maxSize: 5 * 1024 * 1024, 53 maxSize: 5 * 1024 * 1024,
  54 + overFileLimitHiddenUploadEntry: true,
  55 + listType: 'text',
53 showUploadList: () => ({ showPreviewIcon: true, showRemoveIcon: true }), 56 showUploadList: () => ({ showPreviewIcon: true, showRemoveIcon: true }),
54 } 57 }
55 ); 58 );
@@ -75,7 +78,7 @@ @@ -75,7 +78,7 @@
75 78
76 const getMaxFileLimit = computed(() => { 79 const getMaxFileLimit = computed(() => {
77 const { maxFileLimit } = props; 80 const { maxFileLimit } = props;
78 - return isPictureCard.value ? 1 : maxFileLimit; 81 + return isPictureCard.value ? 1 : maxFileLimit || 1;
79 }); 82 });
80 83
81 const handleUpload = async (file: File | string | Blob | Promise<string | Blob | File>) => { 84 const handleUpload = async (file: File | string | Blob | Promise<string | Blob | File>) => {
@@ -131,11 +134,15 @@ @@ -131,11 +134,15 @@
131 :list-type="props.listType" 134 :list-type="props.listType"
132 :disabled="getDisabled" 135 :disabled="getDisabled"
133 :before-upload="handleBeforeUpload" 136 :before-upload="handleBeforeUpload"
  137 + :show-upload-list="showUploadList"
134 @preview="handlePreview" 138 @preview="handlePreview"
135 @download="handleDownload" 139 @download="handleDownload"
136 :remove="handleRemove" 140 :remove="handleRemove"
137 > 141 >
138 - <Spin v-if="!fileList.length" :spinning="loading"> 142 + <Spin
  143 + v-if="!(fileList.length >= getMaxFileLimit) || overFileLimitHiddenUploadEntry"
  144 + :spinning="loading"
  145 + >
139 <div class="w-full h-full flex flex-col justify-center content-center"> 146 <div class="w-full h-full flex flex-col justify-center content-center">
140 <Tooltip title="点击上传或拖拽上传"> 147 <Tooltip title="点击上传或拖拽上传">
141 <InboxOutlined class="text-[3rem] !text-blue-500" /> 148 <InboxOutlined class="text-[3rem] !text-blue-500" />
@@ -135,4 +135,6 @@ export type ComponentType = @@ -135,4 +135,6 @@ export type ComponentType =
135 | 'JavascriptEditorWithTestModal' 135 | 'JavascriptEditorWithTestModal'
136 | 'AttributeConfiguration' 136 | 'AttributeConfiguration'
137 | 'CorrelationFilters' 137 | 'CorrelationFilters'
138 - | 'RelationsQuery'; 138 + | 'RelationsQuery'
  139 + | 'CredentialsCard'
  140 + | 'ApiComplete';
@@ -241,3 +241,95 @@ export enum ScopeNameEnum { @@ -241,3 +241,95 @@ export enum ScopeNameEnum {
241 CLIENT_SCOPE = '客户端属性', 241 CLIENT_SCOPE = '客户端属性',
242 SHARED_SCOPE = '共享属性', 242 SHARED_SCOPE = '共享属性',
243 } 243 }
  244 +
  245 +// External Queue type
  246 +export enum QueueTypeEnum {
  247 + STANDARD = 'STANDARD',
  248 + FIFO = 'FIFO',
  249 +}
  250 +
  251 +export enum QueueTypeNameEnum {
  252 + STANDARD = 'Standard',
  253 + FIFO = 'FIFO',
  254 +}
  255 +
  256 +// Charset encoding
  257 +export enum CharsetEncodingEnum {
  258 + US_ASCII = 'US_ASCII',
  259 + ISO_8859_1 = 'ISO_8859_1',
  260 + UTF_8 = 'UTF_8',
  261 + UTF_16BE = 'UTF_16BE',
  262 + UTF_16LE = 'UTF_16LE',
  263 + UTF_16 = 'UTF_16',
  264 +}
  265 +
  266 +export enum CharsetEncodingNameEnum {
  267 + US_ASCII = 'US-ASCII',
  268 + ISO_8859_1 = 'ISO-8859-1',
  269 + UTF_8 = 'UTF-8',
  270 + UTF_16BE = 'UTF-16BE',
  271 + UTF_16LE = 'UTF-16LE',
  272 + UTF_16 = 'UTF-16',
  273 +}
  274 +
  275 +// Rabbitmq Message properties
  276 +export enum MessagePropertiesEnum {
  277 + BASIC = 'BASIC',
  278 + TEXT_PLAIN = 'TEXT_PLAIN',
  279 + MINIMAL_BASIC = 'MINIMAL_BASIC',
  280 + MINIMAL_PERSISTENT_BASIC = 'MINIMAL_PERSISTENT_BASIC',
  281 + PERSISTENT_BASIC = 'PERSISTENT_BASIC',
  282 + PERSISTENT_TEXT_PLAIN = 'PERSISTENT_TEXT_PLAIN',
  283 +}
  284 +
  285 +// rest api call Request method
  286 +export enum RequestMethodEnum {
  287 + GET = 'GET',
  288 + POST = 'POST',
  289 + PUT = 'PUT',
  290 + DELETE = 'DELETE',
  291 +}
  292 +
  293 +export enum ProtocolEnum {
  294 + HTTP = 'HTTP',
  295 + HTTPS = 'HTTPS',
  296 +}
  297 +
  298 +export enum ProtocolNameEnum {
  299 + HTTP = 'http',
  300 + HTTPS = 'https',
  301 +}
  302 +
  303 +export enum EmailProtocolEnum {
  304 + SMTP = 'SMTP',
  305 + SMTPS = 'SMTPS',
  306 +}
  307 +
  308 +export enum EmailProtocolNameEnum {
  309 + SMTP = 'smtp',
  310 + SMTPS = 'smtps',
  311 +}
  312 +
  313 +export enum TSLVersionEnum {
  314 + TSLV1 = 'TSLV1',
  315 + TSLV1_1 = 'TSLV1_1',
  316 + TSLV1_2 = 'TSLV1_2',
  317 + TSLV1_3 = 'TSLV1_3',
  318 +}
  319 +
  320 +export enum TSLVersionNameEnum {
  321 + TSLV1 = 'TSLV1',
  322 + TSLV1_1 = 'TSLV1.1',
  323 + TSLV1_2 = 'TSLV1.2',
  324 + TSLV1_3 = 'TSLV1.3',
  325 +}
  326 +
  327 +export enum SMSServiceProviderEnum {
  328 + AWS_SNS = 'AWS_SNS',
  329 + TWILIO = 'TWILIO',
  330 +}
  331 +
  332 +export enum SMSServiceProviderNameEnum {
  333 + AWS_SNS = '亚马逊社交网站',
  334 + TWILIO = 'Twilio',
  335 +}
  1 +// Alarm notice
  2 +export enum AlarmNoticeFieldsEnum {
  3 + CONFIGURATION = 'CONFIGURATION',
  4 +}
  5 +
  6 +export enum AlarmNoticeFieldsNameEnum {
  7 + CONFIGURATION = '配置',
  8 +}
  9 +
  10 +// aws sns
  11 +export enum AwsSnsFieldsEnum {
  12 + TOPIC_ARN_PATTERN = 'topicArnPattern',
  13 + ACCESS_KEY_ID = 'accessKeyId',
  14 + SECRET_ACCESS_KEY = 'secretAccessKey',
  15 + REGION = 'region',
  16 +}
  17 +
  18 +export enum AwsSnsFieldsNameEnum {
  19 + TOPIC_ARN_PATTERN = 'Topic ARN pattern',
  20 + ACCESS_KEY_ID = 'AWS Access Key ID',
  21 + SECRET_ACCESS_KEY = 'AWS Secret Access Key',
  22 + REGION = 'AWS Region',
  23 +}
  24 +
  25 +// Aws sqs
  26 +export enum AwsSqsFieldsEnum {
  27 + QUEUE_TYPE = 'queueType',
  28 + QUEUE_URL_PATTERN = 'queueUrlPattern',
  29 + DELAY_SECONDS = 'delaySeconds',
  30 + MESSAGE_ATTRIBUTES = 'messageAttributes',
  31 + ACCESS_KEY_ID = 'accessKeyId',
  32 + SECRET_ACCESS_KEY = 'secretAccessKey',
  33 + REGION = 'region',
  34 +}
  35 +
  36 +export enum AwsSqsFieldsNameEnum {
  37 + QUEUE_TYPE = 'Queue type',
  38 + QUEUE_URL_PATTERN = 'Queue URL pattern',
  39 + DELAY_SECONDS = 'Delay(seconds)',
  40 + MESSAGE_ATTRIBUTES = 'Message attributes',
  41 + ACCESS_KEY_ID = 'AWS Access Key ID',
  42 + SECRET_ACCESS_KEY = 'AWS Secret Access Key',
  43 + REGION = 'AWS Region',
  44 +}
  45 +
  46 +// Azure iot hub
  47 +export enum AzureIotHubFieldsEnum {
  48 + TOPIC_PATTERN = 'topicPattern',
  49 + HOST = 'host',
  50 + PORT = 'port',
  51 + CONNECT_TIMEOUT_SEC = 'connectTimeoutSec',
  52 + CLIENT_ID = 'clientId',
  53 + CLEAN_SESSION = 'cleanSession',
  54 + SSL = 'ssl',
  55 + CREDENTIALS = 'credentials',
  56 +
  57 + TYPE = 'type',
  58 + SAS_KEY = 'sasKey',
  59 + CA_CERT = 'caCert',
  60 + CA_CERT_FILE_NAME = 'caCertFileName',
  61 + PRIVATE_KEY = 'privateKey',
  62 + PRIVATE_KEY_FILE_NAME = 'privateKeyFileName',
  63 + CERT = 'cert',
  64 + CERT_FILE_NAME = 'certFileName',
  65 + PASSWORD = 'password',
  66 +}
  67 +
  68 +export enum AzureIotHubFieldsNameEnum {
  69 + TOPIC_PATTERN = 'Topic',
  70 + HOST = 'Hostname',
  71 + PORT = 'port',
  72 + CONNECT_TIMEOUT_SEC = 'connectTimeoutSec',
  73 + CLIENT_ID = 'Device ID',
  74 + CLEAN_SESSION = 'cleanSession',
  75 + SSL = 'ssl',
  76 + CREDENTIALS = 'credentials',
  77 +
  78 + TYPE = 'Credentials type',
  79 + SAS_KEY = 'sasKey',
  80 + CA_CERT = 'CA certificate file',
  81 + CA_CERT_FILE_NAME = 'caCertFileName',
  82 + PRIVATE_KEY = 'Client private key file',
  83 + PRIVATE_KEY_FILE_NAME = 'privateKeyFileName',
  84 + CERT = 'Client certificate file',
  85 + CERT_FILE_NAME = 'certFileName',
  86 + PASSWORD = 'Private key password',
  87 +}
  88 +
  89 +// GCP pubsub
  90 +export enum GcpPubsubFieldsEnum {
  91 + PROJECT_ID = 'projectId',
  92 + TOPIC_NAME = 'topicName',
  93 + SERVICE_ACCOUNT_KEY = 'serviceAccountKey',
  94 + SERVICE_ACCOUNT_KEY_FILE_NAME = 'serviceAccountKeyFileName',
  95 + MESSAGE_ATTRIBUTES = 'messageAttributes',
  96 +}
  97 +
  98 +export enum GcpPubsubFieldsNameEnum {
  99 + PROJECT_ID = 'GCP project ID',
  100 + TOPIC_NAME = 'Topic name',
  101 + SERVICE_ACCOUNT_KEY = 'GCP service account key file',
  102 + SERVICE_ACCOUNT_KEY_FILE_NAME = 'serviceAccountKeyFileName',
  103 + MESSAGE_ATTRIBUTES = 'Message attributes',
  104 +}
  105 +
  106 +// Kafka
  107 +export enum KafkaFieldsEnum {
  108 + TOPIC_PATTERN = 'topicPattern',
  109 + BOOTSTRAP_SERVERS = 'bootstrapServers',
  110 + RETRIES = 'retries',
  111 + BATCH_SIZE = 'batchSize',
  112 + LINGER = 'linger',
  113 + BUFFER_MEMORY = 'bufferMemory',
  114 + ACKS = 'acks',
  115 + KEY_SERIALIZER = 'keySerializer',
  116 + VALUE_SERIALIZER = 'valueSerializer',
  117 + OTHER_PROPERTIES = 'otherProperties',
  118 + ADD_METADATA_KEY_VALUES_AS_KAFKA_HEADERS = 'addMetadataKeyValuesAsKafkaHeaders',
  119 + KAFKA_HEADERS_CHARSET = 'kafkaHeadersCharset',
  120 +}
  121 +
  122 +export enum KafkaFieldsNameEnum {
  123 + TOPIC_PATTERN = 'Topic pattern',
  124 + BOOTSTRAP_SERVERS = 'Bootstrap servers',
  125 + RETRIES = 'Automatically retry times if fails',
  126 + BATCH_SIZE = 'Produces batch size in bytes',
  127 + LINGER = 'Time to buffer locally(ms)',
  128 + BUFFER_MEMORY = 'Client buffer max size in bytes',
  129 + ACKS = 'Number of acknowledgments',
  130 + KEY_SERIALIZER = 'Key serializer',
  131 + VALUE_SERIALIZER = 'Value serializer',
  132 + OTHER_PROPERTIES = 'Other properties',
  133 + ADD_METADATA_KEY_VALUES_AS_KAFKA_HEADERS = 'Add Message metadata key-value pairs to Kafka record headers',
  134 + KAFKA_HEADERS_CHARSET = 'Charset encoding',
  135 +}
  136 +
  137 +// Mqtt
  138 +export enum MqttFieldsEnum {
  139 + TOPIC_PATTERN = 'topicPattern',
  140 + HOST = 'host',
  141 + PORT = 'port',
  142 + CONNECT_TIMEOUT_SEC = 'connectTimeoutSec',
  143 + CLIENT_ID = 'clientId',
  144 + APPEND_CLIENT_ID_SUFFIX = 'appendClientIdSuffix',
  145 + CLEAN_SESSION = 'cleanSession',
  146 + SSL = 'ssl',
  147 + CREDENTIALS = 'credentials',
  148 +
  149 + TYPE = 'type',
  150 + PASSWORD = 'password',
  151 + CA_CERT = 'caCert',
  152 + CA_CERT_FILE_NAME = 'caCertFileName',
  153 + PRIVATE_KEY = 'privateKey',
  154 + PRIVATE_KEY_FILE_NAME = 'privateKeyFileName',
  155 + CERT = 'cert',
  156 + CERT_FILE_NAME = 'certFileName',
  157 + USERNAME = 'username',
  158 +}
  159 +
  160 +export enum MqttFieldsNameEnum {
  161 + TOPIC_PATTERN = 'Topic pattern',
  162 + HOST = 'Hose',
  163 + PORT = 'Port',
  164 + CONNECT_TIMEOUT_SEC = 'Connection timeout(sec)',
  165 + CLIENT_ID = 'Client ID',
  166 + APPEND_CLIENT_ID_SUFFIX = 'Add Service ID as suffix to Client ID',
  167 + CLEAN_SESSION = 'Clean session',
  168 + SSL = 'Enable SSL',
  169 + CREDENTIALS = 'credentials',
  170 +
  171 + TYPE = 'Credentials type',
  172 + PASSWORD = 'Password',
  173 + CA_CERT = 'Server CA certificate file',
  174 + CA_CERT_FILE_NAME = 'caCertFileName',
  175 + PRIVATE_KEY = 'Client private key file',
  176 + PRIVATE_KEY_FILE_NAME = 'privateKeyFileName',
  177 + CERT = 'Client certificate file',
  178 + CERT_FILE_NAME = 'certFileName',
  179 + USERNAME = 'Username',
  180 +}
  181 +
  182 +// Rabbitmq
  183 +export enum RabbitmqFieldsEnum {
  184 + EXCHANGE_NAME_PATTERN = 'exchangeNamePattern',
  185 + ROUTING_KEY_PATTERN = 'routingKeyPattern',
  186 + MESSAGE_PROPERTIES = 'messageProperties',
  187 + HOST = 'host',
  188 + PORT = 'port',
  189 + VIRTUAL_HOST = 'virtualHost',
  190 + USERNAME = 'username',
  191 + PASSWORD = 'password',
  192 + AUTOMATIC_RECOVERY_ENABLED = 'automaticRecoveryEnabled',
  193 + CONNECTION_TIMEOUT = 'connectionTimeout',
  194 + HANDSHAKE_TIMEOUT = 'handshakeTimeout',
  195 + CLIENT_PROPERTIES = 'clientProperties',
  196 +}
  197 +
  198 +export enum RabbitmqFieldsNameEnum {
  199 + EXCHANGE_NAME_PATTERN = 'Exchange name pattern',
  200 + ROUTING_KEY_PATTERN = 'Routing key pattern',
  201 + MESSAGE_PROPERTIES = 'Message properties',
  202 + HOST = 'Host',
  203 + PORT = 'Port',
  204 + VIRTUAL_HOST = 'Virtual host',
  205 + USERNAME = 'Username',
  206 + PASSWORD = 'Password',
  207 + AUTOMATIC_RECOVERY_ENABLED = 'Automatic recovery',
  208 + CONNECTION_TIMEOUT = 'Connection timeout(ms)',
  209 + HANDSHAKE_TIMEOUT = 'Handshake timeout(ms)',
  210 + CLIENT_PROPERTIES = 'Client properties',
  211 +}
  212 +
  213 +// Rest api call
  214 +export enum RestApiCallFieldsEnum {
  215 + REST_ENDPOINT_URL_PATTERN = 'restEndpointUrlPattern',
  216 + REQUEST_METHOD = 'requestMethod',
  217 + USE_SIMPLE_CLIENT_HTTP_FACTORY = 'useSimpleClientHttpFactory',
  218 + IGNORE_REQUEST_BODY = 'ignoreRequestBody',
  219 + ENABLE_PROXY = 'enableProxy',
  220 + USE_SYSTEM_PROXY_PROPERTIES = 'useSystemProxyProperties',
  221 + PROXY_SCHEME = 'proxyScheme',
  222 + PROXY_HOST = 'proxyHost',
  223 + PROXY_PORT = 'proxyPort',
  224 + PROXY_USER = 'proxyUser',
  225 + PROXY_PASSWORD = 'proxyPassword',
  226 + READ_TIMEOUT_MS = 'readTimeoutMs',
  227 + MAX_PARALLEL_REQUESTS_COUNT = 'maxParallelRequestsCount',
  228 + HEADERS = 'headers',
  229 + USE_REDIS_QUEUE_FOR_MSG_PERSISTENCE = 'useRedisQueueForMsgPersistence',
  230 + TRIM_QUEUE = 'trimQueue',
  231 + MAX_QUEUE_SIZE = 'maxQueueSize',
  232 + CREDENTIALS = 'credentials',
  233 +
  234 + TYPE = 'type',
  235 + PASSWORD = 'password',
  236 + CA_CERT = 'caCert',
  237 + CA_CERT_FILE_NAME = 'caCertFileName',
  238 + PRIVATE_KEY = 'privateKey',
  239 + PRIVATE_KEY_FILE_NAME = 'privateKeyFileName',
  240 + CERT = 'cert',
  241 + CERT_FILE_NAME = 'certFileName',
  242 + USERNAME = 'username',
  243 +}
  244 +
  245 +export enum RestApiCallFieldsNameEnum {
  246 + REST_ENDPOINT_URL_PATTERN = 'Endpoint URL pattern',
  247 + REQUEST_METHOD = 'requestMethod',
  248 + USE_SIMPLE_CLIENT_HTTP_FACTORY = 'useSimpleClientHttpFactory',
  249 + IGNORE_REQUEST_BODY = 'ignoreRequestBody',
  250 + ENABLE_PROXY = 'enableProxy',
  251 + USE_SYSTEM_PROXY_PROPERTIES = 'useSystemProxyProperties',
  252 + PROXY_SCHEME = 'proxyScheme',
  253 + PROXY_HOST = 'proxyHost',
  254 + PROXY_PORT = 'proxyPort',
  255 + PROXY_USER = 'proxyUser',
  256 + PROXY_PASSWORD = 'proxyPassword',
  257 + READ_TIMEOUT_MS = 'readTimeoutMs',
  258 + MAX_PARALLEL_REQUESTS_COUNT = 'maxParallelRequestsCount',
  259 + HEADERS = 'headers',
  260 + USE_REDIS_QUEUE_FOR_MSG_PERSISTENCE = 'useRedisQueueForMsgPersistence',
  261 + TRIM_QUEUE = 'trimQueue',
  262 + MAX_QUEUE_SIZE = 'maxQueueSize',
  263 + CREDENTIALS = 'credentials',
  264 +
  265 + TYPE = 'Credentials type',
  266 + PASSWORD = 'Password',
  267 + CA_CERT = 'Server CA certificate file',
  268 + CA_CERT_FILE_NAME = 'caCertFileName',
  269 + PRIVATE_KEY = 'Client private key file',
  270 + PRIVATE_KEY_FILE_NAME = 'privateKeyFileName',
  271 + CERT = 'Client certificate file',
  272 + CERT_FILE_NAME = 'certFileName',
  273 + USERNAME = 'Username',
  274 +}
  275 +
  276 +// send email
  277 +export enum SendEmailFieldsEnum {
  278 + USE_SYSTEM_SMTP_SETTINGS = 'useSystemSmtpSettings',
  279 + SMTP_PROTOCOL = 'smtpProtocol',
  280 + SMTP_HOST = 'smtpHost',
  281 + SMTP_PORT = 'smtpPort',
  282 + TIMEOUT = 'timeout',
  283 + ENABLE_TLS = 'enableTls',
  284 + TLS_VERSION = 'tlsVersion',
  285 + ENABLE_PROXY = 'enableProxy',
  286 + PROXY_HOST = 'proxyHost',
  287 + PROXY_PORT = 'proxyPort',
  288 + PROXY_USER = 'proxyUser',
  289 + PROXY_PASSWORD = 'proxyPassword',
  290 + USERNAME = 'username',
  291 + PASSWORD = 'password',
  292 +}
  293 +
  294 +export enum SendEmailFieldsNameEnum {
  295 + USE_SYSTEM_SMTP_SETTINGS = 'Use system SMTP settings',
  296 + SMTP_PROTOCOL = 'Protocol',
  297 + SMTP_HOST = 'SMTP host',
  298 + SMTP_PORT = 'SMTP port',
  299 + TIMEOUT = 'Timeout ms',
  300 + ENABLE_TLS = 'Enable TLS',
  301 + TLS_VERSION = 'TLS version',
  302 + ENABLE_PROXY = 'Enable proxy',
  303 + PROXY_HOST = 'Proxy host',
  304 + PROXY_PORT = 'Proxy port',
  305 + PROXY_USER = 'Proxy user',
  306 + PROXY_PASSWORD = 'Proxy password',
  307 + USERNAME = 'Username',
  308 + PASSWORD = 'Password',
  309 +}
  310 +
  311 +export enum SendSMSFieldsEnum {
  312 + NUMBERS_TO_TEMPLATE = 'numbersToTemplate',
  313 + SMS_MESSAGE_TEMPLATE = 'smsMessageTemplate',
  314 + USE_SYSTEM_SMS_SETTINGS = 'useSystemSmsSettings',
  315 + SMS_PROVIDER_CONFIGURATION = 'smsProviderConfiguration',
  316 +
  317 + ACCESS_KEY_ID = 'accessKeyId',
  318 + SECRET_ACCESS_KEY = 'secretAccessKey',
  319 + REGION = 'region',
  320 + TYPE = 'type',
  321 + NUMBER_FROM = 'numberFrom',
  322 + ACCOUNT_SID = 'accountSid',
  323 + ACCOUNT_TOKEN = 'accountToken',
  324 +}
  325 +
  326 +export enum SendSMSFieldsNameEnum {
  327 + NUMBERS_TO_TEMPLATE = 'Phone Numbers To Template',
  328 + SMS_MESSAGE_TEMPLATE = 'SMS message Template',
  329 + USE_SYSTEM_SMS_SETTINGS = 'User system SMS provider settings',
  330 + SMS_PROVIDER_CONFIGURATION = 'smsProviderConfiguration',
  331 +
  332 + ACCESS_KEY_ID = 'AWS访问密钥ID',
  333 + SECRET_ACCESS_KEY = 'AWS访问密钥',
  334 + REGION = 'AWS地区',
  335 + TYPE = 'SMS服务商类型',
  336 + NUMBER_FROM = '发送方电话号码',
  337 + ACCOUNT_SID = 'Twilio账户SID',
  338 + ACCOUNT_TOKEN = 'Twilio账户令牌',
  339 +}
  1 +export enum CheckPointFieldsEnum {
  2 + QUEUE_NAME = 'queueName',
  3 +}
  4 +
  5 +export enum CheckPointFieldsNameEnum {
  6 + QUEUE_NAME = '队列名称',
  7 +}
  8 +
  9 +export enum RuleChainFieldsEnum {
  10 + RULE_CHAIN_ID = 'ruleChainId',
  11 +}
  12 +
  13 +export enum RuleChainFieldsNameEnum {
  14 + RULE_CHAIN_ID = '规则链',
  15 +}
@@ -9,6 +9,7 @@ export const InputConfig: NodeItemConfigType = { @@ -9,6 +9,7 @@ export const InputConfig: NodeItemConfigType = {
9 ...keys, 9 ...keys,
10 categoryType: RuleNodeTypeEnum.ENTRY, 10 categoryType: RuleNodeTypeEnum.ENTRY,
11 clazz: EntryCategoryComponentEnum.INPUT, 11 clazz: EntryCategoryComponentEnum.INPUT,
  12 + disableAction: true,
12 maxConnectionPoint: 1, 13 maxConnectionPoint: 1,
13 backgroundColor: '#95E898', 14 backgroundColor: '#95E898',
14 configurationDescriptor: { 15 configurationDescriptor: {
1 -import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node'; 1 +import { AlarmNoticeFieldsEnum, AlarmNoticeFieldsNameEnum } from '../../../enum/formField/external';
2 import { FormSchema } from '/@/components/Form'; 2 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: AlarmNoticeFieldsEnum.CONFIGURATION,
  7 + component: 'JSONEditor',
  8 + label: AlarmNoticeFieldsNameEnum.CONFIGURATION,
  9 + valueField: 'value',
  10 + changeEvent: 'update:value',
9 }, 11 },
10 ]; 12 ];
@@ -3,6 +3,8 @@ @@ -3,6 +3,8 @@
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 { useJsonParse } from '/@/hooks/business/useJsonParse';
  7 + import { AlarmNoticeFieldsEnum } from '../../../enum/formField/external';
6 8
7 defineProps<{ 9 defineProps<{
8 config: NodeData; 10 config: NodeData;
@@ -16,12 +18,12 @@ @@ -16,12 +18,12 @@
16 const getValue: CreateModalDefineExposeType['getFieldsValue'] = async () => { 18 const getValue: CreateModalDefineExposeType['getFieldsValue'] = async () => {
17 await validate(); 19 await validate();
18 const value = getFieldsValue() || {}; 20 const value = getFieldsValue() || {};
19 - return value; 21 + return useJsonParse(value?.[AlarmNoticeFieldsEnum.CONFIGURATION]).value || {};
20 }; 22 };
21 23
22 const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => { 24 const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => {
23 resetFields(); 25 resetFields();
24 - setFieldsValue(value); 26 + setFieldsValue({ [AlarmNoticeFieldsEnum.CONFIGURATION]: JSON.stringify(value, null, 2) });
25 }; 27 };
26 28
27 defineExpose({ 29 defineExpose({
1 -import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node'; 1 +import { AwsSnsFieldsEnum, AwsSnsFieldsNameEnum } from '../../../enum/formField/external';
2 import { FormSchema } from '/@/components/Form'; 2 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: AwsSnsFieldsEnum.TOPIC_ARN_PATTERN,
  7 + label: AwsSnsFieldsNameEnum.TOPIC_ARN_PATTERN,
7 component: 'Input', 8 component: 'Input',
8 - label: NodeBindDataFieldNameEnum.NAME, 9 + required: true,
  10 + componentProps: {
  11 + placeholder: `请输入${AwsSnsFieldsNameEnum.TOPIC_ARN_PATTERN}`,
  12 + },
  13 + },
  14 + {
  15 + field: AwsSnsFieldsEnum.ACCESS_KEY_ID,
  16 + label: AwsSnsFieldsNameEnum.ACCESS_KEY_ID,
  17 + component: 'Input',
  18 + required: true,
  19 + componentProps: {
  20 + placeholder: `请输入${AwsSnsFieldsNameEnum.ACCESS_KEY_ID}`,
  21 + },
  22 + },
  23 + {
  24 + field: AwsSnsFieldsEnum.SECRET_ACCESS_KEY,
  25 + label: AwsSnsFieldsNameEnum.SECRET_ACCESS_KEY,
  26 + component: 'Input',
  27 + required: true,
  28 + componentProps: {
  29 + placeholder: `请输入${AwsSnsFieldsNameEnum.SECRET_ACCESS_KEY}`,
  30 + },
  31 + },
  32 + {
  33 + field: AwsSnsFieldsEnum.REGION,
  34 + label: AwsSnsFieldsNameEnum.REGION,
  35 + component: 'Input',
  36 + required: true,
  37 + componentProps: {
  38 + placeholder: `请输入${AwsSnsFieldsNameEnum.REGION}`,
  39 + },
9 }, 40 },
10 ]; 41 ];
1 -import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node';  
2 -import { FormSchema } from '/@/components/Form'; 1 +import { QueueTypeEnum, QueueTypeNameEnum } from '../../../enum/form';
  2 +import { AwsSqsFieldsEnum, AwsSqsFieldsNameEnum } from '../../../enum/formField/external';
  3 +import { AttributeConfiguration } from '../../../src/components/AttributeConfiguration';
  4 +import { FormSchema, useComponentRegister } from '/@/components/Form';
  5 +
  6 +useComponentRegister('AttributeConfiguration', AttributeConfiguration);
3 7
4 export const formSchemas: FormSchema[] = [ 8 export const formSchemas: FormSchema[] = [
5 { 9 {
6 - field: NodeBindDataFieldEnum.NAME, 10 + field: AwsSqsFieldsEnum.QUEUE_TYPE,
  11 + label: AwsSqsFieldsNameEnum.QUEUE_TYPE,
  12 + component: 'Select',
  13 + required: true,
  14 + componentProps: {
  15 + options: Object.keys(QueueTypeEnum).map((value) => ({
  16 + label: QueueTypeNameEnum[value],
  17 + value,
  18 + })),
  19 + getPopupContainer: () => document.body,
  20 + placeholder: `请选择${AwsSqsFieldsNameEnum.QUEUE_TYPE}`,
  21 + },
  22 + },
  23 + {
  24 + field: AwsSqsFieldsEnum.QUEUE_URL_PATTERN,
  25 + label: AwsSqsFieldsNameEnum.QUEUE_URL_PATTERN,
  26 + component: 'Input',
  27 + required: true,
  28 + componentProps: {
  29 + placeholder: `请输入${AwsSqsFieldsNameEnum.QUEUE_URL_PATTERN}`,
  30 + },
  31 + },
  32 + {
  33 + field: AwsSqsFieldsEnum.DELAY_SECONDS,
  34 + label: AwsSqsFieldsNameEnum.DELAY_SECONDS,
  35 + component: 'Input',
  36 + required: true,
  37 + helpMessage: [
  38 + 'Hint: use ${metadataKey} for value from metadata, $[messageKey] for value from message body',
  39 + ],
  40 + show: ({ model }) => model[AwsSqsFieldsEnum.QUEUE_TYPE] === QueueTypeEnum.STANDARD,
  41 + componentProps: {
  42 + placeholder: `请输入${AwsSqsFieldsNameEnum.DELAY_SECONDS}`,
  43 + },
  44 + },
  45 + {
  46 + field: AwsSqsFieldsEnum.MESSAGE_ATTRIBUTES,
  47 + label: AwsSqsFieldsNameEnum.MESSAGE_ATTRIBUTES,
  48 + component: 'AttributeConfiguration',
  49 + helpMessage:
  50 + 'Use ${metadataKey} for value from metadata, $[messageKey] for value from message body in name/value fields',
  51 + slot: AwsSqsFieldsEnum.MESSAGE_ATTRIBUTES,
  52 + },
  53 + {
  54 + field: AwsSqsFieldsEnum.ACCESS_KEY_ID,
  55 + label: AwsSqsFieldsNameEnum.ACCESS_KEY_ID,
  56 + component: 'Input',
  57 + required: true,
  58 + componentProps: {
  59 + placeholder: `请输入${AwsSqsFieldsNameEnum.ACCESS_KEY_ID}`,
  60 + },
  61 + },
  62 + {
  63 + field: AwsSqsFieldsEnum.SECRET_ACCESS_KEY,
  64 + label: AwsSqsFieldsNameEnum.SECRET_ACCESS_KEY,
  65 + component: 'Input',
  66 + required: true,
  67 + componentProps: {
  68 + placeholder: `请输入${AwsSqsFieldsNameEnum.SECRET_ACCESS_KEY}`,
  69 + },
  70 + },
  71 + {
  72 + field: AwsSqsFieldsEnum.REGION,
  73 + label: AwsSqsFieldsNameEnum.REGION,
7 component: 'Input', 74 component: 'Input',
8 - label: NodeBindDataFieldNameEnum.NAME, 75 + required: true,
  76 + componentProps: {
  77 + placeholder: `请输入${AwsSqsFieldsNameEnum.REGION}`,
  78 + },
9 }, 79 },
10 ]; 80 ];
@@ -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 { AttributeConfiguration } from '../../../src/components/AttributeConfiguration';
  7 + import { ref, unref } from 'vue';
  8 + import { AwsSqsFieldsEnum } from '../../../enum/formField/external';
6 9
7 defineProps<{ 10 defineProps<{
8 config: NodeData; 11 config: NodeData;
9 }>(); 12 }>();
10 13
  14 + const attributeConfigurationElRef = 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,
@@ -15,13 +20,18 @@ @@ -15,13 +20,18 @@
15 20
16 const getValue: CreateModalDefineExposeType['getFieldsValue'] = async () => { 21 const getValue: CreateModalDefineExposeType['getFieldsValue'] = async () => {
17 await validate(); 22 await validate();
  23 + await unref(attributeConfigurationElRef)?.validate();
18 const value = getFieldsValue() || {}; 24 const value = getFieldsValue() || {};
19 - return value; 25 + const messageFields = unref(attributeConfigurationElRef)?.getFieldsValue();
  26 + return { ...value, [AwsSqsFieldsEnum.MESSAGE_ATTRIBUTES]: messageFields };
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(attributeConfigurationElRef)?.setFieldsValue(
  33 + value?.[AwsSqsFieldsEnum.MESSAGE_ATTRIBUTES]
  34 + );
25 }; 35 };
26 36
27 defineExpose({ 37 defineExpose({
@@ -31,5 +41,15 @@ @@ -31,5 +41,15 @@
31 </script> 41 </script>
32 42
33 <template> 43 <template>
34 - <BasicForm @register="register" /> 44 + <BasicForm @register="register">
  45 + <template #messageAttributes="{ field, model }">
  46 + <AttributeConfiguration
  47 + ref="attributeConfigurationElRef"
  48 + v-model:value="model[field]"
  49 + keyLabel="Name"
  50 + valueLabel="Value"
  51 + :allowEmpty="true"
  52 + />
  53 + </template>
  54 + </BasicForm>
35 </template> 55 </template>
  1 +import {
  2 + AzureIotHubFieldsEnum,
  3 + AzureIotHubFieldsNameEnum,
  4 +} from '../../../../enum/formField/external';
  5 +import { FormSchema } from '/@/components/Form';
  6 +
  7 +export enum CredentialsTypeEnum {
  8 + PEM = 'cert.PEM',
  9 + SHARED_ACCESS_SIGNATURE = 'sas',
  10 +}
  11 +
  12 +export enum CredentialsTypeNameEnum {
  13 + PEM = 'PEM',
  14 + SHARED_ACCESS_SIGNATURE = 'Shared Access Signature',
  15 +}
  16 +
  17 +export interface FileItemType {
  18 + uid: string;
  19 + name: string;
  20 + data?: any;
  21 +}
  22 +
  23 +export const getFileData = async (file: FileItemType) => {
  24 + return new Promise((resolve) => {
  25 + const fileReader = new FileReader();
  26 + fileReader.readAsText(file as unknown as File);
  27 + fileReader.onload = () => {
  28 + if (fileReader.DONE === fileReader.readyState) {
  29 + resolve({
  30 + uid: file.uid,
  31 + name: file.name,
  32 + data: fileReader.result,
  33 + });
  34 + }
  35 + };
  36 + });
  37 +};
  38 +
  39 +export const credentialsTypeOptions = [
  40 + {
  41 + label: CredentialsTypeNameEnum.SHARED_ACCESS_SIGNATURE,
  42 + value: CredentialsTypeEnum.SHARED_ACCESS_SIGNATURE,
  43 + },
  44 + { label: CredentialsTypeNameEnum.PEM, value: CredentialsTypeEnum.PEM },
  45 +];
  46 +
  47 +export const formSchemas = (
  48 + setType: (
  49 + value: CredentialsTypeEnum,
  50 + option: { label: CredentialsTypeNameEnum; value: CredentialsTypeEnum }
  51 + ) => void
  52 +): FormSchema[] => {
  53 + return [
  54 + {
  55 + field: AzureIotHubFieldsEnum.TYPE,
  56 + label: AzureIotHubFieldsNameEnum.TYPE,
  57 + component: 'Select',
  58 + required: true,
  59 + defaultValue: CredentialsTypeEnum.SHARED_ACCESS_SIGNATURE,
  60 + componentProps: ({ formActionType }) => {
  61 + const { setFieldsValue } = formActionType;
  62 + return {
  63 + options: credentialsTypeOptions,
  64 + placeholder: `请选择${AzureIotHubFieldsNameEnum.TYPE}`,
  65 + getPopupContainer: () => document.body,
  66 + onChange(
  67 + value: CredentialsTypeEnum,
  68 + option: { label: CredentialsTypeNameEnum; value: CredentialsTypeEnum }
  69 + ) {
  70 + setType(value, option);
  71 + setFieldsValue({
  72 + [AzureIotHubFieldsEnum.CA_CERT]: [],
  73 + [AzureIotHubFieldsEnum.CA_CERT_FILE_NAME]: null,
  74 + [AzureIotHubFieldsEnum.CERT]: [],
  75 + [AzureIotHubFieldsEnum.CERT_FILE_NAME]: null,
  76 + [AzureIotHubFieldsEnum.PRIVATE_KEY]: [],
  77 + [AzureIotHubFieldsEnum.PRIVATE_KEY_FILE_NAME]: null,
  78 + [AzureIotHubFieldsEnum.PASSWORD]: null,
  79 + [AzureIotHubFieldsEnum.SAS_KEY]: null,
  80 + });
  81 + },
  82 + };
  83 + },
  84 + },
  85 + {
  86 + field: AzureIotHubFieldsEnum.SAS_KEY,
  87 + label: AzureIotHubFieldsNameEnum.SAS_KEY,
  88 + required: true,
  89 + component: 'Input',
  90 + ifShow: ({ model }) =>
  91 + model[AzureIotHubFieldsEnum.TYPE] === CredentialsTypeEnum.SHARED_ACCESS_SIGNATURE,
  92 + componentProps: {
  93 + placeholder: `请输入${AzureIotHubFieldsNameEnum.SAS_KEY}`,
  94 + },
  95 + },
  96 + {
  97 + field: AzureIotHubFieldsEnum.CA_CERT_FILE_NAME,
  98 + label: AzureIotHubFieldsNameEnum.CA_CERT_FILE_NAME,
  99 + component: 'InputPassword',
  100 + show: false,
  101 + },
  102 + {
  103 + field: AzureIotHubFieldsEnum.CA_CERT,
  104 + label: AzureIotHubFieldsNameEnum.CA_CERT,
  105 + component: 'ApiUpload',
  106 + valueField: 'fileList',
  107 + changeEvent: 'update:fileList',
  108 + componentProps: ({ formActionType }) => {
  109 + const { setFieldsValue } = formActionType;
  110 + return {
  111 + overFileLimitHiddenUploadEntry: true,
  112 + api: getFileData,
  113 + 'onUpdate:fileList'(file: FileItemType[]) {
  114 + setFieldsValue({ [AzureIotHubFieldsEnum.CA_CERT_FILE_NAME]: file?.[0]?.name });
  115 + },
  116 + };
  117 + },
  118 + },
  119 + {
  120 + field: AzureIotHubFieldsEnum.CERT_FILE_NAME,
  121 + label: AzureIotHubFieldsNameEnum.CERT_FILE_NAME,
  122 + component: 'InputPassword',
  123 + show: false,
  124 + },
  125 + {
  126 + field: AzureIotHubFieldsEnum.CERT,
  127 + label: AzureIotHubFieldsNameEnum.CERT,
  128 + component: 'ApiUpload',
  129 + valueField: 'fileList',
  130 + changeEvent: 'update:fileList',
  131 + required: true,
  132 + ifShow: ({ model }) => model[AzureIotHubFieldsEnum.TYPE] === CredentialsTypeEnum.PEM,
  133 + componentProps: ({ formActionType }) => {
  134 + const { setFieldsValue } = formActionType;
  135 + return {
  136 + overFileLimitHiddenUploadEntry: true,
  137 + api: getFileData,
  138 + 'onUpdate:fileList'(file: FileItemType[]) {
  139 + setFieldsValue({ [AzureIotHubFieldsEnum.CERT_FILE_NAME]: file?.[0]?.name });
  140 + },
  141 + };
  142 + },
  143 + },
  144 + {
  145 + field: AzureIotHubFieldsEnum.PRIVATE_KEY_FILE_NAME,
  146 + label: AzureIotHubFieldsNameEnum.PRIVATE_KEY_FILE_NAME,
  147 + component: 'InputPassword',
  148 + show: false,
  149 + },
  150 + {
  151 + field: AzureIotHubFieldsEnum.PRIVATE_KEY,
  152 + label: AzureIotHubFieldsNameEnum.PRIVATE_KEY,
  153 + component: 'ApiUpload',
  154 + valueField: 'fileList',
  155 + changeEvent: 'update:fileList',
  156 + required: true,
  157 + ifShow: ({ model }) => model[AzureIotHubFieldsEnum.TYPE] === CredentialsTypeEnum.PEM,
  158 + componentProps: ({ formActionType }) => {
  159 + const { setFieldsValue } = formActionType;
  160 + return {
  161 + overFileLimitHiddenUploadEntry: true,
  162 + api: getFileData,
  163 + 'onUpdate:fileList'(file: FileItemType[]) {
  164 + setFieldsValue({ [AzureIotHubFieldsEnum.PRIVATE_KEY_FILE_NAME]: file?.[0]?.name });
  165 + },
  166 + };
  167 + },
  168 + },
  169 + {
  170 + field: AzureIotHubFieldsEnum.PASSWORD,
  171 + label: AzureIotHubFieldsNameEnum.PASSWORD,
  172 + component: 'InputPassword',
  173 + ifShow: ({ model }) => model[AzureIotHubFieldsEnum.TYPE] === CredentialsTypeEnum.PEM,
  174 + componentProps: {
  175 + placeholder: `请输入${AzureIotHubFieldsNameEnum.PASSWORD}`,
  176 + },
  177 + },
  178 + ];
  179 +};
  1 +export { default as CredentialsCard } from './index.vue';
  1 +<script setup lang="ts">
  2 + import { Collapse } from 'ant-design-vue';
  3 + import { BasicForm, useForm } from '/@/components/Form';
  4 + import { credentialsTypeOptions, FileItemType, formSchemas } from './config';
  5 + import { ref, watch } from 'vue';
  6 + import { buildUUID } from '/@/utils/uuid';
  7 + import { CredentialsTypeEnum, CredentialsTypeNameEnum } from '../../../../enum/form';
  8 + import { AzureIotHubFieldsEnum } from '../../../../enum/formField/external';
  9 +
  10 + const props = withDefaults(
  11 + defineProps<{
  12 + value?: Recordable;
  13 + }>(),
  14 + {
  15 + value: () => ({}),
  16 + }
  17 + );
  18 +
  19 + const activeKey = ref('credentials');
  20 +
  21 + const type = ref(CredentialsTypeNameEnum.SHARED_ACCESS_SIGNATURE);
  22 +
  23 + const [register, { getFieldsValue, setFieldsValue, validate }] = useForm({
  24 + schemas: formSchemas(setCredentialsType),
  25 + showActionButtonGroup: false,
  26 + layout: 'vertical',
  27 + });
  28 +
  29 + function setCredentialsType(
  30 + _value: CredentialsTypeEnum,
  31 + option: { label: CredentialsTypeNameEnum; value: CredentialsTypeEnum }
  32 + ) {
  33 + type.value = option.label;
  34 + }
  35 +
  36 + const getFileValue = (file: FileItemType[]) => {
  37 + return file?.[0]?.data || null;
  38 + };
  39 +
  40 + const setFileValueByKey = (
  41 + value: Recordable,
  42 + key: AzureIotHubFieldsEnum,
  43 + fileNameKey: AzureIotHubFieldsEnum
  44 + ) => {
  45 + return value[key]
  46 + ? [{ uid: buildUUID(), name: value[fileNameKey], data: value[key] } as FileItemType]
  47 + : [];
  48 + };
  49 +
  50 + const getValueByKey = (value: Recordable, key: AzureIotHubFieldsEnum) => {
  51 + return value?.[key] || null;
  52 + };
  53 +
  54 + const getValues = () => {
  55 + const value = getFieldsValue();
  56 + return {
  57 + [AzureIotHubFieldsEnum.CA_CERT]: getFileValue(value?.[AzureIotHubFieldsEnum.CA_CERT]),
  58 + [AzureIotHubFieldsEnum.CA_CERT_FILE_NAME]: getValueByKey(
  59 + value,
  60 + AzureIotHubFieldsEnum.CA_CERT_FILE_NAME
  61 + ),
  62 + [AzureIotHubFieldsEnum.CA_CERT]: getFileValue(value?.[AzureIotHubFieldsEnum.CERT]),
  63 + [AzureIotHubFieldsEnum.CERT_FILE_NAME]: getValueByKey(
  64 + value,
  65 + AzureIotHubFieldsEnum.CERT_FILE_NAME
  66 + ),
  67 + [AzureIotHubFieldsEnum.PRIVATE_KEY]: getFileValue(value?.[AzureIotHubFieldsEnum.PRIVATE_KEY]),
  68 + [AzureIotHubFieldsEnum.PRIVATE_KEY_FILE_NAME]: getValueByKey(
  69 + value,
  70 + AzureIotHubFieldsEnum.PRIVATE_KEY_FILE_NAME
  71 + ),
  72 + [AzureIotHubFieldsEnum.PASSWORD]: getValueByKey(value, AzureIotHubFieldsEnum.PASSWORD),
  73 + [AzureIotHubFieldsEnum.TYPE]: getValueByKey(value, AzureIotHubFieldsEnum.TYPE),
  74 + [AzureIotHubFieldsEnum.SAS_KEY]: getValueByKey(value, AzureIotHubFieldsEnum.SAS_KEY),
  75 + };
  76 + };
  77 +
  78 + const setValues = (value: Recordable) => {
  79 + const typeLabel = credentialsTypeOptions.find(
  80 + (item) => item.value === value?.[AzureIotHubFieldsEnum.TYPE]
  81 + )?.label;
  82 + type.value = typeLabel!;
  83 + setFieldsValue({
  84 + ...value,
  85 + [AzureIotHubFieldsEnum.CA_CERT]: setFileValueByKey(
  86 + value,
  87 + AzureIotHubFieldsEnum.CA_CERT,
  88 + AzureIotHubFieldsEnum.CA_CERT_FILE_NAME
  89 + ),
  90 + [AzureIotHubFieldsEnum.CERT]: setFileValueByKey(
  91 + value,
  92 + AzureIotHubFieldsEnum.CERT,
  93 + AzureIotHubFieldsEnum.CERT_FILE_NAME
  94 + ),
  95 + [AzureIotHubFieldsEnum.PRIVATE_KEY]: setFileValueByKey(
  96 + value,
  97 + AzureIotHubFieldsEnum.PRIVATE_KEY,
  98 + AzureIotHubFieldsEnum.PRIVATE_KEY_FILE_NAME
  99 + ),
  100 + });
  101 + };
  102 +
  103 + watch(
  104 + () => props.value,
  105 + (target) => {
  106 + setValues(target);
  107 + }
  108 + );
  109 +
  110 + defineExpose({
  111 + validate,
  112 + getFieldsValue: getValues,
  113 + setFieldsValue: setValues,
  114 + });
  115 +</script>
  116 +
  117 +<template>
  118 + <Collapse v-model:active-key="activeKey" class="credentials-card" expand-icon-position="right">
  119 + <Collapse.Panel :key="activeKey" class="box-shadow bg-light-50 shadow-2xl">
  120 + <template #header>
  121 + <section class="flex w-full h-full h-8 justify-between items-center">
  122 + <div class="w-1/2 text-left">Credentials</div>
  123 + <div class="w-1/2 text-left font-medium text-gray-400">
  124 + {{ type }}
  125 + </div>
  126 + </section>
  127 + </template>
  128 + <BasicForm @register="register" />
  129 + </Collapse.Panel>
  130 + </Collapse>
  131 +</template>
  132 +
  133 +<style lang="less" scoped>
  134 + .credentials-card {
  135 + :deep(.ant-collapse) {
  136 + &-header {
  137 + @apply !px-6;
  138 + }
  139 +
  140 + &-content {
  141 + &-box {
  142 + @apply px-6;
  143 + }
  144 + }
  145 + }
  146 +
  147 + :deep(.ant-form) {
  148 + > .ant-row {
  149 + > .ant-col {
  150 + > .ant-row {
  151 + > .ant-col {
  152 + min-height: fit-content;
  153 + }
  154 + }
  155 + }
  156 + }
  157 + }
  158 + }
  159 +
  160 + .box-shadow {
  161 + box-shadow: 0 3px 1px -2px #0003, 0 2px 2px 0 #00000024, 0 1px 5px 0 #0000001f;
  162 + }
  163 +</style>
1 -import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node';  
2 -import { FormSchema } from '/@/components/Form'; 1 +import { AzureIotHubFieldsEnum, AzureIotHubFieldsNameEnum } from '../../../enum/formField/external';
  2 +import { FormSchema, useComponentRegister } from '/@/components/Form';
  3 +import { CredentialsCard } from './CredentialsCard';
  4 +
  5 +useComponentRegister('CredentialsCard', CredentialsCard);
3 6
4 export const formSchemas: FormSchema[] = [ 7 export const formSchemas: FormSchema[] = [
5 { 8 {
6 - field: NodeBindDataFieldEnum.NAME, 9 + field: AzureIotHubFieldsEnum.TOPIC_PATTERN,
  10 + label: AzureIotHubFieldsNameEnum.TOPIC_PATTERN,
7 component: 'Input', 11 component: 'Input',
8 - label: NodeBindDataFieldNameEnum.NAME, 12 + required: true,
  13 + helpMessage:
  14 + 'Hint: use ${metadataKey} for value from metadata, $[messageKey] for value from message body',
  15 + componentProps: {
  16 + placeholder: `请输入${AzureIotHubFieldsNameEnum.TOPIC_PATTERN}`,
  17 + },
  18 + },
  19 + {
  20 + field: AzureIotHubFieldsEnum.HOST,
  21 + label: AzureIotHubFieldsNameEnum.HOST,
  22 + component: 'Input',
  23 + required: true,
  24 + componentProps: {
  25 + placeholder: `请输入${AzureIotHubFieldsNameEnum.HOST}`,
  26 + },
  27 + },
  28 + {
  29 + field: AzureIotHubFieldsEnum.CLIENT_ID,
  30 + label: AzureIotHubFieldsNameEnum.CLIENT_ID,
  31 + component: 'Input',
  32 + required: true,
  33 + componentProps: {
  34 + placeholder: `请输入${AzureIotHubFieldsNameEnum.CLIENT_ID}`,
  35 + },
  36 + },
  37 + {
  38 + field: AzureIotHubFieldsEnum.CREDENTIALS,
  39 + label: '',
  40 + component: 'CredentialsCard',
  41 + slot: AzureIotHubFieldsEnum.CREDENTIALS,
9 }, 42 },
10 ]; 43 ];
@@ -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 { CredentialsCard } from './CredentialsCard';
  7 + import { ref, unref } from 'vue';
  8 + import { AzureIotHubFieldsEnum } from '../../../enum/formField/external';
6 9
7 defineProps<{ 10 defineProps<{
8 config: NodeData; 11 config: NodeData;
9 }>(); 12 }>();
10 13
  14 + const credentialsCardElRef = ref<Nullable<InstanceType<typeof CredentialsCard>>>();
  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,
@@ -15,13 +20,23 @@ @@ -15,13 +20,23 @@
15 20
16 const getValue: CreateModalDefineExposeType['getFieldsValue'] = async () => { 21 const getValue: CreateModalDefineExposeType['getFieldsValue'] = async () => {
17 await validate(); 22 await validate();
  23 + await unref(credentialsCardElRef)?.validate();
18 const value = getFieldsValue() || {}; 24 const value = getFieldsValue() || {};
19 - return value; 25 + const credentials = unref(credentialsCardElRef)?.getFieldsValue();
  26 + return {
  27 + ...value,
  28 + [AzureIotHubFieldsEnum.PORT]: 8883,
  29 + [AzureIotHubFieldsEnum.SSL]: true,
  30 + [AzureIotHubFieldsEnum.CLEAN_SESSION]: true,
  31 + [AzureIotHubFieldsEnum.CONNECT_TIMEOUT_SEC]: 10,
  32 + [AzureIotHubFieldsEnum.CREDENTIALS]: credentials,
  33 + };
20 }; 34 };
21 35
22 const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => { 36 const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => {
23 resetFields(); 37 resetFields();
24 setFieldsValue(value); 38 setFieldsValue(value);
  39 + unref(credentialsCardElRef)?.setFieldsValue(value?.[AzureIotHubFieldsEnum.CREDENTIALS]);
25 }; 40 };
26 41
27 defineExpose({ 42 defineExpose({
@@ -31,5 +46,9 @@ @@ -31,5 +46,9 @@
31 </script> 46 </script>
32 47
33 <template> 48 <template>
34 - <BasicForm @register="register" /> 49 + <BasicForm @register="register">
  50 + <template #credentials="{ field, model }">
  51 + <CredentialsCard ref="credentialsCardElRef" v-model:value="model[field]" />
  52 + </template>
  53 + </BasicForm>
35 </template> 54 </template>
1 -import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node';  
2 -import { FormSchema } from '/@/components/Form'; 1 +import { GcpPubsubFieldsEnum, GcpPubsubFieldsNameEnum } from '../../../enum/formField/external';
  2 +import { AttributeConfiguration } from '/@/views/rule/designer/src/components/AttributeConfiguration';
  3 +import { FileItemType, getFileData } from '../../../src/components/CredentialsCard/config';
  4 +import { FormSchema, useComponentRegister } from '/@/components/Form';
  5 +
  6 +useComponentRegister('AttributeConfiguration', AttributeConfiguration);
3 7
4 export const formSchemas: FormSchema[] = [ 8 export const formSchemas: FormSchema[] = [
5 { 9 {
6 - field: NodeBindDataFieldEnum.NAME, 10 + field: GcpPubsubFieldsEnum.PROJECT_ID,
  11 + label: GcpPubsubFieldsNameEnum.PROJECT_ID,
  12 + component: 'Input',
  13 + required: true,
  14 + componentProps: {
  15 + placeholder: `请输入${GcpPubsubFieldsNameEnum.PROJECT_ID}`,
  16 + },
  17 + },
  18 + {
  19 + field: GcpPubsubFieldsEnum.TOPIC_NAME,
  20 + label: GcpPubsubFieldsNameEnum.TOPIC_NAME,
7 component: 'Input', 21 component: 'Input',
8 - label: NodeBindDataFieldNameEnum.NAME, 22 + required: true,
  23 + componentProps: {
  24 + placeholder: `请输入${GcpPubsubFieldsNameEnum.TOPIC_NAME}`,
  25 + },
  26 + },
  27 + {
  28 + field: GcpPubsubFieldsEnum.SERVICE_ACCOUNT_KEY_FILE_NAME,
  29 + label: GcpPubsubFieldsNameEnum.SERVICE_ACCOUNT_KEY_FILE_NAME,
  30 + component: 'Input',
  31 + show: false,
  32 + componentProps: {
  33 + placeholder: `请输入${GcpPubsubFieldsNameEnum.TOPIC_NAME}`,
  34 + },
  35 + },
  36 + {
  37 + field: GcpPubsubFieldsEnum.SERVICE_ACCOUNT_KEY,
  38 + label: GcpPubsubFieldsNameEnum.SERVICE_ACCOUNT_KEY,
  39 + component: 'ApiUpload',
  40 + valueField: 'fileList',
  41 + changeEvent: 'update:fileList',
  42 + required: true,
  43 + componentProps: ({ formActionType }) => {
  44 + const { setFieldsValue } = formActionType;
  45 + return {
  46 + overFileLimitHiddenUploadEntry: true,
  47 + api: getFileData,
  48 + 'onUpdate:fileList'(file: FileItemType[]) {
  49 + setFieldsValue({ [GcpPubsubFieldsEnum.SERVICE_ACCOUNT_KEY_FILE_NAME]: file?.[0]?.name });
  50 + },
  51 + };
  52 + },
  53 + },
  54 + {
  55 + field: GcpPubsubFieldsEnum.MESSAGE_ATTRIBUTES,
  56 + label: GcpPubsubFieldsNameEnum.MESSAGE_ATTRIBUTES,
  57 + component: 'AttributeConfiguration',
  58 + helpMessage:
  59 + 'Use ${metadataKey} for value from metadata, $[messageKey] for value from message body in name/value fields',
  60 + slot: GcpPubsubFieldsEnum.MESSAGE_ATTRIBUTES,
9 }, 61 },
10 ]; 62 ];
@@ -3,6 +3,12 @@ @@ -3,6 +3,12 @@
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 { GcpPubsubFieldsEnum } from '../../../enum/formField/external';
  9 + import { FileItemType } from '../../../src/components/CredentialsCard/config';
  10 +
  11 + const attributeConfigurationElRef = ref<Nullable<InstanceType<typeof AttributeConfiguration>>>();
6 12
7 defineProps<{ 13 defineProps<{
8 config: NodeData; 14 config: NodeData;
@@ -15,13 +21,36 @@ @@ -15,13 +21,36 @@
15 21
16 const getValue: CreateModalDefineExposeType['getFieldsValue'] = async () => { 22 const getValue: CreateModalDefineExposeType['getFieldsValue'] = async () => {
17 await validate(); 23 await validate();
  24 + await unref(attributeConfigurationElRef)?.validate();
18 const value = getFieldsValue() || {}; 25 const value = getFieldsValue() || {};
19 - return value; 26 + const messageAttribute = unref(attributeConfigurationElRef)?.getFieldsValue();
  27 + return {
  28 + ...value,
  29 + [GcpPubsubFieldsEnum.MESSAGE_ATTRIBUTES]: messageAttribute,
  30 + [GcpPubsubFieldsEnum.SERVICE_ACCOUNT_KEY]:
  31 + value?.[GcpPubsubFieldsEnum.SERVICE_ACCOUNT_KEY]?.[0]?.data || null,
  32 + };
20 }; 33 };
21 34
22 const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => { 35 const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => {
23 resetFields(); 36 resetFields();
24 - setFieldsValue(value); 37 +
  38 + const file = value?.[GcpPubsubFieldsEnum.SERVICE_ACCOUNT_KEY]
  39 + ? [
  40 + {
  41 + uid: Date.now().toString(),
  42 + data: value[GcpPubsubFieldsEnum.SERVICE_ACCOUNT_KEY],
  43 + name: value[GcpPubsubFieldsEnum.SERVICE_ACCOUNT_KEY_FILE_NAME],
  44 + } as FileItemType,
  45 + ]
  46 + : [];
  47 + setFieldsValue({
  48 + ...value,
  49 + [GcpPubsubFieldsEnum.SERVICE_ACCOUNT_KEY]: file,
  50 + });
  51 + unref(attributeConfigurationElRef)?.setFieldsValue(
  52 + value?.[GcpPubsubFieldsEnum.MESSAGE_ATTRIBUTES]
  53 + );
25 }; 54 };
26 55
27 defineExpose({ 56 defineExpose({
@@ -31,5 +60,15 @@ @@ -31,5 +60,15 @@
31 </script> 60 </script>
32 61
33 <template> 62 <template>
34 - <BasicForm @register="register" /> 63 + <BasicForm @register="register">
  64 + <template #messageAttributes="{ field, model }">
  65 + <AttributeConfiguration
  66 + ref="attributeConfigurationElRef"
  67 + v-model:value="model[field]"
  68 + keyLabel="Name"
  69 + valueLabel="Value"
  70 + :allowEmpty="true"
  71 + />
  72 + </template>
  73 + </BasicForm>
35 </template> 74 </template>
1 -import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node';  
2 -import { FormSchema } from '/@/components/Form'; 1 +import { KafkaFieldsEnum, KafkaFieldsNameEnum } from '../../../enum/formField/external';
  2 +import { AttributeConfiguration } from '/@/views/rule/designer/src/components/AttributeConfiguration';
  3 +import { FormSchema, useComponentRegister } from '/@/components/Form';
  4 +import { CharsetEncodingEnum, CharsetEncodingNameEnum } from '../../../enum/form';
  5 +
  6 +useComponentRegister('AttributeConfiguration', AttributeConfiguration);
3 7
4 export const formSchemas: FormSchema[] = [ 8 export const formSchemas: FormSchema[] = [
5 { 9 {
6 - field: NodeBindDataFieldEnum.NAME, 10 + field: KafkaFieldsEnum.TOPIC_PATTERN,
  11 + label: KafkaFieldsNameEnum.TOPIC_PATTERN,
  12 + component: 'Input',
  13 + helpMessage:
  14 + 'Hint: use ${metadataKey} for value from metadata, $[messageKey] for value from message body',
  15 + required: true,
  16 + componentProps: {
  17 + placeholder: `请输入${KafkaFieldsEnum.TOPIC_PATTERN}`,
  18 + },
  19 + },
  20 + {
  21 + field: KafkaFieldsEnum.BOOTSTRAP_SERVERS,
  22 + label: KafkaFieldsNameEnum.BOOTSTRAP_SERVERS,
  23 + component: 'Input',
  24 + required: true,
  25 + componentProps: {
  26 + placeholder: `请输入${KafkaFieldsEnum.BOOTSTRAP_SERVERS}`,
  27 + },
  28 + },
  29 + {
  30 + field: KafkaFieldsEnum.RETRIES,
  31 + label: KafkaFieldsNameEnum.RETRIES,
  32 + component: 'InputNumber',
  33 + componentProps: {
  34 + min: 0,
  35 + placeholder: `请输入${KafkaFieldsEnum.BOOTSTRAP_SERVERS}`,
  36 + },
  37 + },
  38 + {
  39 + field: KafkaFieldsEnum.BATCH_SIZE,
  40 + label: KafkaFieldsNameEnum.BATCH_SIZE,
  41 + component: 'InputNumber',
  42 + componentProps: {
  43 + min: 0,
  44 + placeholder: `请输入${KafkaFieldsEnum.BATCH_SIZE}`,
  45 + },
  46 + },
  47 + {
  48 + field: KafkaFieldsEnum.LINGER,
  49 + label: KafkaFieldsNameEnum.LINGER,
  50 + component: 'InputNumber',
  51 + componentProps: {
  52 + min: 0,
  53 + placeholder: `请输入${KafkaFieldsEnum.LINGER}`,
  54 + },
  55 + },
  56 + {
  57 + field: KafkaFieldsEnum.BUFFER_MEMORY,
  58 + label: KafkaFieldsNameEnum.BUFFER_MEMORY,
  59 + component: 'InputNumber',
  60 + componentProps: {
  61 + min: 0,
  62 + placeholder: `请输入${KafkaFieldsEnum.BUFFER_MEMORY}`,
  63 + },
  64 + },
  65 + {
  66 + field: KafkaFieldsEnum.ACKS,
  67 + label: KafkaFieldsEnum.ACKS,
  68 + component: 'Select',
  69 + required: true,
  70 + componentProps: {
  71 + options: [
  72 + { label: 'all', value: 'all' },
  73 + { label: '-1', value: '-1' },
  74 + { label: '0', value: '0' },
  75 + { label: '1', value: '1' },
  76 + ],
  77 + placeholder: `请选择${KafkaFieldsEnum.BUFFER_MEMORY}`,
  78 + getPopupContainer: () => document.body,
  79 + },
  80 + },
  81 + {
  82 + field: KafkaFieldsEnum.KEY_SERIALIZER,
  83 + label: KafkaFieldsNameEnum.KEY_SERIALIZER,
  84 + component: 'Input',
  85 + required: true,
  86 + componentProps: {
  87 + placeholder: `请输入${KafkaFieldsEnum.KEY_SERIALIZER}`,
  88 + },
  89 + },
  90 + {
  91 + field: KafkaFieldsEnum.VALUE_SERIALIZER,
  92 + label: KafkaFieldsNameEnum.VALUE_SERIALIZER,
7 component: 'Input', 93 component: 'Input',
8 - label: NodeBindDataFieldNameEnum.NAME, 94 + required: true,
  95 + componentProps: {
  96 + placeholder: `请输入${KafkaFieldsEnum.VALUE_SERIALIZER}`,
  97 + },
  98 + },
  99 + {
  100 + field: KafkaFieldsEnum.OTHER_PROPERTIES,
  101 + label: KafkaFieldsNameEnum.OTHER_PROPERTIES,
  102 + component: 'AttributeConfiguration',
  103 + slot: KafkaFieldsEnum.OTHER_PROPERTIES,
  104 + },
  105 + {
  106 + field: KafkaFieldsEnum.ADD_METADATA_KEY_VALUES_AS_KAFKA_HEADERS,
  107 + label: KafkaFieldsNameEnum.ADD_METADATA_KEY_VALUES_AS_KAFKA_HEADERS,
  108 + component: 'Checkbox',
  109 + renderComponentContent: () => ({
  110 + default: () =>
  111 + 'If selected, key-value pairs from message metadata will be added to the outgoing records headers as byte arrays with predefined charset encoding.',
  112 + }),
  113 + },
  114 + {
  115 + field: KafkaFieldsEnum.KAFKA_HEADERS_CHARSET,
  116 + label: KafkaFieldsNameEnum.KAFKA_HEADERS_CHARSET,
  117 + component: 'Select',
  118 + required: true,
  119 + show: ({ model }) => model[KafkaFieldsEnum.ADD_METADATA_KEY_VALUES_AS_KAFKA_HEADERS],
  120 + componentProps: {
  121 + options: Object.keys(CharsetEncodingEnum).map((value) => ({
  122 + label: CharsetEncodingNameEnum[value],
  123 + value,
  124 + })),
  125 + placeholder: `请选择${KafkaFieldsEnum.KAFKA_HEADERS_CHARSET}`,
  126 + getPopupContainer: () => document.body,
  127 + },
9 }, 128 },
10 ]; 129 ];
@@ -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 { AttributeConfiguration } from '/@/views/rule/designer/src/components/AttributeConfiguration';
  7 + import { ref, unref } from 'vue';
  8 + import { KafkaFieldsEnum } from '../../../enum/formField/external';
6 9
7 defineProps<{ 10 defineProps<{
8 config: NodeData; 11 config: NodeData;
9 }>(); 12 }>();
10 13
  14 + const attributeConfigurationElRef = 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,
@@ -15,13 +20,19 @@ @@ -15,13 +20,19 @@
15 20
16 const getValue: CreateModalDefineExposeType['getFieldsValue'] = async () => { 21 const getValue: CreateModalDefineExposeType['getFieldsValue'] = async () => {
17 await validate(); 22 await validate();
  23 + await unref(attributeConfigurationElRef)?.validate();
18 const value = getFieldsValue() || {}; 24 const value = getFieldsValue() || {};
19 - return value; 25 + const otherProperties = unref(attributeConfigurationElRef)?.getFieldsValue();
  26 + return {
  27 + ...value,
  28 + [KafkaFieldsEnum.OTHER_PROPERTIES]: otherProperties,
  29 + };
20 }; 30 };
21 31
22 const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => { 32 const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => {
23 resetFields(); 33 resetFields();
24 setFieldsValue(value); 34 setFieldsValue(value);
  35 + unref(attributeConfigurationElRef)?.setFieldsValue(value?.[KafkaFieldsEnum.OTHER_PROPERTIES]);
25 }; 36 };
26 37
27 defineExpose({ 38 defineExpose({
@@ -31,5 +42,15 @@ @@ -31,5 +42,15 @@
31 </script> 42 </script>
32 43
33 <template> 44 <template>
34 - <BasicForm @register="register" /> 45 + <BasicForm @register="register">
  46 + <template #otherProperties="{ field, model }">
  47 + <AttributeConfiguration
  48 + ref="attributeConfigurationElRef"
  49 + v-model:value="model[field]"
  50 + keyLabel="Key"
  51 + valueLabel="Value"
  52 + :allowEmpty="true"
  53 + />
  54 + </template>
  55 + </BasicForm>
35 </template> 56 </template>
  1 +import { MqttFieldsEnum, MqttFieldsNameEnum } from '../../../../enum/formField/external';
  2 +import { FormSchema } from '/@/components/Form';
  3 +
  4 +// Credentials type
  5 +export enum CredentialsTypeEnum {
  6 + BASIC = 'basic',
  7 + PEM = 'cert.PEM',
  8 + ANONYMOUS = 'anonymous',
  9 +}
  10 +
  11 +export enum CredentialsTypeNameEnum {
  12 + BASIC = 'Basic',
  13 + ANONYMOUS = 'Anonymous',
  14 + PEM = 'PEM',
  15 +}
  16 +
  17 +export interface FileItemType {
  18 + uid: string;
  19 + name: string;
  20 + data?: any;
  21 +}
  22 +
  23 +export const getFileData = async (file: FileItemType) => {
  24 + return new Promise((resolve) => {
  25 + const fileReader = new FileReader();
  26 + fileReader.readAsText(file as unknown as File);
  27 + fileReader.onload = () => {
  28 + if (fileReader.DONE === fileReader.readyState) {
  29 + resolve({
  30 + uid: file.uid,
  31 + name: file.name,
  32 + data: fileReader.result,
  33 + });
  34 + }
  35 + };
  36 + });
  37 +};
  38 +
  39 +export const credentialsTypeOptions = [
  40 + { label: CredentialsTypeNameEnum.BASIC, value: CredentialsTypeEnum.BASIC },
  41 + { label: CredentialsTypeNameEnum.ANONYMOUS, value: CredentialsTypeEnum.ANONYMOUS },
  42 + { label: CredentialsTypeNameEnum.PEM, value: CredentialsTypeEnum.PEM },
  43 +];
  44 +
  45 +export const formSchemas = (
  46 + setType: (
  47 + value: CredentialsTypeEnum,
  48 + option: { label: CredentialsTypeNameEnum; value: CredentialsTypeEnum }
  49 + ) => void
  50 +): FormSchema[] => {
  51 + return [
  52 + {
  53 + field: MqttFieldsEnum.TYPE,
  54 + label: MqttFieldsNameEnum.TYPE,
  55 + component: 'Select',
  56 + required: true,
  57 + defaultValue: CredentialsTypeEnum.ANONYMOUS,
  58 + componentProps: ({ formActionType }) => {
  59 + const { setFieldsValue } = formActionType;
  60 + return {
  61 + options: credentialsTypeOptions,
  62 + placeholder: `请选择${MqttFieldsNameEnum.TYPE}`,
  63 + getPopupContainer: () => document.body,
  64 + onChange(
  65 + value: CredentialsTypeEnum,
  66 + option: { label: CredentialsTypeNameEnum; value: CredentialsTypeEnum }
  67 + ) {
  68 + setType(value, option);
  69 + setFieldsValue({
  70 + [MqttFieldsEnum.CA_CERT]: [],
  71 + [MqttFieldsEnum.CA_CERT_FILE_NAME]: null,
  72 + [MqttFieldsEnum.CERT]: [],
  73 + [MqttFieldsEnum.CERT_FILE_NAME]: null,
  74 + [MqttFieldsEnum.PRIVATE_KEY]: [],
  75 + [MqttFieldsEnum.PRIVATE_KEY_FILE_NAME]: null,
  76 + [MqttFieldsEnum.PASSWORD]: null,
  77 + });
  78 + },
  79 + };
  80 + },
  81 + },
  82 + {
  83 + field: MqttFieldsEnum.USERNAME,
  84 + label: MqttFieldsNameEnum.USERNAME,
  85 + component: 'Input',
  86 + required: true,
  87 + ifShow: ({ model }) => model[MqttFieldsEnum.TYPE] === CredentialsTypeEnum.BASIC,
  88 + componentProps: {
  89 + placeholder: `请输入${MqttFieldsNameEnum.USERNAME}`,
  90 + },
  91 + },
  92 + {
  93 + field: MqttFieldsEnum.CA_CERT_FILE_NAME,
  94 + label: MqttFieldsNameEnum.CA_CERT_FILE_NAME,
  95 + component: 'InputPassword',
  96 + show: false,
  97 + },
  98 + {
  99 + field: MqttFieldsEnum.CA_CERT,
  100 + label: MqttFieldsNameEnum.CA_CERT,
  101 + component: 'ApiUpload',
  102 + valueField: 'fileList',
  103 + changeEvent: 'update:fileList',
  104 + required: true,
  105 + ifShow: ({ model }) => model[MqttFieldsEnum.TYPE] === CredentialsTypeEnum.PEM,
  106 + componentProps: ({ formActionType }) => {
  107 + const { setFieldsValue } = formActionType;
  108 + return {
  109 + overFileLimitHiddenUploadEntry: true,
  110 + api: getFileData,
  111 + 'onUpdate:fileList'(file: FileItemType[]) {
  112 + setFieldsValue({ [MqttFieldsEnum.CA_CERT_FILE_NAME]: file?.[0]?.name });
  113 + },
  114 + };
  115 + },
  116 + },
  117 + {
  118 + field: MqttFieldsEnum.CERT_FILE_NAME,
  119 + label: MqttFieldsNameEnum.CERT_FILE_NAME,
  120 + component: 'InputPassword',
  121 + show: false,
  122 + },
  123 + {
  124 + field: MqttFieldsEnum.CERT,
  125 + label: MqttFieldsNameEnum.CERT,
  126 + component: 'ApiUpload',
  127 + valueField: 'fileList',
  128 + changeEvent: 'update:fileList',
  129 + required: true,
  130 + ifShow: ({ model }) => model[MqttFieldsEnum.TYPE] === CredentialsTypeEnum.PEM,
  131 + componentProps: ({ formActionType }) => {
  132 + const { setFieldsValue } = formActionType;
  133 + return {
  134 + overFileLimitHiddenUploadEntry: true,
  135 + api: getFileData,
  136 + 'onUpdate:fileList'(file: FileItemType[]) {
  137 + setFieldsValue({ [MqttFieldsEnum.CERT_FILE_NAME]: file?.[0]?.name });
  138 + },
  139 + };
  140 + },
  141 + },
  142 + {
  143 + field: MqttFieldsEnum.PRIVATE_KEY_FILE_NAME,
  144 + label: MqttFieldsNameEnum.PRIVATE_KEY_FILE_NAME,
  145 + component: 'InputPassword',
  146 + show: false,
  147 + },
  148 + {
  149 + field: MqttFieldsEnum.PRIVATE_KEY,
  150 + label: MqttFieldsNameEnum.PRIVATE_KEY,
  151 + component: 'ApiUpload',
  152 + valueField: 'fileList',
  153 + changeEvent: 'update:fileList',
  154 + required: true,
  155 + ifShow: ({ model }) => model[MqttFieldsEnum.TYPE] === CredentialsTypeEnum.PEM,
  156 + componentProps: ({ formActionType }) => {
  157 + const { setFieldsValue } = formActionType;
  158 + return {
  159 + overFileLimitHiddenUploadEntry: true,
  160 + api: getFileData,
  161 + 'onUpdate:fileList'(file: FileItemType[]) {
  162 + setFieldsValue({ [MqttFieldsEnum.PRIVATE_KEY_FILE_NAME]: file?.[0]?.name });
  163 + },
  164 + };
  165 + },
  166 + },
  167 + {
  168 + field: MqttFieldsEnum.PASSWORD,
  169 + label: MqttFieldsNameEnum.PASSWORD,
  170 + component: 'InputPassword',
  171 + ifShow: ({ model }) =>
  172 + model[MqttFieldsEnum.TYPE] === CredentialsTypeEnum.PEM ||
  173 + model[MqttFieldsEnum.TYPE] === CredentialsTypeEnum.BASIC,
  174 + componentProps: {
  175 + placeholder: `请输入${MqttFieldsNameEnum.PASSWORD}`,
  176 + },
  177 + },
  178 + ];
  179 +};
  1 +export { default as CredentialsCard } from './index.vue';
  1 +<script setup lang="ts">
  2 + import { Collapse } from 'ant-design-vue';
  3 + import { BasicForm, useForm } from '/@/components/Form';
  4 + import { credentialsTypeOptions, FileItemType, formSchemas } from './config';
  5 + import { ref, watch } from 'vue';
  6 + import { buildUUID } from '/@/utils/uuid';
  7 + import { CredentialsTypeEnum, CredentialsTypeNameEnum } from './config';
  8 + import { MqttFieldsEnum } from '../../../../enum/formField/external';
  9 +
  10 + const props = withDefaults(
  11 + defineProps<{
  12 + value?: Recordable;
  13 + }>(),
  14 + {
  15 + value: () => ({}),
  16 + }
  17 + );
  18 +
  19 + const activeKey = ref('credentials');
  20 +
  21 + const type = ref(CredentialsTypeNameEnum.ANONYMOUS);
  22 +
  23 + const [register, { getFieldsValue, setFieldsValue, validate }] = useForm({
  24 + schemas: formSchemas(setCredentialsType),
  25 + showActionButtonGroup: false,
  26 + layout: 'vertical',
  27 + });
  28 +
  29 + function setCredentialsType(
  30 + _value: CredentialsTypeEnum,
  31 + option: { label: CredentialsTypeNameEnum; value: CredentialsTypeEnum }
  32 + ) {
  33 + type.value = option.label;
  34 + }
  35 +
  36 + const getFileValue = (file: FileItemType[]) => {
  37 + return file?.[0]?.data || null;
  38 + };
  39 +
  40 + const setFileValueByKey = (
  41 + value: Recordable,
  42 + key: MqttFieldsEnum,
  43 + fileNameKey: MqttFieldsEnum
  44 + ) => {
  45 + return value[key]
  46 + ? [{ uid: buildUUID(), name: value[fileNameKey], data: value[key] } as FileItemType]
  47 + : [];
  48 + };
  49 +
  50 + const getValues = () => {
  51 + const value = getFieldsValue();
  52 + return {
  53 + ...value,
  54 + ...(value?.[MqttFieldsEnum.TYPE] === CredentialsTypeEnum.PEM
  55 + ? {
  56 + [MqttFieldsEnum.CA_CERT]: getFileValue(value?.[MqttFieldsEnum.CA_CERT]),
  57 + [MqttFieldsEnum.CA_CERT]: getFileValue(value?.[MqttFieldsEnum.CERT]),
  58 + [MqttFieldsEnum.PRIVATE_KEY]: getFileValue(value?.[MqttFieldsEnum.PRIVATE_KEY]),
  59 + }
  60 + : {}),
  61 + };
  62 + };
  63 +
  64 + const setValues = (value: Recordable) => {
  65 + const typeLabel = credentialsTypeOptions.find(
  66 + (item) => item.value === value?.[MqttFieldsEnum.TYPE]
  67 + )?.label;
  68 + type.value = typeLabel!;
  69 + setFieldsValue({
  70 + ...value,
  71 + [MqttFieldsEnum.CA_CERT]: setFileValueByKey(
  72 + value,
  73 + MqttFieldsEnum.CA_CERT,
  74 + MqttFieldsEnum.CA_CERT_FILE_NAME
  75 + ),
  76 + [MqttFieldsEnum.CERT]: setFileValueByKey(
  77 + value,
  78 + MqttFieldsEnum.CERT,
  79 + MqttFieldsEnum.CERT_FILE_NAME
  80 + ),
  81 + [MqttFieldsEnum.PRIVATE_KEY]: setFileValueByKey(
  82 + value,
  83 + MqttFieldsEnum.PRIVATE_KEY,
  84 + MqttFieldsEnum.PRIVATE_KEY_FILE_NAME
  85 + ),
  86 + });
  87 + };
  88 +
  89 + watch(
  90 + () => props.value,
  91 + (target) => {
  92 + setValues(target);
  93 + }
  94 + );
  95 +
  96 + defineExpose({
  97 + validate,
  98 + getFieldsValue: getValues,
  99 + setFieldsValue: setValues,
  100 + });
  101 +</script>
  102 +
  103 +<template>
  104 + <Collapse v-model:active-key="activeKey" class="credentials-card" expand-icon-position="right">
  105 + <Collapse.Panel :key="activeKey" class="box-shadow bg-light-50 shadow-2xl">
  106 + <template #header>
  107 + <section class="flex w-full h-full h-8 justify-between items-center">
  108 + <div class="w-1/2 text-left">Credentials</div>
  109 + <div class="w-1/2 text-left font-medium text-gray-400">
  110 + {{ type }}
  111 + </div>
  112 + </section>
  113 + </template>
  114 + <BasicForm @register="register" />
  115 + </Collapse.Panel>
  116 + </Collapse>
  117 +</template>
  118 +
  119 +<style lang="less" scoped>
  120 + .credentials-card {
  121 + :deep(.ant-collapse) {
  122 + &-header {
  123 + @apply !px-6;
  124 + }
  125 +
  126 + &-content {
  127 + &-box {
  128 + @apply px-6;
  129 + }
  130 + }
  131 + }
  132 +
  133 + :deep(.ant-form) {
  134 + > .ant-row {
  135 + > .ant-col {
  136 + > .ant-row {
  137 + > .ant-col {
  138 + min-height: fit-content;
  139 + }
  140 + }
  141 + }
  142 + }
  143 + }
  144 + }
  145 +
  146 + .box-shadow {
  147 + box-shadow: 0 3px 1px -2px #0003, 0 2px 2px 0 #00000024, 0 1px 5px 0 #0000001f;
  148 + }
  149 +</style>
1 -import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node';  
2 -import { FormSchema } from '/@/components/Form'; 1 +import { MqttFieldsEnum, MqttFieldsNameEnum } from '../../../enum/formField/external';
  2 +import { CredentialsCard } from './CredentialsCard';
  3 +import { FormSchema, useComponentRegister } from '/@/components/Form';
  4 +
  5 +useComponentRegister('CredentialsCard', CredentialsCard);
3 6
4 export const formSchemas: FormSchema[] = [ 7 export const formSchemas: FormSchema[] = [
5 { 8 {
6 - field: NodeBindDataFieldEnum.NAME, 9 + field: MqttFieldsEnum.TOPIC_PATTERN,
  10 + label: MqttFieldsNameEnum.TOPIC_PATTERN,
  11 + component: 'Input',
  12 + helpMessage:
  13 + 'Hint: use ${metadataKey} for value from metadata, $[messageKey] for value from message body',
  14 + required: true,
  15 + componentProps: {
  16 + placeholder: `请输入${MqttFieldsNameEnum.TOPIC_PATTERN}`,
  17 + },
  18 + },
  19 + {
  20 + field: MqttFieldsEnum.HOST,
  21 + label: MqttFieldsNameEnum.HOST,
  22 + component: 'Input',
  23 + required: true,
  24 + colProps: { span: 8 },
  25 + componentProps: {
  26 + placeholder: `请输入${MqttFieldsNameEnum.HOST}`,
  27 + },
  28 + },
  29 + {
  30 + field: MqttFieldsEnum.PORT,
  31 + label: MqttFieldsNameEnum.PORT,
  32 + component: 'InputNumber',
  33 + required: true,
  34 + colProps: { span: 8 },
  35 + componentProps: {
  36 + placeholder: `请输入${MqttFieldsNameEnum.PORT}`,
  37 + },
  38 + },
  39 + {
  40 + field: MqttFieldsEnum.CONNECT_TIMEOUT_SEC,
  41 + label: MqttFieldsNameEnum.CONNECT_TIMEOUT_SEC,
  42 + component: 'Input',
  43 + required: true,
  44 + colProps: { span: 8 },
  45 + componentProps: {
  46 + placeholder: `请输入${MqttFieldsNameEnum.CONNECT_TIMEOUT_SEC}`,
  47 + },
  48 + },
  49 + {
  50 + field: MqttFieldsEnum.CLIENT_ID,
  51 + label: MqttFieldsNameEnum.CLIENT_ID,
7 component: 'Input', 52 component: 'Input',
8 - label: NodeBindDataFieldNameEnum.NAME, 53 + helpMessage:
  54 + 'Hint: Optional. Leave empty for auto-generated Client ID. Be careful when specifying the Client ID. Majority of the MQTT brokers will not allow multiple connections with the same Client ID. To connect to such brokers, your mqtt Client ID must be unique. When platform is running in a micro-services mode, the copy of rule node is launched in each micro-service. This will automatically lead to multiple mqtt clients with the same ID and may cause failures of the rule node. To avoid such failures enable "Add Service ID as suffix to Client ID" option below.',
  55 + componentProps: {
  56 + placeholder: `请输入${MqttFieldsNameEnum.TOPIC_PATTERN}`,
  57 + },
  58 + },
  59 + {
  60 + field: MqttFieldsEnum.APPEND_CLIENT_ID_SUFFIX,
  61 + label: MqttFieldsNameEnum.APPEND_CLIENT_ID_SUFFIX,
  62 + component: 'Checkbox',
  63 + renderComponentContent: () => ({
  64 + default: () =>
  65 + 'Hint: Optional. Applied when "Client ID" specified explicitly. If selected then Service ID will be added to Client ID as a suffix. Helps to avoid failures when platform is running in a micro-services mode.',
  66 + }),
  67 + componentProps: ({ formModel }) => {
  68 + const clientId = formModel[MqttFieldsEnum.CLIENT_ID];
  69 + return {
  70 + disabled: !clientId,
  71 + };
  72 + },
  73 + },
  74 + {
  75 + field: MqttFieldsEnum.CLEAN_SESSION,
  76 + label: '',
  77 + component: 'Checkbox',
  78 + renderComponentContent: () => ({
  79 + default: () => MqttFieldsNameEnum.CLEAN_SESSION,
  80 + }),
  81 + },
  82 + {
  83 + field: MqttFieldsEnum.SSL,
  84 + label: '',
  85 + component: 'Checkbox',
  86 + renderComponentContent: () => ({
  87 + default: () => MqttFieldsNameEnum.SSL,
  88 + }),
  89 + },
  90 + {
  91 + field: MqttFieldsEnum.CREDENTIALS,
  92 + label: MqttFieldsNameEnum.CREDENTIALS,
  93 + component: 'CredentialsCard',
  94 + slot: MqttFieldsEnum.CREDENTIALS,
9 }, 95 },
10 ]; 96 ];
@@ -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 { CredentialsCard } from './CredentialsCard';
  7 + import { ref, unref } from 'vue';
  8 + import { MqttFieldsEnum } from '../../../enum/formField/external';
6 9
7 defineProps<{ 10 defineProps<{
8 config: NodeData; 11 config: NodeData;
9 }>(); 12 }>();
10 13
  14 + const credentialsCardElRef = ref<Nullable<InstanceType<typeof CredentialsCard>>>();
  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,
@@ -15,13 +20,19 @@ @@ -15,13 +20,19 @@
15 20
16 const getValue: CreateModalDefineExposeType['getFieldsValue'] = async () => { 21 const getValue: CreateModalDefineExposeType['getFieldsValue'] = async () => {
17 await validate(); 22 await validate();
  23 + await unref(credentialsCardElRef)?.validate();
18 const value = getFieldsValue() || {}; 24 const value = getFieldsValue() || {};
19 - return value; 25 + const credentialsValue = unref(credentialsCardElRef)?.getFieldsValue();
  26 + return {
  27 + ...value,
  28 + [MqttFieldsEnum.CREDENTIALS]: credentialsValue,
  29 + };
20 }; 30 };
21 31
22 const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => { 32 const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => {
23 resetFields(); 33 resetFields();
24 setFieldsValue(value); 34 setFieldsValue(value);
  35 + unref(credentialsCardElRef)?.setFieldsValue(value?.[MqttFieldsEnum.CREDENTIALS]);
25 }; 36 };
26 37
27 defineExpose({ 38 defineExpose({
@@ -31,5 +42,9 @@ @@ -31,5 +42,9 @@
31 </script> 42 </script>
32 43
33 <template> 44 <template>
34 - <BasicForm @register="register" /> 45 + <BasicForm @register="register">
  46 + <template #credentials="{ field, model }">
  47 + <CredentialsCard ref="credentialsCardElRef" v-model:value="model[field]" />
  48 + </template>
  49 + </BasicForm>
35 </template> 50 </template>
1 -import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node'; 1 +import { MessagePropertiesEnum } from '../../../enum/form';
  2 +import { RabbitmqFieldsEnum, RabbitmqFieldsNameEnum } from '../../../enum/formField/external';
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: RabbitmqFieldsEnum.EXCHANGE_NAME_PATTERN,
  8 + label: RabbitmqFieldsNameEnum.EXCHANGE_NAME_PATTERN,
7 component: 'Input', 9 component: 'Input',
8 - label: NodeBindDataFieldNameEnum.NAME, 10 + componentProps: {
  11 + placeholder: `请输入${RabbitmqFieldsNameEnum.EXCHANGE_NAME_PATTERN}`,
  12 + },
  13 + },
  14 + {
  15 + field: RabbitmqFieldsEnum.ROUTING_KEY_PATTERN,
  16 + label: RabbitmqFieldsNameEnum.ROUTING_KEY_PATTERN,
  17 + component: 'Input',
  18 + componentProps: {
  19 + placeholder: `请输入${RabbitmqFieldsNameEnum.ROUTING_KEY_PATTERN}`,
  20 + },
  21 + },
  22 + {
  23 + field: RabbitmqFieldsEnum.MESSAGE_PROPERTIES,
  24 + label: RabbitmqFieldsNameEnum.MESSAGE_PROPERTIES,
  25 + component: 'Select',
  26 + componentProps: {
  27 + allowClear: true,
  28 + options: Object.keys(MessagePropertiesEnum).map((value) => ({ label: value, value })),
  29 + placeholder: `请选择${RabbitmqFieldsNameEnum.ROUTING_KEY_PATTERN}`,
  30 + },
  31 + },
  32 + {
  33 + field: RabbitmqFieldsEnum.HOST,
  34 + label: RabbitmqFieldsNameEnum.HOST,
  35 + component: 'Input',
  36 + required: true,
  37 + colProps: { span: 12 },
  38 + componentProps: {
  39 + placeholder: `请输入${RabbitmqFieldsNameEnum.HOST}`,
  40 + },
  41 + },
  42 + {
  43 + field: RabbitmqFieldsEnum.PORT,
  44 + label: RabbitmqFieldsNameEnum.PORT,
  45 + component: 'InputNumber',
  46 + required: true,
  47 + colProps: { span: 12 },
  48 + componentProps: {
  49 + placeholder: `请输入${RabbitmqFieldsNameEnum.PORT}`,
  50 + },
  51 + },
  52 + {
  53 + field: RabbitmqFieldsEnum.VIRTUAL_HOST,
  54 + label: RabbitmqFieldsNameEnum.VIRTUAL_HOST,
  55 + component: 'Input',
  56 + componentProps: {
  57 + placeholder: `请输入${RabbitmqFieldsNameEnum.VIRTUAL_HOST}`,
  58 + },
  59 + },
  60 + {
  61 + field: RabbitmqFieldsEnum.USERNAME,
  62 + label: RabbitmqFieldsNameEnum.USERNAME,
  63 + component: 'Input',
  64 + componentProps: {
  65 + placeholder: `请输入${RabbitmqFieldsNameEnum.USERNAME}`,
  66 + },
  67 + },
  68 + {
  69 + field: RabbitmqFieldsEnum.PASSWORD,
  70 + label: RabbitmqFieldsNameEnum.PASSWORD,
  71 + component: 'Input',
  72 + componentProps: {
  73 + placeholder: `请输入${RabbitmqFieldsNameEnum.PASSWORD}`,
  74 + },
  75 + },
  76 + {
  77 + field: RabbitmqFieldsEnum.AUTOMATIC_RECOVERY_ENABLED,
  78 + component: 'Checkbox',
  79 + label: '',
  80 + renderComponentContent: () => ({
  81 + default: () => RabbitmqFieldsNameEnum.AUTOMATIC_RECOVERY_ENABLED,
  82 + }),
  83 + },
  84 + {
  85 + field: RabbitmqFieldsEnum.CONNECTION_TIMEOUT,
  86 + label: RabbitmqFieldsNameEnum.CONNECTION_TIMEOUT,
  87 + component: 'InputNumber',
  88 + componentProps: {
  89 + min: 0,
  90 + placeholder: `请输入${RabbitmqFieldsNameEnum.CONNECTION_TIMEOUT}`,
  91 + },
  92 + },
  93 + {
  94 + field: RabbitmqFieldsEnum.HANDSHAKE_TIMEOUT,
  95 + label: RabbitmqFieldsNameEnum.HANDSHAKE_TIMEOUT,
  96 + component: 'InputNumber',
  97 + componentProps: {
  98 + min: 0,
  99 + placeholder: `请输入${RabbitmqFieldsNameEnum.HANDSHAKE_TIMEOUT}`,
  100 + },
  101 + },
  102 + {
  103 + field: RabbitmqFieldsEnum.CLIENT_PROPERTIES,
  104 + label: RabbitmqFieldsNameEnum.CLIENT_PROPERTIES,
  105 + component: 'Input',
  106 + slot: RabbitmqFieldsEnum.CLIENT_PROPERTIES,
9 }, 107 },
10 ]; 108 ];
@@ -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 { AttributeConfiguration } from '/@/views/rule/designer/src/components/AttributeConfiguration';
  7 + import { ref, unref } from 'vue';
  8 + import { RabbitmqFieldsEnum } from '../../../enum/formField/external';
6 9
7 defineProps<{ 10 defineProps<{
8 config: NodeData; 11 config: NodeData;
9 }>(); 12 }>();
10 13
  14 + const attributeConfigurationElRef = 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,
@@ -15,13 +20,21 @@ @@ -15,13 +20,21 @@
15 20
16 const getValue: CreateModalDefineExposeType['getFieldsValue'] = async () => { 21 const getValue: CreateModalDefineExposeType['getFieldsValue'] = async () => {
17 await validate(); 22 await validate();
  23 + await unref(attributeConfigurationElRef)?.validate();
18 const value = getFieldsValue() || {}; 24 const value = getFieldsValue() || {};
19 - return value; 25 + const clientProperties = unref(attributeConfigurationElRef)?.getFieldsValue();
  26 + return {
  27 + ...value,
  28 + [RabbitmqFieldsEnum.CLIENT_PROPERTIES]: clientProperties,
  29 + };
20 }; 30 };
21 31
22 const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => { 32 const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => {
23 resetFields(); 33 resetFields();
24 setFieldsValue(value); 34 setFieldsValue(value);
  35 + unref(attributeConfigurationElRef)?.setFieldsValue(
  36 + value?.[RabbitmqFieldsEnum.CLIENT_PROPERTIES]
  37 + );
25 }; 38 };
26 39
27 defineExpose({ 40 defineExpose({
@@ -31,5 +44,15 @@ @@ -31,5 +44,15 @@
31 </script> 44 </script>
32 45
33 <template> 46 <template>
34 - <BasicForm @register="register" /> 47 + <BasicForm @register="register">
  48 + <template #clientProperties="{ field, model }">
  49 + <AttributeConfiguration
  50 + ref="attributeConfigurationElRef"
  51 + v-model:value="model[field]"
  52 + keyLabel="Key"
  53 + valueLabel="Value"
  54 + :allowEmpty="true"
  55 + />
  56 + </template>
  57 + </BasicForm>
35 </template> 58 </template>
1 -import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node'; 1 +import { ProtocolEnum, ProtocolNameEnum, RequestMethodEnum } from '../../../enum/form';
  2 +import { RestApiCallFieldsEnum, RestApiCallFieldsNameEnum } from '../../../enum/formField/external';
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: RestApiCallFieldsEnum.REST_ENDPOINT_URL_PATTERN,
  8 + label: RestApiCallFieldsNameEnum.REST_ENDPOINT_URL_PATTERN,
7 component: 'Input', 9 component: 'Input',
8 - label: NodeBindDataFieldNameEnum.NAME, 10 + required: true,
  11 + helpMessage:
  12 + 'Hint: use ${metadataKey} for value from metadata, $[messageKey] for value from message body',
  13 + componentProps: {
  14 + placeholder: `请输入${RestApiCallFieldsNameEnum.REST_ENDPOINT_URL_PATTERN}`,
  15 + },
  16 + },
  17 + {
  18 + field: RestApiCallFieldsEnum.REQUEST_METHOD,
  19 + label: RestApiCallFieldsNameEnum.REQUEST_METHOD,
  20 + component: 'Select',
  21 + required: true,
  22 + componentProps: {
  23 + options: Object.keys(RequestMethodEnum).map((value) => ({ label: value, value })),
  24 + getPopupContainer: () => document.body,
  25 + placeholder: `请选择${RestApiCallFieldsNameEnum.REQUEST_METHOD}`,
  26 + },
  27 + },
  28 + {
  29 + field: RestApiCallFieldsEnum.ENABLE_PROXY,
  30 + label: '',
  31 + component: 'Checkbox',
  32 + renderComponentContent: () => ({
  33 + default: () => RestApiCallFieldsNameEnum.ENABLE_PROXY,
  34 + }),
  35 + },
  36 + {
  37 + field: RestApiCallFieldsEnum.USE_SIMPLE_CLIENT_HTTP_FACTORY,
  38 + label: '',
  39 + component: 'Checkbox',
  40 + show: ({ model }) => !model[RestApiCallFieldsEnum.ENABLE_PROXY],
  41 + renderComponentContent: () => ({
  42 + default: () => RestApiCallFieldsNameEnum.USE_SIMPLE_CLIENT_HTTP_FACTORY,
  43 + }),
  44 + },
  45 + {
  46 + field: RestApiCallFieldsEnum.IGNORE_REQUEST_BODY,
  47 + label: '',
  48 + component: 'Checkbox',
  49 + renderComponentContent: () => ({
  50 + default: () => RestApiCallFieldsNameEnum.IGNORE_REQUEST_BODY,
  51 + }),
  52 + },
  53 + {
  54 + field: RestApiCallFieldsEnum.USE_SYSTEM_PROXY_PROPERTIES,
  55 + label: '',
  56 + component: 'Checkbox',
  57 + show: ({ model }) => model[RestApiCallFieldsEnum.ENABLE_PROXY],
  58 + renderComponentContent: () => ({
  59 + default: () => RestApiCallFieldsNameEnum.USE_SYSTEM_PROXY_PROPERTIES,
  60 + }),
  61 + },
  62 + {
  63 + field: RestApiCallFieldsEnum.PROXY_SCHEME,
  64 + label: RestApiCallFieldsNameEnum.PROXY_SCHEME,
  65 + component: 'Select',
  66 + required: true,
  67 + colProps: { span: 8 },
  68 + componentProps: {
  69 + options: Object.keys(ProtocolEnum).map((value) => ({
  70 + label: ProtocolNameEnum[value],
  71 + value,
  72 + })),
  73 + getPopupContainer: () => document.body,
  74 + placeholder: `请选择${RestApiCallFieldsEnum.PROXY_SCHEME}`,
  75 + },
  76 + },
  77 + {
  78 + field: RestApiCallFieldsEnum.PROXY_HOST,
  79 + label: RestApiCallFieldsNameEnum.PROXY_HOST,
  80 + component: 'Input',
  81 + required: true,
  82 + colProps: { span: 8 },
  83 + componentProps: {
  84 + placeholder: `请输入${RestApiCallFieldsNameEnum.PROXY_HOST}`,
  85 + },
  86 + },
  87 + {
  88 + field: RestApiCallFieldsEnum.PROXY_PORT,
  89 + label: RestApiCallFieldsNameEnum.PROXY_PORT,
  90 + component: 'InputNumber',
  91 + required: true,
  92 + colProps: { span: 8 },
  93 + componentProps: {
  94 + placeholder: `请输入${RestApiCallFieldsNameEnum.PROXY_HOST}`,
  95 + },
  96 + },
  97 + {
  98 + field: RestApiCallFieldsEnum.PROXY_USER,
  99 + label: RestApiCallFieldsNameEnum.PROXY_USER,
  100 + component: 'Input',
  101 + componentProps: {
  102 + placeholder: `请输入${RestApiCallFieldsNameEnum.PROXY_USER}`,
  103 + },
  104 + },
  105 + {
  106 + field: RestApiCallFieldsEnum.PROXY_PASSWORD,
  107 + label: RestApiCallFieldsNameEnum.PROXY_PASSWORD,
  108 + component: 'Input',
  109 + componentProps: {
  110 + placeholder: `请输入${RestApiCallFieldsNameEnum.PROXY_PASSWORD}`,
  111 + },
  112 + },
  113 + {
  114 + field: RestApiCallFieldsEnum.READ_TIMEOUT_MS,
  115 + label: RestApiCallFieldsNameEnum.READ_TIMEOUT_MS,
  116 + component: 'InputNumber',
  117 + componentProps: {
  118 + min: 0,
  119 + placeholder: `请输入${RestApiCallFieldsNameEnum.READ_TIMEOUT_MS}`,
  120 + },
  121 + },
  122 + {
  123 + field: RestApiCallFieldsEnum.MAX_PARALLEL_REQUESTS_COUNT,
  124 + label: RestApiCallFieldsNameEnum.MAX_PARALLEL_REQUESTS_COUNT,
  125 + component: 'InputNumber',
  126 + componentProps: {
  127 + min: 0,
  128 + placeholder: `请输入${RestApiCallFieldsNameEnum.MAX_PARALLEL_REQUESTS_COUNT}`,
  129 + },
  130 + },
  131 + {
  132 + field: RestApiCallFieldsEnum.HEADERS,
  133 + label: RestApiCallFieldsNameEnum.HEADERS,
  134 + component: 'Input',
  135 + slot: RestApiCallFieldsEnum.HEADERS,
  136 + },
  137 + {
  138 + field: RestApiCallFieldsEnum.USE_REDIS_QUEUE_FOR_MSG_PERSISTENCE,
  139 + label: '',
  140 + component: 'Checkbox',
  141 + renderComponentContent: () => ({
  142 + default: () => RestApiCallFieldsNameEnum.USE_REDIS_QUEUE_FOR_MSG_PERSISTENCE,
  143 + }),
  144 + },
  145 + {
  146 + field: RestApiCallFieldsEnum.TRIM_QUEUE,
  147 + label: '',
  148 + component: 'Checkbox',
  149 + ifShow: ({ model }) => model[RestApiCallFieldsEnum.USE_REDIS_QUEUE_FOR_MSG_PERSISTENCE],
  150 + renderComponentContent: () => ({
  151 + default: () => RestApiCallFieldsNameEnum.TRIM_QUEUE,
  152 + }),
  153 + },
  154 + {
  155 + field: RestApiCallFieldsEnum.MAX_QUEUE_SIZE,
  156 + label: RestApiCallFieldsNameEnum.MAX_QUEUE_SIZE,
  157 + component: 'InputNumber',
  158 + ifShow: ({ model }) => model[RestApiCallFieldsEnum.USE_REDIS_QUEUE_FOR_MSG_PERSISTENCE],
  159 + componentProps: {
  160 + min: 0,
  161 + placeholder: `请输入${RestApiCallFieldsNameEnum.MAX_QUEUE_SIZE}`,
  162 + },
  163 + },
  164 + {
  165 + field: RestApiCallFieldsEnum.CREDENTIALS,
  166 + label: RestApiCallFieldsNameEnum.CREDENTIALS,
  167 + component: 'Input',
  168 + slot: RestApiCallFieldsEnum.CREDENTIALS,
9 }, 169 },
10 ]; 170 ];
@@ -3,11 +3,18 @@ @@ -3,11 +3,18 @@
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 '../../../src/components/AttributeConfiguration';
  7 + import { ref, unref } from 'vue';
  8 + import { CredentialsCard } from '../Mqtt/CredentialsCard';
  9 + import { RestApiCallFieldsEnum } from '../../../enum/formField/external';
6 10
7 defineProps<{ 11 defineProps<{
8 config: NodeData; 12 config: NodeData;
9 }>(); 13 }>();
10 14
  15 + const attributeConfigurationElRef = ref<Nullable<InstanceType<typeof AttributeConfiguration>>>();
  16 + const credentialsCardElRef = ref<Nullable<InstanceType<typeof CredentialsCard>>>();
  17 +
11 const [register, { validate, getFieldsValue, setFieldsValue, resetFields }] = useForm({ 18 const [register, { validate, getFieldsValue, setFieldsValue, resetFields }] = useForm({
12 schemas: formSchemas, 19 schemas: formSchemas,
13 showActionButtonGroup: false, 20 showActionButtonGroup: false,
@@ -15,13 +22,25 @@ @@ -15,13 +22,25 @@
15 22
16 const getValue: CreateModalDefineExposeType['getFieldsValue'] = async () => { 23 const getValue: CreateModalDefineExposeType['getFieldsValue'] = async () => {
17 await validate(); 24 await validate();
  25 + await unref(attributeConfigurationElRef)?.validate();
  26 + await unref(credentialsCardElRef)?.validate();
18 const value = getFieldsValue() || {}; 27 const value = getFieldsValue() || {};
19 - return value; 28 +
  29 + const headers = unref(attributeConfigurationElRef)?.getFieldsValue();
  30 + const credentials = unref(credentialsCardElRef)?.getFieldsValue();
  31 +
  32 + return {
  33 + ...value,
  34 + [RestApiCallFieldsEnum.HEADERS]: headers,
  35 + [RestApiCallFieldsEnum.CREDENTIALS]: credentials,
  36 + };
20 }; 37 };
21 38
22 const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => { 39 const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => {
23 resetFields(); 40 resetFields();
24 setFieldsValue(value); 41 setFieldsValue(value);
  42 + unref(attributeConfigurationElRef)?.setFieldsValue(value?.[RestApiCallFieldsEnum.HEADERS]);
  43 + unref(credentialsCardElRef)?.setFieldsValue(value?.[RestApiCallFieldsEnum.CREDENTIALS]);
25 }; 44 };
26 45
27 defineExpose({ 46 defineExpose({
@@ -31,5 +50,18 @@ @@ -31,5 +50,18 @@
31 </script> 50 </script>
32 51
33 <template> 52 <template>
34 - <BasicForm @register="register" /> 53 + <BasicForm @register="register">
  54 + <template #headers="{ field, model }">
  55 + <AttributeConfiguration
  56 + ref="attributeConfigurationElRef"
  57 + v-model:value="model[field]"
  58 + :allowEmpty="true"
  59 + keyLabel="Header"
  60 + valueLabel="Value"
  61 + />
  62 + </template>
  63 + <template #credentials="{ field, model }">
  64 + <CredentialsCard ref="credentialsCardElRef" v-model:value="model[field]" />
  65 + </template>
  66 + </BasicForm>
35 </template> 67 </template>
1 -import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node'; 1 +import {
  2 + EmailProtocolEnum,
  3 + EmailProtocolNameEnum,
  4 + TSLVersionEnum,
  5 + TSLVersionNameEnum,
  6 +} from '../../../enum/form';
  7 +import { SendEmailFieldsEnum, SendEmailFieldsNameEnum } from '../../../enum/formField/external';
2 import { FormSchema } from '/@/components/Form'; 8 import { FormSchema } from '/@/components/Form';
3 9
4 export const formSchemas: FormSchema[] = [ 10 export const formSchemas: FormSchema[] = [
5 { 11 {
6 - field: NodeBindDataFieldEnum.NAME, 12 + field: SendEmailFieldsEnum.USE_SYSTEM_SMTP_SETTINGS,
  13 + label: '',
  14 + component: 'Checkbox',
  15 + renderComponentContent: () => ({
  16 + default: () => SendEmailFieldsNameEnum.USE_SYSTEM_SMTP_SETTINGS,
  17 + }),
  18 + },
  19 + {
  20 + field: SendEmailFieldsEnum.SMTP_PROTOCOL,
  21 + label: SendEmailFieldsNameEnum.SMTP_PROTOCOL,
  22 + component: 'Select',
  23 + required: true,
  24 + ifShow: ({ model }) => !model[SendEmailFieldsEnum.USE_SYSTEM_SMTP_SETTINGS],
  25 + componentProps: {
  26 + options: Object.keys(EmailProtocolEnum).map((value) => ({
  27 + label: EmailProtocolNameEnum[value],
  28 + value,
  29 + })),
  30 + placeholder: `请选择${SendEmailFieldsNameEnum.SMTP_PROTOCOL}`,
  31 + getPopupContainer: () => document.body,
  32 + },
  33 + },
  34 + {
  35 + field: SendEmailFieldsEnum.SMTP_HOST,
  36 + label: SendEmailFieldsNameEnum.SMTP_HOST,
7 component: 'Input', 37 component: 'Input',
8 - label: NodeBindDataFieldNameEnum.NAME, 38 + required: true,
  39 + colProps: { span: 12 },
  40 + ifShow: ({ model }) => !model[SendEmailFieldsEnum.USE_SYSTEM_SMTP_SETTINGS],
  41 + componentProps: {
  42 + placeholder: `请输入${SendEmailFieldsNameEnum.SMTP_HOST}`,
  43 + },
  44 + },
  45 + {
  46 + field: SendEmailFieldsEnum.SMTP_PORT,
  47 + label: SendEmailFieldsNameEnum.SMTP_PORT,
  48 + component: 'InputNumber',
  49 + required: true,
  50 + colProps: { span: 12 },
  51 + ifShow: ({ model }) => !model[SendEmailFieldsEnum.USE_SYSTEM_SMTP_SETTINGS],
  52 + componentProps: {
  53 + min: 0,
  54 + placeholder: `请输入${SendEmailFieldsNameEnum.SMTP_PORT}`,
  55 + },
  56 + },
  57 + {
  58 + field: SendEmailFieldsEnum.TIMEOUT,
  59 + label: SendEmailFieldsNameEnum.TIMEOUT,
  60 + component: 'InputNumber',
  61 + required: true,
  62 + ifShow: ({ model }) => !model[SendEmailFieldsEnum.USE_SYSTEM_SMTP_SETTINGS],
  63 + componentProps: {
  64 + min: 0,
  65 + placeholder: `请输入${SendEmailFieldsNameEnum.TIMEOUT}`,
  66 + },
  67 + },
  68 + {
  69 + field: SendEmailFieldsEnum.ENABLE_TLS,
  70 + label: '',
  71 + component: 'Checkbox',
  72 + ifShow: ({ model }) => !model[SendEmailFieldsEnum.USE_SYSTEM_SMTP_SETTINGS],
  73 + renderComponentContent: () => ({
  74 + default: () => SendEmailFieldsNameEnum.ENABLE_TLS,
  75 + }),
  76 + },
  77 + {
  78 + field: SendEmailFieldsEnum.TLS_VERSION,
  79 + label: SendEmailFieldsNameEnum.TLS_VERSION,
  80 + component: 'Select',
  81 + required: true,
  82 + ifShow: ({ model }) =>
  83 + model[SendEmailFieldsEnum.TLS_VERSION] &&
  84 + !model[SendEmailFieldsEnum.USE_SYSTEM_SMTP_SETTINGS],
  85 + componentProps: {
  86 + options: Object.keys(TSLVersionEnum).map((value) => ({
  87 + label: TSLVersionNameEnum[value],
  88 + value,
  89 + })),
  90 + placeholder: `请选择${SendEmailFieldsNameEnum.TLS_VERSION}`,
  91 + getPopupContainer: () => document.body,
  92 + },
  93 + },
  94 + {
  95 + field: SendEmailFieldsEnum.ENABLE_PROXY,
  96 + label: '',
  97 + component: 'Checkbox',
  98 + ifShow: ({ model }) => !model[SendEmailFieldsEnum.USE_SYSTEM_SMTP_SETTINGS],
  99 + renderComponentContent: () => ({
  100 + default: () => SendEmailFieldsNameEnum.ENABLE_PROXY,
  101 + }),
  102 + },
  103 + {
  104 + field: SendEmailFieldsEnum.PROXY_HOST,
  105 + label: SendEmailFieldsNameEnum.PROXY_HOST,
  106 + component: 'Input',
  107 + required: true,
  108 + colProps: { span: 12 },
  109 + ifShow: ({ model }) =>
  110 + !model[SendEmailFieldsEnum.USE_SYSTEM_SMTP_SETTINGS] && model[SendEmailFieldsEnum.PROXY_HOST],
  111 + componentProps: {
  112 + placeholder: `请输入${SendEmailFieldsNameEnum.PROXY_HOST}`,
  113 + },
  114 + },
  115 + {
  116 + field: SendEmailFieldsEnum.PROXY_PORT,
  117 + label: SendEmailFieldsNameEnum.PROXY_PORT,
  118 + component: 'Input',
  119 + required: true,
  120 + colProps: { span: 12 },
  121 + ifShow: ({ model }) =>
  122 + !model[SendEmailFieldsEnum.USE_SYSTEM_SMTP_SETTINGS] && model[SendEmailFieldsEnum.PROXY_PORT],
  123 + componentProps: {
  124 + placeholder: `请输入${SendEmailFieldsNameEnum.PROXY_PORT}`,
  125 + },
  126 + },
  127 + {
  128 + field: SendEmailFieldsEnum.PROXY_USER,
  129 + label: SendEmailFieldsNameEnum.PROXY_USER,
  130 + component: 'Input',
  131 + ifShow: ({ model }) =>
  132 + !model[SendEmailFieldsEnum.USE_SYSTEM_SMTP_SETTINGS] && model[SendEmailFieldsEnum.PROXY_USER],
  133 + componentProps: {
  134 + placeholder: `请输入${SendEmailFieldsNameEnum.PROXY_USER}`,
  135 + },
  136 + },
  137 + {
  138 + field: SendEmailFieldsEnum.PROXY_PASSWORD,
  139 + label: SendEmailFieldsNameEnum.PROXY_PASSWORD,
  140 + component: 'Input',
  141 + ifShow: ({ model }) => !model[SendEmailFieldsEnum.USE_SYSTEM_SMTP_SETTINGS],
  142 + componentProps: {
  143 + placeholder: `请输入${SendEmailFieldsNameEnum.PROXY_PASSWORD}`,
  144 + },
  145 + },
  146 + {
  147 + field: SendEmailFieldsEnum.USERNAME,
  148 + label: SendEmailFieldsNameEnum.USERNAME,
  149 + component: 'Input',
  150 + ifShow: ({ model }) => !model[SendEmailFieldsEnum.USE_SYSTEM_SMTP_SETTINGS],
  151 + componentProps: {
  152 + placeholder: `请输入${SendEmailFieldsNameEnum.USERNAME}`,
  153 + },
  154 + },
  155 + {
  156 + field: SendEmailFieldsEnum.PASSWORD,
  157 + label: SendEmailFieldsNameEnum.PASSWORD,
  158 + component: 'InputPassword',
  159 + ifShow: ({ model }) => !model[SendEmailFieldsEnum.USE_SYSTEM_SMTP_SETTINGS],
  160 + componentProps: {
  161 + placeholder: `请输入${SendEmailFieldsNameEnum.PASSWORD}`,
  162 + },
9 }, 163 },
10 ]; 164 ];
1 -import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node'; 1 +import { SMSServiceProviderEnum, SMSServiceProviderNameEnum } from '../../../enum/form';
  2 +import { SendSMSFieldsEnum, SendSMSFieldsNameEnum } from '../../../enum/formField/external';
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: SendSMSFieldsEnum.NUMBERS_TO_TEMPLATE,
  8 + label: SendSMSFieldsNameEnum.NUMBERS_TO_TEMPLATE,
7 component: 'Input', 9 component: 'Input',
8 - label: NodeBindDataFieldNameEnum.NAME, 10 + required: true,
  11 + helpMessage:
  12 + 'Comma separated Phone Numbers, use ${metadataKey} for value from metadata, $[messageKey] for value from message body',
  13 + componentProps: {
  14 + placeholder: `请输入${SendSMSFieldsNameEnum.NUMBERS_TO_TEMPLATE}`,
  15 + },
  16 + },
  17 + {
  18 + field: SendSMSFieldsEnum.SMS_MESSAGE_TEMPLATE,
  19 + label: SendSMSFieldsNameEnum.SMS_MESSAGE_TEMPLATE,
  20 + component: 'InputTextArea',
  21 + required: true,
  22 + helpMessage:
  23 + 'Hint: use ${metadataKey} for value from metadata, $[messageKey] for value from message body',
  24 + componentProps: {
  25 + placeholder: `请输入${SendSMSFieldsNameEnum.SMS_MESSAGE_TEMPLATE}`,
  26 + },
  27 + },
  28 + {
  29 + field: SendSMSFieldsEnum.USE_SYSTEM_SMS_SETTINGS,
  30 + label: '',
  31 + component: 'Checkbox',
  32 + renderComponentContent: () => ({
  33 + default: () => SendSMSFieldsNameEnum.USE_SYSTEM_SMS_SETTINGS,
  34 + }),
  35 + },
  36 + {
  37 + field: SendSMSFieldsEnum.TYPE,
  38 + label: SendSMSFieldsNameEnum.TYPE,
  39 + component: 'Select',
  40 + required: true,
  41 + ifShow: ({ model }) => !model[SendSMSFieldsEnum.USE_SYSTEM_SMS_SETTINGS],
  42 + componentProps: {
  43 + options: Object.keys(SMSServiceProviderEnum).map((value) => ({
  44 + label: SMSServiceProviderNameEnum[value],
  45 + value,
  46 + })),
  47 + getPopupContainer: () => document.body,
  48 + placeholder: `请选择${SendSMSFieldsNameEnum.TYPE}`,
  49 + },
  50 + },
  51 + {
  52 + field: SendSMSFieldsEnum.NUMBER_FROM,
  53 + label: SendSMSFieldsNameEnum.NUMBER_FROM,
  54 + component: 'Input',
  55 + required: true,
  56 + helpMessage: `Phone Number in E.164 format/Phone Number's SID/Messaging Service SID, ex. +19995550123/PNXXX/MGXXX`,
  57 + ifShow: ({ model }) =>
  58 + !model[SendSMSFieldsEnum.USE_SYSTEM_SMS_SETTINGS] &&
  59 + model[SendSMSFieldsEnum.TYPE] === SMSServiceProviderEnum.TWILIO,
  60 + componentProps: {
  61 + placeholder: `请输入${SendSMSFieldsEnum.NUMBER_FROM}`,
  62 + },
  63 + },
  64 + {
  65 + field: SendSMSFieldsEnum.ACCOUNT_SID,
  66 + label: SendSMSFieldsNameEnum.ACCOUNT_SID,
  67 + component: 'Input',
  68 + required: true,
  69 + ifShow: ({ model }) =>
  70 + !model[SendSMSFieldsEnum.USE_SYSTEM_SMS_SETTINGS] &&
  71 + model[SendSMSFieldsEnum.TYPE] === SMSServiceProviderEnum.TWILIO,
  72 + componentProps: {
  73 + placeholder: `请选择${SendSMSFieldsNameEnum.ACCOUNT_SID}`,
  74 + },
  75 + },
  76 + {
  77 + field: SendSMSFieldsEnum.ACCOUNT_TOKEN,
  78 + label: SendSMSFieldsNameEnum.ACCOUNT_TOKEN,
  79 + component: 'Input',
  80 + required: true,
  81 + ifShow: ({ model }) =>
  82 + !model[SendSMSFieldsEnum.USE_SYSTEM_SMS_SETTINGS] &&
  83 + model[SendSMSFieldsEnum.TYPE] === SMSServiceProviderEnum.TWILIO,
  84 + componentProps: {
  85 + placeholder: `请选择${SendSMSFieldsNameEnum.ACCOUNT_TOKEN}`,
  86 + },
  87 + },
  88 + {
  89 + field: SendSMSFieldsEnum.ACCESS_KEY_ID,
  90 + label: SendSMSFieldsNameEnum.ACCESS_KEY_ID,
  91 + component: 'Input',
  92 + required: true,
  93 + ifShow: ({ model }) =>
  94 + !model[SendSMSFieldsEnum.USE_SYSTEM_SMS_SETTINGS] &&
  95 + model[SendSMSFieldsEnum.TYPE] === SMSServiceProviderEnum.AWS_SNS,
  96 + componentProps: {
  97 + placeholder: `请选择${SendSMSFieldsNameEnum.ACCESS_KEY_ID}`,
  98 + },
  99 + },
  100 + {
  101 + field: SendSMSFieldsEnum.SECRET_ACCESS_KEY,
  102 + label: SendSMSFieldsNameEnum.SECRET_ACCESS_KEY,
  103 + component: 'InputPassword',
  104 + required: true,
  105 + ifShow: ({ model }) =>
  106 + !model[SendSMSFieldsEnum.USE_SYSTEM_SMS_SETTINGS] &&
  107 + model[SendSMSFieldsEnum.TYPE] === SMSServiceProviderEnum.AWS_SNS,
  108 + componentProps: {
  109 + placeholder: `请选择${SendSMSFieldsNameEnum.SECRET_ACCESS_KEY}`,
  110 + },
  111 + },
  112 + {
  113 + field: SendSMSFieldsEnum.REGION,
  114 + label: SendSMSFieldsNameEnum.REGION,
  115 + component: 'InputPassword',
  116 + required: true,
  117 + ifShow: ({ model }) =>
  118 + !model[SendSMSFieldsEnum.USE_SYSTEM_SMS_SETTINGS] &&
  119 + model[SendSMSFieldsEnum.TYPE] === SMSServiceProviderEnum.AWS_SNS,
  120 + componentProps: {
  121 + placeholder: `请选择${SendSMSFieldsNameEnum.REGION}`,
  122 + },
9 }, 123 },
10 ]; 124 ];
@@ -3,6 +3,7 @@ @@ -3,6 +3,7 @@
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 { SendSMSFieldsEnum } from '../../../enum/formField/external';
6 7
7 defineProps<{ 8 defineProps<{
8 config: NodeData; 9 config: NodeData;
@@ -16,7 +17,20 @@ @@ -16,7 +17,20 @@
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 + [SendSMSFieldsEnum.NUMBERS_TO_TEMPLATE]: value[SendSMSFieldsEnum.NUMBERS_TO_TEMPLATE],
  22 + [SendSMSFieldsEnum.SMS_MESSAGE_TEMPLATE]: value[SendSMSFieldsEnum.SMS_MESSAGE_TEMPLATE],
  23 + [SendSMSFieldsEnum.USE_SYSTEM_SMS_SETTINGS]: value[SendSMSFieldsEnum.USE_SYSTEM_SMS_SETTINGS],
  24 + [SendSMSFieldsEnum.SMS_PROVIDER_CONFIGURATION]: {
  25 + [SendSMSFieldsEnum.ACCESS_KEY_ID]: value[SendSMSFieldsEnum.ACCESS_KEY_ID],
  26 + [SendSMSFieldsEnum.REGION]: value[SendSMSFieldsEnum.REGION],
  27 + [SendSMSFieldsEnum.SECRET_ACCESS_KEY]: value[SendSMSFieldsEnum.SECRET_ACCESS_KEY],
  28 + [SendSMSFieldsEnum.TYPE]: value[SendSMSFieldsEnum.TYPE],
  29 + [SendSMSFieldsEnum.ACCOUNT_SID]: value[SendSMSFieldsEnum.ACCOUNT_SID],
  30 + [SendSMSFieldsEnum.ACCOUNT_TOKEN]: value[SendSMSFieldsEnum.ACCOUNT_TOKEN],
  31 + [SendSMSFieldsEnum.NUMBER_FROM]: value[SendSMSFieldsEnum.NUMBER_FROM],
  32 + },
  33 + };
20 }; 34 };
21 35
22 const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => { 36 const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => {
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 +<script setup lang="ts">
  2 + import { AutoComplete } from 'ant-design-vue';
  3 + import get from 'lodash-es/get';
  4 + import omit from 'lodash-es/omit';
  5 + import { computed, ref, unref, watch, watchEffect } from 'vue';
  6 + import { isFunction } from '/@/utils/is';
  7 + type OptionsItem = { text: string; value: string };
  8 +
  9 + const props = withDefaults(
  10 + defineProps<{
  11 + value?: string;
  12 + api?: (arg?: Recordable) => Promise<OptionsItem[]>;
  13 + onSearchQuery?: boolean;
  14 + params?: Recordable;
  15 + resultField?: string;
  16 + valueField?: string;
  17 + labelField?: string;
  18 + immediate?: boolean;
  19 + }>(),
  20 + {
  21 + valueField: 'value',
  22 + labelField: 'label',
  23 + immediate: true,
  24 + }
  25 + );
  26 +
  27 + const emit = defineEmits(['update:value', 'options-change']);
  28 +
  29 + const loading = ref(false);
  30 +
  31 + const isFirstLoad = ref(true);
  32 +
  33 + const options = ref<OptionsItem[]>([]);
  34 +
  35 + const getOptions = computed(() => {
  36 + const { labelField, valueField } = props;
  37 +
  38 + return unref(options).reduce((prev, next: Recordable) => {
  39 + if (next) {
  40 + const value = next[valueField];
  41 + prev.push({
  42 + ...omit(next, [labelField, valueField]),
  43 + text: next[labelField],
  44 + value,
  45 + });
  46 + }
  47 + return prev;
  48 + }, [] as OptionsItem[]);
  49 + });
  50 +
  51 + const fetch = async () => {
  52 + const api = props.api;
  53 + if (!api || !isFunction(api)) return;
  54 + options.value = [];
  55 + try {
  56 + loading.value = true;
  57 + const res = await api(props.params);
  58 + if (Array.isArray(res)) {
  59 + options.value = res;
  60 + emitChange();
  61 + return;
  62 + }
  63 + if (props.resultField) {
  64 + options.value = get(res, props.resultField) || [];
  65 + }
  66 + emitChange();
  67 + } catch (error) {
  68 + console.warn(error);
  69 + } finally {
  70 + loading.value = false;
  71 + }
  72 + };
  73 +
  74 + function emitChange() {
  75 + emit('options-change', unref(getOptions));
  76 + }
  77 +
  78 + const handleChange = (value: any) => {
  79 + emit('update:value', value);
  80 + };
  81 +
  82 + watchEffect(() => {
  83 + props.immediate && fetch();
  84 + });
  85 +
  86 + watch(
  87 + () => props.params,
  88 + () => {
  89 + !unref(isFirstLoad) && fetch();
  90 + },
  91 + { deep: true }
  92 + );
  93 +</script>
  94 +
  95 +<template>
  96 + <AutoComplete :value="value" @change="handleChange" :options="getOptions" />
  97 +</template>
1 -import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node';  
2 -import { FormSchema } from '/@/components/Form'; 1 +import { CheckPointFieldsEnum, CheckPointFieldsNameEnum } from '../../../enum/formField/flow';
  2 +import { FormSchema, useComponentRegister } from '/@/components/Form';
  3 +import ApiComplete from './ApiComplete.vue';
  4 +import { getTenantQueue } from '/@/api/ruleChainDesigner';
  5 +
  6 +useComponentRegister('ApiComplete', ApiComplete);
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: CheckPointFieldsEnum.QUEUE_NAME,
  11 + label: CheckPointFieldsNameEnum.QUEUE_NAME,
  12 + component: 'ApiComplete',
  13 + required: true,
  14 + helpMessage: ['从下拉列表中选择或自定义名称'],
  15 + valueField: 'value',
  16 + changeEvent: 'update:value',
  17 + componentProps: () => {
  18 + return {
  19 + placeholder: `请选择${CheckPointFieldsNameEnum.QUEUE_NAME}`,
  20 + getPopupContainer: () => document.body,
  21 + api: async (params: Recordable) => {
  22 + const options = await getTenantQueue(params);
  23 + return options.map((value) => ({ label: value, value }));
  24 + },
  25 + params: { serviceType: 'TB_RULE_ENGINE' },
  26 + };
  27 + },
9 }, 28 },
10 ]; 29 ];
1 -import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node'; 1 +import { h } from 'vue';
2 import { FormSchema } from '/@/components/Form'; 2 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: 'desc',
  7 + label: '',
7 component: 'Input', 8 component: 'Input',
8 - label: NodeBindDataFieldNameEnum.NAME, 9 + renderColContent: () =>
  10 + h(
  11 + 'div',
  12 + 'The rule node name corresponds to the relation type of the output message, and it is used to forward messages to other rule nodes in the caller rule chain.'
  13 + ),
9 }, 14 },
10 ]; 15 ];
1 -import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node'; 1 +import { RuleChainFieldsEnum, RuleChainFieldsNameEnum } from '../../../enum/formField/flow';
  2 +import { getRuleChains } from '/@/api/ruleDesigner';
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: RuleChainFieldsEnum.RULE_CHAIN_ID,
  8 + label: RuleChainFieldsNameEnum.RULE_CHAIN_ID,
  9 + component: 'ApiSearchSelect',
  10 + componentProps: () => {
  11 + return {
  12 + placeholder: '请选择所属产品',
  13 + showSearch: true,
  14 + resultField: 'data',
  15 + labelField: 'name',
  16 + valueField: 'id.id',
  17 + params: {
  18 + pageSize: 50,
  19 + page: 0,
  20 + type: 'CORE',
  21 + },
  22 + api: getRuleChains,
  23 + searchApi: getRuleChains,
  24 + getPopupContainer: () => document.body,
  25 + };
  26 + },
9 }, 27 },
10 ]; 28 ];
1 export enum RuleNodeTypeEnum { 1 export enum RuleNodeTypeEnum {
  2 + ENTRY = 'ENTRY',
2 FLOW = 'FLOW', 3 FLOW = 'FLOW',
3 ENRICHMENT = 'ENRICHMENT', 4 ENRICHMENT = 'ENRICHMENT',
4 EXTERNAL = 'EXTERNAL', 5 EXTERNAL = 'EXTERNAL',
@@ -28,6 +28,7 @@ @@ -28,6 +28,7 @@
28 valueLabel?: string; 28 valueLabel?: string;
29 keyComponent?: ComponentType; 29 keyComponent?: ComponentType;
30 valueComponent?: ComponentType; 30 valueComponent?: ComponentType;
  31 + allowEmpty?: boolean;
31 }>(), 32 }>(),
32 { 33 {
33 keyComponent: 'Input', 34 keyComponent: 'Input',
@@ -132,7 +133,7 @@ @@ -132,7 +133,7 @@
132 133
133 list.value = valueList; 134 list.value = valueList;
134 135
135 - if (!list.value.length) list.value = [{ uuid: buildShortUUID() }]; 136 + if (!list.value.length && !props.allowEmpty) list.value = [{ uuid: buildShortUUID() }];
136 }; 137 };
137 138
138 watch( 139 watch(
@@ -37,8 +37,6 @@ @@ -37,8 +37,6 @@
37 const currentEdge = flowActionType.findEdge(props.id); 37 const currentEdge = flowActionType.findEdge(props.id);
38 38
39 (currentEdge!.data as EdgeData).data = data; 39 (currentEdge!.data as EdgeData).data = data;
40 -  
41 - console.log(props);  
42 }; 40 };
43 41
44 const handleDelete = () => { 42 const handleDelete = () => {
@@ -68,7 +68,7 @@ @@ -68,7 +68,7 @@
68 <div> 68 <div>
69 <Icon class="text-2xl dark:text-light-50" :icon="getIcon" /> 69 <Icon class="text-2xl dark:text-light-50" :icon="getIcon" />
70 </div> 70 </div>
71 - <BasicToolbar v-bind="$props" /> 71 + <BasicToolbar v-if="!getData.config?.disableAction" v-bind="$props" />
72 <div class="flex text-xs flex-col ml-2 text-left truncate"> 72 <div class="flex text-xs flex-col ml-2 text-left truncate">
73 <span class="text-gray-700 w-full truncate dark:text-light-50">{{ getLabel }}</span> 73 <span class="text-gray-700 w-full truncate dark:text-light-50">{{ getLabel }}</span>
74 <span class="w-full truncate dark:text-neutral-50">{{ getName }}</span> 74 <span class="w-full truncate dark:text-neutral-50">{{ getName }}</span>
@@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
3 </script> 3 </script>
4 4
5 <template> 5 <template>
6 - <section class="p-2 bg-gray-100 dark:bg-dark-200"> 6 + <section class="bg-gray-100 dark:bg-dark-200">
7 <DebugEvent /> 7 <DebugEvent />
8 </section> 8 </section>
9 </template> 9 </template>
@@ -97,7 +97,7 @@ @@ -97,7 +97,7 @@
97 <Tabs> 97 <Tabs>
98 <Tabs.TabPane :tab="TabsPanelNameEnum[TabsPanelEnum.DETAIL]" :key="TabsPanelEnum.DETAIL"> 98 <Tabs.TabPane :tab="TabsPanelNameEnum[TabsPanelEnum.DETAIL]" :key="TabsPanelEnum.DETAIL">
99 <Spin :spinning="spinning"> 99 <Spin :spinning="spinning">
100 - <section v-if="shadowComponent" class="w-full h-full overflow-y-auto"> 100 + <section v-if="shadowComponent" class="w-full h-full overflow-y-auto pr-4">
101 <BasicForm @register="topFormRegister" @vue:mounted="handleTopFormMounted" /> 101 <BasicForm @register="topFormRegister" @vue:mounted="handleTopFormMounted" />
102 <component 102 <component
103 class="rule-node-form" 103 class="rule-node-form"
@@ -152,7 +152,12 @@ @@ -152,7 +152,12 @@
152 @apply h-full flex flex-col; 152 @apply h-full flex flex-col;
153 153
154 .ant-tabs-content { 154 .ant-tabs-content {
155 - @apply flex-auto overflow-x-hidden overflow-y-auto pr-4; 155 + height: calc(100% - 61px);
  156 + }
  157 +
  158 + .ant-spin-nested-loading,
  159 + .ant-spin-container {
  160 + @apply h-full;
156 } 161 }
157 } 162 }
158 } 163 }
@@ -55,6 +55,11 @@ export interface NodeItemConfigType { @@ -55,6 +55,11 @@ export interface NodeItemConfigType {
55 * @description 配置描述 55 * @description 配置描述
56 */ 56 */
57 configurationDescriptor: ConfigurationDescriptor; 57 configurationDescriptor: ConfigurationDescriptor;
  58 +
  59 + /**
  60 + * @description 禁用所有动作
  61 + */
  62 + disableAction?: boolean;
58 } 63 }
59 64
60 export interface CategoryConfigType { 65 export interface CategoryConfigType {