Commit 45a693ebe2c438ab22f4414c6fb2deccce8f5100

Authored by fengwotao
2 parents c7f16e22 62c99076

Merge branch 'main_dev' into ft

Showing 62 changed files with 4129 additions and 111 deletions

Too many changes to show.

To preserve performance only 62 of 410 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 { RuleChainDetail, RuleChainType } from '/@/views/rule/designer/types/ruleNode';
  5 +
  6 +enum Api {
  7 + GET_RULE_CHAINS_DETAIL = '/ruleChain',
  8 + SAVE = '/ruleChain/metadata',
  9 + GET_RULE_CHAINES = '/ruleChains',
  10 + GET_RULE_NODE_EVENTS = '/events/RULE_NODE',
  11 +}
  12 +
  13 +export const getRuleChainDetail = (id: string) => {
  14 + return defHttp.get<RuleChainDetail>(
  15 + {
  16 + url: `${Api.GET_RULE_CHAINS_DETAIL}/${id}`,
  17 + },
  18 + { joinPrefix: false }
  19 + );
  20 +};
  21 +
  22 +export const getRuleChainData = (id: string) => {
  23 + return defHttp.get<RuleChainType>(
  24 + {
  25 + url: `/ruleChain/${id}/metadata`,
  26 + },
  27 + { joinPrefix: false }
  28 + );
  29 +};
  30 +
  31 +export const saveRuleChainData = (data: RuleChainType) => {
  32 + return defHttp.post(
  33 + {
  34 + url: Api.SAVE,
  35 + data,
  36 + },
  37 + { joinPrefix: false }
  38 + );
  39 +};
  40 +
  41 +export const getRuleChains = (params: Recordable) => {
  42 + return defHttp.get<TBPaginationResult<RuleChainPaginationItemType>>(
  43 + {
  44 + url: Api.GET_RULE_CHAINES,
  45 + params,
  46 + },
  47 + {
  48 + joinPrefix: false,
  49 + }
  50 + );
  51 +};
  52 +
  53 +export const getRuleNodeEventList = (
  54 + ruleNodeId: string,
  55 + params: Recordable,
  56 + data: Recordable & Record<'eventType', string>
  57 +) => {
  58 + return defHttp.post<TBPaginationResult>(
  59 + {
  60 + url: `${Api.GET_RULE_NODE_EVENTS}/${ruleNodeId}`,
  61 + params,
  62 + data,
  63 + },
  64 + { joinPrefix: false }
  65 + );
  66 +};
  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 +}
@@ -22,6 +22,8 @@ enum ScreenManagerApi { @@ -22,6 +22,8 @@ enum ScreenManagerApi {
22 GET_ATTRBUTELIST = '/device/attributes/', 22 GET_ATTRBUTELIST = '/device/attributes/',
23 ALARM_PROFILE = '/alarm/profile/', 23 ALARM_PROFILE = '/alarm/profile/',
24 MASTER_GET_DEVICE = '/device/list', 24 MASTER_GET_DEVICE = '/device/list',
  25 + RULE_CHAINS = '/ruleChains',
  26 + RULE_CHAIN = '/ruleChain',
25 } 27 }
26 28
27 /** 29 /**
@@ -130,3 +132,45 @@ export const byOrganizationIdGetMasterDevice = (params: { @@ -130,3 +132,45 @@ export const byOrganizationIdGetMasterDevice = (params: {
130 }); 132 });
131 }; 133 };
132 //TODO-fengtao 134 //TODO-fengtao
  135 +
  136 +/**
  137 + * 分页查询规则链库
  138 + */
  139 +
  140 +export const getRuleChinsList = (params) => {
  141 + return defHttp.get(
  142 + {
  143 + url: `${ScreenManagerApi.RULE_CHAINS}`,
  144 + params,
  145 + },
  146 + { joinPrefix: false }
  147 + );
  148 +};
  149 +
  150 +/**
  151 + * 新增规则链
  152 + */
  153 +
  154 +export const createRuleChine = (params) => {
  155 + return defHttp.post(
  156 + {
  157 + url: `${ScreenManagerApi.RULE_CHAIN}`,
  158 + params,
  159 + },
  160 + { joinPrefix: false }
  161 + );
  162 +};
  163 +
  164 +/**
  165 + * 删除规则链
  166 + */
  167 +
  168 +export const deleteRuleChine = (params) => {
  169 + return defHttp.delete(
  170 + {
  171 + url: `${ScreenManagerApi.RULE_CHAIN}/`,
  172 + params,
  173 + },
  174 + { joinPrefix: false }
  175 + );
  176 +};
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 {
@@ -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" />
  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>
@@ -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 +}
@@ -38,6 +38,11 @@ export const PLATFORM = 'PLATFORM'; @@ -38,6 +38,11 @@ export const PLATFORM = 'PLATFORM';
38 export const PLATFORM_INFO_CACHE_KEY = 'PLATFORM_INFO'; 38 export const PLATFORM_INFO_CACHE_KEY = 'PLATFORM_INFO';
39 39
40 export const MENU_LIST = 'MENU_LIST'; 40 export const MENU_LIST = 'MENU_LIST';
  41 +
  42 +export const RULE_NODE_LOCAL_CACHE_KEY = 'RULE__NODE__KEY__';
  43 +
  44 +export const RULE_NODE_KEY = 'RULE_NODE';
  45 +
