Commit 18b8395a8c63843df019be9caa5d603bd0aa7eca

Authored by ww
2 parents 877e7e08 055fb44c

Merge branch 'main_dev' into feat/add-rule-chain

Showing 71 changed files with 4122 additions and 123 deletions

Too many changes to show.

To preserve performance only 71 of 398 files are displayed.

@@ -5,22 +5,28 @@ @@ -5,22 +5,28 @@
5 "public/resource/tinymce/langs" 5 "public/resource/tinymce/langs"
6 ], 6 ],
7 "cSpell.words": [ 7 "cSpell.words": [
  8 + "ACKS",
  9 + "clazz",
8 "Cmds", 10 "Cmds",
9 "COAP", 11 "COAP",
10 "echarts", 12 "echarts",
11 "edrx", 13 "edrx",
12 - "EFENTO", 14 + "EFENTO",
13 "fingerprintjs", 15 "fingerprintjs",
14 "flvjs", 16 "flvjs",
15 - "flvjs",  
16 "inited", 17 "inited",
17 "liveui", 18 "liveui",
18 "MQTT", 19 "MQTT",
  20 + "noconflict",
19 "notif", 21 "notif",
20 "PROTOBUF", 22 "PROTOBUF",
  23 + "Rabbitmq",
21 "rtsp", 24 "rtsp",
22 "SCADA", 25 "SCADA",
  26 + "SMTPS",
23 "SNMP", 27 "SNMP",
  28 + "TSLV",
  29 + "UNACK",
24 "unref", 30 "unref",
25 "vben", 31 "vben",
26 "videojs", 32 "videojs",
  1 +import { NodeItemConfigType } from '/@/views/rule/designer/types/node';
  2 +
  3 +export const components: Partial<
  4 + NodeItemConfigType & { id: { id: string }; createdTime: number; actions: any }
  5 +>[] = [];
  1 +import { join } from 'path';
  2 +import { pathExists, ensureFile, writeFile } from 'fs-extra';
  3 +import { NodeItemConfigType } from '/@/views/rule/designer/types/node';
  4 +import { camelCase, upperFirst, snakeCase } from 'lodash-es';
  5 +import { components } from './components';
  6 +type GroupNodeType = { [key: string]: NodeItemConfigType[] };
  7 +
  8 +const RULE_CHAIN_FILE_PATH = join(process.cwd(), '/src/views/rule/designer');
  9 +
  10 +const list: NodeItemConfigType[] = components;
  11 +
  12 +const getCategoryConfigName = (name: string) => {
  13 + return `${upperFirst(camelCase(name))}CategoryConfig`;
  14 +};
  15 +
  16 +const getNodeConfigName = (name: string) => {
  17 + return `${upperFirst(camelCase(name))}Config`;
  18 +};
  19 +
  20 +const getCagegoryEnumName = (name: string) => {
  21 + return `${upperFirst(name.toLowerCase())}CategoryComponentEnum`;
  22 +};
  23 +
  24 +const getEnumKeyName = (name: string) => {
  25 + return snakeCase(name).toUpperCase();
  26 +};
  27 +
  28 +const createFile = async (fileName: string, fileContent?: string, replace = false) => {
  29 + const path = join(RULE_CHAIN_FILE_PATH, './packages', fileName);
  30 +
  31 + const flag = await pathExists(path);
  32 +
  33 + if (flag && !replace) return false;
  34 +
  35 + await ensureFile(path);
  36 +
  37 + fileContent && (await writeFile(path, fileContent, { encoding: 'utf-8' }));
  38 +};
  39 +
  40 +const groupByType = () => {
  41 + const group: { [key: string]: NodeItemConfigType[] } = {};
  42 +
  43 + list.forEach((item) => {
  44 + if (!group[item.type]) group[item.type] = [];
  45 + group[item.type].push(item);
  46 + });
  47 +
  48 + return group;
  49 +};
  50 +
  51 +const generateCategoryEnumFile = async (data: GroupNodeType) => {
  52 + const defaultContent = `
  53 +export enum EntryCategoryComponentEnum {
  54 + INPUT = 'Input',
  55 +}
  56 + `;
  57 +
  58 + const fileContent = Object.keys(data).reduce((prev, next) => {
  59 + const enumName = getCagegoryEnumName(next);
  60 +
  61 + const enumKeys = data[next].map((item) => getEnumKeyName(item.name));
  62 +
  63 + const content = `export enum ${enumName} {
  64 + ${enumKeys.map((name) => `${name} = '${upperFirst(camelCase(name))}'`)}
  65 + }`;
  66 +
  67 + return `${prev} \n ${content}`;
  68 + }, defaultContent);
  69 +
  70 + createFile('../enum/category.ts', fileContent, true);
  71 + return fileContent;
  72 +};
  73 +
  74 +const generateRuleNodeEnumFile = async (data: GroupNodeType) => {
  75 + const categoryKeys = Object.keys(data).map((type) => type.toUpperCase());
  76 + const filePath = join(RULE_CHAIN_FILE_PATH, './packages/index.type.ts');
  77 + const fileContent = `
  78 +export enum RuleNodeTypeEnum {
  79 + ${categoryKeys.map((item) => `${item} = '${item}'`).join(',\n')}
  80 +}
  81 + `;
  82 +
  83 + await writeFile(filePath, fileContent, {
  84 + encoding: 'utf-8',
  85 + });
  86 +
  87 + return fileContent;
  88 +};
  89 +
  90 +const generateCategoryIndexFile = async (type: string, data: NodeItemConfigType[]) => {
  91 + const getComponentsName = data.map((temp) => `${upperFirst(camelCase(temp.name))}`);
  92 + const importContent = getComponentsName.map(
  93 + (name) => `import { ${name}Config } from './${name}';\n`
  94 + );
  95 +
  96 + const components = getComponentsName.map((item) => `${item}Config`);
  97 +
  98 + const content = `import type { CategoryConfigType, NodeItemConfigType } from '../../types/node';
  99 +import { RuleNodeTypeEnum } from '../index.type';
  100 +${importContent.join('')}
  101 +
  102 +export const ${getCategoryConfigName(type)}: CategoryConfigType = {
  103 + category: RuleNodeTypeEnum.${type.toUpperCase()},
  104 + title: '${type}',
  105 + icon: 'tabler:circuit-ground',
  106 + description: '使用配置条件筛选传入消息',
  107 +};
  108 +
  109 +export const ${upperFirst(type.toLowerCase())}Components: NodeItemConfigType[] = [${components}];
  110 +`;
  111 +
  112 + createFile(`./${upperFirst(type.toLowerCase())}/index.ts`, content, true);
  113 +
  114 + return content;
  115 +};
  116 +
  117 +const generateNodeIndexFile = async (type: string, data: NodeItemConfigType) => {
  118 + const categoryEnumName = getCagegoryEnumName(type);
  119 +
  120 + const nodeConfigName = getNodeConfigName(data.name);
  121 +
  122 + const content = `
  123 + import { ${categoryEnumName} } from '../../../enum/category';
  124 + import { useCreateNodeKey } from '../../../hook/useCreateNodeKey';
  125 + import type { NodeItemConfigType } from '../../../types/node';
  126 + import { RuleNodeTypeEnum } from '../../index.type';
  127 +
  128 + const keys = useCreateNodeKey(${categoryEnumName}.${getEnumKeyName(data.name)});
  129 +
  130 + export interface ${upperFirst(camelCase(data.name))}DataType {
  131 + someConfiguration?: Recordable
  132 + }
  133 +
  134 + export const ${nodeConfigName}: NodeItemConfigType = {
  135 + ...keys,
  136 + clazz: '${data.clazz}',
  137 + categoryType: RuleNodeTypeEnum.${type.toUpperCase()},
  138 + name: '${data.name}',
  139 + configurationDescriptor: ${JSON.stringify(data.configurationDescriptor, null, 2)}
  140 + };
  141 + `;
  142 +
  143 + createFile(
  144 + `./${upperFirst(type.toLowerCase())}/${upperFirst(camelCase(data.name))}/index.ts`,
  145 + content
  146 + );
  147 +
  148 + return content;
  149 +};
  150 +
  151 +const generateNodeVueTemplateFile = async (type: string, data: NodeItemConfigType) => {
  152 + const content = `
  153 + <script lang="ts" setup>
  154 + import type { CreateModalDefineExposeType } from '../../../types';
  155 + import { BasicForm, useForm } from '/@/components/Form';
  156 + import { formSchemas } from './create.config';
  157 + import { NodeData } from '../../../types/node';
  158 +
  159 + defineProps<{
  160 + config: NodeData;
  161 + }>();
  162 +
  163 + const [register, { validate, getFieldsValue, setFieldsValue, resetFields }] = useForm({
  164 + schemas: formSchemas,
  165 + showActionButtonGroup: false,
  166 + });
  167 +
  168 + const getValue: CreateModalDefineExposeType['getFieldsValue'] = async () => {
  169 + await validate();
  170 + const value = getFieldsValue() || {};
  171 + return value;
  172 + };
  173 +
  174 + const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => {
  175 + resetFields();
  176 + setFieldsValue(value);
  177 + };
  178 +
  179 + defineExpose({
  180 + setFieldsValue: setValue,
  181 + getFieldsValue: getValue,
  182 + } as CreateModalDefineExposeType);
  183 + </script>
  184 +
  185 + <template>
  186 + <BasicForm @register="register" />
  187 + </template>
  188 + `;
  189 +
  190 + createFile(
  191 + `./${upperFirst(type.toLowerCase())}/${upperFirst(camelCase(data.name))}/create.vue`,
  192 + content
  193 + );
  194 + return content;
  195 +};
  196 +
  197 +const generateNodeVueConfigFile = async (type: string, data: NodeItemConfigType) => {
  198 + const content = `
  199 + import { NodeBindDataFieldEnum, NodeBindDataFieldNameEnum } from '../../../enum/node';
  200 + import { FormSchema } from '/@/components/Form';
  201 +
  202 + export const formSchemas: FormSchema[] = [
  203 + {
  204 + field: NodeBindDataFieldEnum.NAME,
  205 + component: 'Input',
  206 + label: NodeBindDataFieldNameEnum.NAME,
  207 + }
  208 + ];
  209 + `;
  210 +
  211 + createFile(
  212 + `./${upperFirst(type.toLowerCase())}/${upperFirst(camelCase(data.name))}/create.config.ts`,
  213 + content
  214 + );
  215 + return content;
  216 +};
  217 +
  218 +const generateNodeConfigFile = async (type: string, data: NodeItemConfigType) => {
  219 + const nodeConfigName = getNodeConfigName(data.name);
  220 + const categoryConfigName = getCategoryConfigName(type);
  221 + const content = `
  222 + import { cloneDeep } from 'lodash-es';
  223 + import { PublicNodeItemClass } from '../../../types/node';
  224 + import type {
  225 + CategoryConfigType,
  226 + CreateComponentType,
  227 + NodeItemConfigType,
  228 + } from '../../../types/node';
  229 + import { ${categoryConfigName} } from '..';
  230 + import { ${nodeConfigName} } from '.';
  231 +
  232 + export class Config extends PublicNodeItemClass implements CreateComponentType {
  233 + public config: NodeItemConfigType = cloneDeep(${nodeConfigName});
  234 +
  235 + public categoryConfig: CategoryConfigType = cloneDeep(${categoryConfigName});
  236 +
  237 + constructor() {
  238 + super();
  239 + }
  240 + }
  241 + `;
  242 +
  243 + createFile(
  244 + `./${upperFirst(type.toLowerCase())}/${upperFirst(camelCase(data.name))}/config.ts`,
  245 + content
  246 + );
  247 + return content;
  248 +};
  249 +
  250 +const bootstrap = async () => {
  251 + const groupData = groupByType();
  252 + await generateRuleNodeEnumFile(groupData);
  253 + await generateCategoryEnumFile(groupData);
  254 + for (const type of Object.keys(groupData)) {
  255 + const item = groupData[type];
  256 + await generateCategoryIndexFile(type, item);
  257 + for (const temp of item) {
  258 + await generateNodeConfigFile(type, temp);
  259 + await generateNodeIndexFile(type, temp);
  260 + await generateNodeVueConfigFile(type, temp);
  261 + await generateNodeVueTemplateFile(type, temp);
  262 + }
  263 + }
  264 +};
  265 +
  266 +bootstrap();
@@ -32,13 +32,18 @@ @@ -32,13 +32,18 @@
32 "reinstall": "rimraf yarn.lock && rimraf package.lock.json && rimraf node_modules && npm run bootstrap", 32 "reinstall": "rimraf yarn.lock && rimraf package.lock.json && rimraf node_modules && npm run bootstrap",
33 "prepare": "husky install", 33 "prepare": "husky install",
34 "gen:icon": "esno ./build/generate/icon/index.ts", 34 "gen:icon": "esno ./build/generate/icon/index.ts",
35 - "gen:iconfont": "esno ./build/generate/iconfont/index.ts" 35 + "gen:iconfont": "esno ./build/generate/iconfont/index.ts",
  36 + "gen:rule": "esno ./build/generate/ruleChain/index.ts && npx eslint \"src/views/rule/designer/**/*.{vue,ts,tsx}\" --fix"
36 }, 37 },
37 "dependencies": { 38 "dependencies": {
38 "@fingerprintjs/fingerprintjs": "^3.4.1", 39 "@fingerprintjs/fingerprintjs": "^3.4.1",
39 "@iconify/iconify": "^2.0.3", 40 "@iconify/iconify": "^2.0.3",
40 "@logicflow/core": "^0.6.9", 41 "@logicflow/core": "^0.6.9",
41 "@logicflow/extension": "^0.6.9", 42 "@logicflow/extension": "^0.6.9",
  43 + "@vue-flow/background": "^1.2.0",
  44 + "@vue-flow/controls": "^1.1.0",
  45 + "@vue-flow/core": "^1.22.1",
  46 + "@vue-flow/node-toolbar": "^1.1.0",
42 "@vueuse/core": "^10.1.0", 47 "@vueuse/core": "^10.1.0",
43 "@zxcvbn-ts/core": "^1.0.0-beta.0", 48 "@zxcvbn-ts/core": "^1.0.0-beta.0",
44 "ace-builds": "^1.4.14", 49 "ace-builds": "^1.4.14",
@@ -53,6 +58,7 @@ @@ -53,6 +58,7 @@
53 "flv.js": "^1.6.2", 58 "flv.js": "^1.6.2",
54 "hls.js": "^1.0.10", 59 "hls.js": "^1.0.10",
55 "intro.js": "^4.1.0", 60 "intro.js": "^4.1.0",
  61 + "js-beautify": "^1.14.9",
56 "jsoneditor": "^9.7.2", 62 "jsoneditor": "^9.7.2",
57 "jwt-decode": "^3.1.2", 63 "jwt-decode": "^3.1.2",
58 "lodash-es": "^4.17.21", 64 "lodash-es": "^4.17.21",
@@ -68,7 +74,7 @@ @@ -68,7 +74,7 @@
68 "vditor": "^3.8.6", 74 "vditor": "^3.8.6",
69 "video.js": "^7.20.3", 75 "video.js": "^7.20.3",
70 "videojs-flvjs-es6": "^1.0.1", 76 "videojs-flvjs-es6": "^1.0.1",
71 - "vue": "3.2.31", 77 + "vue": "3.3.4",
72 "vue-i18n": "9.1.7", 78 "vue-i18n": "9.1.7",
73 "vue-json-pretty": "^2.0.4", 79 "vue-json-pretty": "^2.0.4",
74 "vue-router": "^4.0.11", 80 "vue-router": "^4.0.11",
  1 +import { DeviceInfoItemType, DeviceTypeItem, PageParams } from './model';
  2 +import { TBPaginationResult } from '/#/axios';
  3 +import { defHttp } from '/@/utils/http/axios';
  4 +
  5 +enum Api {
  6 + GET_DEVICE_INFOS = '/tenant/deviceInfos',
  7 + GET_DEVICE_TYPE = '/device/types',
  8 + TENANT_QUEUE = '/tenant/queues',
  9 +}
  10 +
  11 +enum Entity {
  12 + DEVICES = '/tenant/devices',
  13 + ASSETS = '/tenant/assets',
  14 + ENTITY_VIEW = '/tenant/entityViews',
  15 + TENANT = '/tenant',
  16 + CUSTOMER = '/customers',
  17 + DASHBOARD = '/tenant/dashboards',
  18 + USER = '/users',
  19 + EDGE = '/tenant/edges',
  20 +}
  21 +
  22 +export const getDeviceInfos = () => {
  23 + return defHttp.get<TBPaginationResult<DeviceInfoItemType>>(
  24 + {
  25 + url: Api.GET_DEVICE_INFOS,
  26 + },
  27 + { joinPrefix: false }
  28 + );
  29 +};
  30 +
  31 +export const getDeviceTypes = () => {
  32 + return defHttp.get<TBPaginationResult<DeviceTypeItem[]>>(
  33 + {
  34 + url: Api.GET_DEVICE_TYPE,
  35 + },
  36 + { joinPrefix: false }
  37 + );
  38 +};
  39 +
  40 +export const getTenantQueue = (params: Recordable) => {
  41 + return defHttp.get<string[]>(
  42 + {
  43 + url: Api.TENANT_QUEUE,
  44 + params,
  45 + },
  46 + { joinPrefix: false }
  47 + );
  48 +};
  49 +
  50 +export const getEntityDevice = (params: PageParams) => {
  51 + return defHttp.get(
  52 + {
  53 + url: Entity.DEVICES,
  54 + params,
  55 + },
  56 + {
  57 + joinPrefix: false,
  58 + }
  59 + );
  60 +};
  61 +
  62 +export const getEntityAssets = (params: PageParams) => {
  63 + return defHttp.get(
  64 + {
  65 + url: Entity.ASSETS,
  66 + params,
  67 + },
  68 + {
  69 + joinPrefix: false,
  70 + }
  71 + );
  72 +};
  73 +
  74 +export const getEntityViews = (params: PageParams) => {
  75 + return defHttp.get(
  76 + {
  77 + url: Entity.ENTITY_VIEW,
  78 + params,
  79 + },
  80 + {
  81 + joinPrefix: false,
  82 + }
  83 + );
  84 +};
  85 +
  86 +export const getEntityTenant = (params: Record<'tenantId', string>) => {
  87 + return defHttp.get(
  88 + {
  89 + url: `${Entity.TENANT}/${params.tenantId}`,
  90 + params,
  91 + },
  92 + {
  93 + joinPrefix: false,
  94 + }
  95 + );
  96 +};
  97 +
  98 +export const getEntityCustomer = (params: PageParams) => {
  99 + return defHttp.get(
  100 + {
  101 + url: Entity.CUSTOMER,
  102 + params,
  103 + },
  104 + {
  105 + joinPrefix: false,
  106 + }
  107 + );
  108 +};
  109 +
  110 +export const getEntityUser = (params: PageParams) => {
  111 + return defHttp.get(
  112 + {
  113 + url: Entity.USER,
  114 + params,
  115 + },
  116 + {
  117 + joinPrefix: false,
  118 + }
  119 + );
  120 +};
  121 +
  122 +export const getEntityDashboard = (params: PageParams) => {
  123 + return defHttp.get(
  124 + {
  125 + url: Entity.DASHBOARD,
  126 + params,
  127 + },
  128 + {
  129 + joinPrefix: false,
  130 + }
  131 + );
  132 +};
  133 +
  134 +export const getEntityEdge = (params: PageParams) => {
  135 + return defHttp.get(
  136 + {
  137 + url: Entity.EDGE,
  138 + params,
  139 + },
  140 + {
  141 + joinPrefix: false,
  142 + }
  143 + );
  144 +};
  1 +export interface DeviceInfoItemType {
  2 + id: Id;
  3 + createdTime: number;
  4 + additionalInfo: AdditionalInfo;
  5 + tenantId: Id;
  6 + customerId: Id;
  7 + name: string;
  8 + type: string;
  9 + label: string;
  10 + deviceProfileId: Id;
  11 + deviceData: DeviceData;
  12 + firmwareId: any;
  13 + softwareId: any;
  14 + customerTitle: any;
  15 + customerIsPublic: boolean;
  16 + deviceProfileName: string;
  17 +}
  18 +
  19 +export interface Id {
  20 + entityType: string;
  21 + id: string;
  22 +}
  23 +
  24 +export interface DeviceData {
  25 + configuration: Configuration;
  26 + transportConfiguration: TransportConfiguration;
  27 +}
  28 +
  29 +export interface AdditionalInfo {
  30 + gateway: boolean;
  31 + description: string;
  32 + overwriteActivityTime: boolean;
  33 +}
  34 +export interface Configuration {
  35 + type: string;
  36 +}
  37 +
  38 +export interface TransportConfiguration {
  39 + type: string;
  40 +}
  41 +
  42 +export interface DeviceTypeItem {
  43 + tenantId: Id;
  44 + entityType: string;
  45 + type: string;
  46 +}
  47 +
  48 +export interface PageParams {
  49 + pageSize?: number;
  50 + page?: number;
  51 + textSearch?: string;
  52 + sortProperty?: string;
  53 + sortOrder?: string;
  54 +}
  1 +import { RuleChainPaginationItemType } from './model/type';
  2 +import { TBPaginationResult } from '/#/axios';
  3 +import { defHttp } from '/@/utils/http/axios';
  4 +import { RuleChainType } from '/@/views/rule/designer/types/ruleNode';
  5 +
  6 +enum Api {
  7 + SAVE = '/ruleChain/metadata',
  8 + GET_RULE_CHAINES = '/ruleChains',
  9 + GET_RULE_NODE_EVENTS = '/events/RULE_NODE',
  10 +}
  11 +
  12 +export const getRuleChainData = (id: string) => {
  13 + return defHttp.get<RuleChainType>(
  14 + {
  15 + url: `/ruleChain/${id}/metadata`,
  16 + },
  17 + { joinPrefix: false }
  18 + );
  19 +};
  20 +
  21 +export const saveRuleChainData = (data: RuleChainType) => {
  22 + return defHttp.post(
  23 + {
  24 + url: Api.SAVE,
  25 + data,
  26 + },
  27 + { joinPrefix: false }
  28 + );
  29 +};
  30 +
  31 +export const getRuleChains = (params: Recordable) => {
  32 + return defHttp.get<TBPaginationResult<RuleChainPaginationItemType>>(
  33 + {
  34 + url: Api.GET_RULE_CHAINES,
  35 + params,
  36 + },
  37 + {
  38 + joinPrefix: false,
  39 + }
  40 + );
  41 +};
  42 +
  43 +export const getRuleNodeEventList = (
  44 + ruleNodeId: string,
  45 + params: Recordable,
  46 + data: Recordable & Record<'eventType', string>
  47 +) => {
  48 + return defHttp.post<TBPaginationResult>(
  49 + {
  50 + url: `${Api.GET_RULE_NODE_EVENTS}/${ruleNodeId}`,
  51 + params,
  52 + data,
  53 + },
  54 + { joinPrefix: false }
  55 + );
  56 +};
  1 +export interface RuleChainPaginationItemType {
  2 + id: Id;
  3 + createdTime: number;
  4 + additionalInfo?: AdditionalInfo;
  5 + tenantId: Id;
  6 + name: string;
  7 + type: string;
  8 + firstRuleNodeId: Id;
  9 + root: boolean;
  10 + debugMode: boolean;
  11 + configuration: any;
  12 +}
  13 +
  14 +export interface Id {
  15 + entityType: string;
  16 + id: string;
  17 +}
  18 +
  19 +export interface AdditionalInfo {
  20 + description: string;
  21 +}
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 - import { ref, watch } from 'vue';  
3 - import JSONEditor, { JSONEditorOptions } from 'jsoneditor';  
4 - import 'jsoneditor/dist/jsoneditor.min.css';  
5 - import { unref } from 'vue';  
6 - import { onMounted } from 'vue';  
7 - import { computed } from '@vue/reactivity';  
8 - import { onUnmounted } from 'vue'; 2 + import { onMounted, computed, onUnmounted, unref, ref, watch } from 'vue';
  3 + import { Tooltip } from 'ant-design-vue';
  4 + import { Icon } from '/@/components/Icon';
  5 + import { useFullscreen } from '@vueuse/core';
  6 + import { isNumber, isObject, isString } from '/@/utils/is';
  7 + import AceEditor, { Ace } from 'ace-builds';
  8 + import workerJsonUrl from 'ace-builds/src-noconflict/worker-json?url';
  9 + import githubTheme from 'ace-builds/src-noconflict/theme-github?url';
  10 + import 'ace-builds/src-noconflict/mode-json';
9 11
10 enum EventEnum { 12 enum EventEnum {
11 UPDATE_VALUE = 'update:value', 13 UPDATE_VALUE = 'update:value',
@@ -16,89 +18,134 @@ @@ -16,89 +18,134 @@
16 18
17 const props = withDefaults( 19 const props = withDefaults(
18 defineProps<{ 20 defineProps<{
19 - value?: string;  
20 - options?: JSONEditorOptions;  
21 - height?: number; 21 + value?: string | Recordable;
  22 + height?: number | string;
  23 + title?: string;
  24 + disabled?: boolean;
22 }>(), 25 }>(),
23 { 26 {
24 - options: () =>  
25 - ({  
26 - mode: 'code',  
27 - mainMenuBar: false,  
28 - statusBar: false,  
29 - } as JSONEditorOptions),  
30 height: 150, 27 height: 150,
31 } 28 }
32 ); 29 );
33 30
34 const emit = defineEmits<{ 31 const emit = defineEmits<{
35 - (e: EventEnum.UPDATE_VALUE, value: any, instance?: JSONEditor): void;  
36 - (e: EventEnum.CHANGE, value: any, instance?: JSONEditor): void;  
37 - (e: EventEnum.BLUR, event: Event, instance?: JSONEditor): void;  
38 - (e: EventEnum.FOCUS, event: Event, instance?: JSONEditor): void; 32 + (e: EventEnum.UPDATE_VALUE, value: any, instance?: Ace.Editor): void;
  33 + (e: EventEnum.CHANGE, value: any, instance?: Ace.Editor): void;
  34 + (e: EventEnum.BLUR, event: Event, instance?: Ace.Editor): void;
  35 + (e: EventEnum.FOCUS, event: Event, instance?: Ace.Editor): void;
39 }>(); 36 }>();
40 37
41 const jsonEditorElRef = ref<Nullable<any>>(); 38 const jsonEditorElRef = ref<Nullable<any>>();
42 39
43 - const editoreRef = ref<JSONEditor>(); 40 + const editoreRef = ref<Ace.Editor>();
44 41
45 const isFocus = ref(false); 42 const isFocus = ref(false);
46 43
47 - const handleChange = (value: any) => { 44 + const handleOnChange = () => {
  45 + const value = get();
48 emit(EventEnum.UPDATE_VALUE, value, unref(editoreRef)); 46 emit(EventEnum.UPDATE_VALUE, value, unref(editoreRef));
49 emit(EventEnum.CHANGE, value, unref(editoreRef)); 47 emit(EventEnum.CHANGE, value, unref(editoreRef));
50 }; 48 };
51 49
52 - const handleEmit = (event: Event, key: EventEnum) => {  
53 - emit(key as EventEnum[keyof EventEnum], event, unref(editoreRef)); 50 + const handleOnBlur = (event: Event) => {
  51 + isFocus.value = false;
  52 + emit(EventEnum.BLUR, event, unref(editoreRef));
54 }; 53 };
55 54
56 - const getOptions = computed(() => {  
57 - const { options } = props;  
58 - return {  
59 - ...options,  
60 - onChangeText: handleChange,  
61 - onBlur: (event: Event) => {  
62 - isFocus.value = false;  
63 - handleEmit(event, EventEnum.BLUR);  
64 - },  
65 - onFocus: (event: Event) => {  
66 - isFocus.value = true;  
67 - handleEmit(event, EventEnum.FOCUS);  
68 - },  
69 - } as JSONEditorOptions;  
70 - }); 55 + const handleOnFocus = (event: Event) => {
  56 + isFocus.value = true;
  57 + emit(EventEnum.FOCUS, event, unref(editoreRef));
  58 + };
  59 +
  60 + const getFormatValue = (value: Recordable | string = '') => {
  61 + return isObject(value) ? JSON.stringify(value, null, 2) : value;
  62 + };
71 63
72 const initialize = () => { 64 const initialize = () => {
73 - editoreRef.value = new JSONEditor(unref(jsonEditorElRef), unref(getOptions)); 65 + AceEditor.config.setModuleUrl('ace/mode/json_worker', workerJsonUrl);
  66 + AceEditor.config.setModuleUrl('ace/theme/github', githubTheme);
  67 + const editor = AceEditor.edit(unref(jsonEditorElRef)!, {
  68 + mode: 'ace/mode/json',
  69 + });
  70 + editor.setTheme('ace/theme/github');
  71 + editor.setOptions({
  72 + fontSize: 14,
  73 + });
  74 +
  75 + editor.on('change', handleOnChange);
  76 + editor.on('blur', handleOnBlur);
  77 + editor.on('focus', handleOnFocus);
  78 +
  79 + editoreRef.value = editor;
  80 + unref(editoreRef)?.setValue(getFormatValue(props.value), 1);
  81 + unref(editoreRef)?.setReadOnly(props.disabled);
74 }; 82 };
75 83
76 watch( 84 watch(
77 () => props.value, 85 () => props.value,
78 (target) => { 86 (target) => {
  87 + // const position = unref(editoreRef)?.getCursorPosition();
79 if (unref(isFocus)) return; 88 if (unref(isFocus)) return;
80 - unref(editoreRef)?.setText(target || ''); 89 + unref(editoreRef)?.setValue(getFormatValue(target));
  90 + unref(editoreRef)?.clearSelection();
  91 + // position && unref(editoreRef)?.moveCursorToPosition(position!);
81 }, 92 },
82 { 93 {
83 immediate: true, 94 immediate: true,
84 } 95 }
85 ); 96 );
86 97
  98 + watch(
  99 + () => props.disabled,
  100 + (value) => {
  101 + unref(editoreRef)?.setReadOnly(value);
  102 + }
  103 + );
  104 +
87 const get = (): string => { 105 const get = (): string => {
88 - return unref(editoreRef)?.getText() || ''; 106 + return unref(editoreRef)?.getValue() || '';
89 }; 107 };
90 108
91 const set = (data: any) => { 109 const set = (data: any) => {
92 - return unref(editoreRef)?.set(data); 110 + return unref(editoreRef)?.setValue(getFormatValue(data));
93 }; 111 };
94 112
95 onMounted(() => { 113 onMounted(() => {
96 initialize(); 114 initialize();
97 - unref(editoreRef)?.setText(props.value || ''); 115 + unref(editoreRef)?.setValue(getFormatValue(props.value));
98 }); 116 });
99 117
100 onUnmounted(() => { 118 onUnmounted(() => {
  119 + unref(editoreRef)?.off('change', handleOnChange);
  120 + unref(editoreRef)?.off('blur', handleOnBlur);
  121 + unref(editoreRef)?.off('focus', handleOnFocus);
101 unref(editoreRef)?.destroy(); 122 unref(editoreRef)?.destroy();
  123 + unref(editoreRef)?.container.remove();
  124 + });
  125 +
  126 + const handleFormat = () => {
  127 + const value = get();
  128 + if (isString(value) && !value) return;
  129 + unref(editoreRef)?.setValue(JSON.stringify(JSON.parse(value), null, 2));
  130 + unref(editoreRef)?.clearSelection();
  131 + };
  132 +
  133 + const handleCompress = () => {
  134 + const value = get();
  135 + if (isString(value) && !value) return;
  136 + unref(editoreRef)?.setValue(JSON.stringify(JSON.parse(value)));
  137 + unref(editoreRef)?.clearSelection();
  138 + };
  139 +
  140 + const jsonEditorContainerElRef = ref<Nullable<HTMLDivElement>>();
  141 + const { isFullscreen, isSupported, toggle } = useFullscreen(jsonEditorContainerElRef);
  142 + const handleFullScreen = () => {
  143 + toggle();
  144 + };
  145 +
  146 + const getHeight = computed(() => {
  147 + const { height } = props;
  148 + return isNumber(height) ? `${height}px` : height;
102 }); 149 });
103 150
104 defineExpose({ 151 defineExpose({
@@ -108,22 +155,41 @@ @@ -108,22 +155,41 @@
108 </script> 155 </script>
109 156
110 <template> 157 <template>
111 - <div class="p-2 bg-gray-200" :style="{ height: `${height}px` }">  
112 - <div ref="jsonEditorElRef" class="jsoneditor"></div> 158 + <div
  159 + ref="jsonEditorContainerElRef"
  160 + class="p-2 bg-gray-200 flex flex-col"
  161 + :style="{ height: getHeight }"
  162 + >
  163 + <div class="w-full h-8 flex justify-between items-center">
  164 + <div>
  165 + {{ title }}
  166 + </div>
  167 + <slot name="header"></slot>
  168 + <div class="flex h-8 gap-3 justify-end items-center text-dark-500 svg:text-2xl">
  169 + <slot name="beforeFormat"></slot>
  170 + <Tooltip title="整洁">
  171 + <Icon @click="handleFormat" class="cursor-pointer" icon="gg:format-left" />
  172 + </Tooltip>
  173 + <slot name="beforeCompress"></slot>
  174 +
  175 + <Tooltip title="迷你">
  176 + <Icon @click="handleCompress" class="cursor-pointer" icon="material-symbols:compress" />
  177 + </Tooltip>
  178 + <slot name="beforeFullScreen"></slot>
  179 +
  180 + <Tooltip title="全屏">
  181 + <Icon
  182 + v-if="isSupported"
  183 + class="cursor-pointer"
  184 + :icon="
  185 + isFullscreen ? 'material-symbols:fullscreen-exit' : 'material-symbols:fullscreen'
  186 + "
  187 + @click="handleFullScreen"
  188 + />
  189 + </Tooltip>
  190 + <slot name="afterFullScreen"></slot>
  191 + </div>
  192 + </div>
  193 + <div ref="jsonEditorElRef" class="flex-auto"></div>
113 </div> 194 </div>
114 </template> 195 </template>
115 -  
116 -<style lang="less" scoped>  
117 - .jsoneditor {  
118 - border: none !important;  
119 -  
120 - :deep(.jsoneditor) {  
121 - border: none !important;  
122 -  
123 - .ace-jsoneditor,  
124 - textarea.jsoneditor-text {  
125 - min-height: auto;  
126 - }  
127 - }  
128 - }  
129 -</style>  
@@ -13,6 +13,7 @@ export { default as ApiRadioGroup } from './src/components/ApiRadioGroup.vue'; @@ -13,6 +13,7 @@ export { default as ApiRadioGroup } from './src/components/ApiRadioGroup.vue';
13 export { default as ApiUpload } from './src/components/ApiUpload.vue'; 13 export { default as ApiUpload } from './src/components/ApiUpload.vue';
14 14
15 export { default as StructForm } from './src/externalCompns/components/StructForm/StructForm.vue'; 15 export { default as StructForm } from './src/externalCompns/components/StructForm/StructForm.vue';
  16 +export { default as JavaScriptFunctionEditor } from './src/components/JavaScriptFunctionEditor.vue';
16 17
17 //注册自定义组件 18 //注册自定义组件
18 export { 19 export {
@@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
16 :formProps="getProps" 16 :formProps="getProps"
17 :allDefaultValues="defaultValueRef" 17 :allDefaultValues="defaultValueRef"
18 :formModel="formModel" 18 :formModel="formModel"
  19 + :validateFields="validateFields"
19 :setFormModel="setFormModel" 20 :setFormModel="setFormModel"
20 > 21 >
21 <template #[item]="data" v-for="item in Object.keys($slots)"> 22 <template #[item]="data" v-for="item in Object.keys($slots)">
@@ -229,10 +230,10 @@ @@ -229,10 +230,10 @@
229 230
230 function setFormModel(key: string, value: any) { 231 function setFormModel(key: string, value: any) {
231 formModel[key] = value; 232 formModel[key] = value;
232 - const { validateTrigger } = unref(getBindValue);  
233 - if (!validateTrigger || validateTrigger === 'change') {  
234 - validateFields([key]).catch((_) => {});  
235 - } 233 + // const { validateTrigger } = unref(getBindValue);
  234 + // if (!validateTrigger || validateTrigger === 'change') {
  235 + // validateFields([key]).catch((_) => {});
  236 + // }
236 } 237 }
237 238
238 function handleEnterPress(e: KeyboardEvent) { 239 function handleEnterPress(e: KeyboardEvent) {
@@ -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(
@@ -27,6 +28,7 @@ @@ -27,6 +28,7 @@
27 labelField?: string; 28 labelField?: string;
28 valueField?: string; 29 valueField?: string;
29 immediate?: boolean; 30 immediate?: boolean;
  31 + searchField?: string;
30 queryEmptyDataAgin?: boolean; 32 queryEmptyDataAgin?: boolean;
31 onChangeHook?: ({ options }: OnChangeHookParams) => void; 33 onChangeHook?: ({ options }: OnChangeHookParams) => void;
32 dropdownVisibleChangeHook?: ({ options }: OnChangeHookParams) => void; 34 dropdownVisibleChangeHook?: ({ options }: OnChangeHookParams) => void;
@@ -35,6 +37,7 @@ @@ -35,6 +37,7 @@
35 resultField: '', 37 resultField: '',
36 labelField: 'label', 38 labelField: 'label',
37 valueField: 'value', 39 valueField: 'value',
  40 + searchField: 'text',
38 immediate: true, 41 immediate: true,
39 queryEmptyDataAgin: true, 42 queryEmptyDataAgin: true,
40 } 43 }
@@ -53,17 +56,27 @@ @@ -53,17 +56,27 @@
53 const { labelField, valueField = 'value', numberToString } = props; 56 const { labelField, valueField = 'value', numberToString } = props;
54 return unref(options).reduce((prev, next: Recordable) => { 57 return unref(options).reduce((prev, next: Recordable) => {
55 if (next) { 58 if (next) {
56 - const value = next[valueField]; 59 + const value = get(next, valueField);
  60 + const label = get(next, labelField);
57 prev.push({ 61 prev.push({
58 - label: next[labelField],  
59 - value: numberToString ? `${value}` : value,  
60 ...omit(next, [labelField, valueField]), 62 ...omit(next, [labelField, valueField]),
  63 + label,
  64 + value: numberToString ? `${value}` : value,
61 }); 65 });
62 } 66 }
63 return prev; 67 return prev;
64 }, [] as OptionsItem[]); 68 }, [] as OptionsItem[]);
65 }); 69 });
66 70
  71 + const getBindProps = computed(() => {
  72 + const { searchApi } = props;
  73 + return {
  74 + ...attrs,
  75 + showSearch: true,
  76 + filterOption: !searchApi,
  77 + };
  78 + });
  79 +
67 watchEffect(() => { 80 watchEffect(() => {
68 props.immediate && fetch(); 81 props.immediate && fetch();
69 }); 82 });
@@ -122,8 +135,9 @@ @@ -122,8 +135,9 @@
122 onChangeHook({ options }); 135 onChangeHook({ options });
123 } 136 }
124 137
  138 + const debounceSearchFunction = useDebounceFn(handleSearch, 300);
125 async function handleSearch(params?: string) { 139 async function handleSearch(params?: string) {
126 - let { searchApi, api } = props; 140 + let { searchApi, api, searchField } = props;
127 if (!searchApi || !isFunction(searchApi)) { 141 if (!searchApi || !isFunction(searchApi)) {
128 if (!api || !isFunction(api)) return; 142 if (!api || !isFunction(api)) return;
129 searchApi = api; 143 searchApi = api;
@@ -131,7 +145,7 @@ @@ -131,7 +145,7 @@
131 options.value = []; 145 options.value = [];
132 try { 146 try {
133 loading.value = true; 147 loading.value = true;
134 - const res = await searchApi({ ...props.params, text: params }); 148 + const res = await searchApi({ ...props.params, [searchField]: params });
135 if (Array.isArray(res)) { 149 if (Array.isArray(res)) {
136 options.value = res; 150 options.value = res;
137 emitChange(); 151 emitChange();
@@ -152,11 +166,10 @@ @@ -152,11 +166,10 @@
152 <template> 166 <template>
153 <Select 167 <Select
154 @dropdownVisibleChange="handleFetch" 168 @dropdownVisibleChange="handleFetch"
155 - v-bind="attrs"  
156 - show-search 169 + v-bind="getBindProps"
157 @change="handleChange" 170 @change="handleChange"
158 :options="getOptions" 171 :options="getOptions"
159 - @search="handleSearch" 172 + @search="debounceSearchFunction"
160 v-model:value="state" 173 v-model:value="state"
161 > 174 >
162 <template #[item]="data" v-for="item in Object.keys($slots)"> 175 <template #[item]="data" v-for="item in Object.keys($slots)">
@@ -6,7 +6,7 @@ @@ -6,7 +6,7 @@
6 <script lang="ts" setup> 6 <script lang="ts" setup>
7 import { ref, watchEffect, computed, unref, watch, reactive } from 'vue'; 7 import { ref, watchEffect, computed, unref, watch, reactive } from 'vue';
8 import { Select, Spin } from 'ant-design-vue'; 8 import { Select, Spin } from 'ant-design-vue';
9 - import { isFunction } from '/@/utils/is'; 9 + import { isFunction, isNullAndUnDef } from '/@/utils/is';
10 import { useRuleFormItem } from '/@/hooks/component/useFormItem'; 10 import { useRuleFormItem } from '/@/hooks/component/useFormItem';
11 import { useAttrs } from '/@/hooks/core/useAttrs'; 11 import { useAttrs } from '/@/hooks/core/useAttrs';
12 import { get, omit } from 'lodash-es'; 12 import { get, omit } from 'lodash-es';
@@ -30,8 +30,11 @@ @@ -30,8 +30,11 @@
30 labelField?: string; 30 labelField?: string;
31 valueField?: string; 31 valueField?: string;
32 immediate?: boolean; 32 immediate?: boolean;
33 - pagenation?: Pagination; 33 + searchField?: string;
  34 + pagination?: Pagination;
34 queryEmptyDataAgin?: boolean; 35 queryEmptyDataAgin?: boolean;
  36 + fetchSearch?: boolean;
  37 + filterOption?: (inputValue: string, options: Recordable) => boolean;
35 }>(), 38 }>(),
36 { 39 {
37 resultField: '', 40 resultField: '',
@@ -39,14 +42,15 @@ @@ -39,14 +42,15 @@
39 valueField: 'value', 42 valueField: 'value',
40 immediate: true, 43 immediate: true,
41 queryEmptyDataAgin: true, 44 queryEmptyDataAgin: true,
42 - pagenation: () => ({ page: 1, pageSize: 10 }), 45 + pagination: () => ({ page: 1, pageSize: 10 }),
  46 + fetchSearch: false,
43 } 47 }
44 ); 48 );
45 49
46 const OptionsItem = (_, { attrs }: { attrs: { vNode: any } }) => attrs.vNode; 50 const OptionsItem = (_, { attrs }: { attrs: { vNode: any } }) => attrs.vNode;
47 51
48 const options = ref<OptionsItem[]>([]); 52 const options = ref<OptionsItem[]>([]);
49 - const pagination = reactive(Object.assign({ total: 0, page: 1, pageSize: 10 }, props.pagenation)); 53 + const pagination = reactive<Record<'total' | 'page' | 'pageSize', number>>({} as any);
50 const scrollLoading = ref(false); 54 const scrollLoading = ref(false);
51 const lock = ref(false); 55 const lock = ref(false);
52 const loading = ref(false); 56 const loading = ref(false);
@@ -55,6 +59,13 @@ @@ -55,6 +59,13 @@
55 const attrs = useAttrs(); 59 const attrs = useAttrs();
56 const { t } = useI18n(); 60 const { t } = useI18n();
57 61
  62 + const getPagination = computed(() => {
  63 + return {
  64 + ...props.pagination,
  65 + ...unref(pagination),
  66 + };
  67 + });
  68 +
58 // Embedded in the form, just use the hook binding to perform form verification 69 // Embedded in the form, just use the hook binding to perform form verification
59 const [state] = useRuleFormItem(props, 'value', 'change', emitData); 70 const [state] = useRuleFormItem(props, 'value', 'change', emitData);
60 71
@@ -86,16 +97,18 @@ @@ -86,16 +97,18 @@
86 { deep: true } 97 { deep: true }
87 ); 98 );
88 99
89 - async function fetch() {  
90 - const api = props.api; 100 + async function fetch(searchText?: string) {
  101 + const { api, searchField, fetchSearch } = props;
91 if (!api || !isFunction(api)) return; 102 if (!api || !isFunction(api)) return;
  103 + const isFetchSearchFlag = fetchSearch && !isNullAndUnDef(searchText) && searchField;
92 try { 104 try {
93 !unref(getOptions).length ? (loading.value = true) : (scrollLoading.value = true); 105 !unref(getOptions).length ? (loading.value = true) : (scrollLoading.value = true);
94 lock.value = true; 106 lock.value = true;
95 const { total, items } = await api({ 107 const { total, items } = await api({
96 ...props.params, 108 ...props.params,
97 - page: pagination.page,  
98 - pageSize: pagination.pageSize, 109 + page: unref(getPagination).page,
  110 + pageSize: unref(getPagination).pageSize,
  111 + ...(isFetchSearchFlag ? { [searchField!]: searchText } : {}),
99 }); 112 });
100 113
101 pagination.total = total; 114 pagination.total = total;
@@ -105,11 +118,13 @@ @@ -105,11 +118,13 @@
105 return; 118 return;
106 } 119 }
107 if (props.resultField) { 120 if (props.resultField) {
108 - options.value = [...options.value, ...(get(items, props.resultField) || [])]; 121 + options.value = isFetchSearchFlag
  122 + ? get(items, props.resultField) || []
  123 + : [...options.value, ...(get(items, props.resultField) || [])];
109 } 124 }
110 emitChange(); 125 emitChange();
111 } catch (error) { 126 } catch (error) {
112 - pagination.page = Math.ceil(unref(getOptions).length / pagination.pageSize); 127 + pagination.page = Math.ceil(unref(getOptions).length / unref(getPagination).pageSize);
113 console.warn(error); 128 console.warn(error);
114 } finally { 129 } finally {
115 isFirstLoad.value = false; 130 isFirstLoad.value = false;
@@ -134,17 +149,39 @@ @@ -134,17 +149,39 @@
134 emitData.value = args; 149 emitData.value = args;
135 } 150 }
136 151
  152 + const sleep = async (number: number) => {
  153 + return new Promise((resolve) => {
  154 + setTimeout(() => {
  155 + resolve(number);
  156 + }, number);
  157 + });
  158 + };
  159 +
137 async function handlePopupScroll(event: MouseEvent) { 160 async function handlePopupScroll(event: MouseEvent) {
138 const { scrollHeight, scrollTop, clientHeight } = event.target as HTMLDivElement; 161 const { scrollHeight, scrollTop, clientHeight } = event.target as HTMLDivElement;
139 if (scrollTop + clientHeight >= scrollHeight) { 162 if (scrollTop + clientHeight >= scrollHeight) {
140 - if (unref(getOptions).length < pagination.total && !unref(lock)) {  
141 - pagination.page = pagination.page + 1; 163 + if (unref(getOptions).length < unref(getPagination).total && !unref(lock)) {
  164 + pagination.page = unref(getPagination).page + 1;
  165 + scrollLoading.value = true;
  166 + await sleep(500);
142 await fetch(); 167 await fetch();
143 } 168 }
144 } 169 }
145 } 170 }
146 171
147 const debounceHandlePopupScroll = useDebounceFn(handlePopupScroll, 100); 172 const debounceHandlePopupScroll = useDebounceFn(handlePopupScroll, 100);
  173 +
  174 + const handleFilterOption = async (inputValue: string, option: Recordable) => {
  175 + const { filterOption, fetchSearch } = props;
  176 + if (filterOption && isFunction(filterOption)) {
  177 + filterOption?.(inputValue, option);
  178 + return;
  179 + }
  180 +
  181 + if (fetchSearch) {
  182 + await fetch(inputValue);
  183 + }
  184 + };
148 </script> 185 </script>
149 186
150 <template> 187 <template>
@@ -153,6 +190,8 @@ @@ -153,6 +190,8 @@
153 v-bind="attrs" 190 v-bind="attrs"
154 @change="handleChange" 191 @change="handleChange"
155 :options="getOptions" 192 :options="getOptions"
  193 + :filterOption="handleFilterOption"
  194 + :showSearch="true"
156 v-model:value="state" 195 v-model:value="state"
157 @popup-scroll="debounceHandlePopupScroll" 196 @popup-scroll="debounceHandlePopupScroll"
158 > 197 >
@@ -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" />
@@ -14,6 +14,7 @@ @@ -14,6 +14,7 @@
14 import { upperFirst, cloneDeep } from 'lodash-es'; 14 import { upperFirst, cloneDeep } from 'lodash-es';
15 import { useItemLabelWidth } from '../hooks/useLabelWidth'; 15 import { useItemLabelWidth } from '../hooks/useLabelWidth';
16 import { useI18n } from '/@/hooks/web/useI18n'; 16 import { useI18n } from '/@/hooks/web/useI18n';
  17 + import { NamePath, ValidateOptions } from 'ant-design-vue/lib/form/interface';
17 18
18 export default defineComponent({ 19 export default defineComponent({
19 name: 'BasicFormItem', 20 name: 'BasicFormItem',
@@ -39,6 +40,12 @@ @@ -39,6 +40,12 @@
39 type: Function as PropType<(key: string, value: any) => void>, 40 type: Function as PropType<(key: string, value: any) => void>,
40 default: null, 41 default: null,
41 }, 42 },
  43 + validateFields: {
  44 + type: Function as PropType<
  45 + (nameList?: NamePath[], options?: ValidateOptions) => Promise<any>
  46 + >,
  47 + default: null,
  48 + },
42 tableAction: { 49 tableAction: {
43 type: Object as PropType<TableActionType>, 50 type: Object as PropType<TableActionType>,
44 }, 51 },
@@ -208,6 +215,7 @@ @@ -208,6 +215,7 @@
208 rules[characterInx].message || 215 rules[characterInx].message ||
209 t('component.form.maxTip', [rules[characterInx].max] as Recordable); 216 t('component.form.maxTip', [rules[characterInx].max] as Recordable);
210 } 217 }
  218 + rules.forEach((item) => !item.trigger && (item.trigger = 'change'));
211 return rules; 219 return rules;
212 } 220 }
213 221
@@ -234,6 +242,10 @@ @@ -234,6 +242,10 @@
234 const value = target ? (isCheck ? target.checked : target.value) : e; 242 const value = target ? (isCheck ? target.checked : target.value) : e;
235 props.setFormModel(field, value); 243 props.setFormModel(field, value);
236 }, 244 },
  245 + onBlur: (...args) => {
  246 + unref(getComponentsProps)?.onBlur?.(...args);
  247 + props.validateFields([field], { triggerName: 'blur' }).catch((_) => {});
  248 + },
237 }; 249 };
238 const Comp = componentMap.get(component) as ReturnType<typeof defineComponent>; 250 const Comp = componentMap.get(component) as ReturnType<typeof defineComponent>;
239 251
  1 +<script lang="ts" setup>
  2 + import { Icon } from '/@/components/Icon';
  3 + import { Tooltip } from 'ant-design-vue';
  4 + import { computed, onMounted, onUnmounted, ref, shallowRef, unref, watch } from 'vue';
  5 + import AceEditor, { Ace } from 'ace-builds';
  6 + import workerJavascriptUrl from 'ace-builds/src-noconflict/worker-javascript?url';
  7 + import githubTheme from 'ace-builds/src-noconflict/theme-github?url';
  8 + import 'ace-builds/src-noconflict/mode-javascript';
  9 + import 'ace-builds/src-noconflict/ext-searchbox';
  10 + import 'ace-builds/src-noconflict/ext-language_tools';
  11 + import 'ace-builds/src-noconflict/snippets/javascript';
  12 + import { useBeautify } from '/@/hooks/business/useBeautify';
  13 + import { useFullscreen } from '@vueuse/core';
  14 + import { isNumber } from '/@/utils/is';
  15 +
  16 + const emit = defineEmits(['update:value', 'focus', 'blur']);
  17 + const props = withDefaults(
  18 + defineProps<{
  19 + functionName?: string;
  20 + paramsName?: string[];
  21 + height?: number | string;
  22 + value?: string;
  23 + disabled?: boolean;
  24 + validateStatus?: boolean;
  25 + }>(),
  26 + {
  27 + functionName: 'method',
  28 + paramsName: () => [],
  29 + height: 200,
  30 + value: '',
  31 + }
  32 + );
  33 +
  34 + const getHeight = computed(() => {
  35 + const { height } = props;
  36 + return isNumber(height) ? `${height}px` : height;
  37 + });
  38 +
  39 + const javaScriptEditorElRef = ref<Nullable<HTMLDivElement>>();
  40 + const editorInstance = shallowRef<Ace.Editor>();
  41 + const isFocus = ref(false);
  42 +
  43 + const handleFocus = () => {
  44 + isFocus.value = true;
  45 + emit('focus', unref(editorInstance));
  46 + };
  47 +
  48 + const handleBlur = () => {
  49 + isFocus.value = false;
  50 + emit('update:value', get());
  51 + emit('blur', unref(editorInstance));
  52 + };
  53 +
  54 + const handleChange = () => {
  55 + emit('update:value', get());
  56 + };
  57 +
  58 + const initEditor = () => {
  59 + AceEditor.config.setModuleUrl('ace/mode/javascript_worker', workerJavascriptUrl);
  60 + AceEditor.config.setModuleUrl('ace/theme/github', githubTheme);
  61 + const editor = AceEditor.edit(unref(javaScriptEditorElRef)!, {
  62 + mode: 'ace/mode/javascript',
  63 + });
  64 + editor.setTheme('ace/theme/github');
  65 + editor.setOptions({
  66 + fontSize: 14,
  67 + enableBasicAutocompletion: true,
  68 + enableSnippets: true,
  69 + enableLiveAutocompletion: true,
  70 + });
  71 +
  72 + editor.on('focus', handleFocus);
  73 + editor.on('blur', handleBlur);
  74 + editor.on('change', handleChange);
  75 + editorInstance.value = editor;
  76 + editor.setValue(props.value);
  77 + editor?.clearSelection();
  78 + unref(editorInstance)?.setReadOnly(props.disabled);
  79 + };
  80 +
  81 + const get = () => {
  82 + return unref(editorInstance)?.getValue();
  83 + };
  84 +
  85 + const set = (val: string, cursorPos?: number) => {
  86 + return unref(editorInstance)?.setValue(val, cursorPos);
  87 + };
  88 +
  89 + onMounted(() => {
  90 + initEditor();
  91 + });
  92 +
  93 + onUnmounted(() => {
  94 + unref(editorInstance)?.off('change', handleChange);
  95 + unref(editorInstance)?.off('focus', handleFocus);
  96 + unref(editorInstance)?.off('blur', handleBlur);
  97 + unref(editorInstance)?.destroy();
  98 + unref(editorInstance)?.container.remove();
  99 + });
  100 +
  101 + const { beautifyJs } = useBeautify();
  102 + const handleFormatCode = async () => {
  103 + const res = await beautifyJs(get() || '', { indent_size: 4, wrap_line_length: 60 });
  104 + set(res || '', -1);
  105 + };
  106 +
  107 + const jsFunctionContainerElRef = ref<Nullable<HTMLDivElement>>();
  108 + const { isFullscreen, isSupported, toggle } = useFullscreen(jsFunctionContainerElRef);
  109 + const handleFullScreen = () => {
  110 + toggle();
  111 + };
  112 +
  113 + watch(
  114 + () => props.value,
  115 + (value) => {
  116 + // const position = unref(editorInstance)?.getCursorPosition();
  117 + if (unref(isFocus)) return;
  118 + set(value);
  119 + unref(editorInstance)?.clearSelection();
  120 + // position && unref(editorInstance)?.moveCursorToPosition(position!);
  121 + },
  122 + {
  123 + immediate: true,
  124 + }
  125 + );
  126 +
  127 + watch(
  128 + () => props.disabled,
  129 + (value) => {
  130 + unref(editorInstance)?.setReadOnly(value);
  131 + }
  132 + );
  133 +
  134 + defineExpose({
  135 + get,
  136 + set,
  137 + });
  138 +</script>
  139 +
  140 +<template>
  141 + <section
  142 + ref="jsFunctionContainerElRef"
  143 + class="p-2 bg-gray-200 flex flex-col"
  144 + :style="{ height: getHeight }"
  145 + >
  146 + <head class="flex justify-between h-8 items-center">
  147 + <div class="font-bold">
  148 + <span>function</span>
  149 + <span class="ml-1">{{ functionName }}</span>
  150 + <span>({{ paramsName.join(',') }})</span>
  151 + <span class="ml-1">{</span>
  152 + </div>
  153 + <div class="flex gap-3 items-center svg:text-2xl">
  154 + <slot name="beforeFormat"></slot>
  155 +
  156 + <Tooltip title="整洁">
  157 + <Icon class="cursor-pointer" icon="gg:format-left" @click="handleFormatCode" />
  158 + </Tooltip>
  159 + <slot name="beforeFullScreen"></slot>
  160 + <Tooltip title="全屏">
  161 + <Icon
  162 + v-if="isSupported"
  163 + class="cursor-pointer"
  164 + :icon="
  165 + isFullscreen ? 'material-symbols:fullscreen-exit' : 'material-symbols:fullscreen'
  166 + "
  167 + @click="handleFullScreen"
  168 + />
  169 + </Tooltip>
  170 + <slot name="afterFullScreen"></slot>
  171 + </div>
  172 + </head>
  173 + <main ref="javaScriptEditorElRef" class="flex-auto"> </main>
  174 + <footer class="font-bold">}</footer>
  175 + </section>
  176 +</template>
1 import type { FormProps, FormActionType, UseFormReturnType, FormSchema } from '../types/form'; 1 import type { FormProps, FormActionType, UseFormReturnType, FormSchema } from '../types/form';
2 -import type { NamePath } from 'ant-design-vue/lib/form/interface'; 2 +import type { NamePath, ValidateOptions } from 'ant-design-vue/lib/form/interface';
3 import type { DynamicProps } from '/#/utils'; 3 import type { DynamicProps } from '/#/utils';
4 import { ref, onUnmounted, unref, nextTick, watch } from 'vue'; 4 import { ref, onUnmounted, unref, nextTick, watch } from 'vue';
5 import { isProdMode } from '/@/utils/env'; 5 import { isProdMode } from '/@/utils/env';
@@ -112,9 +112,12 @@ export function useForm(props?: Props): UseFormReturnType { @@ -112,9 +112,12 @@ export function useForm(props?: Props): UseFormReturnType {
112 return form.validate(nameList); 112 return form.validate(nameList);
113 }, 113 },
114 114
115 - validateFields: async (nameList?: NamePath[]): Promise<Recordable> => { 115 + validateFields: async (
  116 + nameList?: NamePath[],
  117 + options?: ValidateOptions
  118 + ): Promise<Recordable> => {
116 const form = await getForm(); 119 const form = await getForm();
117 - return form.validateFields(nameList); 120 + return form.validateFields(nameList, options);
118 }, 121 },
119 }; 122 };
120 123
1 import type { ComputedRef, Ref } from 'vue'; 1 import type { ComputedRef, Ref } from 'vue';
2 import type { FormProps, FormSchema, FormActionType } from '../types/form'; 2 import type { FormProps, FormSchema, FormActionType } from '../types/form';
3 -import type { NamePath } from 'ant-design-vue/lib/form/interface'; 3 +import type { NamePath, ValidateOptions } from 'ant-design-vue/lib/form/interface';
4 import { unref, toRaw } from 'vue'; 4 import { unref, toRaw } from 'vue';
5 import { isArray, isFunction, isObject, isString } from '/@/utils/is'; 5 import { isArray, isFunction, isObject, isString } from '/@/utils/is';
6 import { deepMerge } from '/@/utils'; 6 import { deepMerge } from '/@/utils';
@@ -206,8 +206,8 @@ export function useFormEvents({ @@ -206,8 +206,8 @@ export function useFormEvents({
206 }); 206 });
207 } 207 }
208 208
209 - async function validateFields(nameList?: NamePath[] | undefined) {  
210 - return unref(formElRef)?.validateFields(nameList); 209 + async function validateFields(nameList?: NamePath[] | undefined, options?: ValidateOptions) {
  210 + return unref(formElRef)?.validateFields(nameList, options);
211 } 211 }
212 212
213 async function validate(nameList?: NamePath[] | undefined) { 213 async function validate(nameList?: NamePath[] | undefined) {
1 -import type { NamePath, RuleObject } from 'ant-design-vue/lib/form/interface'; 1 +import type { NamePath, RuleObject, ValidateOptions } from 'ant-design-vue/lib/form/interface';
2 import type { VNode } from 'vue'; 2 import type { VNode } from 'vue';
3 import type { ButtonProps as AntdButtonProps } from '/@/components/Button'; 3 import type { ButtonProps as AntdButtonProps } from '/@/components/Button';
4 import type { FormItem } from './formItem'; 4 import type { FormItem } from './formItem';
@@ -39,7 +39,7 @@ export interface FormActionType { @@ -39,7 +39,7 @@ export interface FormActionType {
39 prefixField: string | undefined, 39 prefixField: string | undefined,
40 first?: boolean | undefined 40 first?: boolean | undefined
41 ) => Promise<void>; 41 ) => Promise<void>;
42 - validateFields: (nameList?: NamePath[]) => Promise<any>; 42 + validateFields: (nameList?: NamePath[], options?: ValidateOptions) => Promise<any>;
43 validate: (nameList?: NamePath[]) => Promise<any>; 43 validate: (nameList?: NamePath[]) => Promise<any>;
44 scrollToField: (name: NamePath, options?: ScrollOptions) => Promise<void>; 44 scrollToField: (name: NamePath, options?: ScrollOptions) => Promise<void>;
45 } 45 }
@@ -130,4 +130,11 @@ export type ComponentType = @@ -130,4 +130,11 @@ export type ComponentType =
130 | 'ControlGroup' 130 | 'ControlGroup'
131 | 'JSONEditor' 131 | 'JSONEditor'
132 | 'OrgTreeSelect' 132 | 'OrgTreeSelect'
133 - | 'ExtendDesc'; 133 + | 'ExtendDesc'
  134 + | 'JavaScriptFunctionEditor'
  135 + | 'JavascriptEditorWithTestModal'
  136 + | 'AttributeConfiguration'
  137 + | 'CorrelationFilters'
  138 + | 'RelationsQuery'
  139 + | 'CredentialsCard'
  140 + | 'ApiComplete';
  1 +export enum AlarmStatus {
  2 + CLEARED_UN_ACK = 'CLEARED_UNACK',
  3 + ACTIVE_UN_ACK = 'ACTIVE_UNACK',
  4 + CLEARED_ACK = 'CLEARED_ACK',
  5 + ACTIVE_ACK = 'ACTIVE_ACK',
  6 +}
  7 +
  8 +export enum AlarmStatusMean {
  9 + CLEARED_UNACK = '清除未确认',
  10 + ACTIVE_UNACK = '激活未确认',
  11 + CLEARED_ACK = '清除已确认',
  12 + ACTIVE_ACK = '激活已确认',
  13 +}
@@ -19,4 +19,8 @@ export enum DictEnum { @@ -19,4 +19,8 @@ export enum DictEnum {
19 DISABLE_CUSTOMER_AUTH = 'disabled_customer_auth', 19 DISABLE_CUSTOMER_AUTH = 'disabled_customer_auth',
20 // 寄存器数据格式 20 // 寄存器数据格式
21 REGISTER_DATA_FORMAT = 'register_data_format', 21 REGISTER_DATA_FORMAT = 'register_data_format',
  22 + // 消息类型 规则节点 Filter message type switch
  23 + MESSAGE_TYPES_FILTER = 'message_types_filter',
  24 + // 实体类型 规则节点 Filter originator types switch
  25 + ORIGINATOR_TYPES = 'originator_types',
22 } 26 }
  1 +import { saveOrEditDictItem } from '/@/api/system/dict';
  2 +import { SysDictItem } from '/@/api/system/model/dictModel';
  3 +
  4 +interface BatchSettingParamsType {
  5 + dictId: string;
  6 + list: any[];
  7 + transformText: (item: any) => any;
  8 + transformValue: (item: any) => any;
  9 + update?: boolean;
  10 +}
  11 +
  12 +export function useBatchSettingDict() {
  13 + const batchSetting = async (options: BatchSettingParamsType) => {
  14 + const { dictId, list, transformText, transformValue, update = false } = options;
  15 + if (!transformText || !transformValue) return;
  16 + let index = 1;
  17 + for (const item of list) {
  18 + const params: Partial<SysDictItem> & { dictId: string } = {
  19 + dictId,
  20 + sort: index++,
  21 + status: 1,
  22 + itemText: transformText(item),
  23 + itemValue: transformValue(item),
  24 + };
  25 +
  26 + await saveOrEditDictItem(params as SysDictItem, update);
  27 + }
  28 + };
  29 +
  30 + const batchDelete = () => {};
  31 +
  32 + return {
  33 + batchSetting,
  34 + batchDelete,
  35 + };
  36 +}
  1 +export function useBeautify() {
  2 + let jsBeautifyModule: any;
  3 +
  4 + async function loadJsBeautify() {
  5 + if (jsBeautifyModule) return jsBeautifyModule;
  6 + jsBeautifyModule = await import('js-beautify/js/lib/beautifier');
  7 + return jsBeautifyModule;
  8 + }
  9 +
  10 + async function beautifyJs(source: string, options?: any) {
  11 + const module = await loadJsBeautify();
  12 + return module.js(source, options);
  13 + }
  14 + return { beautifyJs };
  15 +}
  1 +import { isObject, isString } from '/@/utils/is';
  2 +
  3 +export function useJsonParse(value: any, defaultValue = {}) {
  4 + let flag = false;
  5 + try {
  6 + value = JSON.parse(value);
  7 + if (isObject(value) || (isString(value) && value.trim() === '')) flag = true;
  8 + } catch (error) {
  9 + value = defaultValue;
  10 + }
  11 + return { flag, value };
  12 +}