41 export enum CacheTypeEnum { 46 export enum CacheTypeEnum {
42 SESSION, 47 SESSION,
43 LOCAL, 48 LOCAL,
@@ -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 }
@@ -16,4 +16,6 @@ export const PageEnum = { @@ -16,4 +16,6 @@ export const PageEnum = {
16 DEVICE_LIST: '/device/list', 16 DEVICE_LIST: '/device/list',
17 17
18 SHARE_PAGE: '/share/:viewType/:id/:publicId', 18 SHARE_PAGE: '/share/:viewType/:id/:publicId',
  19 +
  20 + RULE_CHAIN_DETAIL: '/rule/chain/:id',
19 }; 21 };
  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,6 +3,7 @@ import { RouteLocationNormalizedLoaded } from 'vue-router'; @@ -3,6 +3,7 @@ import { RouteLocationNormalizedLoaded } from 'vue-router';
3 const menuMap = new Map(); 3 const menuMap = new Map();
4 4
5 menuMap.set('/visual/board/detail/:boardId/:boardName?', '/visual/board'); 5 menuMap.set('/visual/board/detail/:boardId/:boardName?', '/visual/board');
  6 +menuMap.set('/rule/chain/:id', '/rule/chain');
6 7
7 export const useMenuActiveFix = (route: RouteLocationNormalizedLoaded) => { 8 export const useMenuActiveFix = (route: RouteLocationNormalizedLoaded) => {
8 let flag = false; 9 let flag = false;
@@ -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 +<template>
  2 + <div>
  3 + <BasicModal
  4 + destroyOnClose
  5 + v-bind="$attrs"
  6 + width="30rem"
  7 + @register="register"
  8 + title="添加规则链"
  9 + @ok="handleSuccess"
  10 + @close="handleClose"
  11 + >
  12 + <div>
  13 + <BasicForm @register="registerForm" />
  14 + </div>
  15 + </BasicModal>
  16 + </div>
  17 +</template>
  18 +<script setup lang="ts">
  19 + import { BasicModal, useModalInner } from '/@/components/Modal';
  20 + import { ExecuteReportRecord } from '/@/api/export/model/exportModel';
  21 + import { useForm, BasicForm } from '/@/components/Form';
  22 + import { formSchema } from './config';
  23 + import { createRuleChine } from '/@/api/ruleengine/ruleengineApi';
  24 + import { useMessage } from '/@/hooks/web/useMessage';
  25 +
  26 + const emit = defineEmits(['register', 'success']);
  27 +
  28 + const { createMessage } = useMessage();
  29 +
  30 + const [registerForm, { getFieldsValue, validate }] = useForm({
  31 + labelWidth: 120,
  32 + schemas: formSchema,
  33 + showActionButtonGroup: false,
  34 + });
  35 +
  36 + const [register, { setModalProps, closeModal }] = useModalInner(
  37 + async (data: { record: ExecuteReportRecord }) => {
  38 + setModalProps({ loading: true });
  39 + console.log(data, 'record');
  40 + }
  41 + );
  42 +
  43 + const handleClose = () => {
  44 + closeModal();
  45 + };
  46 +
  47 + const handleSuccess = async () => {
  48 + console.log(1, '1', getFieldsValue());
  49 + await validate();
  50 + const record = getFieldsValue();
  51 + const { description, debugMode, name } = record;
  52 + const form = {
  53 + additionalInfo: {
  54 + description,
  55 + },
  56 + debugMode,
  57 + name,
  58 + type: 'CORE',
  59 + };
  60 +
  61 + await createRuleChine(form);
  62 + createMessage.success('添加成功');
  63 + handleClose();
  64 + emit('success');
  65 + };
  66 +</script>
  67 +<style lang="less" scoped></style>
  1 +import { FormSchema } from '/@/components/Form';
  2 +
  3 +export const formSchema: FormSchema[] = [
  4 + {
  5 + field: 'name',
  6 + label: '名称',
  7 + colProps: { span: 16 },
  8 + component: 'Input',
  9 + required: true,
  10 + componentProps: {
  11 + placeholder: '请输入名称',
  12 + },
  13 + },
  14 + {
  15 + field: 'debugMode',
  16 + label: '调试模式',
  17 + component: 'Checkbox',
  18 + colProps: { span: 8 },
  19 + componentProps: {},
  20 + },
  21 + {
  22 + field: 'description',
  23 + label: '说明',
  24 + component: 'InputTextArea',
  25 + },
  26 +];
  1 +import RuleChainModal from './RuleChainModal.vue';
  2 +
  3 +export { RuleChainModal };
  1 +import { BasicColumn, FormSchema } from '/@/components/Table';
  2 +import { transformTime } from '/@/hooks/web/useDateToLocaleString';
  3 +
  4 +export const columns: BasicColumn[] = [
  5 + {
  6 + title: '创建时间',
  7 + dataIndex: 'createdTime',
  8 + width: 200,
  9 + format: (_text: string, record: Recordable) => {
  10 + return transformTime(record.createdTime);
  11 + },
  12 + },
  13 + {
  14 + title: '名称',
  15 + dataIndex: 'name',
  16 + width: 200,
  17 + },
  18 + {
  19 + title: '是否跟链',
  20 + dataIndex: 'root',
  21 + slots: {
  22 + customRender: 'root',
  23 + },
  24 + width: 200,
  25 + },
  26 +];
  27 +
  28 +export const searchFormSchema: FormSchema[] = [
  29 + {
  30 + field: 'textSearch',
  31 + label: '名称',
  32 + component: 'Input',
  33 +
  34 + colProps: { span: 8 },
  35 + componentProps: {
  36 + placeholder: '请输入名称',
  37 + },
  38 + },
  39 +];
  40 +
  41 +export const encode = (string: string) => {
  42 + return encodeURIComponent(string);
  43 +};
  1 +<template>
  2 + <div>
  3 + <BasicTable @register="registerTable">
  4 + <template #toolbar>
  5 + <Authority>
  6 + <a-button type="primary" @click="handleAdd"> 新增规则链 </a-button>
  7 + </Authority>
  8 + <!-- <Authority>
  9 + <Popconfirm
  10 + title="您确定要批量删除数据"
  11 + ok-text="确定"
  12 + cancel-text="取消"
  13 + @confirm="handleDeleteOrBatchDelete(null)"
  14 + >
  15 + <a-button color="error" :disabled="hasBatchDelete"> 批量删除 </a-button>
  16 + </Popconfirm>
  17 + </Authority> -->
  18 + </template>
  19 + <template #root="{ record }">
  20 + <Tag :color="record.root ? 'green' : 'red'"> {{ record.root ? '是' : '否' }}</Tag>
  21 + </template>
  22 + <template #action="{ record }">
  23 + <TableAction
  24 + :actions="[
  25 + {
  26 + label: '查看',
  27 + icon: 'ant-design:eye-outlined',
  28 + onClick: handleView.bind(null, record),
  29 + },
  30 + {
  31 + label: '删除',
  32 + icon: 'ant-design:delete-outlined',
  33 + color: 'error',
  34 + ifShow: !record.root,
  35 + popConfirm: {
  36 + title: '是否确认删除',
  37 + confirm: handleDeleteOrBatchDelete.bind(null, record),
  38 + },
  39 + },
  40 + ]"
  41 + />
  42 + </template>
  43 + </BasicTable>
  44 + <RuleChainModal @register="registerModal" @success="handleSuccess" />
  45 + </div>
  46 +</template>
  47 +<script lang="ts" setup>
  48 + import { BasicTable, useTable, TableAction } from '/@/components/Table';
  49 + import { columns, encode, searchFormSchema } from './config/config.data';
  50 + import { deleteRuleChine, getRuleChinsList } from '/@/api/ruleengine/ruleengineApi';
  51 + import { useModal } from '/@/components/Modal';
  52 + import { Authority } from '/@/components/Authority';
  53 + import { Tag } from 'ant-design-vue';
  54 + import { RuleChainModal } from './component/index';
  55 + import { useMessage } from '/@/hooks/web/useMessage';
  56 + import { usePermission } from '/@/hooks/web/usePermission';
  57 + import { useRouter } from 'vue-router';
  58 +
  59 + const [registerTable, { reload, setProps }] = useTable({
  60 + title: '规则链库',
  61 + api: getRuleChinsList,
  62 + columns,
  63 + formConfig: {
  64 + labelWidth: 120,
  65 + schemas: searchFormSchema,
  66 + },
  67 + fetchSetting: {
  68 + pageField: 'page',
  69 + listField: 'data',
  70 + },
  71 + pagination: true,
  72 + useSearchForm: true,
  73 + showTableSetting: true,
  74 + bordered: true,
  75 + showIndexColumn: false,
  76 + beforeFetch(params) {
  77 + Reflect.set(params, 'page', params.page - 1);
  78 + Reflect.set(params, 'sortProperty', 'createdTime');
  79 + Reflect.set(params, 'sortOrder', 'DESC');
  80 + return params;
  81 + },
  82 + actionColumn: {
  83 + width: 200,
  84 + title: '操作',
  85 + dataIndex: 'action',
  86 + slots: { customRender: 'action' },
  87 + fixed: 'right',
  88 + },
  89 + });
  90 +
  91 + const [registerModal, { openModal }] = useModal();
  92 +
  93 + const handleSuccess = () => {
  94 + reload();
  95 + };
  96 +
  97 + const handleAdd = () => {
  98 + openModal(true);
  99 + };
  100 +
  101 + const { createMessage } = useMessage();
  102 + const { hasPermission } = usePermission();
  103 + const router = useRouter();
  104 +
  105 + const handleView = (record: Recordable) => {
  106 + const hasDetailPermission = hasPermission('rule:chain:detail');
  107 + if (hasDetailPermission) {
  108 + const boardId = encode(record.id.id);
  109 + router.push(`/rule/chain/${boardId}`);
  110 + } else createMessage.warning('没有权限');
  111 + };
  112 +
  113 + const handleDeleteOrBatchDelete = async (record: Recordable) => {
  114 + setProps({
  115 + loading: true,
  116 + });
  117 + try {
  118 + await deleteRuleChine(record.id.id);
  119 + createMessage.success('删除成功');
  120 + } finally {
  121 + setProps({
  122 + loading: false,
  123 + });
  124 + }
  125 + reload();
  126 + };
  127 +</script>
  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 +export enum RuleChainEntityType {
  2 + RULE_NODE = 'RULE_NODE',
  3 + RULE_CHAIN = 'RULE_CHAIN',
  4 +}
  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 +import { NodeData } from '../types/node';
  5 +
  6 +export const useAddNodes = () => {
  7 + const getAddNodesParams = (
  8 + position: Node['position'],
  9 + data: NodeData,
  10 + options?: Partial<Node>
  11 + ): Node => {
  12 + return {
  13 + id: buildUUID(),
  14 + type: NodeTypeEnum.CUSTOM,
  15 + position,
  16 + data,
  17 + ...options,
  18 + };
  19 + };
  20 +
  21 + return { getAddNodesParams };
  22 +};
  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 + created: !!id?.id,
  126 + },
  127 + {
  128 + id: id?.id || buildUUID(),
  129 + }
  130 + );
  131 + }
  132 +
  133 + function deconstructionNode(nodes: RuleChainType['nodes']) {
  134 + const addNodes: Elements = [];
  135 + for (const node of unref(nodes)) {
  136 + const { type } = node;
  137 + if (!type) continue;
  138 + const nodeConfig = nodeConfigMap.get(type);
  139 +
  140 + if (!nodeConfig) {
  141 + throw `No component configuration of type '${type}' was found`;
  142 + }
  143 +
  144 + const newNode = genNewNodeByData(node, nodeConfig);
  145 +
  146 + addNodes.push(newNode);
  147 + }
  148 + return addNodes;
  149 + }
  150 +
  151 + function deconstructionData(
  152 + ruleChain: RuleChainType | Ref<RuleChainType | undefined>,
  153 + inputNodeId: string
  154 + ) {
  155 + if (!ruleChain || !unref(ruleChain)) return;
  156 + ruleChain = toRaw(unref(ruleChain))!;
  157 +
  158 + const nodes = deconstructionNode(ruleChain?.nodes || []);
  159 +
  160 + const edges = deconstructionConnection(ruleChain!, inputNodeId);
  161 +
  162 + return {
  163 + nodes,
  164 + edges,
  165 + };
  166 + }
  167 +
  168 + initNodeConfigMap();
  169 +
  170 + return {
  171 + mergeData,
  172 + deconstructionData,
  173 + };
  174 +}
  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 { GraphEdge, GraphNode, VueFlowStore } from '@vue-flow/core';
  2 +import { getRuleNodeCache, setRuleNodeCache } from './useRuleCopyPaste';
  3 +import { RuleContextMenuEnum } from './useRuleChainContextMenu';
  4 +import { useAddNodes } from './useAddNodes';
  5 +import { buildUUID } from '/@/utils/uuid';
  6 +import { toRaw, unref } from 'vue';
  7 +import { useSaveAndRedo } from './useSaveAndRedo';
  8 +import { isUnDef } from '/@/utils/is';
  9 +import { useAddEdges } from './useAddEdges';
  10 +import { EdgeData } from '../types/node';
  11 +
  12 +interface HandleContextMenuActionParamsType {
  13 + menuType: RuleContextMenuEnum;
  14 + event?: Event;
  15 + flowActionType?: VueFlowStore;
  16 + node?: GraphNode;
  17 + edge?: GraphEdge;
  18 + useSaveAndRedoActionType?: ReturnType<typeof useSaveAndRedo>;
  19 +}
  20 +
  21 +export const NODE_WIDTH = 176;
  22 +export const NODE_HEIGHT = 48;
  23 +
  24 +function getElementsCenter(nodes: GraphNode[]) {
  25 + let leftTopX: number | undefined;
  26 + let leftTopY: number | undefined;
  27 + let rightBottomX: number | undefined;
  28 + let rightBottomY: number | undefined;
  29 +
  30 + for (const node of nodes) {
  31 + const { position } = node;
  32 + const { x, y } = position;
  33 + if (isUnDef(leftTopX)) {
  34 + leftTopX = x;
  35 + leftTopY = y;
  36 + rightBottomX = x + NODE_WIDTH;
  37 + rightBottomY = y + NODE_HEIGHT;
  38 +
  39 + continue;
  40 + }
  41 +
  42 + if (x < leftTopX!) {
  43 + leftTopX = x;
  44 + if (y < leftTopY!) {
  45 + leftTopY = y;
  46 + }
  47 + continue;
  48 + }
  49 +
  50 + if (x + NODE_WIDTH > rightBottomX!) {
  51 + rightBottomX = x + NODE_WIDTH;
  52 + if (y + NODE_HEIGHT > rightBottomY!) {
  53 + rightBottomY = y + NODE_HEIGHT;
  54 + }
  55 + }
  56 + }
  57 +
  58 + return {
  59 + originX: (rightBottomX! - leftTopX!) / 2 + leftTopX!,
  60 + originY: (rightBottomY! - leftTopY!) / 2 + leftTopY!,
  61 + };
  62 +}
  63 +
  64 +export function useContextMenuAction() {
  65 + const copy = (params: HandleContextMenuActionParamsType) => {
  66 + const { node } = params;
  67 + if (!node) return;
  68 + const { position, data } = node;
  69 + const { getAddNodesParams } = useAddNodes();
  70 + const { x, y } = position;
  71 +
  72 + const value = getAddNodesParams(position, data, { id: buildUUID() });
  73 +
  74 + setRuleNodeCache({
  75 + nodes: [value],
  76 + originX: x + NODE_WIDTH / 2 + x,
  77 + originY: y + NODE_HEIGHT / 2,
  78 + });
  79 + };
  80 +
  81 + const paste = (params: HandleContextMenuActionParamsType) => {
  82 + const { event, flowActionType, useSaveAndRedoActionType } = params;
  83 + const { triggerChange } = useSaveAndRedoActionType || {};
  84 + const { getAddNodesParams } = useAddNodes();
  85 + const { getAddedgesParams } = useAddEdges();
  86 + const clientX = (event as MouseEvent).offsetX;
  87 + const clientY = (event as MouseEvent).offsetY;
  88 +
  89 + const { edges = [], nodes = [], originX, originY } = getRuleNodeCache();
  90 +
  91 + const newNode = nodes.map((node) => {
  92 + const { position, data, id } = node;
  93 + const { x, y } = position;
  94 +
  95 + const newX = clientX - originX! + x + NODE_WIDTH / 2;
  96 + const newY = clientY - originY! + y + NODE_HEIGHT / 2;
  97 +
  98 + return getAddNodesParams({ x: newX, y: newY }, { ...data, created: false }, { id });
  99 + });
  100 +
  101 + const newEdges = edges.map((edge) => getAddedgesParams(edge, edge.data));
  102 +
  103 + flowActionType?.addNodes(newNode);
  104 + flowActionType?.addEdges(newEdges);
  105 +
  106 + triggerChange?.();
  107 +
  108 + flowActionType?.removeSelectedElements();
  109 + };
  110 +
  111 + const selectAll = (params: HandleContextMenuActionParamsType) => {
  112 + const { flowActionType } = params;
  113 + flowActionType?.addSelectedElements(unref(flowActionType.getElements));
  114 + };
  115 +
  116 + const unselect = (params: HandleContextMenuActionParamsType) => {
  117 + const { flowActionType } = params;
  118 + flowActionType?.removeSelectedElements();
  119 + };
  120 +
  121 + const deleteElement = (parmas: HandleContextMenuActionParamsType) => {
  122 + const { useSaveAndRedoActionType, flowActionType, node, edge } = parmas;
  123 + const { triggerChange } = useSaveAndRedoActionType || {};
  124 +
  125 + node && flowActionType?.removeNodes(node);
  126 + edge && flowActionType?.removeEdges(edge);
  127 +
  128 + triggerChange?.();
  129 + };
  130 +
  131 + const deleteElements = (params: HandleContextMenuActionParamsType) => {
  132 + const { flowActionType, useSaveAndRedoActionType } = params;
  133 + flowActionType?.removeNodes(unref(flowActionType.getSelectedNodes));
  134 +
  135 + useSaveAndRedoActionType?.triggerChange?.();
  136 + };
  137 +
  138 + const selectCopy = (params: HandleContextMenuActionParamsType) => {
  139 + const { flowActionType } = params;
  140 + const { getAddNodesParams } = useAddNodes();
  141 + const { getAddedgesParams } = useAddEdges();
  142 +
  143 + const edges = unref(flowActionType?.getSelectedEdges)?.map((edge) =>
  144 + getAddedgesParams(
  145 + {
  146 + source: edge.source,
  147 + target: edge.target,
  148 + sourceHandle: edge.sourceHandle,
  149 + targetHandle: edge.targetHandle,
  150 + },
  151 + toRaw(unref(edge.data as EdgeData)?.data)
  152 + )
  153 + );
  154 +
  155 + const nodes = unref(flowActionType?.getSelectedNodes)?.map((node) => {
  156 + const { id: oldId } = node;
  157 + const newId = buildUUID();
  158 +
  159 + for (const connection of edges || []) {
  160 + if (connection.source.includes(oldId)) {
  161 + connection.source = newId;
  162 + connection.sourceHandle = connection.sourceHandle?.replaceAll(oldId, newId);
  163 + continue;
  164 + }
  165 +
  166 + if (connection.target.includes(oldId)) {
  167 + connection.target = newId;
  168 + connection.targetHandle = connection.targetHandle?.replaceAll(oldId, newId);
  169 + }
  170 + }
  171 +
  172 + return getAddNodesParams(node.position, toRaw(unref(node.data)), { id: newId });
  173 + });
  174 +
  175 + const originRect = getElementsCenter(unref(flowActionType?.getSelectedNodes) || []);
  176 +
  177 + setRuleNodeCache({ nodes, edges, ...originRect });
  178 + };
  179 +
  180 + const applyChange = (params: HandleContextMenuActionParamsType) => {
  181 + const { useSaveAndRedoActionType, flowActionType } = params;
  182 +
  183 + useSaveAndRedoActionType?.handleApplyChange(flowActionType!);
  184 + };
  185 +
  186 + const undoChange = (params: HandleContextMenuActionParamsType) => {
  187 + const { useSaveAndRedoActionType, flowActionType } = params;
  188 +
  189 + useSaveAndRedoActionType?.handleRedoChange(flowActionType!);
  190 + };
  191 +
  192 + const handleContextMenuAction = (params: HandleContextMenuActionParamsType) => {
  193 + const { menuType } = params;
  194 +
  195 + const handlerMapping = {
  196 + [RuleContextMenuEnum.COPY]: copy,
  197 + [RuleContextMenuEnum.PASTE]: paste,
  198 + [RuleContextMenuEnum.SELECT_ALL]: selectAll,
  199 + [RuleContextMenuEnum.UNSELECTED]: unselect,
  200 + [RuleContextMenuEnum.DELETE]: deleteElement,
  201 + [RuleContextMenuEnum.DELETE_SELECT]: deleteElements,
  202 + [RuleContextMenuEnum.SELECT_COPY]: selectCopy,
  203 + [RuleContextMenuEnum.APPLY_CHANGE]: applyChange,
  204 + [RuleContextMenuEnum.UNDO_CHANGE]: undoChange,
  205 + };
  206 +
  207 + if (handlerMapping[menuType]) {
  208 + handlerMapping[menuType]?.(params);
  209 + }
  210 + };
  211 +
  212 + return {
  213 + handleContextMenuAction,
  214 + };
  215 +}
  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 { NodeMouseEvent } from '@vue-flow/core';
  2 +import { useContextMenu } from '../src/components/RuleChainContextMenu';
  3 +import { RuleChainContextMenuItemType } from '../src/components/RuleChainContextMenu/index.type';
  4 +import { ElementsTypeEnum } from '../enum';
  5 +import { checkHasCacheRuleNode } from './useRuleCopyPaste';
  6 +
  7 +export enum RuleContextMenuEnum {
  8 + DETAIL = 'DETAIL',
  9 + COPY = 'COPY',
  10 + DELETE = 'DELETE',
  11 +
  12 + SELECT_COPY = 'SELECT_COPY',
  13 + PASTE = 'PASTE',
  14 + UNSELECTED = 'UNSELECTED',
  15 + DELETE_SELECT = 'DELETE_SELECT',
  16 + APPLY_CHANGE = 'APPLY_CHANGE',
  17 + UNDO_CHANGE = 'UNDO_CHANGE',
  18 +
  19 + SELECT_ALL = 'SELECT_ALL',
  20 +}
  21 +
  22 +export enum RuleContextMenuNameEnum {
  23 + DETAIL = '详情',
  24 + COPY = '复制',
  25 + DELETE = '删除',
  26 +
  27 + SELECT_COPY = '选择副本',
  28 + PASTE = '粘贴',
  29 + UNSELECTED = '取消选择',
  30 + DELETE_SELECT = '删除选定',
  31 + APPLY_CHANGE = '应用更改',
  32 + UNDO_CHANGE = '撤销更改',
  33 +
  34 + SELECT_ALL = '选择全部',
  35 +}
  36 +
  37 +export enum RuleChainContextMenuIconEnum {
  38 + DETAIL = 'material-symbols:menu',
  39 + COPY = 'material-symbols:content-copy',
  40 + DELETE = 'material-symbols:delete',
  41 +
  42 + SELECT_COPY = 'material-symbols:content-copy',
  43 + PASTE = 'material-symbols:content-paste',
  44 + UNSELECTED = 'material-symbols:tab-unselected',
  45 + DELETE_SELECT = 'material-symbols:close',
  46 + APPLY_CHANGE = 'material-symbols:done',
  47 + UNDO_CHANGE = 'material-symbols:close',
  48 +
  49 + SELECT_ALL = 'material-symbols:select-all',
  50 +
  51 + // LINK = 'material-symbols:trending-flat',
  52 +}
  53 +
  54 +export enum RuleChainContextMenuShortcutKeyEnum {
  55 + DELETE = 'Ctrl(⌘) X',
  56 +
  57 + SELECT_COPY = 'Ctrl(⌘) C',
  58 + PASTE = 'Ctrl(⌘) V',
  59 + UNSELECTED = 'Esc',
  60 + DELETE_SELECT = 'Del',
  61 + APPLY_CHANGE = 'Ctrl(⌘) S',
  62 + UNDO_CHANGE = 'Ctrl(⌘) Z',
  63 +
  64 + SELECT_ALL = 'Ctrl(⌘) A',
  65 +}
  66 +
  67 +const getMenuItem = (key: RuleContextMenuEnum, handler: Fn, disabled = false) => {
  68 + return {
  69 + key,
  70 + label: RuleContextMenuNameEnum[key],
  71 + icon: RuleChainContextMenuIconEnum[key],
  72 + shortcutKey: RuleChainContextMenuShortcutKeyEnum[key],
  73 + handler: () => handler?.(key),
  74 + disabled,
  75 + } as RuleChainContextMenuItemType;
  76 +};
  77 +
  78 +const getDivider = (): RuleChainContextMenuItemType => ({ divider: true });
  79 +
  80 +export function useCreateRuleChainContextMenu() {
  81 + const [createContextMenu] = useContextMenu();
  82 +
  83 + const createNodeContextMenu = (params: NodeMouseEvent): Promise<RuleContextMenuEnum | ''> => {
  84 + return new Promise(async (resolve) => {
  85 + await createContextMenu(params, {
  86 + items: [
  87 + getMenuItem(RuleContextMenuEnum.DETAIL, resolve),
  88 + getMenuItem(RuleContextMenuEnum.COPY, resolve),
  89 + getMenuItem(RuleContextMenuEnum.DELETE, resolve),
  90 + ],
  91 + });
  92 + resolve('');
  93 + });
  94 + };
  95 +
  96 + const createElementsSelectedContextMenu = (
  97 + params: NodeMouseEvent,
  98 + changeMarker: boolean,
  99 + elementsType: ElementsTypeEnum.NODE = ElementsTypeEnum.NODE
  100 + ): Promise<RuleContextMenuEnum | ''> => {
  101 + return new Promise(async (resolve) => {
  102 + await createContextMenu(params, {
  103 + items: [
  104 + ...(elementsType === ElementsTypeEnum.NODE
  105 + ? [getMenuItem(RuleContextMenuEnum.SELECT_COPY, resolve)]
  106 + : []),
  107 + getMenuItem(RuleContextMenuEnum.PASTE, resolve, !checkHasCacheRuleNode()),
  108 + getDivider(),
  109 + getMenuItem(RuleContextMenuEnum.UNSELECTED, resolve),
  110 + getMenuItem(RuleContextMenuEnum.DELETE_SELECT, resolve),
  111 + getDivider(),
  112 + getMenuItem(RuleContextMenuEnum.APPLY_CHANGE, resolve, !changeMarker),
  113 + getMenuItem(RuleContextMenuEnum.UNDO_CHANGE, resolve, !changeMarker),
  114 + ],
  115 + });
  116 + resolve('');
  117 + });
  118 + };
  119 +
  120 + const createPanelContextMenu = (
  121 + params: NodeMouseEvent,
  122 + changeMarker: boolean
  123 + ): Promise<RuleContextMenuEnum | ''> => {
  124 + return new Promise(async (resolve) => {
  125 + await createContextMenu(params, {
  126 + items: [
  127 + getMenuItem(RuleContextMenuEnum.PASTE, resolve, !checkHasCacheRuleNode()),
  128 + getMenuItem(RuleContextMenuEnum.SELECT_ALL, resolve),
  129 + getMenuItem(RuleContextMenuEnum.APPLY_CHANGE, resolve, !changeMarker),
  130 + getMenuItem(RuleContextMenuEnum.UNDO_CHANGE, resolve, !changeMarker),
  131 + ],
  132 + });
  133 + resolve('');
  134 + });
  135 + };
  136 +
  137 + const createEdgeContextMenu = (
  138 + params: NodeMouseEvent,
  139 + isInput = false
  140 + ): Promise<RuleContextMenuEnum | ''> => {
  141 + return new Promise(async (resolve) => {
  142 + await createContextMenu(params, {
  143 + items: [
  144 + ...(isInput ? [] : [getMenuItem(RuleContextMenuEnum.DETAIL, resolve)]),
  145 + getMenuItem(RuleContextMenuEnum.DELETE, resolve),
  146 + ],
  147 + });
  148 + resolve('');
  149 + });
  150 + };
  151 + return {
  152 + createNodeContextMenu,
  153 + createElementsSelectedContextMenu,
  154 + createPanelContextMenu,
  155 + createEdgeContextMenu,
  156 + };
  157 +}
  1 +import { Edge, Node } from '@vue-flow/core';
  2 +import { RULE_NODE_KEY, RULE_NODE_LOCAL_CACHE_KEY } from '/@/enums/cacheEnum';
  3 +import { createLocalStorage } from '/@/utils/cache';
  4 +
  5 +const ruleNodeStorage = createLocalStorage({ prefixKey: RULE_NODE_LOCAL_CACHE_KEY });
  6 +
  7 +interface RuleNodeCacheType {
  8 + nodes?: Node[];
  9 + edges?: Edge[];
  10 + originX?: number;
  11 + originY?: number;
  12 +}
  13 +
  14 +export const setRuleNodeCache = ({
  15 + nodes = [],
  16 + edges = [],
  17 + originX,
  18 + originY,
  19 +}: RuleNodeCacheType) => {
  20 + ruleNodeStorage.set(RULE_NODE_KEY, {
  21 + nodes,
  22 + edges,
  23 + originX,
  24 + originY,
  25 + });
  26 +};
  27 +
  28 +export const getRuleNodeCache = (): RuleNodeCacheType => ruleNodeStorage.get(RULE_NODE_KEY);
  29 +
  30 +export const checkHasCacheRuleNode = () => !!getRuleNodeCache();
  31 +
  32 +function initRuleNodeStorage() {
  33 + const value = ruleNodeStorage.get(RULE_NODE_KEY);
  34 + value && ruleNodeStorage.set(RULE_NODE_KEY, value);
  35 +}
  36 +
  37 +initRuleNodeStorage();
  1 +import type {
  2 + Connection,
  3 + EdgeComponent,
  4 + NodeComponent,
  5 + ValidConnectionFunc,
  6 + GraphNode,
  7 + NodeMouseEvent,
  8 + GraphEdge,
  9 + EdgeMouseEvent,
  10 +} from '@vue-flow/core';
  11 +import type { Ref } from 'vue';
  12 +import type { CreateNodeModal } from '../src/components/CreateNodeModal';
  13 +import type { EdgeData, NodeData } from '../types/node';
  14 +import type { CreateEdgeModal } from '../src/components/CreateEdgeModal';
  15 +import { BasicEdge, BasicNode } from '../src/components';
  16 +import { EdgeTypeEnum, ElementsTypeEnum, NodeTypeEnum } from '../enum';
  17 +import { markRaw, toRaw, unref } from 'vue';
  18 +import { isFunction } from 'lodash-es';
  19 +import { ConnectionLineType, SelectionMode, useVueFlow } from '@vue-flow/core';
  20 +import { isInputHandle, isOutputHandle } from '../utils';
  21 +import { useAddEdges } from './useAddEdges';
  22 +import { UpdateNodeDrawer } from '../src/components/UpdateNodeDrawer';
  23 +import { UpdateEdgeDrawer } from '../src/components/UpdateEdgeDrawer';
  24 +import { isNumber } from '/@/utils/is';
  25 +import { RuleContextMenuEnum, useCreateRuleChainContextMenu } from './useRuleChainContextMenu';
  26 +import { RuleChainDetail } from '../types/ruleNode';
  27 +import { useContextMenuAction } from './useContextMenuAction';
  28 +import { useSaveAndRedo } from './useSaveAndRedo';
  29 +import { EntryCategoryComponentEnum } from '../enum/category';
  30 +
  31 +interface UseRuleFlowOptionsType {
  32 + id: string;
  33 + ruleChainDetail: Ref<RuleChainDetail | undefined>;
  34 + createNodeModalActionType: Ref<Nullable<InstanceType<typeof CreateNodeModal>>>;
  35 + createEdgeModalActionType: Ref<Nullable<InstanceType<typeof CreateEdgeModal>>>;
  36 + updateNodeDrawerActionType: Ref<Nullable<InstanceType<typeof UpdateNodeDrawer>>>;
  37 + updateEdgeDrawerActionType: Ref<Nullable<InstanceType<typeof UpdateEdgeDrawer>>>;
  38 + useSaveAndRedoActionType: ReturnType<typeof useSaveAndRedo>;
  39 +}
  40 +
  41 +const validateInputAndOutput: ValidConnectionFunc = (connection: Connection) => {
  42 + const { sourceHandle, targetHandle, source, target } = connection;
  43 +
  44 + return (
  45 + isOutputHandle(sourceHandle || '') && isInputHandle(targetHandle || '') && source !== target
  46 + );
  47 +};
  48 +
  49 +export function useRuleFlow(options: UseRuleFlowOptionsType) {
  50 + const {
  51 + id,
  52 + ruleChainDetail,
  53 + createEdgeModalActionType,
  54 + updateEdgeDrawerActionType,
  55 + updateNodeDrawerActionType,
  56 + useSaveAndRedoActionType,
  57 + } = options;
  58 +
  59 + const { triggerChange } = useSaveAndRedoActionType;
  60 +
  61 + const flowActionType = useVueFlow({
  62 + id,
  63 + maxZoom: 1,
  64 + minZoom: 1,
  65 + panOnScroll: true,
  66 + selectionMode: SelectionMode.Partial,
  67 + nodeTypes: {
  68 + [NodeTypeEnum.CUSTOM]: markRaw(BasicNode) as NodeComponent,
  69 + },
  70 + edgeTypes: {
  71 + [EdgeTypeEnum.CUSTOM]: markRaw(BasicEdge) as EdgeComponent,
  72 + },
  73 + connectionLineOptions: {
  74 + type: ConnectionLineType.Bezier,
  75 + },
  76 + defaultViewport: {
  77 + x: 0,
  78 + y: 0,
  79 + },
  80 + isValidConnection(connection, elements) {
  81 + const validateList = [validateInputAndOutput];
  82 + const targetData = elements.targetNode.data as NodeData;
  83 +
  84 + if (
  85 + targetData.category?.validateConnection &&
  86 + isFunction(targetData.category.validateConnection)
  87 + )
  88 + validateList.push(targetData.category?.validateConnection);
  89 +
  90 + if (targetData.config?.validateConnection && isFunction(targetData.config.validateConnection))
  91 + validateList.push(targetData.config.validateConnection);
  92 +
  93 + if (!validateList.every((item) => item(connection, elements))) return false;
  94 +
  95 + return true;
  96 + },
  97 + });
  98 +
  99 + const {
  100 + getEdges,
  101 + addEdges,
  102 + findEdge,
  103 + findNode,
  104 + setViewport,
  105 + removeEdges,
  106 + onConnect,
  107 + onPaneReady,
  108 + onNodeDoubleClick,
  109 + onEdgeDoubleClick,
  110 + onNodeDragStop,
  111 + onNodeContextMenu,
  112 + onEdgeContextMenu,
  113 + onPaneContextMenu,
  114 + } = flowActionType;
  115 +
  116 + const { getAddedgesParams } = useAddEdges();
  117 +
  118 + onConnect(async (params) => {
  119 + const { source } = params;
  120 + const sourceNode = findNode(source);
  121 + const sourceData = sourceNode?.data as NodeData;
  122 +
  123 + let types: string[] = [];
  124 +
  125 + if (sourceData && validateHasLabelConnection(sourceData)) {
  126 + const { flag, data } = (await unref(createEdgeModalActionType)?.open(sourceData)) || {};
  127 + if (!flag) return;
  128 + types = toRaw(unref(data));
  129 + }
  130 +
  131 + handleMaxConnectionPoint(sourceNode);
  132 +
  133 + addEdges(getAddedgesParams(params, types));
  134 +
  135 + triggerChange();
  136 + });
  137 +
  138 + onPaneReady(async () => {
  139 + setViewport({ x: 0, y: 0, zoom: 1 });
  140 + });
  141 +
  142 + onNodeDoubleClick(async ({ node }) => {
  143 + handleUpdateNode(node);
  144 + });
  145 +
  146 + onEdgeDoubleClick(async ({ edge }) => {
  147 + handleUpdateEdge(edge);
  148 + });
  149 +
  150 + onNodeDragStop(() => {
  151 + triggerChange();
  152 + });
  153 +
  154 + const {
  155 + createNodeContextMenu,
  156 + createElementsSelectedContextMenu,
  157 + createEdgeContextMenu,
  158 + createPanelContextMenu,
  159 + } = useCreateRuleChainContextMenu();
  160 +
  161 + const { handleContextMenuAction } = useContextMenuAction();
  162 +
  163 + onNodeContextMenu(async (params) => {
  164 + const menuType = params.node.selected
  165 + ? await createElementsSelectedContextMenu(
  166 + params,
  167 + unref(useSaveAndRedoActionType.changeMarker)
  168 + )
  169 + : await createNodeContextMenu(params);
  170 +
  171 + if (menuType) {
  172 + if (menuType === RuleContextMenuEnum.DETAIL) {
  173 + handleUpdateNode(params.node);
  174 + return;
  175 + }
  176 +
  177 + handleContextMenuAction({
  178 + menuType,
  179 + flowActionType,
  180 + event: params.event,
  181 + node: params.node,
  182 + useSaveAndRedoActionType,
  183 + });
  184 + }
  185 + });
  186 +
  187 + onEdgeContextMenu(async (params) => {
  188 + const isInputNode =
  189 + (params.edge.sourceNode.data as NodeData).config?.key === EntryCategoryComponentEnum.INPUT;
  190 + const menuType = await createEdgeContextMenu(
  191 + getCreateEdgeContextMenuParams(params),
  192 + isInputNode
  193 + );
  194 +
  195 + if (menuType) {
  196 + if (menuType === RuleContextMenuEnum.DETAIL) {
  197 + handleUpdateEdge(params.edge);
  198 + return;
  199 + }
  200 +
  201 + handleContextMenuAction({
  202 + menuType,
  203 + flowActionType,
  204 + event: params.event,
  205 + useSaveAndRedoActionType,
  206 + edge: params.edge,
  207 + });
  208 + }
  209 + });
  210 +
  211 + onPaneContextMenu(async (params) => {
  212 + const menuType = unref(flowActionType.getSelectedElements).length
  213 + ? await createElementsSelectedContextMenu(
  214 + getCreatePanelContextMenuParams(params),
  215 + unref(useSaveAndRedoActionType.changeMarker)
  216 + )
  217 + : await createPanelContextMenu(
  218 + getCreatePanelContextMenuParams(params),
  219 + unref(useSaveAndRedoActionType.changeMarker)
  220 + );
  221 +
  222 + if (menuType) {
  223 + handleContextMenuAction({
  224 + menuType,
  225 + flowActionType,
  226 + event: params,
  227 + useSaveAndRedoActionType,
  228 + });
  229 + }
  230 + });
  231 +
  232 + /**
  233 + * @description 验证是否有连接label
  234 + * @param sourceData
  235 + * @returns
  236 + */
  237 + function validateHasLabelConnection(sourceData: NodeData) {
  238 + return !!sourceData?.config?.configurationDescriptor.nodeDefinition?.relationTypes?.length;
  239 + }
  240 +
  241 + function handleMaxConnectionPoint(sourceNode?: GraphNode) {
  242 + if (!sourceNode) return;
  243 +
  244 + const maxConnectionPoint = unref(sourceNode).data?.config?.maxConnectionPoint;
  245 +
  246 + if (!maxConnectionPoint || !isNumber(maxConnectionPoint)) return;
  247 +
  248 + const sourceId = sourceNode.id;
  249 + const connectionPool = unref(getEdges).filter((item) => item.source === sourceId);
  250 + if (connectionPool.length >= maxConnectionPoint && connectionPool[0]) {
  251 + removeEdges(connectionPool[0].id);
  252 + }
  253 + }
  254 +
  255 + async function handleUpdateNode(node: GraphNode) {
  256 + if ((node.data as NodeData).config?.disableAction) return;
  257 + const { flag, data } =
  258 + (await unref(updateNodeDrawerActionType)?.open(
  259 + toRaw((node as NodeData)?.data as unknown as NodeData),
  260 + void 0,
  261 + { id: node.id, type: ElementsTypeEnum.NODE }
  262 + )) || {};
  263 +
  264 + if (!flag) return;
  265 +
  266 + const currentNode = findNode(node.id);
  267 + (currentNode!.data as NodeData).data = data;
  268 + triggerChange();
  269 + }
  270 +
  271 + async function handleUpdateEdge(edge: GraphEdge) {
  272 + if (!validateHasLabelConnection(edge.sourceNode.data)) return;
  273 +
  274 + if ((edge.sourceNode.data as NodeData).config?.disableAction) return;
  275 +
  276 + const { flag, data } =
  277 + (await unref(updateEdgeDrawerActionType)?.open(
  278 + toRaw(unref(edge.sourceNode?.data as unknown as NodeData)),
  279 + toRaw(unref(edge.data as EdgeData)),
  280 + { id: edge.id, type: ElementsTypeEnum.EDGE }
  281 + )) || {};
  282 +
  283 + if (!flag) return;
  284 +
  285 + const currentEdge = findEdge(edge.id);
  286 +
  287 + (currentEdge!.data as EdgeData).data = toRaw(unref(data));
  288 +
  289 + triggerChange?.();
  290 + }
  291 +
  292 + function getCreatePanelContextMenuParams(params: Event) {
  293 + return {
  294 + event: params as MouseEvent,
  295 + node: {
  296 + data: {
  297 + data: { name: '规则链' },
  298 + config: {
  299 + name: unref(ruleChainDetail)?.name,
  300 + backgroundColor: '#aac7e4',
  301 + configurationDescriptor: {
  302 + nodeDefinition: {
  303 + icon: 'material-symbols:settings-ethernet',
  304 + },
  305 + },
  306 + },
  307 + },
  308 + },
  309 + } as NodeMouseEvent;
  310 + }
  311 +
  312 + function getCreateEdgeContextMenuParams(params: EdgeMouseEvent) {
  313 + return {
  314 + event: params.event as MouseEvent,
  315 + node: {
  316 + data: {
  317 + data: { name: '链接' },
  318 + config: {
  319 + name: unref(params.edge.data as EdgeData)?.data?.type?.join(' / '),
  320 + backgroundColor: '#aac7e4',
  321 + configurationDescriptor: {
  322 + nodeDefinition: {
  323 + icon: 'material-symbols:trending-flat',
  324 + },
  325 + },
  326 + },
  327 + },
  328 + },
  329 + } as NodeMouseEvent;
  330 + }
  331 +
  332 + return { flowActionType };
  333 +}