@@ -3,20 +3,7 @@ import { FormSchema } from '/@/components/Form'; @@ -3,20 +3,7 @@ import { FormSchema } from '/@/components/Form';
3 import { BasicColumn } from '/@/components/Table'; 3 import { BasicColumn } from '/@/components/Table';
4 import moment from 'moment'; 4 import moment from 'moment';
5 import { findDictItemByCode } from '/@/api/system/dict'; 5 import { findDictItemByCode } from '/@/api/system/dict';
6 -  
7 -export enum AlarmStatus {  
8 - CLEARED_UN_ACK = 'CLEARED_UNACK',  
9 - ACTIVE_UN_ACK = 'ACTIVE_UNACK',  
10 - CLEARED_ACK = 'CLEARED_ACK',  
11 - ACTIVE_ACK = 'ACTIVE_ACK',  
12 -}  
13 -  
14 -export enum AlarmStatusMean {  
15 - CLEARED_UNACK = '清除未确认',  
16 - ACTIVE_UNACK = '激活未确认',  
17 - CLEARED_ACK = '清除已确认',  
18 - ACTIVE_ACK = '激活已确认',  
19 -} 6 +import { AlarmStatus, AlarmStatusMean } from '/@/enums/alarmEnum';
20 7
21 export const alarmSearchSchemas: FormSchema[] = [ 8 export const alarmSearchSchemas: FormSchema[] = [
22 { 9 {
1 -import { AlarmStatus, AlarmStatusMean } from '../config/detail.config';  
2 import { clearOrAckAlarm, getDeviceAlarm } from '/@/api/device/deviceManager'; 1 import { clearOrAckAlarm, getDeviceAlarm } from '/@/api/device/deviceManager';
3 import { notification, Button, Tag } from 'ant-design-vue'; 2 import { notification, Button, Tag } from 'ant-design-vue';
4 import { h, onMounted, onUnmounted } from 'vue'; 3 import { h, onMounted, onUnmounted } from 'vue';
@@ -8,6 +7,7 @@ import { RoleEnum } from '/@/enums/roleEnum'; @@ -8,6 +7,7 @@ import { RoleEnum } from '/@/enums/roleEnum';
8 import { usePermission } from '/@/hooks/web/usePermission'; 7 import { usePermission } from '/@/hooks/web/usePermission';
9 import { useUserStore } from '/@/store/modules/user'; 8 import { useUserStore } from '/@/store/modules/user';
10 import { useGlobSetting } from '/@/hooks/setting'; 9 import { useGlobSetting } from '/@/hooks/setting';
  10 +import { AlarmStatus, AlarmStatusMean } from '/@/enums/alarmEnum';
11 11
12 interface UseAlarmNotifyParams { 12 interface UseAlarmNotifyParams {
13 alarmNotifyStatus?: AlarmStatus; 13 alarmNotifyStatus?: AlarmStatus;
@@ -91,7 +91,7 @@ export const formSchema: FormSchema[] = [ @@ -91,7 +91,7 @@ export const formSchema: FormSchema[] = [
91 valueField: 'fileList', 91 valueField: 'fileList',
92 componentProps: () => { 92 componentProps: () => {
93 return { 93 return {
94 - listType: 'picture-card', 94 + // listType: 'picture-card',
95 maxFileLimit: 1, 95 maxFileLimit: 1,
96 api: async (file: File) => { 96 api: async (file: File) => {
97 try { 97 try {
@@ -110,6 +110,10 @@ export const formSchema: FormSchema[] = [ @@ -110,6 +110,10 @@ export const formSchema: FormSchema[] = [
110 onPreview: (fileList: FileItem) => { 110 onPreview: (fileList: FileItem) => {
111 createImgPreview({ imageList: [fileList.url!] }); 111 createImgPreview({ imageList: [fileList.url!] });
112 }, 112 },
  113 + showUploadList: {
  114 + showDownloadIcon: true,
  115 + showRemoveIcon: true,
  116 + },
113 }; 117 };
114 }, 118 },
115 }, 119 },
@@ -57,7 +57,7 @@ export const formSchema: FormSchema[] = [ @@ -57,7 +57,7 @@ export const formSchema: FormSchema[] = [
57 valueField: 'fileList', 57 valueField: 'fileList',
58 componentProps: () => { 58 componentProps: () => {
59 return { 59 return {
60 - listType: 'picture-card', 60 + // listType: 'picture-card',
61 maxFileLimit: 1, 61 maxFileLimit: 1,
62 api: async (file: File) => { 62 api: async (file: File) => {
63 try { 63 try {
@@ -73,6 +73,10 @@ export const formSchema: FormSchema[] = [ @@ -73,6 +73,10 @@ export const formSchema: FormSchema[] = [
73 return {}; 73 return {};
74 } 74 }
75 }, 75 },
  76 + showUploadList: true,
  77 + onDownload(file) {
  78 + console.log(file);
  79 + },
76 onPreview: (fileList: FileItem) => { 80 onPreview: (fileList: FileItem) => {
77 createImgPreview({ imageList: [fileList.url!] }); 81 createImgPreview({ imageList: [fileList.url!] });
78 }, 82 },
@@ -23,7 +23,7 @@ @@ -23,7 +23,7 @@
23 layout: 'vertical', 23 layout: 'vertical',
24 }); 24 });
25 25
26 - const { genForm } = useGenDynamicForm(); 26 + const { genForm, transformValue } = useGenDynamicForm();
27 27
28 const keys = ref<string[]>([]); 28 const keys = ref<string[]>([]);
29 29
@@ -159,8 +159,8 @@ @@ -159,8 +159,8 @@
159 159
160 sendValue.value = await genModbusCommand(unref(modBUSForm)); 160 sendValue.value = await genModbusCommand(unref(modBUSForm));
161 } else { 161 } else {
162 - const _value = getFieldsValue();  
163 - 162 + await validate();
  163 + const _value = transformValue(getFieldsValue());
164 sendValue.value = unref(keys).reduce((prev, next) => { 164 sendValue.value = unref(keys).reduce((prev, next) => {
165 return { ...prev, [next]: _value[next] }; 165 return { ...prev, [next]: _value[next] };
166 }, {}); 166 }, {});
1 import { DataType, Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel'; 1 import { DataType, Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel';
2 import { JSONEditor } from '/@/components/CodeEditor'; 2 import { JSONEditor } from '/@/components/CodeEditor';
3 import { FormSchema, useComponentRegister } from '/@/components/Form'; 3 import { FormSchema, useComponentRegister } from '/@/components/Form';
  4 +import { useJsonParse } from '/@/hooks/business/useJsonParse';
4 import { DataTypeEnum } from '/@/views/device/profiles/step/cpns/physical/cpns/config'; 5 import { DataTypeEnum } from '/@/views/device/profiles/step/cpns/physical/cpns/config';
5 6
6 export interface BasicCreateFormParams { 7 export interface BasicCreateFormParams {
@@ -106,6 +107,19 @@ export const useGenDynamicForm = () => { @@ -106,6 +107,19 @@ export const useGenDynamicForm = () => {
106 field: identifier, 107 field: identifier,
107 label: functionName, 108 label: functionName,
108 component: 'JSONEditor', 109 component: 'JSONEditor',
  110 + valueField: 'value',
  111 + changeEvent: 'update:value',
  112 + rules: [
  113 + {
  114 + validator: (_rule, value: any) => {
  115 + if (value) {
  116 + const { flag } = useJsonParse(value);
  117 + if (!flag) return Promise.reject(`${functionName} 不是一个有效的JSON对象`);
  118 + }
  119 + return Promise.resolve();
  120 + },
  121 + },
  122 + ],
109 }; 123 };
110 }; 124 };
111 125
@@ -117,11 +131,14 @@ export const useGenDynamicForm = () => { @@ -117,11 +131,14 @@ export const useGenDynamicForm = () => {
117 [DataTypeEnum.IS_STRUCT]: createInputJson, 131 [DataTypeEnum.IS_STRUCT]: createInputJson,
118 }; 132 };
119 133
  134 + const fieldTypeMap = new Map<string, DataTypeEnum>();
120 const genForm = (schemas: StructJSON[]) => { 135 const genForm = (schemas: StructJSON[]) => {
  136 + fieldTypeMap.clear();
121 const formSchema = schemas.map((item) => { 137 const formSchema = schemas.map((item) => {
122 const { functionName, identifier, dataType } = item; 138 const { functionName, identifier, dataType } = item;
123 const { type } = dataType || {}; 139 const { type } = dataType || {};
124 140
  141 + fieldTypeMap.set(identifier!, dataType!.type);
125 const method = schemaMethod[type!]; 142 const method = schemaMethod[type!];
126 143
127 const formSchema = method?.({ 144 const formSchema = method?.({
@@ -136,5 +153,21 @@ export const useGenDynamicForm = () => { @@ -136,5 +153,21 @@ export const useGenDynamicForm = () => {
136 return formSchema.filter(Boolean); 153 return formSchema.filter(Boolean);
137 }; 154 };
138 155
139 - return { genForm }; 156 + const transformValue = (value: Recordable) => {
  157 + return Object.keys(value || {}).reduce((prev, next) => {
  158 + const dataType = fieldTypeMap.get(next)!;
  159 +
  160 + let itemValue = value[next];
  161 + if (dataType === DataTypeEnum.IS_STRUCT) {
  162 + const { value } = useJsonParse(itemValue);
  163 + itemValue = value;
  164 + }
  165 + return {
  166 + ...prev,
  167 + [next]: itemValue,
  168 + };
  169 + }, {} as Recordable);
  170 + };
  171 +
  172 + return { genForm, transformValue };
140 }; 173 };
  1 +export enum EntryCategoryComponentEnum {
  2 + INPUT = 'Input',
  3 +}
  4 +
  5 +export enum FlowCategoryComponentEnum {
  6 + ACKNOWLEDGE = 'Acknowledge',
  7 + CHECKPOINT = 'Checkpoint',
  8 + RULE_CHAIN = 'RuleChain',
  9 + OUTPUT = 'Output',
  10 +}
  11 +export enum EnrichmentCategoryComponentEnum {
  12 + CUSTOMER_ATTRIBUTES = 'CustomerAttributes',
  13 + ORIGINATOR_TELEMETRY = 'OriginatorTelemetry',
  14 + ORIGINATOR_FIELDS = 'OriginatorFields',
  15 + CALCULATE_DELTA = 'CalculateDelta',
  16 + TENANT_ATTRIBUTES = 'TenantAttributes',
  17 + RELATED_DEVICE_ATTRIBUTES = 'RelatedDeviceAttributes',
  18 + RELATED_ATTRIBUTES = 'RelatedAttributes',
  19 + TENANT_DETAILS = 'TenantDetails',
  20 + ORIGINATOR_ATTRIBUTES = 'OriginatorAttributes',
  21 + CUSTOMER_DETAILS = 'CustomerDetails',
  22 +}
  23 +export enum ExternalCategoryComponentEnum {
  24 + ALARM_NOTICE = 'AlarmNotice',
  25 + MQTT = 'Mqtt',
  26 + GCP_PUBSUB = 'GcpPubsub',
  27 + SEND_SMS = 'SendSms',
  28 + AZURE_IOT_HUB = 'AzureIotHub',
  29 + REST_API_CALL = 'RestApiCall',
  30 + KAFKA = 'Kafka',
  31 + AWS_SNS = 'AwsSns',
  32 + SEND_EMAIL = 'SendEmail',
  33 + RABBITMQ = 'Rabbitmq',
  34 + AWS_SQS = 'AwsSqs',
  35 +}
  36 +export enum ActionCategoryComponentEnum {
  37 + CREATE_ALARM = 'CreateAlarm',
  38 + PUSH_TO_EDGE = 'PushToEdge',
  39 + GENERATOR = 'Generator',
  40 + ASSIGN_TO_CUSTOMER = 'AssignToCustomer',
  41 + UNASSIGN_FROM_CUSTOMER = 'UnassignFromCustomer',
  42 + SAVE_TIMESERIES = 'SaveTimeseries',
  43 + GPS_GEOFENCING_EVENTS = 'GpsGeofencingEvents',
  44 + DEVICE_PROFILE = 'DeviceProfile',
  45 + RPC_CALL_REPLY = 'RpcCallReply',
  46 + COPY_TO_VIEW = 'CopyToView',
  47 + LOG = 'Log',
  48 + SAVE_TO_CUSTOM_TABLE = 'SaveToCustomTable',
  49 + CLEAR_ALARM = 'ClearAlarm',
  50 + DELETE_RELATION = 'DeleteRelation',
  51 + DELAY_DEPRECATED = 'DelayDeprecated',
  52 + MESSAGE_COUNT = 'MessageCount',
  53 + SAVE_EVENT = 'SaveEvent',
  54 + RPC_CALL_REQUEST = 'RpcCallRequest',
  55 + SYNCHRONIZATION_START = 'SynchronizationStart',
  56 + SYNCHRONIZATION_END = 'SynchronizationEnd',
  57 + SAVE_ATTRIBUTES = 'SaveAttributes',
  58 + CREATE_RELATION = 'CreateRelation',
  59 +}
  60 +export enum FilterCategoryComponentEnum {
  61 + SCRIPT = 'Script',
  62 + SCENE_REACT = 'SceneReact',
  63 + CHECK_EXISTENCE_FIELDS = 'CheckExistenceFields',
  64 + ORIGINATOR_TYPE_SWITCH = 'OriginatorTypeSwitch',
  65 + CHECK_RELATION = 'CheckRelation',
  66 + MESSAGE_TYPE = 'MessageType',
  67 + ORIGINATOR_TYPE = 'OriginatorType',
  68 + MESSAGE_TYPE_SWITCH = 'MessageTypeSwitch',
  69 + SWITCH = 'Switch',
  70 + GPS_GEOFENCING_FILTER = 'GpsGeofencingFilter',
  71 + CHECK_ALARM_STATUS = 'CheckAlarmStatus',
  72 +}
  73 +export enum TransformationCategoryComponentEnum {
  74 + CHANGE_ORIGINATOR = 'ChangeOriginator',
  75 + SCRIPT = 'Script',
  76 + TO_EMAIL = 'ToEmail',
  77 +}
  1 +/**
  2 + * @description 方向
  3 + */
  4 +export enum DirectionEnum {
  5 + FROM = 'FROM',
  6 + TO = 'TO',
  7 +}
  8 +
  9 +export enum DirectionNameEnum {
  10 + FROM = '从',
  11 + TO = '到',
  12 +}
  13 +/**
  14 + * @description 关联类型
  15 + */
  16 +export enum RelationTypeEnum {
  17 + CONTAINS = 'CONTAINS',
  18 + MANAGES = 'MANAGES',
  19 +}
  20 +
  21 +export enum RelationTypeNameEnum {
  22 + CONTAINS = 'Contains',
  23 + MANAGES = 'Manages',
  24 +}
  25 +
  26 +/**
  27 + * @description 类型
  28 + */
  29 +export enum EntityTypeEnum {
  30 + DEVICE = 'DEVICE',
  31 + ASSET = 'ASSET',
  32 + ENTITY_VIEW = 'ENTITY_VIEW',
  33 + TENANT = 'TENANT',
  34 + CUSTOMER = 'CUSTOMER',
  35 + USER = 'USER',
  36 + DASHBOARD = 'DASHBOARD',
  37 + EDGE = 'EDGE',
  38 +}
  39 +
  40 +export enum EntityTypeNameEnum {
  41 + DEVICE = '设备',
  42 + ASSET = '资产',
  43 + ENTITY_VIEW = '实体视图',
  44 + TENANT = '租户',
  45 + CUSTOMER = '客户',
  46 + USER = '用户',
  47 + DASHBOARD = '仪表板',
  48 + EDGE = '边缘',
  49 +}
  50 +
  51 +/**
  52 + * @description Filter Perimeter type
  53 + */
  54 +export enum PerimeterTypeEnum {
  55 + POLYGON = 'POLYGON',
  56 + CIRCLE = 'CIRCLE',
  57 +}
  58 +
  59 +export enum MessageTypesEnum {
  60 + POST_ATTRIBUTES_REQUEST = 'POST_ATTRIBUTES_REQUEST',
  61 + POST_TELEMETRY_REQUEST = 'POST_TELEMETRY_REQUEST',
  62 + TO_SERVER_RPC_REQUEST = 'TO_SERVER_RPC_REQUEST',
  63 + RPC_CALL_FROM_SERVER_TO_DEVICE = 'RPC_CALL_FROM_SERVER_TO_DEVICE',
  64 + ACTIVITY_EVENT = 'ACTIVITY_EVENT',
  65 + INACTIVITY_EVENT = 'INACTIVITY_EVENT',
  66 + CONNECT_EVENT = 'CONNECT_EVENT',
  67 + DISCONNECT_EVENT = 'DISCONNECT_EVENT',
  68 + ENTITY_CREATED = 'ENTITY_CREATED',
  69 + ENTITY_UPDATED = 'ENTITY_UPDATED',
  70 + ENTITY_DELETED = 'ENTITY_DELETED',
  71 + ENTITY_ASSIGNED = 'ENTITY_ASSIGNED',
  72 + ENTITY_UNASSIGNED = 'ENTITY_UNASSIGNED',
  73 + ATTRIBUTES_UPDATED = 'ATTRIBUTES_UPDATED',
  74 + ATTRIBUTES_DELETED = 'ATTRIBUTES_DELETED',
  75 + TIMESERIES_UPDATED = 'TIMESERIES_UPDATED',
  76 + TIMESERIES_DELETED = 'TIMESERIES_DELETED',
  77 + RPC_QUEUED = 'RPC_QUEUED',
  78 + RPC_DELIVERED = 'RPC_DELIVERED',
  79 + RPC_SUCCESSFUL = 'RPC_SUCCESSFUL',
  80 + RPC_TIMEOUT = 'RPC_TIMEOUT',
  81 + RPC_FAILED = 'RPC_FAILED',
  82 +}
  83 +
  84 +export enum MessageTypesNameEnum {
  85 + POST_ATTRIBUTES_REQUEST = 'Post attributes',
  86 + POST_TELEMETRY_REQUEST = 'Post telemetry',
  87 + TO_SERVER_RPC_REQUEST = 'RPC Request from Device',
  88 + RPC_CALL_FROM_SERVER_TO_DEVICE = 'RPC Request to Device',
  89 + ACTIVITY_EVENT = 'Activity Event',
  90 + INACTIVITY_EVENT = 'Inactivity Event',
  91 + CONNECT_EVENT = 'Connect Event',
  92 + DISCONNECT_EVENT = 'Disconnect Event',
  93 + ENTITY_CREATED = 'Entity Created',
  94 + ENTITY_UPDATED = 'Entity Updated',
  95 + ENTITY_DELETED = 'Entity Deleted',
  96 + ENTITY_ASSIGNED = 'Entity Assigned',
  97 + ENTITY_UNASSIGNED = 'Entity Unassigned',
  98 + ATTRIBUTES_UPDATED = 'Attributes Updated',
  99 + ATTRIBUTES_DELETED = 'Attributes Deleted',
  100 + TIMESERIES_UPDATED = 'Timeseries Updated',
  101 + TIMESERIES_DELETED = 'Timeseries Deleted',
  102 + RPC_QUEUED = 'RPC Queued',
  103 + RPC_DELIVERED = 'RPC Delivered',
  104 + RPC_SUCCESSFUL = 'RPC Successful',
  105 + RPC_TIMEOUT = 'RPC Timeout',
  106 + RPC_FAILED = 'RPC Failed',
  107 +}
  108 +/**
  109 + * @description Filter Range util
  110 + */
  111 +export enum RangeUtilEnum {
  112 + METER = 'METER',
  113 + KILOMETER = 'KILOMETER',
  114 + FOOT = 'FOOT',
  115 + MILE = 'MILE',
  116 + NAUTICAL_MILE = 'NAUTICAL_MILE',
  117 +}
  118 +
  119 +export enum RangeUtilNameEnum {
  120 + METER = 'Meter',
  121 + KILOMETER = 'Kilometer',
  122 + FOOT = 'Foot',
  123 + MILE = 'Mile',
  124 + NAUTICAL_MILE = 'Nautical mile',
  125 +}
  126 +
  127 +// Enrichment Customer details
  128 +export enum DetailsListEnum {
  129 + TITLE = 'TITLE',
  130 + COUNTRY = 'COUNTRY',
  131 + STATE = 'STATE',
  132 + CITY = 'CITY',
  133 + ZIP = 'ZIP',
  134 + ADDRESS = 'ADDRESS',
  135 + ADDRESS_2 = 'ADDRESS_2',
  136 + PHONE = 'PHONE',
  137 + EMAIL = 'EMAIL',
  138 + ADDITIONAL_INFO = 'ADDITIONAL_INFO',
  139 +}
  140 +
  141 +export enum DetailsListNameEnum {
  142 + TITLE = 'Title',
  143 + COUNTRY = 'Country',
  144 + STATE = 'State',
  145 + CITY = 'City',
  146 + ZIP = 'Zip',
  147 + ADDRESS = 'Address',
  148 + ADDRESS_2 = 'Address2',
  149 + PHONE = 'Phone',
  150 + EMAIL = 'Email',
  151 + ADDITIONAL_INFO = 'Additional Info',
  152 +}
  153 +
  154 +// Enrichment Originator Telemetry FetchMode
  155 +export enum FetchModeEnum {
  156 + FIRST = 'FIRST',
  157 + LAST = 'LAST',
  158 + ALL = 'ALL',
  159 +}
  160 +
  161 +// Enrichment Originator Telemetry Time interval unit
  162 +export enum TimeIntervalUnitEnum {
  163 + MILLISECONDS = 'MILLISECONDS',
  164 + SECONDS = 'SECONDS',
  165 + MINUTES = 'MINUTES',
  166 + HOURS = 'HOURS',
  167 + DAYS = 'DAYS',
  168 +}
  169 +
  170 +export enum TimeIntervalUnitNameEnum {
  171 + MILLISECONDS = 'Milliseconds',
  172 + SECONDS = 'Seconds',
  173 + MINUTES = 'Minutes',
  174 + HOURS = 'Hours',
  175 + DAYS = 'Days',
  176 +}
  177 +
  178 +// Enrichment Originator Telemetry OrderBy
  179 +export enum OrderByEnum {
  180 + DESC = 'DESC',
  181 + ASC = 'ASC',
  182 +}
  183 +
  184 +// Enrichment Originator Telemetry Aggregation
  185 +export enum AggregationEnum {
  186 + MIN = 'MIN',
  187 + MAX = 'MAX',
  188 + AVG = 'AVG',
  189 + SUM = 'SUM',
  190 + COUNT = 'COUNT',
  191 + NONE = 'NONE',
  192 +}
  193 +
  194 +export enum AggregationNameEnum {
  195 + MIN = '最少值',
  196 + MAX = '最大值',
  197 + AVG = '平均值',
  198 + SUM = '求和',
  199 + COUNT = '计数',
  200 + NONE = '空',
  201 +}
  202 +
  203 +// Originator source
  204 +export enum OriginatorSourceEnum {
  205 + CUSTOMER = 'CUSTOMER',
  206 + TENANT = 'TENANT',
  207 + RELATED = 'RELATED',
  208 + ALARM_ORIGINATOR = 'ALARM_ORIGINATOR',
  209 +}
  210 +
  211 +export enum OriginatorSourceNameEnum {
  212 + CUSTOMER = 'Customer',
  213 + TENANT = 'Tenant',
  214 + RELATED = 'Related',
  215 + ALARM_ORIGINATOR = 'Alarm Originator',
  216 +}
  217 +
  218 +// Transformation To Email
  219 +export enum MailBodyTypeEnum {
  220 + DYNAMIC = 'dynamic',
  221 + HTML = 'true',
  222 + PLAIN_TEXT = 'false',
  223 +}
  224 +
  225 +export enum MailBodyTypeNameEnum {
  226 + DYNAMIC = 'Dynamic',
  227 + HTML = 'HTML',
  228 + PLAIN_TEXT = 'Plain Text',
  229 +}
  230 +
  231 +// Action Push to edge
  232 +export enum ScopeEnum {
  233 + SERVER_SCOPE = 'SERVER_SCOPE',
  234 + CLIENT_SCOPE = 'CLIENT_SCOPE',
  235 + SHARED_SCOPE = 'SHARED_SCOPE',
  236 +}
  237 +
  238 +export enum ScopeNameEnum {
  239 + SERVER_SCOPE = '服务端属性',
  240 + CLIENT_SCOPE = '客户端属性',
  241 + SHARED_SCOPE = '共享属性',
  242 +}
  243 +
  244 +// External Queue type
  245 +export enum QueueTypeEnum {
  246 + STANDARD = 'STANDARD',
  247 + FIFO = 'FIFO',
  248 +}
  249 +
  250 +export enum QueueTypeNameEnum {
  251 + STANDARD = 'Standard',
  252 + FIFO = 'FIFO',
  253 +}
  254 +
  255 +// Charset encoding
  256 +export enum CharsetEncodingEnum {
  257 + US_ASCII = 'US_ASCII',
  258 + ISO_8859_1 = 'ISO_8859_1',
  259 + UTF_8 = 'UTF_8',
  260 + UTF_16BE = 'UTF_16BE',
  261 + UTF_16LE = 'UTF_16LE',
  262 + UTF_16 = 'UTF_16',
  263 +}
  264 +
  265 +export enum CharsetEncodingNameEnum {
  266 + US_ASCII = 'US-ASCII',
  267 + ISO_8859_1 = 'ISO-8859-1',
  268 + UTF_8 = 'UTF-8',
  269 + UTF_16BE = 'UTF-16BE',
  270 + UTF_16LE = 'UTF-16LE',
  271 + UTF_16 = 'UTF-16',
  272 +}
  273 +
  274 +// Rabbitmq Message properties
  275 +export enum MessagePropertiesEnum {
  276 + BASIC = 'BASIC',
  277 + TEXT_PLAIN = 'TEXT_PLAIN',
  278 + MINIMAL_BASIC = 'MINIMAL_BASIC',
  279 + MINIMAL_PERSISTENT_BASIC = 'MINIMAL_PERSISTENT_BASIC',
  280 + PERSISTENT_BASIC = 'PERSISTENT_BASIC',
  281 + PERSISTENT_TEXT_PLAIN = 'PERSISTENT_TEXT_PLAIN',
  282 +}
  283 +
  284 +// rest api call Request method
  285 +export enum RequestMethodEnum {
  286 + GET = 'GET',
  287 + POST = 'POST',
  288 + PUT = 'PUT',
  289 + DELETE = 'DELETE',
  290 +}
  291 +
  292 +export enum ProtocolEnum {
  293 + HTTP = 'HTTP',
  294 + HTTPS = 'HTTPS',
  295 +}
  296 +
  297 +export enum ProtocolNameEnum {
  298 + HTTP = 'http',
  299 + HTTPS = 'https',
  300 +}
  301 +
  302 +export enum EmailProtocolEnum {
  303 + SMTP = 'SMTP',
  304 + SMTPS = 'SMTPS',
  305 +}
  306 +
  307 +export enum EmailProtocolNameEnum {
  308 + SMTP = 'smtp',
  309 + SMTPS = 'smtps',
  310 +}
  311 +
  312 +export enum TSLVersionEnum {
  313 + TSLV1 = 'TSLV1',
  314 + TSLV1_1 = 'TSLV1_1',
  315 + TSLV1_2 = 'TSLV1_2',
  316 + TSLV1_3 = 'TSLV1_3',
  317 +}
  318 +
  319 +export enum TSLVersionNameEnum {
  320 + TSLV1 = 'TSLV1',
  321 + TSLV1_1 = 'TSLV1.1',
  322 + TSLV1_2 = 'TSLV1.2',
  323 + TSLV1_3 = 'TSLV1.3',
  324 +}
  325 +
  326 +export enum SMSServiceProviderEnum {
  327 + AWS_SNS = 'AWS_SNS',
  328 + TWILIO = 'TWILIO',
  329 +}
  330 +
  331 +export enum SMSServiceProviderNameEnum {
  332 + AWS_SNS = '亚马逊社交网站',
  333 + TWILIO = 'Twilio',
  334 +}
  1 +// Assign to customer
  2 +export enum AssignToCustomerFieldsEnum {
  3 + CUSTOMER_NAME_PATTERN = 'customerNamePattern',
  4 + CREATE_CUSTOMER_IF_NOT_EXISTS = 'createCustomerIfNotExists',
  5 + CUSTOMER_CACHE_EXPIRATION = 'customerCacheExpiration',
  6 +}
  7 +
  8 +export enum AssignToCustomerFieldsNameEnum {
  9 + CUSTOMER_NAME_PATTERN = 'Customer name pattern',
  10 + CREATE_CUSTOMER_IF_NOT_EXISTS = 'Create new customer if not exists',
  11 + CUSTOMER_CACHE_EXPIRATION = 'Customers cache expiration time(sec)',
  12 +}
  13 +
  14 +// clear alarm
  15 +export enum ClearAlarmFieldsEnum {
  16 + ALARM_TYPE = 'alarmType',
  17 + ALARM_DETAILS_BUILD_JS = 'alarmDetailsBuildJs',
  18 +}
  19 +
  20 +export enum ClearAlarmFieldsNameEnum {
  21 + ALARM_TYPE = 'Alarm type',
  22 + ALARM_DETAILS_BUILD_JS = 'Alarm details builder',
  23 +}
  24 +
  25 +// Create alarm
  26 +export enum CreateAlarmFieldsEnum {
  27 + ALARM_DETAILS_BUILD_JS = 'alarmDetailsBuildJs',
  28 + USE_MESSAGE_ALARM_DATA = 'useMessageAlarmData',
  29 + OVERWRITE_ALARM_DETAILS = 'overwriteAlarmDetails',
  30 + ALARM_TYPE = 'alarmType',
  31 + SEVERITY = 'severity',
  32 + PROPAGATE = 'propagate',
  33 + RELATION_TYPES = 'relationTypes',
  34 + PROPAGATE_TO_OWNER = 'propagateToOwner',
  35 + PROPAGATE_TO_TENANT = 'propagateToTenant',
  36 + DYNAMIC_SEVERITY = 'dynamicSeverity',
  37 +}
  38 +
  39 +export enum CreateAlarmFieldsNameEnum {
  40 + ALARM_DETAILS_BUILD_JS = 'Alarm details builder',
  41 + USE_MESSAGE_ALARM_DATA = 'Use message alarm data',
  42 + OVERWRITE_ALARM_DETAILS = 'Overwrite alarm details',
  43 + ALARM_TYPE = 'Alarm type',
  44 + SEVERITY = 'Alarm severity pattern',
  45 + PROPAGATE = 'Propagate alarm to related entities',
  46 + RELATION_TYPES = 'Relation types to propagate',
  47 + PROPAGATE_TO_OWNER = 'Propagate alarm to entity owner (Customer or Tenant)',
  48 + PROPAGATE_TO_TENANT = 'Propagate alarm to Tenant',
  49 + DYNAMIC_SEVERITY = 'Use alarm severity pattern',
  50 +}
  51 +
  52 +// Create relation
  53 +export enum CreateRelationFieldsEnum {
  54 + DIRECTION = 'direction',
  55 + ENTITY_TYPE = 'entityType',
  56 + ENTITY_NAME_PATTERN = 'entityNamePattern',
  57 + ENTITY_TYPE_PATTERN = 'entityTypePattern',
  58 + RELATION_TYPE = 'relationType',
  59 + CREATE_ENTITY_IF_NOT_EXISTS = 'createEntityIfNotExists',
  60 + REMOVE_CURRENT_RELATIONS = 'removeCurrentRelations',
  61 + CHANGE_ORIGINATOR_TO_RELATED_ENTITY = 'changeOriginatorToRelatedEntity',
  62 + ENTITY_CACHE_EXPIRATION = 'entityCacheExpiration',
  63 +}
  64 +
  65 +export enum CreateRelationFieldsNameEnum {
  66 + DIRECTION = '方向',
  67 + ENTITY_TYPE = '类型',
  68 + ENTITY_NAME_PATTERN = 'Name pattern',
  69 + ENTITY_TYPE_PATTERN = 'Type pattern',
  70 + RELATION_TYPE = 'Relation type pattern',
  71 + CREATE_ENTITY_IF_NOT_EXISTS = 'Create new entity if not exists',
  72 + REMOVE_CURRENT_RELATIONS = 'Remove current relations',
  73 + CHANGE_ORIGINATOR_TO_RELATED_ENTITY = 'Change originator to related entity',
  74 + ENTITY_CACHE_EXPIRATION = 'Entities cache expiration time(sec)',
  75 +}
  76 +
  77 +// Delay deprecated
  78 +export enum DelayDeprecatedFieldsEnum {
  79 + USE_METADATA_PERIOD_IN_SECONDS_PATTERNS = 'useMetadataPeriodInSecondsPatterns',
  80 + PERIOD_IN_SECONDS = 'periodInSeconds',
  81 + PERIOD_IN_SECONDS_PATTERN = 'periodInSecondsPattern',
  82 + MAX_PENDING_MSGS = 'maxPendingMsgs',
  83 +}
  84 +
  85 +export enum DelayDeprecatedFieldsNameEnum {
  86 + USE_METADATA_PERIOD_IN_SECONDS_PATTERNS = 'Use period in seconds pattern',
  87 + PERIOD_IN_SECONDS = 'Period in seconds',
  88 + PERIOD_IN_SECONDS_PATTERN = 'Period in seconds pattern',
  89 + MAX_PENDING_MSGS = 'Maximum pending messages',
  90 +}
  91 +
  92 +export enum DeleteRelationFieldsEnum {
  93 + DELETE_FOR_SINGLE_ENTITY = 'deleteForSingleEntity',
  94 + DIRECTION = 'direction',
  95 + ENTITY_TYPE = 'entityType',
  96 + ENTITY_NAME_PATTERN = 'entityNamePattern',
  97 + RELATION_TYPE = 'relationType',
  98 + ENTITY_CACHE_EXPIRATION = 'entityCacheExpiration',
  99 +}
  100 +
  101 +export enum DeleteRelationFieldsNameEnum {
  102 + DELETE_FOR_SINGLE_ENTITY = 'Delete relation to specific entity',
  103 + DIRECTION = '方向',
  104 + ENTITY_TYPE = '类型',
  105 + ENTITY_NAME_PATTERN = 'Name pattern',
  106 + RELATION_TYPE = 'Relation type pattern',
  107 + ENTITY_CACHE_EXPIRATION = 'Entities cache expiration time(sec)',
  108 +}
  109 +
  110 +// device profile
  111 +export enum DeviceProfileFieldsEnum {
  112 + PERSIST_ALARM_RULES_STATE = 'persistAlarmRulesState',
  113 + FETCH_ALARM_RULES_STATE_ON_START = 'fetchAlarmRulesStateOnStart',
  114 +}
  115 +
  116 +export enum DeviceProfileFieldsNameEnum {
  117 + PERSIST_ALARM_RULES_STATE = 'Persist state of alarm rules',
  118 + FETCH_ALARM_RULES_STATE_ON_START = 'Fetch state of alarm rules',
  119 +}
  120 +
  121 +// Generator
  122 +export enum GeneratorFieldsEnum {
  123 + MSG_COUNT = 'msgCount',
  124 + PERIOD_IN_SECONDS = 'periodInSeconds',
  125 + JS_SCRIPT = 'jsScript',
  126 + ORIGINATOR_ID = 'originatorId',
  127 + ORIGINATOR_TYPE = 'originatorType',
  128 +}
  129 +
  130 +export enum GeneratorFieldsNameEnum {
  131 + MSG_COUNT = 'Message count(0 - unlimited)',
  132 + PERIOD_IN_SECONDS = 'Period in seconds',
  133 + JS_SCRIPT = 'Generate',
  134 + // ORIGINATOR_ID = '资产',
  135 + ORIGINATOR_TYPE = '类型',
  136 +}
  137 +
  138 +// Gps geofencing events
  139 +export enum GpsGeofencingEventsFieldsEnum {
  140 + LATITUDE_KEY_NAME = 'latitudeKeyName',
  141 + LONGITUDE_KEY_NAME = 'longitudeKeyName',
  142 + PERIMETER_TYPE = 'perimeterType',
  143 + FETCH_PERIMETER_INFO_FROM_MESSAGE_METADATA = 'fetchPerimeterInfoFromMessageMetadata',
  144 + PERIMETER_KEY_NAME = 'perimeterKeyName',
  145 + CENTER_LATITUDE = 'centerLatitude',
  146 + CENTER_LONGITUDE = 'centerLongitude',
  147 + RANGE = 'range',
  148 + RANGE_UNIT = 'rangeUnit',
  149 + POLYGONS_DEFINITION = 'polygonsDefinition',
  150 + MIN_INSIDE_DURATION = 'minInsideDuration',
  151 + MIN_INSIDE_DURATION_TIME_UNIT = 'minInsideDurationTimeUnit',
  152 + MIN_OUTSIDE_DURATION = 'minOutsideDuration',
  153 + MIN_OUTSIDE_DURATION_TIME_UNIT = 'minOutsideDurationTimeUnit',
  154 +}
  155 +
  156 +export enum GpsGeofencingEventsFieldsNameEnum {
  157 + LATITUDE_KEY_NAME = 'Latitude key name',
  158 + LONGITUDE_KEY_NAME = 'longitude key name',
  159 + PERIMETER_TYPE = 'Perimeter type',
  160 + FETCH_PERIMETER_INFO_FROM_MESSAGE_METADATA = 'Fetch perimeter information from message metadata',
  161 + PERIMETER_KEY_NAME = 'Perimeter key name',
  162 + CENTER_LATITUDE = 'Center Latitude',
  163 + CENTER_LONGITUDE = 'Center Longitude',
  164 + RANGE = 'Range',
  165 + RANGE_UNIT = 'Range units',
  166 + POLYGONS_DEFINITION = 'Polygon definition',
  167 + MIN_INSIDE_DURATION = 'Minimal inside duration',
  168 + MIN_INSIDE_DURATION_TIME_UNIT = 'Minimal inside duration time unit',
  169 + MIN_OUTSIDE_DURATION = 'Minimal outside duration',
  170 + MIN_OUTSIDE_DURATION_TIME_UNIT = 'Minimal outside duration time unit',
  171 +}
  172 +
  173 +// Log
  174 +export enum LogFieldsEnum {
  175 + JS_SCRIPT = 'jsScript',
  176 +}
  177 +
  178 +export enum LogFieldsNameEnum {
  179 + JS_SCRIPT = 'To string',
  180 +}
  181 +
  182 +// Message Count
  183 +export enum MessageCountFieldsEnum {
  184 + INTERVAL = 'interval',
  185 + TELEMETRY_PREFIX = 'telemetryPrefix',
  186 +}
  187 +
  188 +export enum MessageCountFieldsNameEnum {
  189 + INTERVAL = 'Interval in seconds',
  190 + TELEMETRY_PREFIX = 'Output timeseries key prefix',
  191 +}
  192 +
  193 +// Push to edge
  194 +export enum PushToEdgeFieldsEnum {
  195 + SCOPE = 'scope',
  196 +}
  197 +
  198 +export enum PushToEdgeFieldsNameEnum {
  199 + SCOPE = '设备属性范围',
  200 +}
  201 +// Rpc call reply
  202 +export enum RpcCallReplyFieldsEnum {
  203 + REQUEST_ID_META_DATA_ATTRIBUTE = 'requestIdMetaDataAttribute',
  204 +}
  205 +
  206 +export enum RpcCallReplyFieldsNameEnum {
  207 + REQUEST_ID_META_DATA_ATTRIBUTE = 'Request Id Metadata attribute name',
  208 +}
  209 +
  210 +// Rpc call request
  211 +export enum RpcCallRequestFieldsEnum {
  212 + TIMEOUT_IN_SECONDS = 'timeoutInSeconds',
  213 +}
  214 +
  215 +export enum RpcCallRequestFieldsNameEnum {
  216 + TIMEOUT_IN_SECONDS = 'Timeout in seconds',
  217 +}
  218 +
  219 +// Save attribute
  220 +export enum SaveAttributesFieldsEnum {
  221 + NOTIFY_DEVICE = 'notifyDevice',
  222 + SCOPE = 'scope',
  223 +}
  224 +
  225 +export enum SaveAttributesFieldsNameEnum {
  226 + NOTIFY_DEVICE = 'Notify Device',
  227 + SCOPE = '设备属性范围',
  228 +}
  229 +
  230 +// Save event
  231 +export enum SaveEventFieldsEnum {
  232 + CONFIGURATION = 'CONFIGURATION',
  233 +}
  234 +
  235 +export enum SaveEventFieldsNameEnum {
  236 + CONFIGURATION = '配置',
  237 +}
  238 +
  239 +// Save timeseries
  240 +export enum SaveTimeseriesFieldsEnum {
  241 + DEFAULT_TTL = 'defaultTTL',
  242 + SKIP_LATEST_PERSISTENCE = 'skipLatestPersistence',
  243 + USE_SERVER_TS = 'useServerTs',
  244 +}
  245 +
  246 +export enum SaveTimeseriesFieldsNameEnum {
  247 + DEFAULT_TTL = 'Default TTL in seconds',
  248 + SKIP_LATEST_PERSISTENCE = 'Skit latest persistence',
  249 + USE_SERVER_TS = 'Use server ts',
  250 +}
  251 +
  252 +// save to custom table
  253 +export enum SaveToCustomTableFieldsEnum {
  254 + FIELDS_MAPPING = 'fieldsMapping',
  255 + TABLE_NAME = 'tableName',
  256 +}
  257 +
  258 +export enum SaveToCustomTableFieldsNameEnum {
  259 + FIELDS_MAPPING = 'Fields mapping',
  260 + TABLE_NAME = 'Custom table name',
  261 +}
  262 +
  263 +// Unassign from customer
  264 +export enum UnassignFromCustomerFieldsEnum {
  265 + CUSTOMER_NAME_PATTERN = 'customerNamePattern',
  266 + CUSTOMER_CACHE_EXPIRATION = 'customerCacheExpiration',
  267 +}
  268 +
  269 +export enum UnassignFromCustomerFieldsNameEnum {
  270 + CUSTOMER_NAME_PATTERN = 'Customer name pattern',
  271 + CUSTOMER_CACHE_EXPIRATION = 'Customer cache expiration time(sec)',
  272 +}
  1 +export enum CommonFieldsEnum {
  2 + NAME = 'name',
  3 + DESCRIPTION = 'description',
  4 + DEBUG_MODE = 'debugMode',
  5 +}
  6 +
  7 +export enum CommonFieldsNameEnum {
  8 + NAME = '名称',
  9 + DESCRIPTION = '说明',
  10 + DEBUG_MODE = '调试模式',
  11 +}
  12 +
  13 +export const CommonFields = { ...CommonFieldsEnum };
  14 +export const CommonFieldsName = { ...CommonFieldsNameEnum };
  15 +export type CommonFieldsType = typeof CommonFields;
  1 +// Enrichment Calculate delta
  2 +export enum CalculateDeltaFieldsEnum {
  3 + INPUT_VALUE_KEY = 'inputValueKey',
  4 + OUTPUT_VALUE_KEY = 'outputValueKey',
  5 + ROUND = 'round',
  6 + USE_CACHE = 'useCache',
  7 + TELL_FAILURE_IF_DELTA_IS_NEGATIVE = 'tellFailureIfDeltaIsNegative',
  8 + ADD_PERIOD_BETWEEN_MSGS = 'addPeriodBetweenMsgs',
  9 + PERIOD_VALUE_KEY = 'periodValueKey',
  10 +}
  11 +
  12 +export enum CalculateDeltaFieldsNameEnum {
  13 + INPUT_VALUE_KEY = 'Input value key',
  14 + OUTPUT_VALUE_KEY = 'Output value key',
  15 + ROUND = 'Decimals',
  16 + USE_CACHE = 'Use cache for latest value',
  17 + TELL_FAILURE_IF_DELTA_IS_NEGATIVE = 'Tell Failure if delta is negative',
  18 + ADD_PERIOD_BETWEEN_MSGS = 'Add period between messages',
  19 + PERIOD_VALUE_KEY = 'Period value key',
  20 +}
  21 +
  22 +// Enrichment Customer Attributes
  23 +export enum CustomerAttributesFieldsEnum {
  24 + ATTR_MAPING = 'attrMapping',
  25 + TELEMETRY = 'telemetry',
  26 +}
  27 +
  28 +export enum CustomerAttributesFieldsNameEnum {
  29 + ATTR_MAPING = 'Attributes mapping',
  30 + TELEMETRY = 'Latest telemetry',
  31 +}
  32 +
  33 +// Enrichment Customer details
  34 +export enum CustomerDetailsFieldsEnum {
  35 + DETAILS_LIST = 'detailsList',
  36 + ADD_TO_METADATA = 'addToMetadata',
  37 +}
  38 +
  39 +export enum CustomerDetailsFieldsNameEnum {
  40 + DETAILS_LIST = 'Select entity details',
  41 + ADD_TO_METADATA = 'Add selected details to message metadata',
  42 +}
  43 +
  44 +// Enrichment Originator attributes
  45 +export enum OriginatorAttributesEnum {
  46 + TELL_FAILURE_IF_ABSENT = 'tellFailureIfAbsent',
  47 + CLIENT_ATTRIBUTE_NAMES = 'clientAttributeNames',
  48 + SHARED_ATTRIBUTE_NAMES = 'sharedAttributeNames',
  49 + SERVER_ATTRIBUTE_NAMES = 'serverAttributeNames',
  50 + LATEST_TS_KEY_NAMES = 'latestTsKeyNames',
  51 + GET_LATEST_VALUE_WITH_TS = 'getLatestValueWithTs',
  52 +}
  53 +
  54 +export enum OriginatorAttributesNameEnum {
  55 + TELL_FAILURE_IF_ABSENT = 'Tell Failure',
  56 + CLIENT_ATTRIBUTE_NAMES = 'Client attributes',
  57 + SHARED_ATTRIBUTE_NAMES = 'Shared attributes',
  58 + SERVER_ATTRIBUTE_NAMES = 'Server attributes',
  59 + LATEST_TS_KEY_NAMES = 'Latest timeseries',
  60 + GET_LATEST_VALUE_WITH_TS = 'Fetch Latest telemetry with Timestamp',
  61 +}
  62 +
  63 +// Enrichment Originator Fields
  64 +export enum OriginatorFieldsEnum {
  65 + FIELDS_MAPPING = 'fieldsMapping',
  66 +}
  67 +
  68 +export enum OriginatorFieldsNameEnum {
  69 + FIELDS_MAPPING = 'Fields mapping',
  70 +}
  71 +
  72 +// Enrichment originator telemetry
  73 +export enum OriginatorTelemetryFieldsEnum {
  74 + LATEST_TS_KEY_NAMES = 'latestTsKeyNames',
  75 + AGGREGATION = 'aggregation',
  76 + FETCH_MODE = 'fetchMode',
  77 + ORDER_BY = 'orderBy',
  78 + LIMIT = 'limit',
  79 + USE_METADATA_INTERVAL_PATTERNS = 'useMetadataIntervalPatterns',
  80 + START_INTERVAL = 'startInterval',
  81 + START_INTERVAL_TIME_UNIT = 'startIntervalTimeUnit',
  82 + END_INTERVAL = 'endInterval',
  83 + END_INTERVAL_TIME_UNIT = 'endIntervalTimeUnit',
  84 + START_INTERVAL_PATTERN = 'startIntervalPattern',
  85 + END_INTERVAL_PATTERN = 'endIntervalPattern',
  86 +}
  87 +
  88 +export enum OriginatorTelemetryFieldsNameEnum {
  89 + LATEST_TS_KEY_NAMES = 'Latest timeseries',
  90 + AGGREGATION = '数据聚合功能',
  91 + FETCH_MODE = 'Fetch Mode',
  92 + ORDER_BY = 'Order by',
  93 + LIMIT = 'Limit',
  94 + USE_METADATA_INTERVAL_PATTERNS = 'useMetadataIntervalPatterns',
  95 + START_INTERVAL = 'Start Interval',
  96 + START_INTERVAL_TIME_UNIT = 'Start Interval Time Unit',
  97 + END_INTERVAL = 'End Interval',
  98 + END_INTERVAL_TIME_UNIT = 'End Interval Time Unit',
  99 + START_INTERVAL_PATTERN = 'startIntervalPattern',
  100 + END_INTERVAL_PATTERN = 'endIntervalPattern',
  101 +}
  102 +
  103 +// Enrichment Related attributes
  104 +export enum RelatedAttributesFieldsEnum {
  105 + RELATIONS_QUERY = 'relationsQuery',
  106 + ATTR_MAPPING = 'attrMapping',
  107 + TELEMETRY = 'telemetry',
  108 +}
  109 +
  110 +export enum RelatedAttributesFieldsNameEnum {
  111 + RELATIONS_QUERY = 'Relations query',
  112 + ATTR_MAPPING = 'Attributes mapping',
  113 + TELEMETRY = 'Latest telemetry',
  114 +}
  115 +
  116 +// Enrichment Related device Attributes
  117 +export enum RelatedDeviceAttributeFieldsEnum {
  118 + DEVICE_RELATIONS_QUERY = 'deviceRelationsQuery',
  119 + TELL_FAILURE_IF_ABSENT = 'tellFailureIfAbsent',
  120 + CLIENT_ATTRIBUTE_NAMES = 'clientAttributeNames',
  121 + SHARED_ATTRIBUTE_NAMES = 'sharedAttributeNames',
  122 + SERVER_ATTRIBUTE_NAMES = 'serverAttributeNames',
  123 + LATEST_TS_KEY_NAMES = 'latestTsKeyNames',
  124 + GET_LATEST_VALUE_WITH_TS = 'getLatestValueWithTs',
  125 + FETCH_LAST_LEVEL_ONLY = 'fetchLastLevelOnly',
  126 +
  127 + // DEVICE_RELATIONS_QUERY
  128 + DIRECTION = 'direction',
  129 + MAX_LEVEL = 'maxLevel',
  130 + RELATION_TYPE = 'relationType',
  131 + DEVICE_TYPES = 'deviceTypes',
  132 +}
  133 +
  134 +export enum RelatedDeviceAttributeFieldsNameEnum {
  135 + DEVICE_RELATIONS_QUERY = 'deviceRelationsQuery',
  136 + TELL_FAILURE_IF_ABSENT = 'Tell Failure',
  137 + CLIENT_ATTRIBUTE_NAMES = 'Client attributes',
  138 + SHARED_ATTRIBUTE_NAMES = 'Shared attributes',
  139 + SERVER_ATTRIBUTE_NAMES = 'Server attributes',
  140 + LATEST_TS_KEY_NAMES = 'Latest timeseries',
  141 + GET_LATEST_VALUE_WITH_TS = 'Fetch Latest telemetry with Timestamp',
  142 + FETCH_LAST_LEVEL_ONLY = '仅获取最后一级关联',
  143 +
  144 + // DEVICE_RELATIONS_QUERY
  145 + DIRECTION = '方向',
  146 + MAX_LEVEL = 'Max relation level',
  147 + RELATION_TYPE = '关联类型',
  148 + DEVICE_TYPES = '设备类型',
  149 +}
  150 +
  151 +// Tenant attributes
  152 +export enum TenantAttributesFieldsEnum {
  153 + ATTR_MAPING = 'attrMapping',
  154 + TELEMETRY = 'telemetry',
  155 +}
  156 +
  157 +export enum TenantAttributesFieldsNameEnum {
  158 + ATTR_MAPING = 'attrMapping',
  159 + TELEMETRY = 'Latest telemetry',
  160 +}
  161 +
  162 +// Enrichment Tenant details
  163 +export enum TenantDetailsFieldsEnum {
  164 + DETAILS_LIST = 'detailsList',
  165 + ADD_TO_METADATA = 'addToMetadata',
  166 +}
  167 +
  168 +export enum TenantDetailsFieldsNameEnum {
  169 + DETAILS_LIST = 'Add selected details to message metadata',
  170 + ADD_TO_METADATA = 'Select entity details',
  171 +}
  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 = 'Request method',
  248 + USE_SIMPLE_CLIENT_HTTP_FACTORY = 'Use simple client HTTP factory',
  249 + IGNORE_REQUEST_BODY = 'Without request body',
  250 + ENABLE_PROXY = 'Enable proxy',
  251 + USE_SYSTEM_PROXY_PROPERTIES = 'Use system proxy properties',
  252 + PROXY_SCHEME = 'Proxy scheme',
  253 + PROXY_HOST = 'Proxy host',
  254 + PROXY_PORT = 'Proxy port',
  255 + PROXY_USER = 'Proxy user',
  256 + PROXY_PASSWORD = 'Proxy password',
  257 + READ_TIMEOUT_MS = 'Read timeout in millis',
  258 + MAX_PARALLEL_REQUESTS_COUNT = 'Max number of parallel request',
  259 + HEADERS = 'Header',
  260 + USE_REDIS_QUEUE_FOR_MSG_PERSISTENCE = 'Use redis queue for message persistence',
  261 + TRIM_QUEUE = 'Trim redis queue',
  262 + MAX_QUEUE_SIZE = 'Redis queue max size',
  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 +// Filter Check Alarm Status Fields
  2 +export enum CheckAlarmStatusFieldEnum {
  3 + ALARM_STATUS_LIST = 'alarmStatusList',
  4 +}
  5 +
  6 +export enum CheckAlarmStatusFieldNameEnum {
  7 + ALARM_STATUS_LIST = 'Alarm status filter',
  8 +}
  9 +
  10 +// Filter CHeck Existence Fields
  11 +export enum CheckExistenceFieldsEnum {
  12 + MESSAGE_NAMES = 'messageNames',
  13 + METADATA_NAMES = 'metadataNames',
  14 + CHECK_ALL_KEYS = 'checkAllKeys',
  15 +}
  16 +
  17 +export enum CheckExistenceFieldsNameEnum {
  18 + MESSAGE_NAMES = '消息数据',
  19 + METADATA_NAMES = '消息元数据',
  20 + CHECK_ALL_KEYS = '检查所有选择的键是否都存在',
  21 +}
  22 +
  23 +// Filter Check Relation Fields
  24 +export enum CheckRelationFieldsEnum {
  25 + DIRECTION = 'direction',
  26 + CHECK_FOR_SINGLE_ENTITY = 'checkForSingleEntity',
  27 + ENTITY_TYPE = 'entityType',
  28 + RELEATION_TYPE = 'relationType',
  29 + ENTITY_ID = 'entityId',
  30 +}
  31 +
  32 +export enum CheckRelationFieldsNameEnum {
  33 + DIRECTION = '方向',
  34 + CHECK_FOR_SINGLE_ENTITY = 'Check relation to specific entity',
  35 + ENTITY_TYPE = '类型',
  36 + RELEATION_TYPE = '关联类型',
  37 +}
  38 +
  39 +// Filter Gps geofencing filter
  40 +export enum GpsGeofencingFilterFieldsEnum {
  41 + LATITUDE_KEY_NAME = 'latitudeKeyName',
  42 + LONGITUDE_KEY_NAME = 'longitudeKeyName',
  43 + PERIMETER_TYPE = 'perimeterType',
  44 + FETCH_PERIMETER_INFO_FROM_MESSAGE_METADATA = 'fetchPerimeterInfoFromMessageMetadata',
  45 + PERIMETER_KEY_NAME = 'perimeterKeyName',
  46 + CENTER_LATITUDE = 'centerLatitude',
  47 + CENTER_LONGITUDE = 'centerLongitude',
  48 + RANGE = 'range',
  49 + RANGE_UNIT = 'rangeUnit',
  50 + POLYGONS_DEFINITION = 'polygonsDefinition',
  51 +}
  52 +
  53 +export enum GpsGeofencingFilterFieldsNameEnum {
  54 + LATITUDE_KEY_NAME = 'Latitude Key Name',
  55 + LONGITUDE_KEY_NAME = 'Longitude Key Name',
  56 + PERIMETER_TYPE = 'Perimeter Type',
  57 + FETCH_PERIMETER_INFO_FROM_MESSAGE_METADATA = 'Fetch perimeter information from message metadata',
  58 + CENTER_LATITUDE = 'Center latitude',
  59 + CENTER_LONGITUDE = 'Center longitude',
  60 + RANGE = 'Range',
  61 + RANGE_UNIT = 'Range unit',
  62 + PERIMETER_KEY_NAME = 'Perimeter key name',
  63 + POLYGONS_DEFINITION = 'Polygons definition',
  64 +}
  65 +
  66 +// Filter Message Type
  67 +export enum MessageTypeFieldsEnum {
  68 + MESSAGE_TYPES = 'messageTypes',
  69 +}
  70 +
  71 +export enum MessageTypeFieldsNameEnum {
  72 + MESSAGE_TYPES = 'Message Types Filter',
  73 +}
  74 +
  75 +// Filter Originator Type
  76 +export enum OriginatorTypeFieldsEnum {
  77 + ORIGINATOR_TYPES = 'originatorTypes',
  78 +}
  79 +
  80 +export enum OriginatorTypeFieldsNameEnum {
  81 + ORIGINATOR_TYPES = 'Originator types filter',
  82 +}
  83 +
  84 +// Filter Script
  85 +export enum ScriptFieldsEnum {
  86 + JS_SCRIPT = 'jsScript',
  87 +}
  88 +
  89 +export enum ScriptFieldsNameEnum {
  90 + JS_SCRIPT = 'Filter',
  91 +}
  92 +
  93 +// Filter Switch
  94 +export enum SwitchFieldsEnum {
  95 + JS_SCRIPT = 'jsScript',
  96 +}
  97 +
  98 +export enum SwitchFieldsNameEnum {
  99 + JS_SCRIPT = 'Filter',
  100 +}
  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 +}
  1 +// Change originator
  2 +export enum ChangeOriginatorFieldsEnum {
  3 + ORIGINATOR_SOURCE = 'originatorSource',
  4 + RELATIONS_QUERY = 'relationsQuery',
  5 +}
  6 +
  7 +export enum ChangeOriginatorFieldsNameEnum {
  8 + ORIGINATOR_SOURCE = 'Originator source',
  9 + RELATIONS_QUERY = 'Relations Query',
  10 +}
  11 +
  12 +export enum ScriptFieldsEnum {
  13 + JS_SCRIPT = 'jsScript',
  14 +}
  15 +
  16 +export enum ScriptFieldsNameEnum {
  17 + JS_SCRIPT = 'Transform',
  18 +}
  19 +
  20 +export enum ToEmailFieldsEnum {
  21 + FROM_TEMPLATE = 'fromTemplate',
  22 + TO_TEMPLATE = 'toTemplate',
  23 + CC_TEMPLATE = 'ccTemplate',
  24 + BCC_TEMPLATE = 'bccTemplate',
  25 + SUBJECT_TEMPLATE = 'subjectTemplate',
  26 + MAIL_BODY_TYPE = 'mailBodyType',
  27 + IS_HTML_TEMPLATE = 'isHtmlTemplate',
  28 + BODY_TEMPLATE = 'bodyTemplate',
  29 +}
  30 +
  31 +export enum ToEmailFieldsNameEnum {
  32 + FROM_TEMPLATE = 'From Template',
  33 + TO_TEMPLATE = 'To Template',
  34 + CC_TEMPLATE = 'Cc Template',
  35 + BCC_TEMPLATE = 'Bcc Template',
  36 + SUBJECT_TEMPLATE = 'Subject Template',
  37 + MAIL_BODY_TYPE = 'Mail body type',
  38 + IS_HTML_TEMPLATE = 'Dynamic mail body type',
  39 + BODY_TEMPLATE = 'Body Template',
  40 +}
  1 +export enum HandleTypeEnum {
  2 + SOURCE = 'source',
  3 + TARGET = 'target',
  4 +}
  5 +
  6 +export enum NodeTypeEnum {
  7 + CUSTOM = 'custom',
  8 +}
  9 +
  10 +export enum EdgeTypeEnum {
  11 + CUSTOM = 'custom',
  12 +}
  13 +
  14 +export enum BasicConnectionModalEnum {
  15 + BASIC = 'BASIC',
  16 + CUSTOM = 'CUSTOM',
  17 +}
  18 +
  19 +export enum MarkerArrowEnum {
  20 + BASIC_ARROW = 'basicArrow',
  21 + BASIC_ARROW_SELECTED = 'basicArrowSelected',
  22 +}
  23 +
  24 +export enum ElementsTypeEnum {
  25 + NODE = 'NODE',
  26 + EDGE = 'EDGE',
  27 +}
  1 +import { CommonFields, CommonFieldsName } from './formField/common';
  2 +
  3 +export enum FetchNodeComFlagTypeENum {
  4 + CONNECTION_MODAL = 'CONNECTION_MODAL',
  5 + CREATE_MODAL = 'CREATE_MODAL',
  6 +}
  7 +
  8 +export enum EdgeBindDataFieldEnum {
  9 + TYPE = 'type',
  10 +}
  11 +
  12 +export enum EdgeBindDataFieldNameEnum {
  13 + TYPE = '链接标签',
  14 +}
  15 +
  16 +export const NodeBindDataFieldEnum = {
  17 + ...CommonFields,
  18 +};
  19 +
  20 +export const NodeBindDataFieldNameEnum = {
  21 + ...CommonFieldsName,
  22 +};
  1 +import type { Connection } from '@vue-flow/core';
  2 +import { EdgeTypeEnum } from '../enum';
  3 +import type { EdgeData } from '../types/node';
  4 +import { buildUUID } from '/@/utils/uuid';
  5 +
  6 +export function useAddEdges() {
  7 + const getAddedgesParams = (params: Connection, data: string | string[] | any) => {
  8 + return { type: EdgeTypeEnum.CUSTOM, data: { data } as EdgeData, id: buildUUID(), ...params };
  9 + };
  10 +
  11 + return { getAddedgesParams };
  12 +}
  1 +import { Node } from '@vue-flow/core';
  2 +import { NodeTypeEnum } from '../enum';
  3 +import { buildUUID } from '/@/utils/uuid';
  4 +
  5 +export const useAddNodes = () => {
  6 + const getAddNodesParams = (
  7 + position: Node['position'],
  8 + data: object,
  9 + options?: Partial<Node>
  10 + ): Node => {
  11 + return {
  12 + id: buildUUID(),
  13 + type: NodeTypeEnum.CUSTOM,
  14 + position,
  15 + data,
  16 + ...options,
  17 + };
  18 + };
  19 +
  20 + return { getAddNodesParams };
  21 +};
  1 +import { Component, computed, ref, shallowRef, toRaw, unref } from 'vue';
  2 +import { fetchConnectionComponent, fetchCreateComponent } from '../packages';
  3 +import { CreateModalDefineExposeType, AwaitPopupWindowReturnDataType } from '../types';
  4 +import { EdgeData, NodeData } from '../types/node';
  5 +import { DataActionModeEnum } from '/@/enums/toolEnum';
  6 +import { ElementsTypeEnum } from '../enum';
  7 +
  8 +interface OptionsType {
  9 + mode: DataActionModeEnum;
  10 + type: ElementsTypeEnum;
  11 +}
  12 +
  13 +export interface ElementInfo {
  14 + id: string;
  15 + type: ElementsTypeEnum;
  16 +}
  17 +
  18 +export function useAwaitPopupWindowBindData(
  19 + options: OptionsType = { mode: DataActionModeEnum.CREATE, type: ElementsTypeEnum.NODE }
  20 +) {
  21 + const { type, mode } = options;
  22 +
  23 + const visible = ref(false);
  24 +
  25 + const nodeData = ref<NodeData>();
  26 +
  27 + const edgeData = ref<EdgeData>();
  28 +
  29 + const spinning = ref(false);
  30 +
  31 + const resolveFn = ref<(options: AwaitPopupWindowReturnDataType) => void>();
  32 +
  33 + const createComponentEl = ref<Nullable<CreateModalDefineExposeType>>();
  34 +
  35 + const shadowComponent = shallowRef<Nullable<Component>>();
  36 +
  37 + const elementInfo = ref<ElementInfo>();
  38 +
  39 + const getNodeSetValue = computed(() => {
  40 + return unref(mode) === DataActionModeEnum.CREATE
  41 + ? unref(nodeData)?.config?.configurationDescriptor.nodeDefinition.defaultConfiguration
  42 + : unref(nodeData)?.data?.configuration;
  43 + });
  44 +
  45 + const getEdgeSetValue = computed(() => {
  46 + return {
  47 + type:
  48 + unref(mode) === DataActionModeEnum.CREATE
  49 + ? unref(nodeData)?.config?.configurationDescriptor?.nodeDefinition.relationTypes
  50 + : unref(edgeData)?.data,
  51 + };
  52 + });
  53 +
  54 + const getSetValue = computed(() =>
  55 + unref(type) === ElementsTypeEnum.EDGE ? unref(getEdgeSetValue) : unref(getNodeSetValue)
  56 + );
  57 +
  58 + const getComponentKey = computed(() => unref(nodeData)?.config?.key);
  59 +
  60 + const handleFetchComponent = async (nodeOptions: NodeData, edgeOptions?: EdgeData) => {
  61 + nodeData.value = nodeOptions;
  62 + edgeData.value = edgeOptions;
  63 +
  64 + spinning.value = true;
  65 + shadowComponent.value = null;
  66 + const modules =
  67 + ElementsTypeEnum.NODE === type
  68 + ? await fetchCreateComponent(nodeOptions.config!)
  69 + : await fetchConnectionComponent(nodeOptions.config!);
  70 +
  71 + const component = (await modules?.()) as Record<'default', Component>;
  72 + shadowComponent.value = component?.default;
  73 + spinning.value = false;
  74 + };
  75 +
  76 + const open = async (
  77 + nodeData: NodeData,
  78 + edgeData?: EdgeData,
  79 + _elementInfo?: ElementInfo
  80 + ): Promise<AwaitPopupWindowReturnDataType> => {
  81 + elementInfo.value = _elementInfo;
  82 + await handleFetchComponent(nodeData, edgeData);
  83 + return new Promise((_resolve) => {
  84 + visible.value = true;
  85 + resolveFn.value = _resolve;
  86 + });
  87 + };
  88 +
  89 + const handleOnMounted = () => {
  90 + unref(createComponentEl)?.setFieldsValue?.(toRaw(unref(getSetValue)), toRaw(unref(nodeData)));
  91 + };
  92 +
  93 + const validate = async () => {
  94 + return (await unref(createComponentEl)?.validate?.()) || { flag: true };
  95 + };
  96 +
  97 + const handleSubmitData = (extraData: Recordable = {}, value: Recordable = {}) => {
  98 + return Object.assign(
  99 + {},
  100 + extraData,
  101 + unref(type) === ElementsTypeEnum.NODE ? { configuration: toRaw(unref(value)) } : value
  102 + );
  103 + };
  104 +
  105 + const handleSubmit = async (extraData: Recordable = {}) => {
  106 + const { flag } = await validate();
  107 + if (!flag) return;
  108 + const value = await unref(createComponentEl)?.getFieldsValue?.();
  109 + unref(resolveFn)?.({
  110 + flag: true,
  111 + data: handleSubmitData(extraData, value),
  112 + } as AwaitPopupWindowReturnDataType);
  113 + visible.value = false;
  114 + };
  115 +
  116 + const handleCancel = () => {
  117 + unref(resolveFn)?.({ flag: false, data: null } as AwaitPopupWindowReturnDataType);
  118 + visible.value = false;
  119 + };
  120 +
  121 + return {
  122 + visible,
  123 + nodeData,
  124 + spinning,
  125 + elementInfo,
  126 + shadowComponent,
  127 + createComponentEl,
  128 + getComponentKey,
  129 + open,
  130 + handleOnMounted,
  131 + handleSubmit,
  132 + handleCancel,
  133 + };
  134 +}
  1 +import { Ref, toRaw, unref } from 'vue';
  2 +import { BasicNodeBindData, NodeData } from '../types/node';
  3 +import { Elements, GraphNode } from '@vue-flow/core';
  4 +import { RuleChainType } from '../types/ruleNode';
  5 +import { allComponents } from '../packages';
  6 +import { RuleNodeTypeEnum } from '../packages/index.type';
  7 +import { buildUUID } from '/@/utils/uuid';
  8 +import { isNullOrUnDef } from '/@/utils/is';
  9 +import { useAddNodes } from './useAddNodes';
  10 +import { useAddEdges } from './useAddEdges';
  11 +
  12 +export function useBasicDataTransform() {
  13 + const nodeConfigMap = new Map<string, NodeData>();
  14 +
  15 + function initNodeConfigMap() {
  16 + for (const key of Object.keys(allComponents)) {
  17 + const category = allComponents[key as RuleNodeTypeEnum];
  18 + for (const nodeConfig of category.components) {
  19 + const { clazz } = nodeConfig;
  20 + nodeConfigMap.set(clazz, { config: nodeConfig, category: category.category });
  21 + }
  22 + }
  23 + }
  24 +
  25 + function mergeData(data: NodeData['data'], nodeData: NodeData, node: GraphNode) {
  26 + const { x: layoutX, y: layoutY } = node.computedPosition;
  27 +
  28 + return {
  29 + debugMode: !!data?.debugMode,
  30 + name: data?.name,
  31 + type: nodeData.config?.clazz,
  32 + configuration: toRaw(unref(data?.configuration)),
  33 + additionalInfo: {
  34 + description: data?.description,
  35 + layoutX,
  36 + layoutY,
  37 + },
  38 + };
  39 + }
  40 +
  41 + function deconstructionConnection(
  42 + ruleChain: Ref<RuleChainType> | RuleChainType,
  43 + inputNodeId: string
  44 + ) {
  45 + const { connections, nodes, firstNodeIndex } = unref(ruleChain);
  46 + const indexMap = new Map<number, BasicNodeBindData>();
  47 + const groupByConnections = new Map<string, string[]>();
  48 + const SOURCE_HANDLE = '__handle-right';
  49 + const TARGET_HANDLE = '__handle-left';
  50 + const SEPARATOR = ',';
  51 + const edges: Elements = [];
  52 + const { getAddedgesParams } = useAddEdges();
  53 +
  54 + nodes.forEach((item, index) => indexMap.set(index, item));
  55 +
  56 + (connections || []).forEach((item) => {
  57 + const { fromIndex, toIndex, type } = item;
  58 + const key = `${fromIndex}${SEPARATOR}${toIndex}`;
  59 + if (!groupByConnections.has(key)) groupByConnections.set(key, []);
  60 +
  61 + const types = groupByConnections.get(key)!;
  62 + types.push(type);
  63 + });
  64 +
  65 + for (const [key, types] of Array.from(groupByConnections.entries())) {
  66 + const [fromIndex, toIndex] = key.split(SEPARATOR);
  67 +
  68 + const sourceNode = indexMap.get(Number(fromIndex));
  69 + const targetNode = indexMap.get(Number(toIndex));
  70 + const source = sourceNode!.id!.id;
  71 + const target = targetNode!.id!.id;
  72 + const sourceHandle = `${source}${SOURCE_HANDLE}`;
  73 + const targetHandle = `${target}${TARGET_HANDLE}`;
  74 +
  75 + edges.push(
  76 + getAddedgesParams(
  77 + {
  78 + source,
  79 + target,
  80 + sourceHandle,
  81 + targetHandle,
  82 + },
  83 + { type: types }
  84 + )
  85 + );
  86 + }
  87 +
  88 + if (!isNullOrUnDef(firstNodeIndex)) {
  89 + const targetNode = indexMap.get(firstNodeIndex);
  90 + const source = inputNodeId;
  91 + const target = targetNode!.id!.id;
  92 + const sourceHandle = `${source}$${SOURCE_HANDLE}`;
  93 + const targetHandle = `${target}${TARGET_HANDLE}`;
  94 + edges.push(
  95 + getAddedgesParams(
  96 + {
  97 + source,
  98 + target,
  99 + sourceHandle,
  100 + targetHandle,
  101 + },
  102 + {}
  103 + )
  104 + );
  105 + }
  106 +
  107 + return edges;
  108 + }
  109 +
  110 + function genNewNodeByData(node: BasicNodeBindData, config: NodeData) {
  111 + const { additionalInfo, configuration, debugMode, name, id } = node;
  112 + const { layoutX, layoutY, description } = additionalInfo || {};
  113 + const { getAddNodesParams } = useAddNodes();
  114 +
  115 + return getAddNodesParams(
  116 + { x: layoutX!, y: layoutY! },
  117 + {
  118 + ...config,
  119 + data: {
  120 + configuration,
  121 + debugMode,
  122 + description,
  123 + name,
  124 + },
  125 + },
  126 + {
  127 + id: id?.id || buildUUID(),
  128 + }
  129 + );
  130 + }
  131 +
  132 + function deconstructionNode(nodes: RuleChainType['nodes']) {
  133 + const addNodes: Elements = [];
  134 + for (const node of unref(nodes)) {
  135 + const { type } = node;
  136 + if (!type) continue;
  137 + const nodeConfig = nodeConfigMap.get(type);
  138 +
  139 + if (!nodeConfig) {
  140 + throw `No component configuration of type '${type}' was found`;
  141 + }
  142 +
  143 + const newNode = genNewNodeByData(node, nodeConfig);
  144 +
  145 + addNodes.push(newNode);
  146 + }
  147 + return addNodes;
  148 + }
  149 +
  150 + function deconstructionData(
  151 + ruleChain: RuleChainType | Ref<RuleChainType | undefined>,
  152 + inputNodeId: string
  153 + ) {
  154 + if (!ruleChain || !unref(ruleChain)) return;
  155 + ruleChain = toRaw(unref(ruleChain))!;
  156 +
  157 + const nodes = deconstructionNode(ruleChain?.nodes || []);
  158 +
  159 + const edges = deconstructionConnection(ruleChain!, inputNodeId);
  160 +
  161 + return {
  162 + nodes,
  163 + edges,
  164 + };
  165 + }
  166 +
  167 + initNodeConfigMap();
  168 +
  169 + return {
  170 + mergeData,
  171 + deconstructionData,
  172 + };
  173 +}
  1 +import { EdgeProps, useEdge } from '@vue-flow/core';
  2 +import { computed, unref } from 'vue';
  3 +import { MarkerArrowEnum } from '../enum';
  4 +
  5 +export const useConnectionFocus = (props: EdgeProps) => {
  6 + const { edge } = useEdge(props.id);
  7 +
  8 + const getSelected = computed(() => unref(edge)?.selected);
  9 +
  10 + const getMarkerEnd = computed(
  11 + () =>
  12 + `url(#${
  13 + unref(getSelected) ? MarkerArrowEnum.BASIC_ARROW_SELECTED : MarkerArrowEnum.BASIC_ARROW
  14 + })`
  15 + );
  16 +
  17 + return {
  18 + getMarkerEnd,
  19 + getSelected,
  20 + };
  21 +};
  1 +import type { NodeItemConfigType } from '../types/node';
  2 +
  3 +export function useCreateNodeKey(scope: string) {
  4 + return {
  5 + key: scope,
  6 + createComponentKey: `VC_CREATE_${scope}`,
  7 + connectionComponentKey: `VCC_CONNECTION_${scope}`,
  8 + } as NodeItemConfigType;
  9 +}
  1 +import { Ref, toRaw, unref } from 'vue';
  2 +import { NodeData } from '../types/node';
  3 +
  4 +export function useDataTool() {
  5 + /**
  6 + * @description 通过NodeData获取默认配置信息
  7 + * @param nodeData
  8 + * @returns
  9 + */
  10 + function getDefaultConfigurationByNodeData<T = any>(nodeData: Ref<NodeData> | NodeData) {
  11 + nodeData = toRaw(unref(nodeData));
  12 +
  13 + const { nodeDefinition } = nodeData.config?.configurationDescriptor || {};
  14 + const { defaultConfiguration } = nodeDefinition || {};
  15 +
  16 + return { defaultConfiguration: defaultConfiguration as T };
  17 + }
  18 +
  19 + /**
  20 + * @description 通过NodeData获取节点绑定信息
  21 + * @param nodeData
  22 + * @returns
  23 + */
  24 + function getBindDataByNodeData<T = any>(nodeData: Ref<NodeData> | NodeData) {
  25 + nodeData = toRaw(unref(nodeData));
  26 +
  27 + const data = nodeData.data as T;
  28 +
  29 + return { data };
  30 + }
  31 +
  32 + return { getDefaultConfigurationByNodeData, getBindDataByNodeData };
  33 +}
  1 +import { Ref, toRaw, unref } from 'vue';
  2 +import type { VueFlowStore } from '@vue-flow/core';
  3 +import type { FlowElRef } from '../types/flow';
  4 +import type { CreateNodeModal } from '../src/components/CreateNodeModal';
  5 +import type { DragTransferData } from '../types/node';
  6 +import { useAddNodes } from './useAddNodes';
  7 +
  8 +type EffectSymbol = DataTransfer['dropEffect'];
  9 +
  10 +export const TRANSFER_DATA_KEY = 'NODE_INFO';
  11 +export const EFFECT_SYMBOL: EffectSymbol = 'move';
  12 +
  13 +interface UseDragCreateOptionsType {
  14 + el: Ref<Nullable<FlowElRef>>;
  15 + createNodeModalActionType: Ref<Nullable<InstanceType<typeof CreateNodeModal>>>;
  16 + flowActionType: VueFlowStore;
  17 + triggerChange: () => void;
  18 +}
  19 +
  20 +export function useDragCreate(options: UseDragCreateOptionsType) {
  21 + const { el, createNodeModalActionType, flowActionType, triggerChange } = options;
  22 + const { project, addNodes } = flowActionType;
  23 + const { getAddNodesParams } = useAddNodes();
  24 +
  25 + const handleOnDrop = async (event: DragEvent) => {
  26 + const value = event.dataTransfer?.getData(TRANSFER_DATA_KEY);
  27 + const transferData: DragTransferData = JSON.parse(value || '');
  28 + const { options: nodeData, offsetX, offsetY } = transferData;
  29 + const { flag, data } = (await unref(createNodeModalActionType)?.open(nodeData)) || {};
  30 + if (!flag) return;
  31 +
  32 + const flowBounds = unref(el)?.$el.getBoundingClientRect() as DOMRect;
  33 +
  34 + const position = project({
  35 + x: event.clientX - flowBounds.left - offsetX,
  36 + y: event.clientY - flowBounds.top - offsetY,
  37 + });
  38 +
  39 + const newNode = getAddNodesParams(position, { ...toRaw(unref(nodeData)), data });
  40 + addNodes(newNode);
  41 +
  42 + triggerChange();
  43 + };
  44 +
  45 + const handleOnDragOver = (event: DragEvent) => {
  46 + event.preventDefault();
  47 +
  48 + if (event.dataTransfer) event.dataTransfer.dropEffect = EFFECT_SYMBOL;
  49 + };
  50 +
  51 + const handleOnDragStart = (event: DragEvent, options: object) => {
  52 + if (event.dataTransfer) {
  53 + event.dataTransfer.setData(TRANSFER_DATA_KEY, JSON.stringify(options));
  54 + event.dataTransfer.effectAllowed = EFFECT_SYMBOL;
  55 + }
  56 + };
  57 +
  58 + return {
  59 + handleOnDrop,
  60 + handleOnDragOver,
  61 + handleOnDragStart,
  62 + };
  63 +}
  1 +import type { Ref } from 'vue';
  2 +import { inject, provide } from 'vue';
  3 +import type { VueFlowStore } from '@vue-flow/core';
  4 +import type { CreateNodeModal } from '../src/components/CreateNodeModal';
  5 +import type { CreateEdgeModal } from '../src/components/CreateEdgeModal';
  6 +import { UpdateNodeDrawer } from '../src/components/UpdateNodeDrawer';
  7 +import { UpdateEdgeDrawer } from '../src/components/UpdateEdgeDrawer';
  8 +
  9 +const SYMBOL = Symbol('flow-context');
  10 +
  11 +interface FlowContextOptionsType {
  12 + /**
  13 + * @description 节点 actions
  14 + */
  15 + createNodeModalActionType: Ref<Nullable<InstanceType<typeof CreateNodeModal>>>;
  16 +
  17 + /**
  18 + * @description 连接线 actions
  19 + */
  20 + createEdgeModalActionType: Ref<Nullable<InstanceType<typeof CreateEdgeModal>>>;
  21 +
  22 + /**
  23 + * @description 节点更新 actions
  24 + */
  25 + updateNodeDrawerActionType: Ref<Nullable<InstanceType<typeof UpdateNodeDrawer>>>;
  26 +
  27 + /**
  28 + * @description 连接线更新 actions
  29 + */
  30 + updateEdgeDrawerActionType: Ref<Nullable<InstanceType<typeof UpdateEdgeDrawer>>>;
  31 +
  32 + /**
  33 + * @description vue flow store
  34 + */
  35 + flowActionType: VueFlowStore;
  36 +
  37 + /**
  38 + * @description 更新变化
  39 + */
  40 + triggerChange: () => void;
  41 +}
  42 +
  43 +export const createFlowContext = (options: FlowContextOptionsType) => {
  44 + provide(SYMBOL, options);
  45 +};
  46 +
  47 +export const useFlowContext = () => {
  48 + return inject(SYMBOL) as FlowContextOptionsType;
  49 +};
  1 +import { useFullscreen } from '@vueuse/core';
  2 +import type { Ref } from 'vue';
  3 +import { computed, unref } from 'vue';
  4 +
  5 +export function useFullScreen(el: Ref<Nullable<HTMLDivElement>>) {
  6 + const { toggle, isFullscreen } = useFullscreen(el);
  7 +
  8 + const getFullScreenIcon = computed(() =>
  9 + unref(isFullscreen) ? 'bx:exit-fullscreen' : 'mdi:fullscreen'
  10 + );
  11 +
  12 + return {
  13 + getFullScreenIcon,
  14 + handleFullScreen: toggle,
  15 + };
  16 +}
  1 +import { Config as InputConfig } from '../packages/Entry/Input/config';
  2 +import { useAddNodes } from './useAddNodes';
  3 +
  4 +export function useInputNode() {
  5 + const getInputNodeConfig = (id?: string) => {
  6 + const { getAddNodesParams } = useAddNodes();
  7 +
  8 + const newNode = getAddNodesParams(
  9 + { x: 80, y: 50 },
  10 + {
  11 + ...new InputConfig(),
  12 + data: {
  13 + name: '输入',
  14 + description: '规则链的逻辑输入,将传入消息转发到下一个相关规则节点。',
  15 + },
  16 + },
  17 + { id, draggable: false, selectable: false }
  18 + );
  19 +
  20 + return newNode;
  21 + };
  22 +
  23 + return { getInputNodeConfig };
  24 +}
  1 +import type {
  2 + Connection,
  3 + EdgeComponent,
  4 + NodeComponent,
  5 + ValidConnectionFunc,
  6 + GraphNode,
  7 +} from '@vue-flow/core';
  8 +import { ConnectionLineType, SelectionMode, useVueFlow } from '@vue-flow/core';
  9 +import type { Ref } from 'vue';
  10 +import { markRaw, toRaw, unref } from 'vue';
  11 +import { isFunction } from 'lodash-es';
  12 +import type { CreateNodeModal } from '../src/components/CreateNodeModal';
  13 +import { EdgeTypeEnum, ElementsTypeEnum, NodeTypeEnum } from '../enum';
  14 +import { BasicEdge, BasicNode } from '../src/components';
  15 +import type { EdgeData, NodeData } from '../types/node';
  16 +import type { CreateEdgeModal } from '../src/components/CreateEdgeModal';
  17 +import { isInputHandle, isOutputHandle } from '../utils';
  18 +import { useAddEdges } from './useAddEdges';
  19 +import { UpdateNodeDrawer } from '../src/components/UpdateNodeDrawer';
  20 +import { UpdateEdgeDrawer } from '../src/components/UpdateEdgeDrawer';
  21 +import { isNumber } from '/@/utils/is';
  22 +
  23 +interface UseRuleFlowOptionsType {
  24 + id: string;
  25 + createNodeModalActionType: Ref<Nullable<InstanceType<typeof CreateNodeModal>>>;
  26 + createEdgeModalActionType: Ref<Nullable<InstanceType<typeof CreateEdgeModal>>>;
  27 + updateNodeDrawerActionType: Ref<Nullable<InstanceType<typeof UpdateNodeDrawer>>>;
  28 + updateEdgeDrawerActionType: Ref<Nullable<InstanceType<typeof UpdateEdgeDrawer>>>;
  29 + triggerChange: () => void;
  30 +}
  31 +
  32 +const validateInputAndOutput: ValidConnectionFunc = (connection: Connection) => {
  33 + const { sourceHandle, targetHandle, source, target } = connection;
  34 +
  35 + return (
  36 + isOutputHandle(sourceHandle || '') && isInputHandle(targetHandle || '') && source !== target
  37 + );
  38 +};
  39 +
  40 +export function useRuleFlow(options: UseRuleFlowOptionsType) {
  41 + const {
  42 + id,
  43 + createEdgeModalActionType,
  44 + updateEdgeDrawerActionType,
  45 + updateNodeDrawerActionType,
  46 + triggerChange,
  47 + } = options;
  48 +
  49 + const flowActionType = useVueFlow({
  50 + id,
  51 + maxZoom: 1,
  52 + minZoom: 1,
  53 + panOnScroll: true,
  54 + selectionMode: SelectionMode.Partial,
  55 + nodeTypes: {
  56 + [NodeTypeEnum.CUSTOM]: markRaw(BasicNode) as NodeComponent,
  57 + },
  58 + edgeTypes: {
  59 + [EdgeTypeEnum.CUSTOM]: markRaw(BasicEdge) as EdgeComponent,
  60 + },
  61 + connectionLineOptions: {
  62 + type: ConnectionLineType.Bezier,
  63 + },
  64 + defaultViewport: {
  65 + x: 0,
  66 + y: 0,
  67 + },
  68 + isValidConnection(connection, elements) {
  69 + const validateList = [validateInputAndOutput];
  70 + const targetData = elements.targetNode.data as NodeData;
  71 +
  72 + if (
  73 + targetData.category?.validateConnection &&
  74 + isFunction(targetData.category.validateConnection)
  75 + )
  76 + validateList.push(targetData.category?.validateConnection);
  77 +
  78 + if (targetData.config?.validateConnection && isFunction(targetData.config.validateConnection))
  79 + validateList.push(targetData.config.validateConnection);
  80 +
  81 + if (!validateList.every((item) => item(connection, elements))) return false;
  82 +
  83 + return true;
  84 + },
  85 + });
  86 +
  87 + const {
  88 + getEdges,
  89 + addEdges,
  90 + findEdge,
  91 + findNode,
  92 + setViewport,
  93 + removeEdges,
  94 + onConnect,
  95 + onPaneReady,
  96 + onNodeDoubleClick,
  97 + onEdgeDoubleClick,
  98 + onNodeDragStop,
  99 + } = flowActionType;
  100 +
  101 + const { getAddedgesParams } = useAddEdges();
  102 +
  103 + onConnect(async (params) => {
  104 + const { source } = params;
  105 + const sourceNode = findNode(source);
  106 + const sourceData = sourceNode?.data as NodeData;
  107 +
  108 + let types: string[] = [];
  109 +
  110 + if (sourceData && validateHasLabelConnection(sourceData)) {
  111 + const { flag, data } = (await unref(createEdgeModalActionType)?.open(sourceData)) || {};
  112 + if (!flag) return;
  113 + types = toRaw(unref(data));
  114 + }
  115 +
  116 + handleMaxConnectionPoint(sourceNode);
  117 +
  118 + addEdges(getAddedgesParams(params, types));
  119 +
  120 + triggerChange();
  121 + });
  122 +
  123 + onPaneReady(async () => {
  124 + setViewport({ x: 0, y: 0, zoom: 1 });
  125 + });
  126 +
  127 + onNodeDoubleClick(async ({ node }) => {
  128 + if ((node.data as NodeData).config?.disableAction) return;
  129 + const { flag, data } =
  130 + (await unref(updateNodeDrawerActionType)?.open(
  131 + toRaw((node as NodeData)?.data as unknown as NodeData),
  132 + void 0,
  133 + { id: node.id, type: ElementsTypeEnum.NODE }
  134 + )) || {};
  135 +
  136 + if (!flag) return;
  137 +
  138 + const currentNode = findNode(node.id);
  139 + (currentNode!.data as NodeData).data = data;
  140 + });
  141 +
  142 + onEdgeDoubleClick(async ({ edge }) => {
  143 + if (!validateHasLabelConnection(edge.sourceNode.data)) return;
  144 +
  145 + if ((edge.sourceNode.data as NodeData).config?.disableAction) return;
  146 +
  147 + const { flag, data } =
  148 + (await unref(updateEdgeDrawerActionType)?.open(
  149 + toRaw(unref(edge.sourceNode?.data as unknown as NodeData)),
  150 + toRaw(unref(edge.data as EdgeData)),
  151 + { id: edge.id, type: ElementsTypeEnum.EDGE }
  152 + )) || {};
  153 +
  154 + if (!flag) return;
  155 +
  156 + const currentEdge = findEdge(edge.id);
  157 +
  158 + (currentEdge!.data as EdgeData).data = toRaw(unref(data));
  159 + });
  160 +
  161 + onNodeDragStop(() => {
  162 + triggerChange();
  163 + });
  164 +
  165 + /**
  166 + * @description 验证是否有连接label
  167 + * @param sourceData
  168 + * @returns
  169 + */
  170 + function validateHasLabelConnection(sourceData: NodeData) {
  171 + return !!sourceData?.config?.configurationDescriptor.nodeDefinition?.relationTypes?.length;
  172 + }
  173 +
  174 + function handleMaxConnectionPoint(sourceNode?: GraphNode) {
  175 + if (!sourceNode) return;
  176 +
  177 + const maxConnectionPoint = unref(sourceNode).data?.config?.maxConnectionPoint;
  178 +
  179 + if (!maxConnectionPoint || !isNumber(maxConnectionPoint)) return;
  180 +
  181 + const sourceId = sourceNode.id;
  182 + const connectionPool = unref(getEdges).filter((item) => item.source === sourceId);
  183 + if (connectionPool.length >= maxConnectionPoint && connectionPool[0]) {
  184 + removeEdges(connectionPool[0].id);
  185 + }
  186 + }
  187 +
  188 + return { flowActionType };
  189 +}
  1 +import type { VueFlowStore, Getters, Elements } from '@vue-flow/core';
  2 +import { ComputedRef, computed, ref, unref } from 'vue';
  3 +import { BasicNodeBindData, EdgeData, NodeData } from '../types/node';
  4 +import { EntryCategoryComponentEnum } from '../enum/category';
  5 +import { useBasicDataTransform } from './useBasicDataTransform';
  6 +import { getRuleChainData, saveRuleChainData } from '/@/api/ruleDesigner';
  7 +import { ConnectionItemType, RuleChainType } from '../types/ruleNode';
  8 +import { useInputNode } from './useInputNode';
  9 +import { buildUUID } from '/@/utils/uuid';
  10 +import { useRoute } from 'vue-router';
  11 +
  12 +const ignoreNodeKeys: string[] = [EntryCategoryComponentEnum.INPUT];
  13 +
  14 +export function useSaveAndRedo() {
  15 + const changeMarker = ref(false);
  16 +
  17 + const loading = ref(false);
  18 +
  19 + const redoDataRef = ref<Elements>([]);
  20 +
  21 + const route = useRoute();
  22 +
  23 + const getRuleChainId = computed(() => (route.params as Record<'id', string>).id);
  24 +
  25 + const { mergeData, deconstructionData } = useBasicDataTransform();
  26 +
  27 + const triggerChange = () => {
  28 + changeMarker.value = true;
  29 + };
  30 +
  31 + const resetChange = () => {
  32 + changeMarker.value = false;
  33 + };
  34 +
  35 + /**
  36 + * @description 保存连接信息
  37 + */
  38 + function getConnections(
  39 + nodesRef: ComputedRef<Getters['getNodes']> | Getters['getNodes'],
  40 + edges: ComputedRef<Getters['getEdges']>
  41 + ) {
  42 + const nodeIndexMap = new Map();
  43 +
  44 + const connections: ConnectionItemType[] = [];
  45 +
  46 + unref(nodesRef).forEach((item, index) => {
  47 + nodeIndexMap.set(item.id, index);
  48 + });
  49 +
  50 + for (const item of unref(edges)) {
  51 + const { data, target, source } = item;
  52 + const { data: bindData } = data as EdgeData;
  53 + const { type } = bindData || {};
  54 + const fromIndex = nodeIndexMap.get(source);
  55 + const toIndex = nodeIndexMap.get(target);
  56 + type?.forEach((key) => {
  57 + connections.push({ fromIndex, toIndex, type: key });
  58 + });
  59 + }
  60 +
  61 + return connections;
  62 + }
  63 +
  64 + function getNodes(nodesRef: ComputedRef<Getters['getNodes']> | Getters['getNodes']) {
  65 + const nodes: BasicNodeBindData[] = [];
  66 +
  67 + for (const node of unref(nodesRef)) {
  68 + const nodeData = node.data as NodeData;
  69 +
  70 + if (ignoreNodeKeys.includes(nodeData.config?.key as string)) continue;
  71 +
  72 + const data = nodeData.data;
  73 +
  74 + nodes.push(mergeData(data, nodeData, node));
  75 + }
  76 +
  77 + return nodes;
  78 + }
  79 +
  80 + function getFirsetNodeIndex(
  81 + nodesRef: ComputedRef<Getters['getNodes']> | Getters['getNodes'],
  82 + edges: ComputedRef<Getters['getEdges']>
  83 + ) {
  84 + const inputNode = unref(edges).find(
  85 + (item) => (item.sourceNode.data as NodeData).config?.key === EntryCategoryComponentEnum.INPUT
  86 + );
  87 +
  88 + if (inputNode) {
  89 + const targetId = inputNode.target;
  90 + const index = unref(nodesRef).findIndex((item) => item.id === targetId);
  91 + return index;
  92 + }
  93 + }
  94 +
  95 + const handleApplyChange = (flowActionType: VueFlowStore) => {
  96 + if (!unref(changeMarker)) return;
  97 +
  98 + const edgesRef = flowActionType.getEdges;
  99 +
  100 + const extraIgnoreNodeRef = unref(flowActionType.getNodes).filter(
  101 + (item) => !ignoreNodeKeys.includes((item.data as NodeData).config?.key as string)
  102 + );
  103 +
  104 + const connections = getConnections(extraIgnoreNodeRef, edgesRef);
  105 +
  106 + const nodes = getNodes(extraIgnoreNodeRef);
  107 +
  108 + const firstNodeIndex = getFirsetNodeIndex(extraIgnoreNodeRef, edgesRef);
  109 +
  110 + handleSaveRuleChain(connections, nodes, firstNodeIndex);
  111 + };
  112 +
  113 + const handleRedoChange = (flowActionType: VueFlowStore) => {
  114 + if (!unref(changeMarker)) return;
  115 + flowActionType.setElements(unref(redoDataRef));
  116 + resetChange();
  117 + };
  118 +
  119 + async function handleSaveRuleChain(
  120 + connections: ConnectionItemType[],
  121 + nodes: BasicNodeBindData[],
  122 + firstNodeIndex?: number
  123 + ) {
  124 + try {
  125 + loading.value = true;
  126 + const data = await saveRuleChainData({
  127 + connections,
  128 + nodes,
  129 + firstNodeIndex,
  130 + ruleChainId: {
  131 + entityType: 'RULE_CHAIN',
  132 + id: unref(getRuleChainId),
  133 + },
  134 + });
  135 +
  136 + parseRuleChain(data);
  137 +
  138 + resetChange();
  139 + } finally {
  140 + loading.value = false;
  141 + }
  142 + }
  143 +
  144 + async function getCurrentPageMetaData(flowActionType: VueFlowStore) {
  145 + try {
  146 + loading.value = true;
  147 +
  148 + const data = await getRuleChainData(unref(getRuleChainId));
  149 +
  150 + const elements = parseRuleChain(data);
  151 +
  152 + flowActionType.setElements(elements);
  153 +
  154 + resetChange();
  155 + } finally {
  156 + loading.value = false;
  157 + }
  158 + }
  159 +
  160 + function parseRuleChain(ruleChain: RuleChainType) {
  161 + const inputId = buildUUID();
  162 +
  163 + const { getInputNodeConfig } = useInputNode();
  164 +
  165 + const value = deconstructionData(ruleChain, inputId);
  166 +
  167 + const { nodes = [], edges = [] } = value || {};
  168 +
  169 + const inputNode = getInputNodeConfig(inputId);
  170 +
  171 + const elements = [inputNode, ...nodes, ...edges];
  172 +
  173 + redoDataRef.value = elements;
  174 +
  175 + return elements;
  176 + }
  177 +
  178 + return {
  179 + loading,
  180 + changeMarker,
  181 + triggerChange,
  182 + handleApplyChange,
  183 + handleRedoChange,
  184 + getCurrentPageMetaData,
  185 + };
  186 +}
  1 +export function useTextWidth(text: string, fontSize: number) {
  2 + const LetterMap = {
  3 + ' ': 0.3329986572265625,
  4 + a: 0.5589996337890625,
  5 + A: 0.6569992065429687,
  6 + b: 0.58599853515625,
  7 + B: 0.6769989013671875,
  8 + c: 0.5469985961914062,
  9 + C: 0.7279998779296875,
  10 + d: 0.58599853515625,
  11 + D: 0.705999755859375,
  12 + e: 0.554998779296875,
  13 + E: 0.63699951171875,
  14 + f: 0.37299957275390627,
  15 + F: 0.5769989013671875,
  16 + g: 0.5909988403320312,
  17 + G: 0.7479995727539063,
  18 + h: 0.555999755859375,
  19 + H: 0.7199996948242188,
  20 + i: 0.255999755859375,
  21 + I: 0.23699951171875,
  22 + j: 0.26699981689453123,
  23 + J: 0.5169998168945312,
  24 + k: 0.5289993286132812,
  25 + K: 0.6899993896484375,
  26 + l: 0.23499908447265624,
  27 + L: 0.5879989624023437,
  28 + m: 0.854998779296875,
  29 + M: 0.8819992065429687,
  30 + n: 0.5589996337890625,
  31 + N: 0.7189987182617188,
  32 + o: 0.58599853515625,
  33 + O: 0.7669998168945312,
  34 + p: 0.58599853515625,
  35 + P: 0.6419998168945312,
  36 + q: 0.58599853515625,
  37 + Q: 0.7669998168945312,
  38 + r: 0.3649993896484375,
  39 + R: 0.6759994506835938,
  40 + s: 0.504998779296875,
  41 + S: 0.6319992065429687,
  42 + t: 0.354998779296875,
  43 + T: 0.6189987182617187,
  44 + u: 0.5599990844726562,
  45 + U: 0.7139999389648437,
  46 + v: 0.48199920654296874,
  47 + V: 0.6389999389648438,
  48 + w: 0.754998779296875,
  49 + W: 0.929998779296875,
  50 + x: 0.5089996337890625,
  51 + X: 0.63699951171875,
  52 + y: 0.4959991455078125,
  53 + Y: 0.66199951171875,
  54 + z: 0.48699951171875,
  55 + Z: 0.6239990234375,
  56 + 0: 0.6,
  57 + 1: 0.40099945068359377,
  58 + 2: 0.6,
  59 + 3: 0.6,
  60 + 4: 0.6,
  61 + 5: 0.6,
  62 + 6: 0.6,
  63 + 7: 0.5469985961914062,
  64 + 8: 0.6,
  65 + 9: 0.6,
  66 + '[': 0.3329986572265625,
  67 + ']': 0.3329986572265625,
  68 + ',': 0.26399993896484375,
  69 + '.': 0.26399993896484375,
  70 + ';': 0.26399993896484375,
  71 + ':': 0.26399993896484375,
  72 + '{': 0.3329986572265625,
  73 + '}': 0.3329986572265625,
  74 + '\\': 0.5,
  75 + '|': 0.19499969482421875,
  76 + '=': 0.604998779296875,
  77 + '+': 0.604998779296875,
  78 + '-': 0.604998779296875,
  79 + _: 0.5,
  80 + '`': 0.3329986572265625,
  81 + ' ~': 0.8329986572265625,
  82 + '!': 0.3329986572265625,
  83 + '@': 0.8579986572265625,
  84 + '#': 0.6,
  85 + $: 0.6,
  86 + '%': 0.9699996948242188,
  87 + '^': 0.517999267578125,
  88 + '&': 0.7259994506835937,
  89 + '*': 0.505999755859375,
  90 + '(': 0.3329986572265625,
  91 + ')': 0.3329986572265625,
  92 + '<': 0.604998779296875,
  93 + '>': 0.604998779296875,
  94 + '/': 0.5,
  95 + '?': 0.53699951171875,
  96 + '"': 0.33699951171875,
  97 + };
  98 +
  99 + // 计算非中文字符宽度
  100 + const getLetterWidth = (letter: string, fontSize: number) => fontSize * (LetterMap[letter] || 1);
  101 +
  102 + // 计算文本宽度
  103 + const getTextWidth = (text: string, fontSize: number) => {
  104 + // 中文匹配正则
  105 + const pattern = new RegExp('[\u4E00-\u9FA5]+');
  106 + // 文本宽度
  107 + const textWidth = text.split('').reduce((pre, curLetter) => {
  108 + // 单个字符宽度
  109 + const letterWidth = pattern.test(curLetter) ? fontSize : getLetterWidth(curLetter, fontSize);
  110 + return pre + letterWidth;
  111 + }, 0);
  112 + return textWidth;
  113 + };
  114 +
  115 + return getTextWidth(text, fontSize);
  116 +}
  1 +export { default as RuleChainsDesigner } from './index.vue';
  1 +<script setup lang="ts">
  2 + import { Spin } from 'ant-design-vue';
  3 + import { Background, BackgroundVariant } from '@vue-flow/background';
  4 + import { Controls } from '@vue-flow/controls';
  5 + import { Panel, PanelPosition, VueFlow } from '@vue-flow/core';
  6 + import { computed, onMounted, Ref, ref, unref } from 'vue';
  7 + import './style';
  8 + import {
  9 + BasicConnectionArrow,
  10 + BasicConnectionLine,
  11 + BasicEdge,
  12 + MenuSidebar,
  13 + } from './src/components';
  14 + import type { FlowElRef } from './types/flow';
  15 + import { useDragCreate } from './hook/useDragCreate';
  16 + import { createFlowContext } from './hook/useFlowContext';
  17 + import { CreateNodeModal } from './src/components/CreateNodeModal';
  18 + import { useRuleFlow } from './hook/useRuleFlow';
  19 + import { CreateEdgeModal } from './src/components/CreateEdgeModal';
  20 + import { useFullScreen } from './hook/useFullScreen';
  21 + import { useSaveAndRedo } from './hook/useSaveAndRedo';
  22 + import { Icon } from '/@/components/Icon';
  23 + import { UpdateNodeDrawer } from './src/components/UpdateNodeDrawer';
  24 + import { UpdateEdgeDrawer } from './src/components/UpdateEdgeDrawer';
  25 +
  26 + const getId = Number(Math.random().toString().substring(2)).toString(16);
  27 +
  28 + const rootElRef = ref<Nullable<HTMLDivElement>>(null);
  29 +
  30 + const createNodeModalActionType = ref<Nullable<InstanceType<typeof CreateNodeModal>>>(null);
  31 +
  32 + const createEdgeModalActionType = ref<Nullable<InstanceType<typeof CreateEdgeModal>>>(null);
  33 +
  34 + const updateNodeDrawerActionType = ref<Nullable<InstanceType<typeof UpdateNodeDrawer>>>(null);
  35 +
  36 + const updateEdgeDrawerActionType = ref<Nullable<InstanceType<typeof UpdateEdgeDrawer>>>(null);
  37 +
  38 + const flowElRef = ref<Nullable<FlowElRef>>(null);
  39 +
  40 + const elements = ref([]);
  41 +
  42 + const {
  43 + loading,
  44 + changeMarker,
  45 + getCurrentPageMetaData,
  46 + triggerChange,
  47 + handleApplyChange,
  48 + handleRedoChange,
  49 + } = useSaveAndRedo();
  50 +
  51 + const { flowActionType } = useRuleFlow({
  52 + id: getId,
  53 + createNodeModalActionType,
  54 + createEdgeModalActionType,
  55 + updateEdgeDrawerActionType,
  56 + updateNodeDrawerActionType,
  57 + triggerChange,
  58 + });
  59 +
  60 + const { handleOnDragOver, handleOnDrop } = useDragCreate({
  61 + el: flowElRef,
  62 + createNodeModalActionType,
  63 + flowActionType,
  64 + triggerChange,
  65 + });
  66 +
  67 + const { handleFullScreen, getFullScreenIcon } = useFullScreen(
  68 + rootElRef as unknown as Ref<Nullable<HTMLDivElement>>
  69 + );
  70 +
  71 + const handleGetContainer = () => unref(rootElRef)!;
  72 +
  73 + const getDeleteDisplayState = computed(() => unref(flowActionType.getSelectedElements).length);
  74 +
  75 + const handleDeleteSelectionElements = () => {
  76 + flowActionType.removeEdges(unref(flowActionType.getSelectedEdges));
  77 + flowActionType.removeNodes(unref(flowActionType.getSelectedNodes));
  78 + };
  79 +
  80 + onMounted(() => {
  81 + getCurrentPageMetaData(flowActionType);
  82 + });
  83 +
  84 + createFlowContext({
  85 + createEdgeModalActionType,
  86 + createNodeModalActionType,
  87 + updateEdgeDrawerActionType,
  88 + updateNodeDrawerActionType,
  89 + flowActionType,
  90 + triggerChange,
  91 + });
  92 +</script>
  93 +
  94 +<template>
  95 + <main ref="rootElRef" class="w-full h-full flex relative" @drop="handleOnDrop">
  96 + <!-- <Sidebar /> -->
  97 + <MenuSidebar />
  98 +
  99 + <Spin :spinning="loading" wrapperClassName="w-full h-ful rule-chain-designer-loading-container">
  100 + <VueFlow
  101 + :id="getId"
  102 + ref="flowElRef"
  103 + v-model="elements"
  104 + class="w-full h-full"
  105 + @dragover="handleOnDragOver"
  106 + >
  107 + <template #connection-line="props">
  108 + <BasicConnectionLine v-bind="props" />
  109 + </template>
  110 + <template #edge-custom="props">
  111 + <BasicEdge v-bind="props" />
  112 + </template>
  113 +
  114 + <BasicConnectionArrow />
  115 +
  116 + <Background :variant="BackgroundVariant.Lines" :gap="25" pattern-color="#cfcfcf" />
  117 +
  118 + <Controls :position="PanelPosition.BottomLeft" />
  119 +
  120 + <Panel position="bottom-right" class="controls">
  121 + <section class="flex gap-4">
  122 + <button
  123 + :style="{ transform: `translateY(${getDeleteDisplayState ? 0 : '72px'})` }"
  124 + class="button-box-shadow w-14 h-14 flex justify-center items-center bg-orange-600 rounded-full transition-transform transform"
  125 + @click="handleDeleteSelectionElements"
  126 + >
  127 + <Icon class="!text-3xl !text-light-50" icon="mdi:delete" />
  128 + </button>
  129 + <button
  130 + class="button-box-shadow w-14 h-14 flex justify-center items-center bg-gray-400 rounded-full opacity-50"
  131 + >
  132 + <Icon class="!text-3xl !text-light-50" icon="carbon:debug" />
  133 + </button>
  134 + <button
  135 + :class="changeMarker ? '!bg-orange-600 !opacity-100' : 'opacity-50'"
  136 + class="button-box-shadow w-14 h-14 flex justify-center items-center bg-gray-400 rounded-full"
  137 + @click="handleApplyChange(flowActionType)"
  138 + >
  139 + <Icon class="!text-3xl !text-light-50" icon="mdi:tick" />
  140 + </button>
  141 + <button
  142 + :class="changeMarker ? '!bg-orange-600 !opacity-100' : 'opacity-50'"
  143 + class="button-box-shadow w-14 h-14 flex justify-center items-center bg-gray-400 rounded-full"
  144 + @click="handleRedoChange(flowActionType)"
  145 + >
  146 + <Icon class="!text-3xl !text-light-50" icon="ic:baseline-close" />
  147 + </button>
  148 + </section>
  149 + </Panel>
  150 +
  151 + <Panel position="top-right">
  152 + <button
  153 + class="w-10 h-10 bg-gray-300 flex justify-center items-center rounded-full dark:bg-dark-50"
  154 + @click="handleFullScreen"
  155 + >
  156 + <Icon class="!text-2xl dark:text-light-50" :icon="getFullScreenIcon" />
  157 + </button>
  158 + </Panel>
  159 + </VueFlow>
  160 + </Spin>
  161 +
  162 + <CreateNodeModal ref="createNodeModalActionType" :get-container="handleGetContainer" />
  163 + <CreateEdgeModal ref="createEdgeModalActionType" :get-container="handleGetContainer" />
  164 +
  165 + <UpdateEdgeDrawer ref="updateEdgeDrawerActionType" />
  166 + <UpdateNodeDrawer ref="updateNodeDrawerActionType" />
  167 + </main>
  168 +</template>
  169 +
  170 +<style scoped>
  171 + .button-box-shadow {
  172 + box-shadow: 0 3px 5px -1px #0003, 0 6px 10px 0 #00000024, 0 1px 18px 0 #0000001f;
  173 + }
  174 +</style>
  175 +
  176 +<style lang="less">
  177 + .rule-chain-designer-loading-container {
  178 + .ant-spin-container {
  179 + @apply w-full h-full;
  180 + }
  181 + }
  182 +</style>
  1 +import { cloneDeep } from 'lodash-es';
  2 +import { PublicNodeItemClass } from '../../../types/node';
  3 +import type {
  4 + CategoryConfigType,
  5 + CreateComponentType,
  6 + NodeItemConfigType,
  7 +} from '../../../types/node';
  8 +import { ActionCategoryConfig } from '..';
  9 +import { AssignToCustomerConfig } from '.';
  10 +
  11 +export class Config extends PublicNodeItemClass implements CreateComponentType {
  12 + public config: NodeItemConfigType = cloneDeep(AssignToCustomerConfig);
  13 +
  14 + public categoryConfig: CategoryConfigType = cloneDeep(ActionCategoryConfig);
  15 +
  16 + constructor() {
  17 + super();
  18 + }
  19 +}
  1 +import {
  2 + AssignToCustomerFieldsEnum,
  3 + AssignToCustomerFieldsNameEnum,
  4 +} from '../../../enum/formField/action';
  5 +import { FormSchema } from '/@/components/Form';
  6 +
  7 +export const formSchemas: FormSchema[] = [
  8 + {
  9 + field: AssignToCustomerFieldsEnum.CUSTOMER_NAME_PATTERN,
  10 + component: 'Input',
  11 + label: AssignToCustomerFieldsNameEnum.CUSTOMER_NAME_PATTERN,
  12 + helpMessage:
  13 + 'Hint: use ${metadataKey} for value from metadata, $[messageKey] for value from message body',
  14 + componentProps: {
  15 + placeholder: `请输入${AssignToCustomerFieldsNameEnum.CUSTOMER_NAME_PATTERN}`,
  16 + },
  17 + },
  18 + {
  19 + field: AssignToCustomerFieldsEnum.CREATE_CUSTOMER_IF_NOT_EXISTS,
  20 + component: 'Checkbox',
  21 + label: '',
  22 + renderComponentContent: () => ({
  23 + default: () => AssignToCustomerFieldsNameEnum.CREATE_CUSTOMER_IF_NOT_EXISTS,
  24 + }),
  25 + },
  26 + {
  27 + field: AssignToCustomerFieldsEnum.CUSTOMER_CACHE_EXPIRATION,
  28 + component: 'InputNumber',
  29 + label: AssignToCustomerFieldsNameEnum.CUSTOMER_CACHE_EXPIRATION,
  30 + required: true,
  31 + helpMessage:
  32 + 'Specifies maximum time interval allowed to store found customer records. 0 value means that records will never expire.',
  33 + componentProps: {
  34 + placeholder: `请输入${AssignToCustomerFieldsNameEnum.CUSTOMER_CACHE_EXPIRATION}`,
  35 + min: 0,
  36 + },
  37 + },
  38 +];
  1 +<script lang="ts" setup>
  2 + import type { CreateModalDefineExposeType } from '../../../types';
  3 + import { BasicForm, useForm } from '/@/components/Form';
  4 + import { formSchemas } from './create.config';
  5 + import { NodeData } from '../../../types/node';
  6 +
  7 + defineProps<{
  8 + config: NodeData;
  9 + }>();
  10 +
  11 + const [register, { validate, getFieldsValue, setFieldsValue, resetFields }] = useForm({
  12 + schemas: formSchemas,
  13 + showActionButtonGroup: false,
  14 + });
  15 +
  16 + const getValue: CreateModalDefineExposeType['getFieldsValue'] = async () => {
  17 + await validate();
  18 + const value = getFieldsValue() || {};
  19 + return value;
  20 + };
  21 +
  22 + const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => {
  23 + resetFields();
  24 + setFieldsValue(value);
  25 + };
  26 +
  27 + defineExpose({
  28 + setFieldsValue: setValue,
  29 + getFieldsValue: getValue,
  30 + } as CreateModalDefineExposeType);
  31 +</script>
  32 +
  33 +<template>
  34 + <BasicForm @register="register" />
  35 +</template>
  1 +import { ActionCategoryComponentEnum } from '../../../enum/category';
  2 +import { useCreateNodeKey } from '../../../hook/useCreateNodeKey';
  3 +import type { NodeItemConfigType } from '../../../types/node';
  4 +import { RuleNodeTypeEnum } from '../../index.type';
  5 +
  6 +const keys = useCreateNodeKey(ActionCategoryComponentEnum.ASSIGN_TO_CUSTOMER);
  7 +
  8 +export interface AssignToCustomerDataType {
  9 + someConfiguration?: Recordable;
  10 +}
  11 +
  12 +export const AssignToCustomerConfig: NodeItemConfigType = {
  13 + ...keys,
  14 + clazz: 'org.thingsboard.rule.engine.action.TbAssignToCustomerNode',
  15 + categoryType: RuleNodeTypeEnum.ACTION,
  16 + name: 'assign to customer',
  17 + configurationDescriptor: {
  18 + nodeDefinition: {
  19 + details:
  20 + "Finds target Customer by customer name pattern and then assign Originator Entity to this customer. Will create new Customer if it doesn't exists and 'Create new Customer if not exists' is set to true.",
  21 + description: 'Assign Message Originator Entity to Customer',
  22 + inEnabled: true,
  23 + outEnabled: true,
  24 + relationTypes: ['Success', 'Failure'],
  25 + customRelations: false,
  26 + ruleChainNode: false,
  27 + defaultConfiguration: {
  28 + customerNamePattern: '',
  29 + customerCacheExpiration: 300,
  30 + createCustomerIfNotExists: false,
  31 + },
  32 + uiResources: ['static/rulenode/rulenode-core-config.js'],
  33 + configDirective: 'tbActionNodeAssignToCustomerConfig',
  34 + icon: 'ic:baseline-add-circle',
  35 + iconUrl: '',
  36 + docUrl: '',
  37 + },
  38 + },
  39 +};
  1 +import { cloneDeep } from 'lodash-es';
  2 +import { PublicNodeItemClass } from '../../../types/node';
  3 +import type {
  4 + CategoryConfigType,
  5 + CreateComponentType,
  6 + NodeItemConfigType,
  7 +} from '../../../types/node';
  8 +import { ActionCategoryConfig } from '..';
  9 +import { ClearAlarmConfig } from '.';
  10 +
  11 +export class Config extends PublicNodeItemClass implements CreateComponentType {
  12 + public config: NodeItemConfigType = cloneDeep(ClearAlarmConfig);
  13 +
  14 + public categoryConfig: CategoryConfigType = cloneDeep(ActionCategoryConfig);
  15 +
  16 + constructor() {
  17 + super();
  18 + }
  19 +}
  1 +import { ClearAlarmFieldsEnum, ClearAlarmFieldsNameEnum } from '../../../enum/formField/action';
  2 +import { JavascriptEditorWithTestModal } from '../../../src/components/JavaScriptFilterModal';
  3 +import { FormSchema, useComponentRegister } from '/@/components/Form';
  4 +
  5 +useComponentRegister('JavascriptEditorWithTestModal', JavascriptEditorWithTestModal);
  6 +
  7 +export const formSchemas: FormSchema[] = [
  8 + {
  9 + field: ClearAlarmFieldsEnum.ALARM_DETAILS_BUILD_JS,
  10 + component: 'JavascriptEditorWithTestModal',
  11 + label: ClearAlarmFieldsNameEnum.ALARM_DETAILS_BUILD_JS,
  12 + componentProps: {
  13 + javaScriptEditorProps: {
  14 + functionName: 'Details',
  15 + paramsName: ['msg', 'metadata', 'msgType'],
  16 + },
  17 + buttonName: 'Test details function',
  18 + },
  19 + },
  20 + {
  21 + field: ClearAlarmFieldsEnum.ALARM_TYPE,
  22 + component: 'Input',
  23 + label: ClearAlarmFieldsNameEnum.ALARM_TYPE,
  24 + required: true,
  25 + componentProps: {
  26 + placeholder: `请输入${ClearAlarmFieldsNameEnum.ALARM_TYPE}`,
  27 + },
  28 + },
  29 +];
  1 +<script lang="ts" setup>
  2 + import type { CreateModalDefineExposeType } from '../../../types';
  3 + import { BasicForm, useForm } from '/@/components/Form';
  4 + import { formSchemas } from './create.config';
  5 + import { NodeData } from '../../../types/node';
  6 +
  7 + defineProps<{
  8 + config: NodeData;
  9 + }>();
  10 +
  11 + const [register, { validate, getFieldsValue, setFieldsValue, resetFields }] = useForm({
  12 + schemas: formSchemas,
  13 + showActionButtonGroup: false,
  14 + });
  15 +
  16 + const getValue: CreateModalDefineExposeType['getFieldsValue'] = async () => {
  17 + await validate();
  18 + const value = getFieldsValue() || {};
  19 + return value;
  20 + };
  21 +
  22 + const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => {
  23 + resetFields();
  24 + setFieldsValue(value);
  25 + };
  26 +
  27 + defineExpose({
  28 + setFieldsValue: setValue,
  29 + getFieldsValue: getValue,
  30 + } as CreateModalDefineExposeType);
  31 +</script>
  32 +
  33 +<template>
  34 + <BasicForm @register="register" />
  35 +</template>
  1 +import { ActionCategoryComponentEnum } from '../../../enum/category';
  2 +import { useCreateNodeKey } from '../../../hook/useCreateNodeKey';
  3 +import type { NodeItemConfigType } from '../../../types/node';
  4 +import { RuleNodeTypeEnum } from '../../index.type';
  5 +
  6 +const keys = useCreateNodeKey(ActionCategoryComponentEnum.CLEAR_ALARM);
  7 +
  8 +export interface ClearAlarmDataType {
  9 + someConfiguration?: Recordable;
  10 +}
  11 +
  12 +export const ClearAlarmConfig: NodeItemConfigType = {
  13 + ...keys,
  14 + clazz: 'org.thingsboard.rule.engine.action.TbClearAlarmNode',
  15 + categoryType: RuleNodeTypeEnum.ACTION,
  16 + name: 'clear alarm',
  17 + configurationDescriptor: {
  18 + nodeDefinition: {
  19 + details:
  20 + "Details - JS function that creates JSON object based on incoming message. This object will be added into Alarm.details field.\nNode output:\nIf alarm was not cleared, original message is returned. Otherwise new Message returned with type 'ALARM', Alarm object in 'msg' property and 'metadata' will contains 'isClearedAlarm' property. Message payload can be accessed via <code>msg</code> property. For example <code>'temperature = ' + msg.temperature ;</code>. Message metadata can be accessed via <code>metadata</code> property. For example <code>'name = ' + metadata.customerName;</code>.",
  21 + description: 'Clear Alarm',
  22 + inEnabled: true,
  23 + outEnabled: true,
  24 + relationTypes: ['Cleared', 'False', 'Failure'],
  25 + customRelations: false,
  26 + ruleChainNode: false,
  27 + defaultConfiguration: {
  28 + alarmType: 'General Alarm',
  29 + alarmDetailsBuildJs:
  30 + 'var details = {};\nif (metadata.prevAlarmDetails) {\n details = JSON.parse(metadata.prevAlarmDetails);\n //remove prevAlarmDetails from metadata\n delete metadata.prevAlarmDetails;\n //now metadata is the same as it comes IN this rule node\n}\n\n\nreturn details;',
  31 + },
  32 + uiResources: ['static/rulenode/rulenode-core-config.js'],
  33 + configDirective: 'tbActionNodeClearAlarmConfig',
  34 + icon: 'material-symbols:notifications-off',
  35 + iconUrl: '',
  36 + docUrl: '',
  37 + },
  38 + },
  39 +};
  1 +import { cloneDeep } from 'lodash-es';
  2 +import { PublicNodeItemClass } from '../../../types/node';
  3 +import type {
  4 + CategoryConfigType,
  5 + CreateComponentType,
  6 + NodeItemConfigType,
  7 +} from '../../../types/node';
  8 +import { ActionCategoryConfig } from '..';
  9 +import { CopyToViewConfig } from '.';
  10 +
  11 +export class Config extends PublicNodeItemClass implements CreateComponentType {
  12 + public config: NodeItemConfigType = cloneDeep(CopyToViewConfig);
  13 +
  14 + public categoryConfig: CategoryConfigType = cloneDeep(ActionCategoryConfig);
  15 +
  16 + constructor() {
  17 + super();
  18 + }
  19 +}
  1 +import { FormSchema } from '/@/components/Form';
  2 +
  3 +export const formSchemas: FormSchema[] = [];
  1 +<script lang="ts" setup>
  2 + import type { CreateModalDefineExposeType } from '../../../types';
  3 + import { BasicForm, useForm } from '/@/components/Form';
  4 + import { formSchemas } from './create.config';
  5 + import { NodeData } from '../../../types/node';
  6 +
  7 + defineProps<{
  8 + config: NodeData;
  9 + }>();
  10 +
  11 + const [register, { validate, getFieldsValue, setFieldsValue, resetFields }] = useForm({
  12 + schemas: formSchemas,
  13 + showActionButtonGroup: false,
  14 + });
  15 +
  16 + const getValue: CreateModalDefineExposeType['getFieldsValue'] = async () => {
  17 + await validate();
  18 + const value = getFieldsValue() || {};
  19 + return value;
  20 + };
  21 +
  22 + const setValue: CreateModalDefineExposeType['setFieldsValue'] = (value) => {
  23 + resetFields();
  24 + setFieldsValue(value);
  25 + };
  26 +
  27 + defineExpose({
  28 + setFieldsValue: setValue,
  29 + getFieldsValue: getValue,
  30 + } as CreateModalDefineExposeType);
  31 +</script>
  32 +
  33 +<template>
  34 + <BasicForm @register="register" />
  35 +</template>
  1 +import { ActionCategoryComponentEnum } from '../../../enum/category';
  2 +import { useCreateNodeKey } from '../../../hook/useCreateNodeKey';
  3 +import type { NodeItemConfigType } from '../../../types/node';
  4 +import { RuleNodeTypeEnum } from '../../index.type';
  5 +
  6 +const keys = useCreateNodeKey(ActionCategoryComponentEnum.COPY_TO_VIEW);
  7 +
  8 +export interface CopyToViewDataType {
  9 + someConfiguration?: Recordable;
  10 +}
  11 +
  12 +export const CopyToViewConfig: NodeItemConfigType = {
  13 + ...keys,
  14 + clazz: 'org.thingsboard.rule.engine.action.TbCopyAttributesToEntityViewNode',
  15 + categoryType: RuleNodeTypeEnum.ACTION,
  16 + name: 'copy to view',
  17 + configurationDescriptor: {
  18 + nodeDefinition: {
  19 + details:
  20 + 'Copy attributes from asset/device to related entity view according to entity view configuration. \n Copy will be done only for attributes that are between start and end dates and according to attribute keys configuration. \nChanges message originator to related entity view and produces new messages according to count of updated entity views',
  21 + description:
  22 + 'Copy attributes from asset/device to entity view and changes message originator to related entity view',
  23 + inEnabled: true,
  24 + outEnabled: true,
  25 + relationTypes: ['Success', 'Failure'],
  26 + customRelations: false,
  27 + ruleChainNode: false,
  28 + defaultConfiguration: {
  29 + version: 0,
  30 + },
  31 + uiResources: ['static/rulenode/rulenode-core-config.js'],
  32 + configDirective: 'tbNodeEmptyConfig',
  33 + icon: 'material-symbols:content-copy',
  34 + iconUrl: '',
  35 + docUrl: '',
  36 + },
  37 + },
  38 +};
  1 +import { cloneDeep } from 'lodash-es';
  2 +import { PublicNodeItemClass } from '../../../types/node';
  3 +import type {
  4 + CategoryConfigType,
  5 + CreateComponentType,
  6 + NodeItemConfigType,
  7 +} from '../../../types/node';
  8 +import { ActionCategoryConfig } from '..';
  9 +import { CreateAlarmConfig } from '.';
  10 +
  11 +export class Config extends PublicNodeItemClass implements CreateComponentType {
  12 + public config: NodeItemConfigType = cloneDeep(CreateAlarmConfig);
  13 +
  14 + public categoryConfig: CategoryConfigType = cloneDeep(ActionCategoryConfig);
  15 +
  16 + constructor() {
  17 + super();
  18 + }
  19 +}