Commit f6d3f37c69374b9a947a432b4bef61654dd0eded

Authored by ww
1 parent 387c34bd

refactor: 重构场景联动

Showing 55 changed files with 3254 additions and 1038 deletions

Too many changes to show.

To preserve performance only 55 of 73 files are displayed.

... ... @@ -25,7 +25,7 @@ module.exports = defineConfig({
25 25 'plugin:jest/recommended',
26 26 ],
27 27 rules: {
28   - 'no-console': 'off',
  28 + 'no-console': 'error',
29 29 'vue/script-setup-uses-vars': 'error',
30 30 '@typescript-eslint/ban-ts-ignore': 'off',
31 31 '@typescript-eslint/explicit-function-return-type': 'off',
... ...
1 1 import { DataType } from '../../device/model/modelOfMatterModel';
2   -import { DataTypeEnum } from '/@/components/Form/src/externalCompns/components/StructForm/config';
  2 +import { DataTypeEnum } from '/@/enums/objectModelEnum';
3 3 import { DataSource } from '/@/views/visual/palette/types';
4 4
5 5 export interface AddDataBoardParams {
... ...
1   -import {
2   - DataTypeEnum,
3   - FunctionType,
4   -} from '/@/views/device/profiles/step/cpns/physical/cpns/config';
  1 +import { DataTypeEnum } from '/@/enums/objectModelEnum';
  2 +import { FunctionType } from '/@/views/device/profiles/step/cpns/physical/cpns/config';
5 3
6 4 export interface Specs {
7 5 min: string;
... ... @@ -26,7 +24,7 @@ export interface DataType {
26 24
27 25 export interface StructJSON {
28 26 functionName?: string;
29   - identifier?: string;
  27 + identifier: string;
30 28 remark?: string;
31 29 dataType?: DataType;
32 30 serviceCommand?: string;
... ...
1 1 import { BasicPageParams } from '/@/api/model/baseModel';
  2 +import { SceneLinkageDataType } from '/@/views/rule/linkedge/components/SceneLinkageDrawer/type';
2 3
3 4 export type ScreenLinkPageQueryParam = BasicPageParams & ScreenParams;
4 5
... ... @@ -14,97 +15,7 @@ export type ScreenByDeptIdParams = {
14 15 // organizationId: '2f5c8f2a-196c-4941-8771-290f9da76219';
15 16 };
16 17
17   -export interface ScreenAddModel {
18   - createTime?: string;
19   - creator?: string;
20   - defaultConfig?: string;
21   - description?: string;
22   - doAction?: [
23   - {
24   - command: string;
25   - createTime: string;
26   - creator: string;
27   - defaultConfig: string;
28   - description: string;
29   - deviceId: string;
30   - enabled: true;
31   - icon: string;
32   - id: string;
33   - name: string;
34   - outPut: string;
35   - outTarget: string;
36   - roleIds: [];
37   - tenantCode: string;
38   - tenantExpireTime: string;
39   - tenantId: string;
40   - tenantStatus: 'DISABLED';
41   - updateTime: string;
42   - updater: string;
43   - }
44   - ];
45   - doCondition?: [
46   - {
47   - compare: 0;
48   - createTime: string;
49   - creator: string;
50   - defaultConfig: string;
51   - description: string;
52   - deviceId: string;
53   - enabled: true;
54   - icon: string;
55   - id: string;
56   - name: string;
57   - property: string;
58   - roleIds: [];
59   - status: string;
60   - tenantCode: string;
61   - tenantExpireTime: string;
62   - tenantId: string;
63   - tenantStatus: 'DISABLED';
64   - updateTime: string;
65   - updater: string;
66   - value: string;
67   - }
68   - ];
69   - enabled?: true;
70   - icon?: string;
71   - id?: string;
72   - name?: string;
73   - organizationId?: string;
74   - roleIds?: [string];
75   - status?: string;
76   - tenantCode?: string;
77   - tenantExpireTime?: string;
78   - tenantId?: string;
79   - tenantStatus?: 'DISABLED';
80   - triggers?: [
81   - {
82   - attributeChoose?: string;
83   - compare?: 0;
84   - createTime?: string;
85   - creator?: string;
86   - defaultConfig?: string;
87   - description?: string;
88   - deviceId?: string;
89   - enabled?: true;
90   - icon?: string;
91   - id?: string;
92   - name?: string;
93   - roleIds?: [];
94   - tenantCode?: string;
95   - tenantExpireTime?: string;
96   - tenantId?: string;
97   - tenantStatus?: 'DISABLED';
98   - tiggerEvent?: string;
99   - touchWay?: string;
100   - updateTime?: string;
101   - updater?: string;
102   - value?: string;
103   - }
104   - ];
105   - updateTime?: string;
106   - updater?: string;
107   -}
  18 +export type ScreenAddModel = SceneLinkageDataType;
108 19
109 20 export interface IChangeStatus {
110 21 status?: number;
... ...
... ... @@ -6,6 +6,7 @@ import {
6 6 ScreenByDeptIdParams,
7 7 IChangeStatus,
8 8 } from '/@/api/ruleengine/model/ruleengineModel';
  9 +import { DeviceModel } from '../device/model/deviceModel';
9 10
10 11 enum ScreenManagerApi {
11 12 /**
... ... @@ -126,7 +127,7 @@ export const byOrganizationIdGetMasterDevice = (params: {
126 127 deviceProfileId?: string;
127 128 }) => {
128 129 const { organizationId, deviceProfileId } = params;
129   - return defHttp.get({
  130 + return defHttp.get<DeviceModel[]>({
130 131 url: `${ScreenManagerApi.MASTER_GET_DEVICE}`,
131 132 params: { deviceProfileId, organizationId },
132 133 });
... ...
... ... @@ -191,6 +191,10 @@
191 191 <slot name="afterFullScreen"></slot>
192 192 </div>
193 193 </div>
194   - <div ref="jsonEditorElRef" class="flex-auto"></div>
  194 + <div
  195 + ref="jsonEditorElRef"
  196 + class="flex-auto"
  197 + :style="{ backgroundColor: disabled ? '#f0f0f0' : '' }"
  198 + ></div>
195 199 </div>
196 200 </template>
... ...
... ... @@ -15,6 +15,8 @@ export { default as ApiUpload } from './src/components/ApiUpload.vue';
15 15 export { default as StructForm } from './src/externalCompns/components/StructForm/StructForm.vue';
16 16 export { default as JavaScriptFunctionEditor } from './src/components/JavaScriptFunctionEditor.vue';
17 17
  18 +export { ThingsModelForm } from './src/externalCompns/components/ThingsModelForm';
  19 +
18 20 //注册自定义组件
19 21 export {
20 22 JEasyCron,
... ...
... ... @@ -69,7 +69,7 @@
69 69 name: 'BasicForm',
70 70 components: { FormItem, Form, Row, FormAction },
71 71 props: basicProps,
72   - emits: ['advanced-change', 'reset', 'submit', 'register'],
  72 + emits: ['advanced-change', 'reset', 'submit', 'register', 'fieldValueChange'],
73 73 setup(props, { emit, attrs }) {
74 74 const formModel = reactive<Recordable>({});
75 75 const modalFn = useModalContext();
... ... @@ -230,6 +230,7 @@
230 230
231 231 function setFormModel(key: string, value: any) {
232 232 formModel[key] = value;
  233 + emit('fieldValueChange', key, value);
233 234 const { validateTrigger } = unref(getBindValue);
234 235 if (!validateTrigger || validateTrigger === 'change') {
235 236 validateFields([key]).catch((_) => {});
... ...
... ... @@ -5,7 +5,8 @@
5 5 import { BasicModal } from '/@/components/Modal';
6 6 import { PlusCircleOutlined } from '@ant-design/icons-vue';
7 7 import { FormFieldsEnum, formSchemas } from './config';
8   - import { DataTypeEnum } from '../StructForm/config';
  8 + import { DataTypeEnum } from '/@/enums/objectModelEnum';
  9 +
9 10 const show = ref(false);
10 11
11 12 const props = withDefaults(
... ... @@ -50,7 +51,7 @@
50 51 updateSchema([
51 52 {
52 53 field: FormFieldsEnum.ZOOM_FACTOR,
53   - ifShow: props.dataType == DataTypeEnum.IS_BOOL ? false : true,
  54 + ifShow: props.dataType == DataTypeEnum.BOOL ? false : true,
54 55 },
55 56 ]);
56 57 }
... ...
1 1 <script lang="ts" setup>
2 2 import { Card } from 'ant-design-vue';
3 3 import { computed, nextTick, onMounted, onUpdated, unref, watch } from 'vue';
4   - import { DataTypeEnum } from '../StructForm/config';
5 4 import { BasicCreateFormParams } from './type';
6 5 import { DynamicProps } from '/#/utils';
7 6 import { Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel';
8 7 import { BasicForm, FormProps, FormSchema, useForm } from '/@/components/Form';
  8 + import { DataTypeEnum } from '/@/enums/objectModelEnum';
9 9 import { ReadAndWriteEnum } from '/@/enums/toolEnum';
10 10
11 11 const props = withDefaults(
... ... @@ -86,7 +86,7 @@
86 86 // step: step,
87 87 // formatter: (value: string) => value,
88 88 // parser: (string: string) => {
89   - // if (dataType === DataTypeEnum.IS_NUMBER_INT) {
  89 + // if (dataType === DataTypeEnum.NUMBER_INT) {
90 90 // return Number(Number(string).toFixed());
91 91 // }
92 92 // return Number(string);
... ... @@ -188,19 +188,19 @@
188 188 dataType: dataType! as unknown as DataTypeEnum,
189 189 specs: specs as Partial<Specs>,
190 190 };
191   - if (type === DataTypeEnum.IS_NUMBER_INT || type === DataTypeEnum.IS_NUMBER_DOUBLE) {
  191 + if (type === DataTypeEnum.NUMBER_INT || type === DataTypeEnum.NUMBER_DOUBLE) {
192 192 schemas.push(createInputNumber(params));
193 193 }
194 194
195   - if (type === DataTypeEnum.IS_BOOL) {
  195 + if (type === DataTypeEnum.BOOL) {
196 196 schemas.push(createSelect(params));
197 197 }
198 198
199   - if (type === DataTypeEnum.IS_STRING) {
  199 + if (type === DataTypeEnum.STRING) {
200 200 schemas.push(createInput(params));
201 201 }
202 202
203   - if (type === DataTypeEnum.IS_STRUCT) {
  203 + if (type === DataTypeEnum.STRUCT) {
204 204 schemas.push(createInputJson(params));
205 205 }
206 206 }
... ...
1   -import { DataTypeEnum } from '../StructForm/config';
2 1 import { Specs } from '/@/api/device/model/modelOfMatterModel';
  2 +import { DataTypeEnum } from '/@/enums/objectModelEnum';
3 3
4 4 export interface BasicCreateFormParams {
5 5 identifier: string;
... ...
1 1 import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel';
2 2 import { findDictItemByCode } from '/@/api/system/dict';
3 3 import { FormSchema } from '/@/components/Table';
  4 +import { DataTypeEnum } from '/@/enums/objectModelEnum';
4 5 import { isNullOrUnDef } from '/@/utils/is';
5 6 import { FormField } from '/@/views/device/profiles/step/cpns/physical/cpns/config';
6 7
7   -export enum DataTypeEnum {
8   - IS_NUMBER_INT = 'INT',
9   - IS_NUMBER_DOUBLE = 'DOUBLE',
10   - IS_STRING = 'TEXT',
11   - IS_STRUCT = 'STRUCT',
12   - IS_BOOL = 'BOOL',
13   -}
14   -
15 8 export const validateValueRange = (_rule, value: Record<'min' | 'max', number>, _callback) => {
16 9 value = value || {};
17 10 const { min, max } = value;
... ... @@ -77,10 +70,9 @@ export const formSchemas = (
77 70 try {
78 71 const record = await findDictItemByCode(params);
79 72 return hasStructForm
80   - ? record.filter((item) => item.itemValue !== DataTypeEnum.IS_STRUCT)
  73 + ? record.filter((item) => item.itemValue !== DataTypeEnum.STRUCT)
81 74 : record;
82 75 } catch (error) {
83   - console.log(error);
84 76 return [];
85 77 }
86 78 },
... ... @@ -91,7 +83,7 @@ export const formSchemas = (
91 83 valueField: 'itemValue',
92 84 getPopupContainer: () => document.body,
93 85 onChange: (value: string) => {
94   - if (value == DataTypeEnum.IS_STRUCT) {
  86 + if (value == DataTypeEnum.STRUCT) {
95 87 updateSchema({
96 88 field: FormField.SPECS_LIST,
97 89 componentProps: {
... ... @@ -114,8 +106,8 @@ export const formSchemas = (
114 106 span: 18,
115 107 },
116 108 ifShow: ({ values }) =>
117   - values[FormField.TYPE] === DataTypeEnum.IS_NUMBER_INT ||
118   - values[FormField.TYPE] === DataTypeEnum.IS_NUMBER_DOUBLE,
  109 + values[FormField.TYPE] === DataTypeEnum.NUMBER_INT ||
  110 + values[FormField.TYPE] === DataTypeEnum.NUMBER_DOUBLE,
119 111 rules: [{ validator: validateValueRange }],
120 112 },
121 113 {
... ... @@ -134,8 +126,8 @@ export const formSchemas = (
134 126 },
135 127 },
136 128 ifShow: ({ values }) =>
137   - values[FormField.TYPE] === DataTypeEnum.IS_NUMBER_INT ||
138   - values[FormField.TYPE] === DataTypeEnum.IS_NUMBER_DOUBLE,
  129 + values[FormField.TYPE] === DataTypeEnum.NUMBER_INT ||
  130 + values[FormField.TYPE] === DataTypeEnum.NUMBER_DOUBLE,
139 131 dynamicRules: ({ model }) => {
140 132 const valueRange = model[FormField.VALUE_RANGE] || {};
141 133 const { min, max } = valueRange;
... ... @@ -199,8 +191,8 @@ export const formSchemas = (
199 191 };
200 192 },
201 193 ifShow: ({ values }) =>
202   - values[FormField.TYPE] === DataTypeEnum.IS_NUMBER_INT ||
203   - values[FormField.TYPE] === DataTypeEnum.IS_NUMBER_DOUBLE,
  194 + values[FormField.TYPE] === DataTypeEnum.NUMBER_INT ||
  195 + values[FormField.TYPE] === DataTypeEnum.NUMBER_DOUBLE,
204 196 },
205 197 {
206 198 field: FormField.BOOL_CLOSE,
... ... @@ -214,7 +206,7 @@ export const formSchemas = (
214 206 placeholder: '如:关',
215 207 },
216 208 defaultValue: '关',
217   - ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.IS_BOOL,
  209 + ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.BOOL,
218 210 dynamicRules: ({ model }) => {
219 211 const close = model[FormField.BOOL_CLOSE];
220 212 const open = model[FormField.BOOL_OPEN];
... ... @@ -243,7 +235,7 @@ export const formSchemas = (
243 235 placeholder: '如:开',
244 236 },
245 237 defaultValue: '开',
246   - ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.IS_BOOL,
  238 + ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.BOOL,
247 239 dynamicRules: ({ model }) => {
248 240 const close = model[FormField.BOOL_CLOSE];
249 241 const open = model[FormField.BOOL_OPEN];
... ... @@ -277,7 +269,7 @@ export const formSchemas = (
277 269 suffix: () => '字节',
278 270 };
279 271 },
280   - ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.IS_STRING,
  272 + ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.STRING,
281 273 },
282 274 {
283 275 field: FormField.EXTENSION_DESC,
... ... @@ -322,7 +314,7 @@ export const formSchemas = (
322 314 valueField: 'value',
323 315 changeEvent: 'update:value',
324 316 colProps: { span: 24 },
325   - ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.IS_STRUCT,
  317 + ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.STRUCT,
326 318 rules: [{ required: true, validator: validateJSON }],
327 319 },
328 320 {
... ...
1   -import { DataTypeEnum } from './config';
2 1 import { StructJSON } from '/@/api/device/model/modelOfMatterModel';
  2 +import { DataTypeEnum } from '/@/enums/objectModelEnum';
3 3 import { FormField } from '/@/views/device/profiles/step/cpns/physical/cpns/config';
4 4
5 5 export enum OpenModalMode {
... ...
1 1 import { cloneDeep } from 'lodash-es';
2   -import { DataTypeEnum } from './config';
3 2 import { StructFormValue } from './type';
4 3 import { DataType, ModelOfMatterParams, StructJSON } from '/@/api/device/model/modelOfMatterModel';
5 4 import { isArray } from '/@/utils/is';
  5 +import { DataTypeEnum } from '/@/enums/objectModelEnum';
6 6
7 7 export function transfromToStructJSON(value: StructFormValue): StructJSON {
8 8 const {
... ... @@ -24,21 +24,21 @@ export function transfromToStructJSON(value: StructFormValue): StructJSON {
24 24 let dataType = {} as unknown as DataType;
25 25
26 26 switch (type) {
27   - case DataTypeEnum.IS_NUMBER_INT:
  27 + case DataTypeEnum.NUMBER_INT:
28 28 dataType = {
29 29 type,
30 30 specs: { valueRange, step, unit, unitName },
31 31 };
32 32 break;
33 33
34   - case DataTypeEnum.IS_NUMBER_DOUBLE:
  34 + case DataTypeEnum.NUMBER_DOUBLE:
35 35 dataType = {
36 36 type,
37 37 specs: { valueRange, step, unit, unitName },
38 38 };
39 39 break;
40 40
41   - case DataTypeEnum.IS_BOOL:
  41 + case DataTypeEnum.BOOL:
42 42 dataType = {
43 43 type,
44 44 specs: {
... ... @@ -48,14 +48,14 @@ export function transfromToStructJSON(value: StructFormValue): StructJSON {
48 48 };
49 49 break;
50 50
51   - case DataTypeEnum.IS_STRING:
  51 + case DataTypeEnum.STRING:
52 52 dataType = {
53 53 type,
54 54 specs: { length },
55 55 };
56 56 break;
57 57
58   - case DataTypeEnum.IS_STRUCT:
  58 + case DataTypeEnum.STRUCT:
59 59 dataType = {
60 60 type,
61 61 specs: specs! as unknown as ModelOfMatterParams[],
... ...
  1 +<script setup lang="ts">
  2 + import { StructJSON } from '/@/api/device/model/modelOfMatterModel';
  3 + import { JsonPreview } from '/@/components/CodeEditor';
  4 + import { BasicModal, useModalInner } from '/@/components/Modal';
  5 +
  6 + defineProps<{
  7 + inputData?: StructJSON[];
  8 + }>();
  9 +
  10 + defineEmits(['register']);
  11 +
  12 + const data = {};
  13 +
  14 + const [register] = useModalInner();
  15 +</script>
  16 +
  17 +<template>
  18 + <BasicModal @register="register" title="">
  19 + <!-- -->
  20 + <JsonPreview :data="data" />
  21 + </BasicModal>
  22 +</template>
... ...
  1 +import { FormSchema } from '../../../types/form';
  2 +import { Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel';
  3 +import { DataTypeEnum } from '/@/enums/objectModelEnum';
  4 +import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const';
  5 +
  6 +export const getFormSchemas = ({
  7 + structJSON: structJson,
  8 + required,
  9 + transportType,
  10 +}: {
  11 + structJSON: StructJSON[];
  12 + required?: boolean;
  13 + transportType?: string;
  14 +}): FormSchema[] => {
  15 + const createInputNumber = ({ identifier, functionName, dataType }: StructJSON): FormSchema => {
  16 + const { specs } = dataType || {};
  17 + const { valueRange } = specs! as Specs;
  18 + const { max = 2147483647, min = -2147483648 } = valueRange || {};
  19 + return {
  20 + field: identifier,
  21 + label: functionName,
  22 + component: 'InputNumber',
  23 + rules: [
  24 + {
  25 + required,
  26 + message: `${functionName}是必填项`,
  27 + },
  28 + {
  29 + type: 'number',
  30 + trigger: 'change',
  31 + validator: (_rule, value) => {
  32 + const reg = /^[0-9]*$/;
  33 + if (!reg.test(value))
  34 + return Promise.reject(new Error(`${functionName}不是一个有效的数字`));
  35 + if (value < min || value > max)
  36 + return Promise.reject(new Error(`${functionName}取值范围在${min}~${max}之间`));
  37 +
  38 + return Promise.resolve(value);
  39 + },
  40 + },
  41 + ],
  42 + componentProps: {
  43 + max,
  44 + min,
  45 + placeholder: `请输入${functionName}`,
  46 + },
  47 + } as FormSchema;
  48 + };
  49 +
  50 + const createInput = ({ identifier, functionName, dataType }: StructJSON): FormSchema => {
  51 + const { specs } = dataType || {};
  52 + const { length = 10240 } = specs! as Partial<Specs>;
  53 + return {
  54 + field: identifier,
  55 + label: functionName,
  56 + component: 'Input',
  57 + rules: [
  58 + {
  59 + required,
  60 + message: `${functionName}是必填项`,
  61 + },
  62 + {
  63 + type: 'string',
  64 + trigger: 'change',
  65 + validator: (_rule, value) => {
  66 + if ((value?.length || 0) > length)
  67 + return Promise.reject(new Error(`${functionName}数据长度应该小于${length}`));
  68 +
  69 + return Promise.resolve(value);
  70 + },
  71 + },
  72 + ],
  73 + componentProps: {
  74 + maxLength: length,
  75 + placeholder: `请输入${functionName}`,
  76 + },
  77 + } as FormSchema;
  78 + };
  79 +
  80 + const createSelect = ({ identifier, functionName, dataType }: StructJSON): FormSchema => {
  81 + const { specs } = dataType || {};
  82 + const { boolClose, boolOpen } = specs! as Partial<Specs>;
  83 + return {
  84 + field: identifier,
  85 + label: functionName!,
  86 + component: 'Select',
  87 + rules: [
  88 + {
  89 + required,
  90 + message: `${functionName}是必填项`,
  91 + type: 'number',
  92 + },
  93 + ],
  94 + componentProps: {
  95 + options: [
  96 + { label: `${boolClose}-0`, value: 0 },
  97 + { label: `${boolOpen}-1`, value: 1 },
  98 + ],
  99 + placeholder: `请选择${functionName}`,
  100 + },
  101 + };
  102 + };
  103 +
  104 + const createStructJson = ({ identifier, functionName, dataType }: StructJSON): FormSchema => {
  105 + return {
  106 + field: identifier,
  107 + label: functionName!,
  108 + component: 'Input',
  109 + changeEvent: 'update:value',
  110 + componentProps: () => {
  111 + return {
  112 + inputData: dataType?.specs || [],
  113 + };
  114 + },
  115 + colSlot: identifier,
  116 + };
  117 + };
  118 +
  119 + const createTCPServiceCommandInput = ({
  120 + serviceCommand,
  121 + functionName,
  122 + }: StructJSON): FormSchema => {
  123 + return {
  124 + field: 'serviceCommand',
  125 + label: '服务命令',
  126 + component: 'Input',
  127 + required,
  128 + componentProps: {
  129 + defaultValue: serviceCommand,
  130 + placeholder: `请输入${functionName}`,
  131 + },
  132 + };
  133 + };
  134 +
  135 + const schemas: FormSchema[] = [];
  136 +
  137 + for (const item of structJson) {
  138 + const { dataType } = item;
  139 + const { type } = dataType || {};
  140 + if (transportType === TransportTypeEnum.TCP) {
  141 + item.serviceCommand && schemas.push(createTCPServiceCommandInput(item));
  142 + break;
  143 + }
  144 +
  145 + if (type === DataTypeEnum.BOOL) schemas.push(createSelect(item));
  146 + else if (type === DataTypeEnum.NUMBER_INT) schemas.push(createInputNumber(item));
  147 + else if (type === DataTypeEnum.NUMBER_DOUBLE) schemas.push(createInputNumber(item));
  148 + else if (type === DataTypeEnum.STRING) schemas.push(createInput(item));
  149 + else if (type === DataTypeEnum.STRUCT) schemas.push(createStructJson(item));
  150 + }
  151 +
  152 + return schemas;
  153 +};
... ...
  1 +import { ValidatorRule } from 'ant-design-vue/lib/form/interface';
  2 +export { default as ThingsModelForm } from './index.vue';
  3 +
  4 +export const validateTCPCustomCommand: ValidatorRule['validator'] = (_rule, value) => {
  5 + const reg = /^[\s0-9a-fA-F]+$/;
  6 + if (reg.test(value)) return Promise.resolve();
  7 + return Promise.reject('请输入ASCII或HEX服务命令(0~9/A~F)');
  8 +};
  9 +
  10 +export const trimBlankSpace = (string: string) => string.replace(/\s/g, '');
... ...
  1 +<script setup lang="ts">
  2 + import { Card } from 'ant-design-vue';
  3 + import { ComponentPublicInstance, computed, nextTick, reactive, unref, watch } from 'vue';
  4 + import { getFormSchemas } from './config';
  5 + import { ThingsModelForm } from '.';
  6 + import { DefineComponentsBasicExpose } from '/#/utils';
  7 + import { StructJSON } from '/@/api/device/model/modelOfMatterModel';
  8 + import { useForm } from '../../../hooks/useForm';
  9 + import { DataTypeEnum } from '/@/enums/objectModelEnum';
  10 + import { BasicForm } from '/@/components/Form';
  11 +
  12 + const props = withDefaults(
  13 + defineProps<{
  14 + value?: Recordable;
  15 + inputData?: StructJSON[];
  16 + required?: boolean;
  17 + title?: string;
  18 + transportType?: string;
  19 + disabled?: boolean;
  20 + identifier?: string;
  21 + }>(),
  22 + {
  23 + inputData: () => [],
  24 + required: true,
  25 + }
  26 + );
  27 +
  28 + const thingsModelFormListElMap = reactive<
  29 + Record<string, { el: InstanceType<typeof ThingsModelForm>; structJSON: StructJSON }>
  30 + >({});
  31 +
  32 + const [register, formActionType] = useForm({
  33 + schemas: getFormSchemas({
  34 + structJSON: props.inputData || [],
  35 + required: props.required,
  36 + transportType: props.transportType,
  37 + }),
  38 + showActionButtonGroup: false,
  39 + layout: 'inline',
  40 + labelWidth: 100,
  41 + });
  42 +
  43 + const getStructFormItem = computed(() => {
  44 + const { inputData } = props;
  45 + return inputData.filter((item) => item.dataType?.type === DataTypeEnum.STRUCT);
  46 + });
  47 +
  48 + const setFormElRef = (
  49 + el: Nullable<Element | ComponentPublicInstance>,
  50 + structJSON: StructJSON
  51 + ) => {
  52 + const _structJSON = unref(getStructFormItem).find(
  53 + (item) => item.identifier === structJSON.identifier
  54 + );
  55 +
  56 + if (_structJSON)
  57 + thingsModelFormListElMap[structJSON.identifier!] = {
  58 + el: el as InstanceType<typeof ThingsModelForm>,
  59 + structJSON,
  60 + };
  61 + };
  62 +
  63 + const getFieldsValue = () => {
  64 + const basicValue = formActionType.getFieldsValue();
  65 +
  66 + const structValue: Recordable = {};
  67 + for (const key of Object.keys(thingsModelFormListElMap)) {
  68 + const item = thingsModelFormListElMap[key];
  69 + const { el } = item;
  70 + structValue[key] = el.getFieldsValue() || {};
  71 + }
  72 +
  73 + return {
  74 + ...basicValue,
  75 + ...structValue,
  76 + };
  77 + };
  78 +
  79 + const setFieldsValue = (value: Recordable) => {
  80 + formActionType.setFieldsValue(value);
  81 + };
  82 +
  83 + const validate = async () => {
  84 + await formActionType.validate();
  85 + for (const key of Object.keys(thingsModelFormListElMap))
  86 + await thingsModelFormListElMap[key]?.el?.validate?.();
  87 + };
  88 +
  89 + watch(
  90 + () => props.value,
  91 + async (value) => {
  92 + await nextTick();
  93 + formActionType.resetFields();
  94 + formActionType.setFieldsValue(value || {});
  95 + },
  96 + { immediate: true }
  97 + );
  98 +
  99 + watch(
  100 + () => [props.inputData, props.identifier],
  101 + (value) => {
  102 + if (value && value.length) {
  103 + const schemas = getFormSchemas({
  104 + structJSON: props.inputData || [],
  105 + required: props.required,
  106 + transportType: props.transportType,
  107 + });
  108 + formActionType.setProps({ schemas });
  109 + }
  110 + }
  111 + );
  112 +
  113 + watch(
  114 + () => props.disabled,
  115 + (value) => {
  116 + formActionType.setProps({ disabled: value });
  117 + }
  118 + );
  119 +
  120 + defineExpose<DefineComponentsBasicExpose>({
  121 + getFieldsValue,
  122 + setFieldsValue,
  123 + validate,
  124 + });
  125 +</script>
  126 +
  127 +<template>
  128 + <Card
  129 + class="!border-2 !border-dashed"
  130 + :title="title"
  131 + :style="{ backgroundColor: disabled ? '#f0f0f0' : '', borderColor: disabled ? '#e3e3e3' : '' }"
  132 + >
  133 + <BasicForm class="things-model-form" @register="register" :disabled="disabled">
  134 + <template
  135 + v-for="item in getStructFormItem"
  136 + #[item.identifier!]="{ model, field }"
  137 + :key="item.identifier"
  138 + >
  139 + <ThingsModelForm
  140 + class="!ml-10"
  141 + :ref="(el) => setFormElRef(el, item)"
  142 + v-model:value="model[field]"
  143 + :input-data="(item.dataType?.specs as StructJSON[]) || []"
  144 + :title="item.functionName"
  145 + :disabled="disabled"
  146 + />
  147 + </template>
  148 + </BasicForm>
  149 + </Card>
  150 +</template>
  151 +
  152 +<style lang="less" scoped>
  153 + .things-model-form {
  154 + :deep(.ant-input-number) {
  155 + width: 100%;
  156 + }
  157 +
  158 + :deep(.ant-form-item-label) {
  159 + label {
  160 + width: 100%;
  161 + display: block;
  162 + overflow: hidden;
  163 + text-overflow: ellipsis;
  164 + }
  165 + }
  166 + }
  167 +
  168 + :deep(.ant-form-item-control) {
  169 + margin-left: 6px;
  170 + }
  171 +</style>
... ...
... ... @@ -123,6 +123,7 @@ export type ComponentType =
123 123 | 'TransferModal'
124 124 | 'TransferTableModal'
125 125 | 'ObjectModelValidateForm'
  126 + | 'ThingsModelForm'
126 127 | 'DevicePicker'
127 128 | 'ProductPicker'
128 129 | 'PollCommandInput'
... ... @@ -138,4 +139,8 @@ export type ComponentType =
138 139 | 'RelationsQuery'
139 140 | 'CredentialsCard'
140 141 | 'ApiComplete'
141   - | 'DeviceProfileForm';
  142 + | 'DeviceProfileForm'
  143 + | 'ConditionFilter'
  144 + | 'TimeRangePicker'
  145 + | 'TriggerDurationInput'
  146 + | 'AlarmProfileSelect';
... ...
... ... @@ -11,3 +11,19 @@ export enum AlarmStatusMean {
11 11 CLEARED_ACK = '清除已确认',
12 12 ACTIVE_ACK = '激活已确认',
13 13 }
  14 +
  15 +export enum AlarmLevelEnum {
  16 + CRITICAL = 'CRITICAL',
  17 + MAJOR = 'MAJOR',
  18 + MINOR = 'MINOR',
  19 + WARNING = 'WARNING',
  20 + INDETERMINATE = 'INDETERMINATE',
  21 +}
  22 +
  23 +export enum AlarmLevelNameEnum {
  24 + CRITICAL = '紧急',
  25 + MAJOR = '重要',
  26 + MINOR = '次要',
  27 + WARNING = '警告',
  28 + INDETERMINATE = '不确定',
  29 +}
... ...
  1 +/**
  2 + * 运算符枚举值
  3 + */
  4 +
  5 +// 数字运算符或者时间运算符
  6 +export enum NumberOperationEnum {
  7 + EQUAL = 'EQUAL',
  8 + NOT_EQUAL = 'NOT_EQUAL',
  9 + LESS = 'LESS',
  10 + LESS_OR_EQUAL = 'LESS_OR_EQUAL',
  11 + GREATER = 'GREATER',
  12 + GREATER_OR_EQUAL = 'GREATER_OR_EQUAL',
  13 +}
  14 +
  15 +export enum NumberOperationNameEnum {
  16 + EQUAL = '等于',
  17 + NOT_EQUAL = '不等于',
  18 + LESS = '小于',
  19 + LESS_OR_EQUAL = '小于等于',
  20 + GREATER = '大于',
  21 + GREATER_OR_EQUAL = '大于等于',
  22 +}
  23 +
  24 +export enum StringOperationEnum {
  25 + EQUAL = 'EQUAL',
  26 + NOT_EQUAL = 'NOT_EQUAL',
  27 + STARTS_WITH = 'STARTS_WITH',
  28 + ENDS_WITH = 'ENDS_WITH',
  29 + CONTAINS = 'CONTAINS',
  30 + NOT_CONTAINS = 'NOT_CONTAINS',
  31 +}
  32 +
  33 +export enum StringOperationNameEnum {
  34 + EQUAL = '等于',
  35 + NOT_EQUAL = '不等于',
  36 + STARTS_WITH = '开始于',
  37 + ENDS_WITH = '结束于',
  38 + CONTAINS = '包含',
  39 + NOT_CONTAINS = '不包含',
  40 +}
  41 +
  42 +export enum BooleanOperationEnum {
  43 + EQUAL = 'EQUAL',
  44 + NOT_EQUAL = 'NOT_EQUAL',
  45 +}
  46 +
  47 +export enum BooleanOperationNameEnum {
  48 + EQUAL = '等于',
  49 + NOT_EQUAL = '不等于',
  50 +}
  51 +
  52 +export enum BooleanOperationValueEnum {
  53 + TRUE = 'true',
  54 + FALSE = 'false',
  55 +}
  56 +
  57 +export enum BooleanOperationValueNameEnum {
  58 + TRUE = '真',
  59 + FALSE = '假',
  60 +}
  61 +
  62 +export enum FlipFlopTypeEnum {
  63 + SIMPLE = 'SIMPLE',
  64 + DURATION = 'DURATION',
  65 + REPEATING = 'REPEATING',
  66 +}
  67 +
  68 +export enum ScheduleTypeEnum {
  69 + ANY_TIME = 'ANY_TIME',
  70 + SPECIFIC_TIME = 'SPECIFIC_TIME',
  71 + CUSTOM = 'CUSTOM',
  72 +}
  73 +
  74 +export enum ScheduleTypeNameEnum {
  75 + ANY_TIME = '始终启用',
  76 + SPECIFIC_TIME = '定时启用',
  77 + CUSTOM = '自定义启用',
  78 +}
  79 +
  80 +export enum FlipFlopTypeNameEnum {
  81 + SIMPLE = '简单',
  82 + DURATION = '持续时长',
  83 + REPEATING = '重复次数',
  84 +}
  85 +
  86 +export enum TriggerTypeEnum {
  87 + DEVICE_TRIGGER = 'DEVICE_TRIGGER',
  88 + SCHEDULE_TRIGGER = 'SCHEDULE_TRIGGER',
  89 + SCENE_TRIGGER = 'SCENE_TRIGGER',
  90 + HAND_ACT = 'HAND_ACT',
  91 +}
  92 +
  93 +export enum TriggerTypeNameEnum {
  94 + DEVICE_TRIGGER = '设备触发',
  95 + SCHEDULE_TRIGGER = '定时触发',
  96 + SCENE_TRIGGER = '场景触发',
  97 + HAND_ACT = '手动触发',
  98 +}
  99 +
  100 +export enum DeviceTriggerTypeEum {
  101 + TIME_SERIES = 'TIME_SERIES',
  102 +}
  103 +
  104 +export enum DeviceTriggerTypeNameEum {
  105 + TIME_SERIES = '属性触发',
  106 +}
  107 +
  108 +export enum TriggerEntityTypeEnum {
  109 + ALL = 'ALL',
  110 + PART = 'PART',
  111 +}
  112 +
  113 +export enum TriggerEntityTypeNameEnum {
  114 + ALL = '全部',
  115 + PART = '部分',
  116 +}
  117 +
  118 +export enum TriggerValueTypeEnum {
  119 + NUMERIC = 'NUMERIC',
  120 + BOOLEAN = 'BOOLEAN',
  121 + STRING = 'STRING',
  122 + DATE_TIME = 'DATE_TIME',
  123 +}
  124 +
  125 +export enum TriggerValueTypeNameEnum {
  126 + NUMERIC = '数字',
  127 + BOOLEAN = '布尔值',
  128 + STRING = '字符串',
  129 + DATE_TIME = '时间',
  130 +}
  131 +
  132 +export enum TriggerUnitEnum {
  133 + SECONDS = 'SECONDS',
  134 + MINUTES = 'MINUTES',
  135 + HOURS = 'HOURS',
  136 + DAYS = 'DAYS',
  137 +}
  138 +
  139 +export enum TriggerUnitNameEnum {
  140 + SECONDS = '秒',
  141 + MINUTES = '分',
  142 + HOURS = '时',
  143 + DAYS = '天',
  144 +}
  145 +
  146 +export enum ExecutionActionEnum {
  147 + DEVICE_OUT = 'DEVICE_OUT',
  148 + MSG_NOTIFY = 'MSG_NOTIFY',
  149 +}
  150 +
  151 +export enum ExecutionActionNameEnum {
  152 + DEVICE_OUT = '设备输出',
  153 + MSG_NOTIFY = '告警输出',
  154 +}
  155 +
  156 +export enum CommandTypeEnum {
  157 + CUSTOM = 0,
  158 + SERVICE = 1,
  159 + ATTRIBUTE = 2,
  160 + API = 'api',
  161 +}
  162 +
  163 +export enum CommandTypeNameEnum {
  164 + CUSTOM = '自定义',
  165 + SERVICE = '服务',
  166 + ATTRIBUTE = '属性',
  167 +}
... ...
  1 +export enum DataTypeEnum {
  2 + NUMBER_INT = 'INT',
  3 + NUMBER_DOUBLE = 'DOUBLE',
  4 + STRING = 'TEXT',
  5 + STRUCT = 'STRUCT',
  6 + BOOL = 'BOOL',
  7 +}
... ...
1   -/**
2   - * 运算符枚举值
3   - */
4   -
5   -// 数字运算符或者时间运算符
6   -export enum Number_Operation {
7   - EQUAL = 'EQUAL',
8   - NOT_EQUAL = 'NOT_EQUAL',
9   - LESS = 'LESS',
10   - LESS_OR_EQUAL = 'LESS_OR_EQUAL',
11   - GREATER = 'GREATER',
12   - GREATER_OR_EQUAL = 'GREATER_OR_EQUAL',
13   -}
14   -
15   -export enum String_Operation {
16   - EQUAL = 'EQUAL',
17   - NOT_EQUAL = 'NOT_EQUAL',
18   - BEGAN_IN = 'STARTS_WITH',
19   - END_IN = 'ENDS_WITH',
20   - INCLUDE = 'CONTAINS',
21   - NOT_INCLUDE = 'NOT_CONTAINS',
22   -}
23   -
24   -export enum Boolean_Operation {
25   - EQUAL = 'EQUAL',
26   - NOT_EQUAL = 'NOT_EQUAL',
27   -}
... ... @@ -7,7 +7,7 @@ export enum DataActionModeEnum {
7 7 }
8 8
9 9 export enum DataActionModeNameEnum {
10   - CREATE = '创建',
  10 + CREATE = '新增',
11 11 READ = '查看',
12 12 UPDATE = '编辑',
13 13 DELETE = '删除',
... ... @@ -40,6 +40,11 @@ export enum ServiceCallTypeEnum {
40 40 SYNC = 'SYNC',
41 41 }
42 42
  43 +export enum ServiceCallTypeNameEnum {
  44 + ASYNC = '异步',
  45 + SYNC = '同步',
  46 +}
  47 +
43 48 export enum CommandDeliveryWayEnum {
44 49 ONE_WAY = 'oneway',
45 50 TWO_WAY = 'twoway',
... ...
... ... @@ -16,15 +16,17 @@
16 16 value?: string | string[];
17 17 apiTreeSelectProps?: Recordable;
18 18 showCreate?: boolean;
  19 + disabled?: boolean;
19 20 }>(),
20 21 {
21 22 showCreate: true,
  23 + disabled: false,
22 24 }
23 25 );
24 26
25 27 const needReload = ref(true);
26 28
27   - const emit = defineEmits(['change']);
  29 + const emit = defineEmits(['change', 'optionsChange']);
28 30
29 31 const handleOpenCreate = () => {
30 32 openDrawer(true, { isUpdate: false });
... ... @@ -35,13 +37,14 @@
35 37 const orgList = ref<Recordable[]>([]);
36 38
37 39 const getBindProps = computed<Recordable>(() => {
38   - const { value, apiTreeSelectProps = {} } = props;
  40 + const { value, apiTreeSelectProps = {}, disabled } = props;
39 41 const { params = {} } = apiTreeSelectProps;
40 42 return {
41 43 replaceFields: { children: 'children', key: 'id', title: 'name', value: 'id' },
42 44 getPopupContainer: () => document.body,
43 45 placeholder: '请选择所属组织',
44 46 maxLength: 250,
  47 + disabled,
45 48 ...apiTreeSelectProps,
46 49 value,
47 50 dropdownStyle: { maxHeight: '300px' },
... ... @@ -63,6 +66,7 @@
63 66 onChange: (...args: any[]) => {
64 67 emit('change', ...args);
65 68 },
  69 + onOptionsChange: (...args: any[]) => emit('optionsChange', ...args),
66 70 };
67 71 });
68 72
... ... @@ -80,7 +84,9 @@
80 84 <template>
81 85 <section class="flex">
82 86 <ApiTreeSelect v-bind="getBindProps" />
83   - <Button v-if="getShowCreate" type="link" @click="handleOpenCreate">新增组织</Button>
  87 + <Button v-if="getShowCreate" type="link" @click="handleOpenCreate" :disabled="disabled"
  88 + >新增组织</Button
  89 + >
84 90 <OrganizationDrawer v-if="getShowCreate" @register="registerDrawer" @success="handleReload" />
85 91 </section>
86 92 </template>
... ...
... ... @@ -15,13 +15,13 @@
15 15 import { useHistoryData } from '../../hook/useHistoryData';
16 16 import { formatToDateTime } from '/@/utils/dateUtil';
17 17 import { useTable, BasicTable, BasicColumn } from '/@/components/Table';
18   - import { DataTypeEnum } from '/@/components/Form/src/externalCompns/components/StructForm/config';
19 18 import {
20 19 ModeSwitchButton,
21 20 TABLE_CHART_MODE_LIST,
22 21 EnumTableChartMode,
23 22 } from '/@/components/Widget';
24 23 import { SchemaFiled } from '/@/views/visual/palette/components/HistoryTrendModal/config';
  24 + import { DataTypeEnum } from '/@/enums/objectModelEnum';
25 25
26 26 interface DeviceDetail {
27 27 tbDeviceId: string;
... ... @@ -188,7 +188,7 @@
188 188 method.setFieldsValue({ keys: props.attr });
189 189 const attrInfo = unref(deviceAttrs).find((item) => item.identifier === props.attr);
190 190 if (
191   - [DataTypeEnum.IS_STRING, DataTypeEnum.IS_STRUCT].includes(
  191 + [DataTypeEnum.STRING, DataTypeEnum.STRUCT].includes(
192 192 attrInfo?.detail.dataType.type as unknown as DataTypeEnum
193 193 )
194 194 ) {
... ...
... ... @@ -16,7 +16,6 @@
16 16 import { DeviceModelOfMatterAttrs, DeviceRecord } from '/@/api/device/model/deviceModel';
17 17 import { Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel';
18 18 import { isArray, isNull, isObject } from '/@/utils/is';
19   - import { DataTypeEnum } from '/@/components/Form/src/externalCompns/components/StructForm/config';
20 19 import { useGlobSetting } from '/@/hooks/setting';
21 20 import { ModeSwitchButton, EnumTableCardMode } from '/@/components/Widget';
22 21 import { toRaw } from 'vue';
... ... @@ -25,6 +24,7 @@
25 24 import { ModalParamsType } from '/#/utils';
26 25 import { AreaChartOutlined } from '@ant-design/icons-vue';
27 26 import { SvgIcon } from '/@/components/Icon';
  27 + import { DataTypeEnum } from '/@/enums/objectModelEnum';
28 28
29 29 interface ReceiveMessage {
30 30 data: {
... ... @@ -220,7 +220,7 @@
220 220 };
221 221
222 222 const isStructAndTextType = (type: DataTypeEnum) => {
223   - return [DataTypeEnum.IS_STRUCT, DataTypeEnum.IS_STRING].includes(type);
  223 + return [DataTypeEnum.STRUCT, DataTypeEnum.STRING].includes(type);
224 224 };
225 225
226 226 const setDataSource = () => {
... ... @@ -311,7 +311,7 @@
311 311 });
312 312
313 313 const formatValue = (item: DataSource) => {
314   - return item.type === DataTypeEnum.IS_BOOL
  314 + return item.type === DataTypeEnum.BOOL
315 315 ? !isNull(item.value)
316 316 ? !!Number(item.value)
317 317 ? item.boolOpen
... ...
... ... @@ -6,7 +6,6 @@
6 6 import { StructJSON } from '/@/api/device/model/modelOfMatterModel';
7 7 import { BasicForm, useForm } from '/@/components/Form';
8 8 import { BasicModal, useModalInner } from '/@/components/Modal';
9   - import { DataTypeEnum } from '/@/views/device/profiles/step/cpns/physical/cpns/config';
10 9 import { sendCommandOneway } from '/@/api/dataBoard';
11 10 import { useMessage } from '/@/hooks/web/useMessage';
12 11 import { unref } from 'vue';
... ... @@ -14,6 +13,7 @@
14 13 import { TaskTypeEnum } from '/@/views/task/center/config';
15 14 import { SingleToHex, formSchemasConfig } from './config';
16 15 import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const';
  16 + import { DataTypeEnum } from '/@/enums/objectModelEnum';
17 17
18 18 defineEmits(['register']);
19 19 const props = defineProps<{ deviceId: string; deviceName: string }>();
... ... @@ -51,7 +51,7 @@
51 51
52 52 let schemas = [{ dataType: dataType, identifier, functionName: name } as StructJSON];
53 53
54   - if (type === DataTypeEnum.IS_STRUCT) {
  54 + if (type === DataTypeEnum.STRUCT) {
55 55 schemas = dataType?.specs as StructJSON[];
56 56 }
57 57
... ...
1 1 import { DataType, Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel';
2 2 import { JSONEditor } from '/@/components/CodeEditor';
3 3 import { FormSchema, useComponentRegister } from '/@/components/Form';
  4 +import { DataTypeEnum } from '/@/enums/objectModelEnum';
4 5 import { useJsonParse } from '/@/hooks/business/useJsonParse';
5   -import { DataTypeEnum } from '/@/views/device/profiles/step/cpns/physical/cpns/config';
6 6
7 7 export interface BasicCreateFormParams {
8 8 identifier: string;
... ... @@ -19,9 +19,9 @@ const validateDouble = (
19 19 max?: number | string
20 20 ) => {
21 21 min =
22   - Number(min) || type === DataTypeEnum.IS_NUMBER_INT ? Number.MIN_SAFE_INTEGER : Number.MIN_VALUE;
  22 + Number(min) || type === DataTypeEnum.NUMBER_INT ? Number.MIN_SAFE_INTEGER : Number.MIN_VALUE;
23 23 max =
24   - Number(max) || type === DataTypeEnum.IS_NUMBER_INT ? Number.MAX_SAFE_INTEGER : Number.MAX_VALUE;
  24 + Number(max) || type === DataTypeEnum.NUMBER_INT ? Number.MAX_SAFE_INTEGER : Number.MAX_VALUE;
25 25
26 26 return {
27 27 flag: value < min || value > max,
... ... @@ -56,13 +56,11 @@ export const useGenDynamicForm = () => {
56 56 },
57 57 ],
58 58 componentProps: {
59   - max:
60   - max ?? type === DataTypeEnum.IS_NUMBER_INT ? Number.MAX_SAFE_INTEGER : Number.MAX_VALUE,
61   - min:
62   - min ?? type === DataTypeEnum.IS_NUMBER_INT ? Number.MIN_SAFE_INTEGER : Number.MIN_VALUE,
  59 + max: max ?? type === DataTypeEnum.NUMBER_INT ? Number.MAX_SAFE_INTEGER : Number.MAX_VALUE,
  60 + min: min ?? type === DataTypeEnum.NUMBER_INT ? Number.MIN_SAFE_INTEGER : Number.MIN_VALUE,
63 61 step,
64 62 placeholder: `请输入${functionName}`,
65   - precision: type === DataTypeEnum.IS_NUMBER_INT ? 0 : 2,
  63 + precision: type === DataTypeEnum.NUMBER_INT ? 0 : 2,
66 64 },
67 65 } as FormSchema;
68 66 };
... ... @@ -141,11 +139,11 @@ export const useGenDynamicForm = () => {
141 139 };
142 140
143 141 const schemaMethod = {
144   - [DataTypeEnum.IS_BOOL]: createSelect,
145   - [DataTypeEnum.IS_NUMBER_DOUBLE]: createInputNumber,
146   - [DataTypeEnum.IS_NUMBER_INT]: createInputNumber,
147   - [DataTypeEnum.IS_STRING]: createInput,
148   - [DataTypeEnum.IS_STRUCT]: createInputJson,
  142 + [DataTypeEnum.BOOL]: createSelect,
  143 + [DataTypeEnum.NUMBER_DOUBLE]: createInputNumber,
  144 + [DataTypeEnum.NUMBER_INT]: createInputNumber,
  145 + [DataTypeEnum.STRING]: createInput,
  146 + [DataTypeEnum.STRUCT]: createInputJson,
149 147 };
150 148
151 149 const fieldTypeMap = new Map<string, DataTypeEnum>();
... ... @@ -176,7 +174,7 @@ export const useGenDynamicForm = () => {
176 174 const dataType = fieldTypeMap.get(next)!;
177 175
178 176 let itemValue = value[next];
179   - if (dataType === DataTypeEnum.IS_STRUCT) {
  177 + if (dataType === DataTypeEnum.STRUCT) {
180 178 const { value } = useJsonParse(itemValue);
181 179 itemValue = value;
182 180 }
... ...
1 1 import { FormSchema } from '/@/components/Table';
2 2 import { findDictItemByCode } from '/@/api/system/dict';
  3 +import { DataTypeEnum } from '/@/enums/objectModelEnum';
3 4
4 5 export enum FormField {
5 6 FUNCTION_NAME = 'functionName',
... ... @@ -40,30 +41,19 @@ export enum AssessMode {
40 41 WRITE = 'w',
41 42 }
42 43
43   -/**
44   - * 新增参数 动态显示表单
45   - */
46   -export enum DataTypeEnum {
47   - IS_NUMBER_INT = 'INT',
48   - IS_NUMBER_DOUBLE = 'DOUBLE',
49   - IS_STRING = 'TEXT',
50   - IS_STRUCT = 'STRUCT',
51   - IS_BOOL = 'BOOL',
52   -}
53   -
54 44 const isNumber = (type: string) => {
55   - return type === DataTypeEnum.IS_NUMBER_INT || type === DataTypeEnum.IS_NUMBER_DOUBLE;
  45 + return type === DataTypeEnum.NUMBER_INT || type === DataTypeEnum.NUMBER_DOUBLE;
56 46 };
57 47
58 48 const isString = (type: string) => {
59   - return type === DataTypeEnum.IS_STRING;
  49 + return type === DataTypeEnum.STRING;
60 50 };
61 51 const isStruct = (type: string) => {
62   - return type === DataTypeEnum.IS_STRUCT;
  52 + return type === DataTypeEnum.STRUCT;
63 53 };
64 54
65 55 const isBool = (type: string) => {
66   - return type === DataTypeEnum.IS_BOOL;
  56 + return type === DataTypeEnum.BOOL;
67 57 };
68 58
69 59 export const serviceSchemas = (tcpDeviceFlag: boolean): FormSchema[] => {
... ...
1   -<template>
2   - <div>
3   - <BasicDrawer
4   - v-bind="$attrs"
5   - @register="registerDrawer"
6   - @ok="handleSubmit"
7   - destroy-on-close
8   - width="50%"
9   - showFooter
10   - @close="handleClose"
11   - :title="title"
12   - >
13   - <div>
14   - <!-- 基础表单 -->
15   - <BasicForm @register="registerForm" />
16   - <!-- 基础表单 -->
17   - <!-- 触发器-begin -->
18   - <!-- <Divider orientation="left">触发器</Divider> -->
19   - <Divider orientation="left">
20   - <a-tooltip>
21   - <template #title>触发器不可为空,消息只要满足触发条件中任意一个即可触发。</template>
22   - <label class="validate-dot"></label> 触发器
23   - <QuestionCircleOutlined :style="{ fontSize: '14px', marginLeft: '5px' }" />
24   - </a-tooltip>
25   - </Divider>
26   - <div>
27   - <template v-for="(item, index) in triggerData" :key="item">
28   - <TriggerOrCondition
29   - class="mt-4"
30   - title="触发器"
31   - :index="index"
32   - :provideOrgid="provideOrgid"
33   - :ref="skipUnwrap.triggerItemRefs"
34   - @delete="deleteTriggerOrCondition"
35   - />
36   - </template>
37   - <!-- 按钮 -->
38   - <a-button type="primary" class="mt-4" @click="addTrigger" v-if="isView">
39   - <PlusOutlined />
40   - 触发器(OR)<span style="visibility: hidden">发</span>
41   - </a-button>
42   - <!-- 按钮 -->
43   - </div>
44   - <!-- 触发器-end -->
45   -
46   - <!-- 执行条件-begin -->
47   - <Divider orientation="left">
48   - <a-tooltip>
49   - <template #title>执行条件可为空,消息需要满足所有执行条件才会被处理。</template>
50   - 执行条件
51   - <QuestionCircleOutlined :style="{ fontSize: '14px', marginLeft: '5px' }" />
52   - </a-tooltip>
53   - </Divider>
54   - <div>
55   - <template v-for="(item, index) in conditionData" :key="item">
56   - <TriggerOrCondition
57   - class="mt-4"
58   - title="执行条件"
59   - :index="index"
60   - :provideOrgid="provideOrgid"
61   - :ref="skipUnwrap.conditionItemRefs"
62   - @delete="deleteTriggerOrCondition"
63   - />
64   - </template>
65   - <!-- 按钮 -->
66   - <a-button type="primary" class="mt-4" @click="addCondition" v-if="isView">
67   - <PlusOutlined />
68   - 执行条件(AND)
69   - </a-button>
70   - <!-- 按钮 -->
71   - </div>
72   - <!-- 执行条件-end -->
73   -
74   - <!-- 执行动作-begin -->
75   - <Divider orientation="left">
76   - <a-tooltip>
77   - <template #title
78   - >触发器和执行条件都满足时,场景联动会做什么,例如:设备联动、告警通知等。</template
79   - >
80   - <label class="validate-dot"></label> 执行动作
81   - <QuestionCircleOutlined :style="{ fontSize: '14px', marginLeft: '5px' }" />
82   - </a-tooltip>
83   - </Divider>
84   - <div>
85   - <template v-for="(item, index) in actionData" :key="item">
86   - <Action
87   - class="mt-4"
88   - :actionIndex="index"
89   - :actionData="actionData"
90   - :triggerData="triggerData"
91   - :ref="skipUnwrap.actionItemRefs"
92   - :provideOrgid="provideOrgid"
93   - :deviceList="getMasterDeviceList"
94   - :arr="arr"
95   - @deleteAction="deleteAction"
96   - @getActionFormArr="getActionFormArr"
97   - @dynamicChangeAlarmConfig="handleDynamicChangeAlarmConfig"
98   - />
99   - </template>
100   - <!-- 按钮 -->
101   - <a-button type="primary" class="mt-4" @click="addAction" v-if="isView">
102   - <PlusOutlined />
103   - 新增执行动作
104   - </a-button>
105   - <!-- 按钮 -->
106   - </div>
107   - <!-- 执行动作-end -->
108   - </div>
109   - </BasicDrawer>
110   - </div>
111   -</template>
112   -<script lang="ts" setup>
113   - import { ref, watch, unref, computed, nextTick } from 'vue';
114   - import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
115   - import { formSchema, organizationId as organizationIdRef } from './config/config.data';
116   - import { BasicForm, useForm } from '/@/components/Form';
117   - import { genTriggerOrConditionData, genActionData } from './config/formatData';
118   - import { Divider } from 'ant-design-vue';
119   - import { PlusOutlined, QuestionCircleOutlined } from '@ant-design/icons-vue';
120   - import { useMessage } from '/@/hooks/web/useMessage';
121   - import {
122   - screenLinkPageAddApi,
123   - screenLinkPageByDeptIdGetDevice,
124   - getOrganizationAlarmConfig,
125   - } from '/@/api/ruleengine/ruleengineApi';
126   - import TriggerOrCondition from './cpns/Trigger-Condition.vue';
127   - import Action from './cpns/Action.vue';
128   - import { findOperation } from './config/formatData';
129   - import { formatToDateTime } from '/@/utils/dateUtil';
130   - import ObjectModelValidateForm from '/@/components/Form/src/externalCompns/components/ObjectModelValidateForm/ObjectModelValidateForm.vue';
131   - import { isEmpty } from '/@/utils/is';
132   - import { add } from '/@/components/Form/src/componentMap';
133   -
134   - add('ObjectModelValidateForm', ObjectModelValidateForm);
135   -
136   - const emit = defineEmits(['register', 'success']);
137   - const provideOrgid = ref('');
138   -
139   - const { createMessage } = useMessage();
140   - const triggerData = ref<number[]>([]);
141   - const conditionData = ref<number[]>([]);
142   - const actionData = ref<number[]>([]);
143   - const skipUnwrap = {
144   - triggerItemRefs: ref<InstanceType<typeof TriggerOrCondition>[]>([]),
145   - conditionItemRefs: ref<InstanceType<typeof TriggerOrCondition>[]>([]),
146   - actionItemRefs: ref<InstanceType<typeof Action>[]>([]),
147   - };
148   - const title = computed(
149   - () => `${isUpdate.value === 3 ? '查看' : isUpdate.value ? '编辑' : '新增'}场景联动`
150   - );
151   - let getTriggerFormValue = ref([]);
152   - let getConditionFormValue = ref([]);
153   - let getActionFormValue = ref([]);
154   - const editEntryIdData = ref([]);
155   - const editAlarmConfigData = ref([]);
156   - const isUpdate = ref<boolean | number>(false);
157   - const id = ref(undefined);
158   - const tenantId = ref(undefined);
159   - const isView = ref(true);
160   - const [
161   - registerForm,
162   - { resetFields, validate, setFieldsValue, getFieldsValue, setProps, updateSchema },
163   - ] = useForm({
164   - labelWidth: 120,
165   - schemas: formSchema,
166   - showActionButtonGroup: false,
167   - });
168   - const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
169   - setDrawerProps({ confirmLoading: false, loading: true });
170   - isUpdate.value = data.isUpdate;
171   - getActionFormArr();
172   - if (!unref(isUpdate)) {
173   - resetFields();
174   - //初始化执行动作
175   - actionData.value = [Date.now()];
176   - } else {
177   - // 取值
178   - const {
179   - id: recordId,
180   - tenantId: recordTenantId,
181   - organizationId,
182   - triggers,
183   - doConditions,
184   - doActions,
185   - } = data.record;
186   - organizationIdRef.value = organizationId;
187   - // 赋值
188   - await setFieldsValue(data.record);
189   - id.value = recordId;
190   - tenantId.value = recordTenantId;
191   - //TODO-fengtao-把组织id传给子组件
192   - provideOrgid.value = organizationId;
193   - //TODO-fengtao获取当前执行动作下的设备(master那个接口)
194   - // getMasterDeviceList.value = await byOrganizationIdGetMasterDevice(organizationId);
195   - //TODO-fengtao
196   - // 获取当前组织下的设备列表
197   - const options = await screenLinkPageByDeptIdGetDevice({
198   - organizationId,
199   - });
200   - // 获取当前组织下的告警配置
201   - const alarmConfig = await getOrganizationAlarmConfig({ organizationId });
202   -
203   - // 生成回显时对应得组件数量
204   - triggerData.value = [...new Array(triggers.length).keys()];
205   - conditionData.value = [...new Array(doConditions.length).keys()];
206   - actionData.value = [...new Array(doActions.length).keys()];
207   - // 回显设备列表
208   - editEntryIdData.value = options.items.map((item) => {
209   - return {
210   - value: item.tbDeviceId,
211   - label: item.name,
212   - };
213   - });
214   - editAlarmConfigData.value = alarmConfig.map((item) => {
215   - return {
216   - value: item.id,
217   - label: item.name,
218   - status: item.status,
219   - };
220   - });
221   - deviceList.value = editEntryIdData.value;
222   - nextTick(() => {
223   - setEditFields(skipUnwrap.triggerItemRefs, editEntryIdData);
224   - setEditFields(skipUnwrap.conditionItemRefs, editEntryIdData);
225   - setEditFields(skipUnwrap.actionItemRefs, getMasterDeviceList);
226   - setEditAlarmConfig(skipUnwrap.actionItemRefs, editAlarmConfigData);
227   - });
228   -
229   - const map = {
230   - ANY_TIME: 0,
231   - SPECIFIC_TIME: 1,
232   - CUSTOM: 2,
233   - };
234   - // 回显触发器数据---此处是个闭包!
235   - triggers.forEach((trigger, index) => {
236   - nextTick(async () => {
237   - const selectProductId = ref('');
238   - // 回显启用规则
239   - unref(skipUnwrap.triggerItemRefs)[index].currentIndex =
240   - map[trigger.triggerCondition.schedule.type];
241   - unref(skipUnwrap.triggerItemRefs)[index].scheduleData = trigger.triggerCondition.schedule;
242   - unref(skipUnwrap.triggerItemRefs)[index].isUpdate = true;
243   - unref(skipUnwrap.triggerItemRefs)[index].alarmScheduleRef.scheduleData =
244   - trigger.triggerCondition.schedule;
245   - unref(skipUnwrap.triggerItemRefs)[index].setFieldsFormValueFun({
246   - triggered: trigger?.triggerCondition?.condition?.spec?.type,
247   - device: trigger?.entityType,
248   - deviceType: trigger?.deviceType,
249   - triggerType: trigger?.triggerType,
250   - type1: trigger?.triggerCondition?.condition?.condition[0]?.key?.type,
251   - type2: trigger?.triggerCondition?.condition?.condition[0]?.key?.key,
252   - operationType: trigger?.triggerCondition?.condition?.condition[0]?.valueType,
253   - detail: trigger?.triggerCondition?.alarmDetails,
254   - entityId: trigger?.entityId,
255   - deviceProfileId: trigger?.deviceProfileId,
256   - replaceValue: trigger?.triggerCondition?.condition?.spec?.predicate?.defaultValue,
257   - time: trigger?.triggerCondition?.condition?.spec?.predicate?.defaultValue,
258   - timeUnit: trigger?.triggerCondition?.condition?.spec?.unit,
259   - });
260   - if (trigger.deviceProfileId != undefined) {
261   - selectProductId.value = trigger?.deviceProfileId;
262   - }
263   - //fengtao-把设备id回传给子组件
264   - unref(skipUnwrap.triggerItemRefs)[index].updateFieldAttributeFunc(selectProductId.value);
265   - //fengtao
266   - // 设置值operationType
267   - unref(skipUnwrap.triggerItemRefs)[index].operationType =
268   - trigger.triggerCondition?.condition.condition[0].valueType;
269   -
270   - const ConditionScreeningForm = await unref(skipUnwrap.triggerItemRefs)[
271   - index
272   - ].getRefItemConditionScreeningRefs();
273   -
274   - // 设置对应条件筛选的个数
275   - unref(skipUnwrap.triggerItemRefs)[index].setConditionScreeningList([
276   - ...new Array(trigger.triggerCondition.condition.condition.length).keys(),
277   - ]);
278   - // 操作符类型 NUMERIC|String|Boolean|DATE_TIME
279   - const valueType = trigger.triggerCondition?.condition.condition[0].valueType;
280   -
281   - // 循环设置条件筛选值。TODO:此处设置顺序有问题
282   - nextTick(() => {
283   - const richTextList = [];
284   - trigger.triggerCondition.condition.condition.forEach((item, index) => {
285   - const formItem = {
286   - operation: item.predicate.operation,
287   - value:
288   - valueType === 'DATE_TIME'
289   - ? formatToDateTime(
290   - Number(item.predicate.value.defaultValue),
291   - 'YYYY-MM-DD HH:mm:ss'
292   - )
293   - : String(item.predicate.value.defaultValue),
294   - ignoreCase: item.valueType === 'STRING' ? item.predicate.ignoreCase : undefined,
295   - };
296   - richTextList.push({
297   - // 查询中文操作符
298   - operation: findOperation(valueType, item.predicate.operation).label,
299   - value:
300   - valueType === 'DATE_TIME'
301   - ? formatToDateTime(
302   - Number(item.predicate.value.defaultValue),
303   - 'YYYY-MM-DD HH:mm:ss'
304   - )
305   - : String(item.predicate.value.defaultValue),
306   - attribute: trigger.triggerCondition?.condition.condition[0]?.key?.key,
307   - });
308   - ConditionScreeningForm.value[index].setFieldsValue(formItem);
309   - });
310   - unref(skipUnwrap.triggerItemRefs)[index].setRichText(richTextList);
311   - });
312   - });
313   - });
314   -
315   - doConditions.forEach((condition, index) => {
316   - nextTick(async () => {
317   - const selectProductId = ref('');
318   - // 回显启用规则
319   - unref(skipUnwrap.conditionItemRefs)[index].currentIndex =
320   - map[condition.triggerCondition.schedule.type];
321   - unref(skipUnwrap.conditionItemRefs)[index].scheduleData =
322   - condition.triggerCondition.schedule;
323   - unref(skipUnwrap.conditionItemRefs)[index].isUpdate = true;
324   - unref(skipUnwrap.conditionItemRefs)[index].alarmScheduleRef.scheduleData =
325   - condition.triggerCondition.schedule;
326   - unref(skipUnwrap.conditionItemRefs)[index].setFieldsFormValueFun({
327   - triggered: condition?.triggerCondition?.condition?.spec?.type,
328   - device: condition?.entityType,
329   - triggerType: condition?.triggerType,
330   - type1: condition?.triggerCondition?.condition?.condition[0]?.key?.type,
331   - type2: condition?.triggerCondition?.condition?.condition[0]?.key?.key,
332   - operationType: condition?.triggerCondition?.condition?.condition[0]?.valueType,
333   - detail: condition?.triggerCondition?.alarmDetails,
334   - entityId: condition?.entityId,
335   - deviceProfileId: condition?.deviceProfileId,
336   - deviceType: condition?.deviceType,
337   - replaceValue: condition?.triggerCondition?.condition?.spec?.predicate?.defaultValue,
338   - time: condition?.triggerCondition?.condition?.spec?.predicate?.defaultValue,
339   - timeUnit: condition?.triggerCondition?.condition?.spec?.unit,
340   - });
341   - if (condition?.deviceProfileId != undefined) {
342   - selectProductId.value = condition?.deviceProfileId;
343   - }
344   - //fengtao-把设备id回传给子组件
345   - unref(skipUnwrap.conditionItemRefs)[index].updateFieldAttributeFunc(
346   - selectProductId.value
347   - );
348   - //fengtao
349   - // 设置值operationType
350   - unref(skipUnwrap.conditionItemRefs)[index].operationType =
351   - condition.triggerCondition?.condition.condition[0].valueType;
352   -
353   - const ConditionScreeningForm = await unref(skipUnwrap.conditionItemRefs)[
354   - index
355   - ].getRefItemConditionScreeningRefs();
356   -
357   - // 设置对应条件筛选的个数
358   - unref(skipUnwrap.conditionItemRefs)[index].setConditionScreeningList([
359   - ...new Array(condition.triggerCondition.condition.condition.length).keys(),
360   - ]);
361   - // 操作符类型 NUMERIC|String|Boolean|DATorganizeImportsE_TIME
362   - const valueType = condition.triggerCondition?.condition.condition[0].valueType;
363   -
364   - // 循环设置条件筛选值。TODO:此处设置顺序有问题
365   - nextTick(() => {
366   - const richTextList = [];
367   - condition.triggerCondition.condition.condition.forEach((item, index) => {
368   - const formItem = {
369   - operation: item.predicate.operation,
370   - value:
371   - valueType === 'DATE_TIME'
372   - ? formatToDateTime(
373   - Number(item.predicate.value.defaultValue),
374   - 'YYYY-MM-DD HH:mm:ss'
375   - )
376   - : String(item.predicate.value.defaultValue),
377   - ignoreCase: item.valueType === 'STRING' ? item.predicate.ignoreCase : undefined,
378   - };
379   - richTextList.push({
380   - // 查询中文操作符
381   - operation: findOperation(valueType, item.predicate.operation).label,
382   - value:
383   - valueType === 'DATE_TIME'
384   - ? formatToDateTime(
385   - Number(item.predicate.value.defaultValue),
386   - 'YYYY-MM-DD HH:mm:ss'
387   - )
388   - : String(item.predicate.value.defaultValue),
389   - attribute: condition.triggerCondition?.condition.condition[0]?.key?.key,
390   - });
391   - ConditionScreeningForm.value[index].setFieldsValue(formItem);
392   - });
393   - unref(skipUnwrap.conditionItemRefs)[index].setRichText(richTextList);
394   - });
395   - });
396   - });
397   - doActions.forEach((action, index) => {
398   - nextTick(() => {
399   - const selectProductId = ref('');
400   - // 设置执行动作外层值
401   - unref(skipUnwrap.actionItemRefs)[index].setFieldsFormValueFun({
402   - ...action,
403   - outTarget: action.outTarget,
404   - device: action.entityType,
405   - deviceId: action.deviceId,
406   - alarm_config: action.alarmProfileId,
407   - alarm_level: action.doContext.alarmLevel,
408   - });
409   - // 如果是设备输出设置脚本值
410   - if (action.outTarget === 'DEVICE_OUT') {
411   - if (action.commandType.toString() === '0') {
412   - unref(skipUnwrap.actionItemRefs)[index].setJsonValue(action.doContext.params);
413   - }
414   - }
415   - // 清除告警有值?{数组}
416   - if (action?.doContext?.clearRule?.length) {
417   - unref(skipUnwrap.actionItemRefs)[index].checked = true;
418   - // 生成对应清除告警的数组长度
419   - unref(skipUnwrap.actionItemRefs)[index].clearRuleList = [
420   - ...new Array(action?.doContext?.clearRule?.length).keys(),
421   - ];
422   - // 推迟执行时机-DOM渲染完毕在执行
423   - nextTick(async () => {
424   - unref(skipUnwrap.actionItemRefs)[index].refItem.clearRuleRefs.value.map(
425   - (item, index) => {
426   - // 回显启用规则
427   - item.currentIndex =
428   - map[action.doContext.clearRule[index].triggerCondition.schedule.type];
429   - item.scheduleData = action.doContext.clearRule[index].triggerCondition.schedule;
430   - item.isUpdate = true;
431   - item.alarmScheduleRef.scheduleData =
432   - action.doContext.clearRule[index].triggerCondition.schedule;
433   - item.setFieldsFormValueFun({
434   - triggered:
435   - action.doContext.clearRule[index].triggerCondition.condition.spec.type,
436   - device: action.doContext.clearRule[index].entityType,
437   - deviceType: action.doContext.clearRule[index].deviceType,
438   - triggerType: action.doContext.clearRule[index].triggerType,
439   - type1:
440   - action.doContext.clearRule[index].triggerCondition.condition.condition[0].key
441   - .type,
442   - type2:
443   - action.doContext.clearRule[index].triggerCondition.condition.condition[0].key
444   - .key,
445   - operationType:
446   - action.doContext.clearRule[index].triggerCondition.condition.condition[0]
447   - .valueType,
448   - detail: action.doContext.clearRule[index].triggerCondition.alarmDetails,
449   - entityId: action.doContext.clearRule[index].entityId,
450   - deviceProfileId: action.doContext.clearRule[index]?.deviceProfileId,
451   - replaceValue:
452   - action.doContext.clearRule[index].triggerCondition.condition.spec.predicate
453   - .defaultValue,
454   - time: action.doContext.clearRule[index].triggerCondition.condition.spec
455   - .predicate.defaultValue,
456   - timeUnit:
457   - action.doContext.clearRule[index].triggerCondition.condition.spec.unit,
458   - });
459   - if (action.doContext.clearRule[index]?.deviceProfileId != undefined) {
460   - selectProductId.value = action.doContext.clearRule[index]?.deviceProfileId;
461   - }
462   - //fengtao-把设备id回传给子组件
463   - item.updateFieldAttributeFunc(selectProductId.value);
464   - item.updateFieldDeviceId(deviceList.value, provideOrgid.value, isUpdate.value);
465   - //fengtao
466   - // 单独设置operationType值 操作符类型 NUMERIC|String|Boolean|DATE_TIME
467   - item.operationType =
468   - action.doContext.clearRule[
469   - index
470   - ].triggerCondition.condition.condition[0].valueType;
471   - }
472   - );
473   -
474   - const ConditionScreeningForm = await unref(skipUnwrap.actionItemRefs)[
475   - index
476   - ].getRefItemConditionScreeningRefs();
477   -
478   - // 循环设置条件筛选值。TODO:此处设置顺序有问题
479   - action.doContext.clearRule.map((rule, ruleIndex) => {
480   - // 生成对应条件筛选的数组个数
481   - unref(skipUnwrap.actionItemRefs)[index].setConditionScreeningList([
482   - ...new Array(
483   - action.doContext.clearRule[
484   - ruleIndex
485   - ].triggerCondition.condition.condition.length
486   - ).keys(),
487   - ]);
488   - nextTick(() => {
489   - const richTextList = [];
490   - rule.triggerCondition.condition.condition.forEach((item, conditionIndex) => {
491   - //TODO-fengtao之前是Number( item.predicate.value.defaultValue)-发现回显是Invalide Time
492   - const formItem = {
493   - operation: item.predicate.operation,
494   - value:
495   - item.valueType === 'DATE_TIME'
496   - ? formatToDateTime(
497   - Number(item.predicate.value.defaultValue),
498   - 'YYYY-MM-DD HH:mm:ss'
499   - )
500   - : String(item.predicate.value.defaultValue),
501   - ignoreCase:
502   - item.valueType === 'STRING' ? item.predicate.ignoreCase : undefined,
503   - };
504   - //TODO-fengtao之前是Number( item.predicate.value.defaultValue)-发现回显是Invalide Time
505   - richTextList.push({
506   - // 查询中文操作符
507   - operation: findOperation(item.valueType, item.predicate.operation).label,
508   - value:
509   - item.valueType === 'DATE_TIME'
510   - ? formatToDateTime(
511   - item.predicate.value.defaultValue,
512   - 'YYYY-MM-DD HH:mm:ss'
513   - )
514   - : String(item.predicate.value.defaultValue),
515   - attribute: item?.key?.key,
516   - });
517   - //TODO-fengtao之前是Number( item.predicate.value.defaultValue)-发现回显是Invalide Time
518   - ConditionScreeningForm[ruleIndex].value[conditionIndex].setFieldsValue(
519   - formItem
520   - );
521   - });
522   - unref(skipUnwrap.actionItemRefs)[index].setRichText(richTextList, ruleIndex);
523   - });
524   - });
525   - });
526   - }
527   - nextTick(() => {
528   - setEditFields(skipUnwrap.actionItemRefs, editEntryIdData);
529   - });
530   - });
531   - });
532   - }
533   - if (unref(isUpdate) === 3) isView.value = false;
534   - setDrawerProps({
535   - showFooter: unref(isView),
536   - loading: false,
537   - });
538   -
539   - if (isUpdate.value == 3) {
540   - setProps({ disabled: true });
541   - updateSchema({
542   - field: 'organizationId',
543   - componentProps: { apiTreeSelectProps: { disabled: true } },
544   - });
545   - await nextTick();
546   - unref(skipUnwrap.triggerItemRefs)?.forEach((item) => {
547   - item.setDisabledProps({ disabled: true });
548   - });
549   -
550   - unref(skipUnwrap.conditionItemRefs)?.forEach((item) => {
551   - item.setDisabledProps({ disabled: true });
552   - });
553   -
554   - unref(skipUnwrap.actionItemRefs)?.forEach((item) => {
555   - item.setDisabledProps({ disabled: true });
556   - });
557   - } else {
558   - updateSchema({
559   - field: 'organizationId',
560   - componentProps: { apiTreeSelectProps: { disabled: false } },
561   - });
562   - unref(skipUnwrap.triggerItemRefs)?.forEach((item) => {
563   - item.setCancelDisabled();
564   - });
565   - unref(skipUnwrap.conditionItemRefs)?.forEach((item) => {
566   - item.setCancelDisabled();
567   - });
568   - }
569   - });
570   -
571   - // 设置设备的options
572   - const setEditFields = (linkAge, deviceList) => {
573   - unref(linkAge).map((item) => {
574   - // TODO-fengtao
575   - item.updateFieldDeviceId(deviceList, orgId, isUpdate);
576   - // TODO-fengtao
577   - });
578   - };
579   - // 设置告警配置options
580   - const setEditAlarmConfig = (linkAge, alarmConfigList) => {
581   - unref(linkAge).map((item) => {
582   - item.updateEditFieldAlarmConfig(alarmConfigList);
583   - });
584   - };
585   - // 监听组织变化更新设备列表
586   - const deviceList = ref([]);
587   - const getMasterDeviceList = ref([]);
588   - const orgId = ref('');
589   - const alarmConfigList = ref([]);
590   - //FT add 2022-10-27
591   - const addOrgId = ref('');
592   - //FT add 2022-10-27
593   - watch(organizationIdRef, async (newValue: string) => {
594   - if (!newValue) return;
595   - //FT add 2022-10-27
596   - addOrgId.value = newValue;
597   - //FT add 2022-10-27
598   - const { items = [] } = await screenLinkPageByDeptIdGetDevice({ organizationId: newValue });
599   - //TODO fengtao
600   - // getMasterDeviceList.value = await byOrganizationIdGetMasterDevice(newValue);
601   - //TODO fengtao
602   - deviceList.value = (items || []).map((item) => ({
603   - ...item,
604   - label: item.name,
605   - value: item.tbDeviceId,
606   - }));
607   - //TODO fengtao
608   - orgId.value = newValue;
609   - //TODO fengtao
610   - setFields(skipUnwrap.triggerItemRefs, true);
611   - setFields(skipUnwrap.conditionItemRefs, true);
612   - setFields(skipUnwrap.actionItemRefs, true);
613   - const data = await getOrganizationAlarmConfig({ organizationId: newValue });
614   - alarmConfigList.value = data.map((item) => ({
615   - label: item.name,
616   - value: item.id,
617   - status: item.status,
618   - }));
619   - setEditAlarmConfig(skipUnwrap.actionItemRefs, alarmConfigList);
620   - // setFields(skipUnwrap.actionItemRefs, true);
621   - // console.log(unref(organizationIdRef));
622   - // setAlarmConfig(skipUnwrap.actionItemRefs, true);
623   - });
624   - //FT add 2022-10-27
625   - const handleDynamicChangeAlarmConfig = async () => {
626   - const data = await getOrganizationAlarmConfig({
627   - organizationId: isUpdate.value ? provideOrgid.value : addOrgId.value,
628   - });
629   - const res = data.map((item) => ({ label: item.name, value: item.id, status: item.status }));
630   - isUpdate.value ? (editAlarmConfigData.value = res) : (alarmConfigList.value = res);
631   - unref(skipUnwrap.actionItemRefs)?.map((item: any) => {
632   - if (isUpdate.value) {
633   - item.updateEditFieldAlarmConfig(editAlarmConfigData);
634   - } else {
635   - item.updateFieldAlarmConfig(alarmConfigList);
636   - }
637   - });
638   - };
639   - //FT add 2022-10-27
640   - // 根据上面组织变化动态改变触发器,执行条件,执行动作的设备值
641   - function setFields(linkAge, isOrganizationChange = false) {
642   - unref(linkAge).map((item) => {
643   - isOrganizationChange && item.resetFieldsValueFunc();
644   - //TODO fengtao
645   - item.updateFieldDeviceId(deviceList, orgId, isUpdate);
646   - if (isUpdate.value) {
647   - item.updateEditFieldAlarmConfig(editAlarmConfigData);
648   - } else {
649   - item.updateFieldAlarmConfig(alarmConfigList);
650   - }
651   - //TODO fengtao
652   - });
653   - }
654   - // function setAlarmConfig(linkAge, isOrganizationChange = false) {
655   - // unref(linkAge).map((item) => {
656   - // isOrganizationChange && item.resetFieldsValueFunc();
657   - // item.updateFieldAlarmConfig(alarmConfigList);
658   - // });
659   - // }
660   -
661   - const isEmptyThrowError = (obj) => {
662   - if (isEmpty(obj)) {
663   - createMessage.error('请选择组织');
664   - throw Error('请选择组织');
665   - }
666   - if (!Reflect.get(obj, 'organizationId')) {
667   - createMessage.error('请选择组织');
668   - throw Error('请选择组织');
669   - }
670   - };
671   -
672   - // 添加触发器
673   - const addTrigger = () => {
674   - unref(triggerData).push(Date.now());
675   - nextTick(() => {
676   - setFields(skipUnwrap.triggerItemRefs);
677   - });
678   - isEmptyThrowError(getFieldsValue());
679   - };
680   - // 添加执行条件
681   - const addCondition = () => {
682   - unref(conditionData).push(Date.now());
683   - nextTick(() => {
684   - setFields(skipUnwrap.conditionItemRefs);
685   - });
686   - isEmptyThrowError(getFieldsValue());
687   - };
688   - // 添加执行动作
689   - const addAction = () => {
690   - unref(actionData).push(Date.now());
691   - nextTick(() => {
692   - setFields(skipUnwrap.actionItemRefs);
693   - });
694   - isEmptyThrowError(getFieldsValue());
695   - };
696   -
697   - /**
698   - * 获取触发器、执行条件、执行动作表单值--多个
699   - */
700   - const getFormValueFunc = () => {
701   - getTriggerFormValue.value = unref(skipUnwrap.triggerItemRefs)?.map((item) =>
702   - genTriggerOrConditionData(item.getFieldsValueFunc())
703   - );
704   - getConditionFormValue.value = unref(skipUnwrap.conditionItemRefs)?.map((item) =>
705   - genTriggerOrConditionData(item.getFieldsValueFunc())
706   - );
707   - getActionFormValue.value = unref(skipUnwrap.actionItemRefs)?.map((item) =>
708   - genActionData(item.getFieldsValueFunc())
709   - );
710   - };
711   - const handleSubmit = async () => {
712   - let basicFormValue = await validate();
713   - if (!basicFormValue) return;
714   - for (const item of unref(skipUnwrap.actionItemRefs)) {
715   - const valid = await item.validateForm();
716   - if (!valid) return;
717   - }
718   - try {
719   - setDrawerProps({ confirmLoading: true });
720   - getFormValueFunc();
721   - //新增的代码2022-11-22
722   - if (unref(getTriggerFormValue).length === 0 && unref(getConditionFormValue).length === 0) {
723   - createMessage.error('请添加触发器或者执行条件');
724   - throw '请添加触发器或者执行条件';
725   - }
726   -
727   - // return;
728   - //新增的代码2022-11-22
729   - const postAddOrEditData = {
730   - ...basicFormValue,
731   - triggers: !unref(getTriggerFormValue).length ? null : unref(getTriggerFormValue),
732   - doConditions: !unref(getConditionFormValue).length ? null : unref(getConditionFormValue),
733   - doActions: unref(getActionFormValue).flat(),
734   - id: unref(id),
735   - tenantId: unref(tenantId),
736   - };
737   - //FT change
738   - let mustTriggerCondition = false;
739   - let mustCondition = false;
740   - if (postAddOrEditData?.triggers !== null && postAddOrEditData?.triggers.length > 0) {
741   - postAddOrEditData?.triggers.some((s) => {
742   - if (s.triggerCondition?.condition?.condition == undefined) {
743   - mustTriggerCondition = true;
744   - }
745   - });
746   - } else {
747   - mustTriggerCondition = false;
748   - }
749   - if (postAddOrEditData?.doConditions !== null && postAddOrEditData?.doConditions.length > 0) {
750   - postAddOrEditData?.doConditions.some((s) => {
751   - if (s.triggerCondition?.condition?.condition == undefined) {
752   - mustCondition = true;
753   - }
754   - });
755   - } else {
756   - mustCondition = false;
757   - }
758   - if (mustTriggerCondition) return;
759   - if (mustCondition) return;
760   - //FT change
761   - await screenLinkPageAddApi(postAddOrEditData, unref(isUpdate));
762   - createMessage.success(`${unref(isUpdate) ? '编辑' : '新增'}成功`);
763   - closeDrawer();
764   - handleClose();
765   - emit('success');
766   - } finally {
767   - setDrawerProps({ confirmLoading: false });
768   - }
769   - };
770   - // 删除
771   - const deleteTriggerOrCondition = ({ index, title }) => {
772   - if (title === '触发器') {
773   - unref(triggerData).splice(index, 1);
774   - } else if (title === '执行条件') {
775   - unref(conditionData).splice(index, 1);
776   - }
777   - };
778   - const deleteAction = (actionIndex) => {
779   - unref(actionData).splice(actionIndex, 1);
780   - unref(arr).splice(actionIndex, 1);
781   - };
782   - const arr = ref([]);
783   - const getActionFormArr = () => {
784   - arr.value = unref(skipUnwrap.actionItemRefs).map((item) => item.getFieldsValue());
785   - };
786   - const handleClose = () => {
787   - id.value = undefined;
788   - tenantId.value = undefined;
789   - organizationIdRef.value = undefined;
790   - isView.value = true;
791   - getTriggerFormValue.value = [];
792   - getConditionFormValue.value = [];
793   - getActionFormValue.value = [];
794   - triggerData.value = [];
795   - conditionData.value = [];
796   - actionData.value = [];
797   - unref(skipUnwrap.triggerItemRefs).map((item) => {
798   - item.resetFieldsValueFunc();
799   - });
800   - unref(skipUnwrap.conditionItemRefs).map((item) => {
801   - item.resetFieldsValueFunc();
802   - });
803   - unref(skipUnwrap.actionItemRefs).map((item) => {
804   - item.resetFieldsValueFunc();
805   - });
806   - window.localStorage.removeItem('isViewDisabledBtn');
807   - // window.localStorage.setItem('isViewDisabledBtn', 'no')
808   - };
809   -</script>
810   -
811   -<style lang="less" scoped>
812   - //TODO-fengtao
813   - ///移除选择框默认样式(24px)否则超出默认宽度会造成页面样式错乱
814   - :deep(.ant-select-selector) {
815   - padding-right: 0 !important;
816   - }
817   -
818   - :deep(.ant-select-selection-overflow) {
819   - max-width: 10vw !important;
820   - }
821   -
822   - //TODO-fengtao
823   -
824   - label.validate-dot::before {
825   - display: inline-block;
826   - color: #ff4d4f;
827   - font-size: 14px;
828   - font-family: SimSun, sans-serif;
829   - line-height: 1;
830   - content: '*';
831   - }
832   -</style>
  1 +import { OperationType } from './type';
  2 +import { FormSchema } from '/@/components/Form';
  3 +import {
  4 + BooleanOperationEnum,
  5 + BooleanOperationNameEnum,
  6 + BooleanOperationValueEnum,
  7 + BooleanOperationValueNameEnum,
  8 + NumberOperationEnum,
  9 + NumberOperationNameEnum,
  10 + StringOperationEnum,
  11 + StringOperationNameEnum,
  12 + TriggerValueTypeEnum,
  13 +} from '/@/enums/linkedgeEnum';
  14 +
  15 +export enum FormFieldsEnum {
  16 + CONDITION_OPERATION = 'operation',
  17 + CONDITION_VALUE = 'defaultValue',
  18 + CONDITION_VALUE_IGNORE_CASE = 'ignoreCase',
  19 +}
  20 +
  21 +export enum FormFieldsNameEnum {
  22 + CONDITION_OPERATION = '执行操作',
  23 + CONDITION_VALUE = '操作值',
  24 + CONDITION_VALUE_IGNORE_CASE = '忽略大小写',
  25 +}
  26 +
  27 +interface GetFormSchemasParamsType {
  28 + triggerType: TriggerValueTypeEnum;
  29 +}
  30 +
  31 +export interface ConditionFormRecordType {
  32 + [FormFieldsEnum.CONDITION_OPERATION]: OperationType;
  33 + [FormFieldsEnum.CONDITION_VALUE]: any;
  34 + [FormFieldsEnum.CONDITION_VALUE_IGNORE_CASE]?: boolean;
  35 +}
  36 +
  37 +export const getOperationMap = (type: TriggerValueTypeEnum) => {
  38 + const keyMap = {
  39 + [TriggerValueTypeEnum.NUMERIC]: { value: NumberOperationEnum, label: NumberOperationNameEnum },
  40 + [TriggerValueTypeEnum.BOOLEAN]: {
  41 + value: BooleanOperationEnum,
  42 + label: BooleanOperationNameEnum,
  43 + },
  44 + [TriggerValueTypeEnum.STRING]: { value: StringOperationEnum, label: StringOperationNameEnum },
  45 + [TriggerValueTypeEnum.DATE_TIME]: {
  46 + value: NumberOperationEnum,
  47 + label: NumberOperationNameEnum,
  48 + },
  49 + };
  50 +
  51 + return keyMap[type];
  52 +};
  53 +
  54 +const getOperation = (type: TriggerValueTypeEnum) => {
  55 + const res = getOperationMap(type);
  56 +
  57 + const { label, value } = res;
  58 + return Object.keys(value).map((value) => ({ label: label[value], value }));
  59 +};
  60 +
  61 +const getOperationValueFormSchemas = (triggerType: TriggerValueTypeEnum): FormSchema[] => {
  62 + const getNumberSchemas = (): FormSchema[] => {
  63 + return [
  64 + {
  65 + field: FormFieldsEnum.CONDITION_VALUE,
  66 + label: FormFieldsNameEnum.CONDITION_VALUE,
  67 + component: 'InputNumber',
  68 + required: true,
  69 + componentProps: () => {
  70 + return {
  71 + placeholder: `请输入${FormFieldsNameEnum.CONDITION_VALUE}`,
  72 + };
  73 + },
  74 + },
  75 + ];
  76 + };
  77 +
  78 + const getBoolSchemas = (): FormSchema[] => {
  79 + return [
  80 + {
  81 + field: FormFieldsEnum.CONDITION_VALUE,
  82 + label: FormFieldsNameEnum.CONDITION_VALUE,
  83 + component: 'Select',
  84 + required: true,
  85 + componentProps: () => {
  86 + return {
  87 + options: [
  88 + { label: BooleanOperationValueNameEnum.TRUE, value: BooleanOperationValueEnum.TRUE },
  89 + {
  90 + label: BooleanOperationValueNameEnum.FALSE,
  91 + value: BooleanOperationValueEnum.FALSE,
  92 + },
  93 + ],
  94 + placeholder: `请输入${FormFieldsNameEnum.CONDITION_VALUE}`,
  95 + };
  96 + },
  97 + },
  98 + ];
  99 + };
  100 +
  101 + const getStringSchemas = (): FormSchema[] => {
  102 + return [
  103 + {
  104 + field: FormFieldsEnum.CONDITION_VALUE,
  105 + label: FormFieldsNameEnum.CONDITION_VALUE,
  106 + component: 'Input',
  107 + required: true,
  108 + componentProps: () => {
  109 + return {
  110 + placeholder: `请输入${FormFieldsNameEnum.CONDITION_VALUE}`,
  111 + };
  112 + },
  113 + },
  114 + {
  115 + field: FormFieldsEnum.CONDITION_VALUE_IGNORE_CASE,
  116 + label: ' ',
  117 + component: 'Checkbox',
  118 + renderComponentContent: () => ({
  119 + default: () => FormFieldsNameEnum.CONDITION_VALUE_IGNORE_CASE,
  120 + }),
  121 + },
  122 + ];
  123 + };
  124 +
  125 + const getDateSchemas = (): FormSchema[] => {
  126 + return [
  127 + {
  128 + field: FormFieldsEnum.CONDITION_VALUE,
  129 + label: FormFieldsNameEnum.CONDITION_VALUE,
  130 + component: 'DatePicker',
  131 + required: true,
  132 + componentProps: () => {
  133 + return {
  134 + showTime: true,
  135 + placeholder: `请输入${FormFieldsNameEnum.CONDITION_VALUE}`,
  136 + };
  137 + },
  138 + },
  139 + ];
  140 + };
  141 +
  142 + const schemasMap = {
  143 + [TriggerValueTypeEnum.BOOLEAN]: getBoolSchemas,
  144 + [TriggerValueTypeEnum.DATE_TIME]: getDateSchemas,
  145 + [TriggerValueTypeEnum.NUMERIC]: getNumberSchemas,
  146 + [TriggerValueTypeEnum.STRING]: getStringSchemas,
  147 + };
  148 +
  149 + return schemasMap[triggerType]?.() || [];
  150 +};
  151 +
  152 +export const getFormSchemas = ({ triggerType }: GetFormSchemasParamsType): FormSchema[] => {
  153 + return [
  154 + {
  155 + field: FormFieldsEnum.CONDITION_OPERATION,
  156 + label: FormFieldsNameEnum.CONDITION_OPERATION,
  157 + component: 'Select',
  158 + required: true,
  159 + componentProps: () => {
  160 + return {
  161 + options: getOperation(triggerType),
  162 + placeholder: `请选择${FormFieldsNameEnum.CONDITION_OPERATION}`,
  163 + };
  164 + },
  165 + },
  166 + ...getOperationValueFormSchemas(triggerType),
  167 + ];
  168 +};
... ...
  1 +import { ConditionListItemType } from './type';
  2 +import { buildUUID } from '/@/utils/uuid';
  3 +
  4 +export { default as ConditionFilter } from './index.vue';
  5 +
  6 +export function getNewConditionFilterItem(value?: Recordable): ConditionListItemType {
  7 + return {
  8 + key: buildUUID(),
  9 + value,
  10 + };
  11 +}
... ...
  1 +<script setup lang="ts">
  2 + import { ComponentPublicInstance, computed, ref, unref, watch } from 'vue';
  3 + import { Card, Tag, Button } from 'ant-design-vue';
  4 + import { Icon } from '/@/components/Icon';
  5 + import { getFormSchemas, FormFieldsEnum } from './config';
  6 + import { CollapseContainer } from '/@/components/Container';
  7 + import { BasicForm, FormActionType } from '/@/components/Form';
  8 + import { TriggerValueTypeEnum } from '/@/enums/linkedgeEnum';
  9 + import { ConditionFilterProps, ConditionListItemType } from './type';
  10 + import { ConditionMessageItemType, useConditionFilterMessage } from './useConditionFilterMessage';
  11 + import { useConditionData } from './useConditionData';
  12 + import { getNewConditionFilterItem } from '.';
  13 + import { useSceneLinkageDrawerContext } from '../SceneLinkageDrawer/sceneLinkageDrawerContext';
  14 +
  15 + const { disabledDrawer } = useSceneLinkageDrawerContext();
  16 +
  17 + const props = withDefaults(defineProps<ConditionFilterProps>(), {
  18 + triggerType: TriggerValueTypeEnum.BOOLEAN,
  19 + });
  20 +
  21 + const getSchemas = computed(() => {
  22 + const { triggerType } = props;
  23 + return getFormSchemas({ triggerType });
  24 + });
  25 +
  26 + const conditionListElRef = ref<ConditionListItemType[]>([getNewConditionFilterItem()]);
  27 +
  28 + const getConditionFormValues = (): Record<FormFieldsEnum, string>[] => {
  29 + return unref(conditionListElRef).map(
  30 + (conditionForm) => conditionForm.ref?.getFieldsValue() as Record<FormFieldsEnum, string>
  31 + );
  32 + };
  33 +
  34 + const conditionMessageList = ref<ConditionMessageItemType[]>([]);
  35 +
  36 + const handleConditionFormValueChange = () => {
  37 + const condition = getConditionFormValues();
  38 + const { triggerType } = props;
  39 + const message = useConditionFilterMessage({ triggerType, condition });
  40 + conditionMessageList.value = message;
  41 + };
  42 +
  43 + const setConditionRef = (
  44 + conditionItem: ConditionListItemType,
  45 + el: Element | ComponentPublicInstance | null
  46 + ) => {
  47 + conditionItem.ref = el as unknown as FormActionType;
  48 + };
  49 +
  50 + const handleDelete = (conditionItem: ConditionListItemType) => {
  51 + const { key } = conditionItem;
  52 +
  53 + const index = unref(conditionListElRef).findIndex((item) => item.key === key);
  54 +
  55 + ~index && unref(conditionListElRef).splice(index, 1);
  56 + };
  57 +
  58 + const handleAdd = () => {
  59 + conditionListElRef.value.push(getNewConditionFilterItem());
  60 + };
  61 +
  62 + watch(
  63 + () => props.triggerType,
  64 + () => {
  65 + conditionListElRef.value = [getNewConditionFilterItem()];
  66 + conditionMessageList.value = [];
  67 + }
  68 + );
  69 +
  70 + const { getFieldsValue, setFieldsValue, validate } = useConditionData(
  71 + props,
  72 + conditionListElRef,
  73 + handleConditionFormValueChange
  74 + );
  75 +
  76 + const handelValidate = async () => {
  77 + await validate?.();
  78 + };
  79 +
  80 + defineExpose({ getFieldsValue, setFieldsValue, validate });
  81 +</script>
  82 +
  83 +<template>
  84 + <Card class="condition-filter-card !border-dashed !border-2 !border-gray-200 !rounded-lg">
  85 + <CollapseContainer class="rounded-lg" :is-close="!disabledDrawer">
  86 + <template #title>
  87 + <section class="flex !p-2">
  88 + <div class="w-20 flex-grow-0 flex-shrink-0"> 条件筛选 </div>
  89 + <div class="flex flex-wrap">
  90 + <template v-for="(item, index) in conditionMessageList" :key="index">
  91 + <Tag class="!rounded-md !m-0 !text-sky-700"> {{ objectModel?.name }} </Tag>
  92 + <span class="mx-2">{{ item.operation }}</span>
  93 + <Tag class="!rounded-md !m-0 !text-orange-300">
  94 + {{ item.defaultValue }}
  95 + </Tag>
  96 + <span class="mx-2" v-if="conditionMessageList.length - 1 !== index">和</span>
  97 + </template>
  98 + </div>
  99 + </section>
  100 + </template>
  101 + <!-- -->
  102 + <main
  103 + class="flex gap-4 items-center mb-2"
  104 + v-for="(conditionItem, index) in conditionListElRef"
  105 + :key="conditionItem.key"
  106 + >
  107 + <section class="border-2 border-dashed border-gray-200 rounded-lg px-4 py-2 flex-1">
  108 + <!-- -->
  109 + <BasicForm
  110 + :ref="(el) => setConditionRef(conditionItem, el)"
  111 + :show-action-button-group="false"
  112 + layout="horizontal"
  113 + :disabled="disabledDrawer"
  114 + :label-width="80"
  115 + :schemas="getSchemas"
  116 + :base-col-props="{ span: 24 / getSchemas.length, xl: 8, lg: 12, sm: 24 }"
  117 + @field-value-change="handleConditionFormValueChange"
  118 + />
  119 + </section>
  120 + <span
  121 + class="w-5"
  122 + :style="{ visibility: conditionListElRef.length - 1 !== index ? 'visible' : 'hidden' }"
  123 + >
  124 +
  125 + </span>
  126 + <Icon
  127 + v-if="!disabledDrawer"
  128 + icon="fluent:delete-off-20-regular"
  129 + size="24"
  130 + class="cursor-pointer"
  131 + @click="handleDelete(conditionItem)"
  132 + />
  133 + </main>
  134 + <section class="flex mt-4 items-center justify-between">
  135 + <Button v-if="!disabledDrawer" type="primary" @click="handleAdd">
  136 + <Icon icon="ant-design:plus-outlined" />
  137 + 新增条件筛选
  138 + </Button>
  139 + <Button v-if="!disabledDrawer" type="primary" @click="handelValidate">添加</Button>
  140 + </section>
  141 + </CollapseContainer>
  142 + </Card>
  143 +</template>
  144 +
  145 +<style lang="less" scoped>
  146 + .condition-filter-card {
  147 + :deep(.ant-card-body) {
  148 + @apply p-3;
  149 +
  150 + .vben-collapse-container__header {
  151 + height: fit-content;
  152 + }
  153 +
  154 + .ant-calendar-picker {
  155 + min-width: auto !important;
  156 + width: 100%;
  157 + }
  158 +
  159 + .ant-input-number {
  160 + min-width: auto !important;
  161 + }
  162 + }
  163 + }
  164 +</style>
  165 +./type
... ...
  1 +import { ScheduleType } from '../EnablingRule/type';
  2 +import { DeviceModelOfMatterAttrs } from '/@/api/device/model/deviceModel';
  3 +import { FormActionType } from '/@/components/Form';
  4 +import {
  5 + BooleanOperationEnum,
  6 + DeviceTriggerTypeEum,
  7 + FlipFlopTypeEnum,
  8 + NumberOperationEnum,
  9 + StringOperationNameEnum,
  10 + TriggerTypeEnum,
  11 + TriggerValueTypeEnum,
  12 +} from '/@/enums/linkedgeEnum';
  13 +
  14 +export interface ConditionListItemType {
  15 + key: string;
  16 + value?: Recordable;
  17 + ref?: FormActionType;
  18 +}
  19 +
  20 +export interface ConditionFilterProps {
  21 + triggerType?: TriggerValueTypeEnum;
  22 + objectModel?: DeviceModelOfMatterAttrs;
  23 + conditionType?: DeviceTriggerTypeEum;
  24 +}
  25 +
  26 +export interface TriggerCondition {
  27 + condition: ConditionType;
  28 + alarmDetails?: Nullable<any>;
  29 + dashboardId?: Nullable<any>;
  30 + schedule: ScheduleType;
  31 + triggerType?: TriggerTypeEnum;
  32 +}
  33 +
  34 +export interface ConditionType {
  35 + condition: ConditionItemType[];
  36 + spec: { type: FlipFlopTypeEnum; unit?: string; predicate?: { defaultValue: any } };
  37 +}
  38 +
  39 +export type OperationType = NumberOperationEnum | StringOperationNameEnum | BooleanOperationEnum;
  40 +
  41 +export interface ConditionItemType {
  42 + key: { type: DeviceTriggerTypeEum; key: string };
  43 + predicate: {
  44 + type: TriggerValueTypeEnum;
  45 + operation: OperationType;
  46 + ignoreCase?: boolean;
  47 + value: {
  48 + defaultValue: number | string | boolean;
  49 + userValue?: Nullable<number | string | boolean>;
  50 + dynamicValue?: Nullable<number | string | boolean>;
  51 + };
  52 + };
  53 + valueType: TriggerValueTypeEnum;
  54 + value?: Nullable<any>;
  55 +}
... ...
  1 +import { Ref, nextTick, unref } from 'vue';
  2 +import { ConditionFilterProps, ConditionItemType, ConditionListItemType } from './type';
  3 +import { DefineComponentsBasicExpose } from '/#/utils';
  4 +import { DeviceTriggerTypeEum, TriggerValueTypeEnum } from '/@/enums/linkedgeEnum';
  5 +import { ConditionFormRecordType, FormFieldsEnum } from './config';
  6 +import { getNewConditionFilterItem } from '.';
  7 +import { dateUtil } from '/@/utils/dateUtil';
  8 +
  9 +function getPredicateType(triggerType: TriggerValueTypeEnum) {
  10 + const map = {
  11 + [TriggerValueTypeEnum.BOOLEAN]: TriggerValueTypeEnum.BOOLEAN,
  12 + [TriggerValueTypeEnum.STRING]: TriggerValueTypeEnum.STRING,
  13 + [TriggerValueTypeEnum.NUMERIC]: TriggerValueTypeEnum.NUMERIC,
  14 + [TriggerValueTypeEnum.DATE_TIME]: TriggerValueTypeEnum.NUMERIC,
  15 + };
  16 +
  17 + return map[triggerType];
  18 +}
  19 +
  20 +export function useConditionData(
  21 + props: ConditionFilterProps,
  22 + conditionItemRef: Ref<ConditionListItemType[]>,
  23 + handleConditionFormValueChange: Fn
  24 +): DefineComponentsBasicExpose<ConditionItemType[]> {
  25 + const createConditionItem = ({
  26 + operation,
  27 + defaultValue,
  28 + ignoreCase,
  29 + }: ConditionFormRecordType): ConditionItemType => {
  30 + const { triggerType, objectModel } = props;
  31 + const { identifier } = objectModel || {};
  32 + return {
  33 + key: { key: identifier!, type: DeviceTriggerTypeEum.TIME_SERIES },
  34 + predicate: {
  35 + ...(ignoreCase ? { ignoreCase } : {}),
  36 + operation,
  37 + type: getPredicateType(triggerType!),
  38 + value: {
  39 + defaultValue:
  40 + triggerType === TriggerValueTypeEnum.DATE_TIME
  41 + ? Number(dateUtil(defaultValue).format('x'))
  42 + : defaultValue,
  43 + },
  44 + },
  45 + valueType: triggerType!,
  46 + };
  47 + };
  48 +
  49 + const getFieldsValue = (): ConditionItemType[] => {
  50 + return unref(conditionItemRef).map((conditionItem) => {
  51 + const value = conditionItem.ref?.getFieldsValue() as ConditionFormRecordType;
  52 + return createConditionItem(value);
  53 + });
  54 + };
  55 +
  56 + const setFieldsValue = (condition: ConditionItemType[]) => {
  57 + conditionItemRef.value = Array.from({ length: condition.length }, () =>
  58 + getNewConditionFilterItem()
  59 + );
  60 +
  61 + nextTick(() => {
  62 + unref(conditionItemRef).forEach((conditionItem, index) => {
  63 + const result = condition[index] || {};
  64 +
  65 + const { operation, value, ignoreCase, type } = result.predicate || {};
  66 +
  67 + conditionItem.ref?.setFieldsValue({
  68 + [FormFieldsEnum.CONDITION_OPERATION]: operation,
  69 + [FormFieldsEnum.CONDITION_VALUE]:
  70 + type === TriggerValueTypeEnum.BOOLEAN
  71 + ? value.defaultValue.toString()
  72 + : value.defaultValue,
  73 + [FormFieldsEnum.CONDITION_VALUE_IGNORE_CASE]: ignoreCase,
  74 + });
  75 + });
  76 + handleConditionFormValueChange();
  77 + });
  78 + };
  79 +
  80 + const validate = async () => {
  81 + for (const conditionItem of unref(conditionItemRef)) {
  82 + await conditionItem.ref?.validate();
  83 + }
  84 + };
  85 + return {
  86 + getFieldsValue,
  87 + setFieldsValue,
  88 + validate,
  89 + };
  90 +}
... ...
  1 +import { FormFieldsEnum, getOperationMap } from './config';
  2 +import {
  3 + BooleanOperationValueEnum,
  4 + BooleanOperationValueNameEnum,
  5 + TriggerValueTypeEnum,
  6 +} from '/@/enums/linkedgeEnum';
  7 +
  8 +export type ConditionMessageItemType = Record<
  9 + Exclude<FormFieldsEnum, FormFieldsEnum.CONDITION_VALUE_IGNORE_CASE>,
  10 + string
  11 +>;
  12 +
  13 +interface UseConditionFilterMessageParamsType {
  14 + triggerType: TriggerValueTypeEnum;
  15 + condition: ConditionMessageItemType[];
  16 +}
  17 +
  18 +export function useConditionFilterMessage({
  19 + triggerType,
  20 + condition,
  21 +}: UseConditionFilterMessageParamsType) {
  22 + const { label } = getOperationMap(triggerType);
  23 + const list: ConditionMessageItemType[] = [];
  24 + for (const item of condition) {
  25 + let { defaultValue, operation } = item;
  26 + if (!defaultValue || !operation) continue;
  27 + operation = label[operation];
  28 + if (triggerType === TriggerValueTypeEnum.BOOLEAN)
  29 + defaultValue =
  30 + defaultValue === BooleanOperationValueEnum.TRUE
  31 + ? BooleanOperationValueNameEnum.TRUE
  32 + : BooleanOperationValueNameEnum.FALSE;
  33 + list.push({ operation, defaultValue });
  34 + }
  35 + return list;
  36 +}
... ...
  1 +import TimeRangePicker from './TimeRangePicker.vue';
  2 +import { WeekName } from './config';
  3 +import { FormSchema, useComponentRegister } from '/@/components/Form';
  4 +
  5 +enum FormFieldsEnum {
  6 + DAY_OF_WEEK = 'dayOfWeek',
  7 + ENABLED = 'enabled',
  8 + ENDS_ON = 'endsOn',
  9 + STARTS_ON = 'startsOn',
  10 + TIME_RANGE = 'timeRange',
  11 +}
  12 +
  13 +export interface RuleFormRecordItem {
  14 + [FormFieldsEnum.DAY_OF_WEEK]: number;
  15 + [FormFieldsEnum.ENABLED]: boolean;
  16 + [FormFieldsEnum.TIME_RANGE]: {
  17 + [FormFieldsEnum.STARTS_ON]: number;
  18 + [FormFieldsEnum.ENDS_ON]: number;
  19 + };
  20 +}
  21 +
  22 +useComponentRegister('TimeRangePicker', TimeRangePicker);
  23 +
  24 +export const getFormSchemas = (index: number): FormSchema[] => {
  25 + return [
  26 + {
  27 + field: FormFieldsEnum.DAY_OF_WEEK,
  28 + label: '',
  29 + component: 'InputNumber',
  30 + defaultValue: index + 1,
  31 + ifShow: false,
  32 + },
  33 + {
  34 + field: FormFieldsEnum.ENABLED,
  35 + label: '',
  36 + component: 'Checkbox',
  37 + defaultValue: false,
  38 + renderComponentContent: () => ({
  39 + default: () => `星期${WeekName[index]}`,
  40 + }),
  41 + colProps: { span: 5 },
  42 + },
  43 + {
  44 + field: FormFieldsEnum.TIME_RANGE,
  45 + label: '',
  46 + changeEvent: 'update:value',
  47 + valueField: 'value',
  48 + component: 'TimeRangePicker',
  49 + dynamicDisabled: ({ model }) => !model[FormFieldsEnum.ENABLED],
  50 + colProps: { span: 16 },
  51 + defaultValue: { startsOn: 0, endsOn: 0 },
  52 + dynamicRules: ({ model }) => {
  53 + return [
  54 + {
  55 + required: model[FormFieldsEnum.ENABLED],
  56 + validator(_rule, value: Record<'startsOn' | 'endsOn', number>, _callback) {
  57 + if (!model[FormFieldsEnum.ENABLED]) return Promise.resolve();
  58 + if (!value?.endsOn) return Promise.reject('请选择结束时间');
  59 + if (!value?.startsOn) return Promise.reject('请选择开始时间');
  60 + return Promise.resolve();
  61 + },
  62 + },
  63 + ];
  64 + },
  65 + },
  66 + ];
  67 +};
... ...
  1 +<script setup lang="ts">
  2 + import { CustomRuleItemType } from './type';
  3 + import { ComponentPublicInstance, nextTick, ref, unref, watch } from 'vue';
  4 + import { BasicForm, FormActionType } from '/@/components/Form';
  5 + import { getFormSchemas, RuleFormRecordItem } from './CustomRule.config';
  6 + import { buildUUID } from '/@/utils/uuid';
  7 + import { DefineComponentsBasicExpose } from '/#/utils';
  8 +
  9 + interface CustomRuleItemRefType {
  10 + key: string;
  11 + ref?: FormActionType;
  12 + value?: CustomRuleItemType;
  13 + }
  14 +
  15 + const props = withDefaults(
  16 + defineProps<{
  17 + value?: CustomRuleItemType[];
  18 + }>(),
  19 + {
  20 + value: () => [],
  21 + }
  22 + );
  23 +
  24 + const customRuleListRef = ref<CustomRuleItemRefType[]>(
  25 + Array.from({ length: 7 }, () => ({ key: buildUUID() }))
  26 + );
  27 +
  28 + const setRuleItemRef = (
  29 + el: Nullable<Element | ComponentPublicInstance>,
  30 + ruleItem: CustomRuleItemRefType
  31 + ) => {
  32 + ruleItem.ref = el as unknown as FormActionType;
  33 + };
  34 +
  35 + const validate = async () => {
  36 + for (const item of unref(customRuleListRef)) {
  37 + await item.ref?.validate?.();
  38 + }
  39 + };
  40 +
  41 + const getFieldsValue = (): CustomRuleItemType[] => {
  42 + return unref(customRuleListRef).map((item) => {
  43 + const { dayOfWeek, enabled, timeRange } = item.ref?.getFieldsValue() as RuleFormRecordItem;
  44 + return {
  45 + dayOfWeek,
  46 + enabled,
  47 + ...timeRange,
  48 + };
  49 + });
  50 + };
  51 +
  52 + const setFieldsValue = async (ruleList: CustomRuleItemType[]) => {
  53 + if (!ruleList.length) return;
  54 + await nextTick();
  55 +
  56 + ruleList.forEach((value, index) => {
  57 + const refEl = unref(customRuleListRef)[index];
  58 + refEl.ref?.setFieldsValue({
  59 + ...value,
  60 + timeRange: {
  61 + startsOn: value.startsOn,
  62 + endsOn: value.endsOn,
  63 + },
  64 + } as RuleFormRecordItem);
  65 + });
  66 + };
  67 +
  68 + watch(
  69 + () => props.value,
  70 + () => {
  71 + setFieldsValue(props.value);
  72 + },
  73 + { immediate: true }
  74 + );
  75 +
  76 + defineExpose<DefineComponentsBasicExpose<CustomRuleItemType[]>>({
  77 + getFieldsValue,
  78 + setFieldsValue,
  79 + validate,
  80 + });
  81 +</script>
  82 +
  83 +<template>
  84 + <section>
  85 + <template v-for="(item, index) in customRuleListRef" :key="index">
  86 + <BasicForm
  87 + :ref="(el) => setRuleItemRef(el, item)"
  88 + :show-action-button-group="false"
  89 + :schemas="getFormSchemas(index)"
  90 + class="h-14"
  91 + />
  92 + </template>
  93 + </section>
  94 +</template>
... ...
  1 +<script setup lang="ts">
  2 + import { TimePicker } from 'ant-design-vue';
  3 + import { Moment } from 'moment';
  4 + import { computed, unref } from 'vue';
  5 + import { dateUtil } from '/@/utils/dateUtil';
  6 +
  7 + interface TimeRangePickerProps {
  8 + timestampToNumber?: boolean;
  9 + value?: Record<string, number>;
  10 + startValue?: number;
  11 + endValue?: number;
  12 + startField?: string;
  13 + endField?: string;
  14 + disabled?: boolean;
  15 + }
  16 +
  17 + enum TimePickerTypeEnum {
  18 + START = 'START',
  19 + END = 'END',
  20 + }
  21 +
  22 + const emit = defineEmits(['update:value', 'update:startValue', 'update:endValue', 'change']);
  23 +
  24 + const props = withDefaults(defineProps<TimeRangePickerProps>(), {
  25 + value: () => ({}),
  26 + startField: 'startsOn',
  27 + endField: 'endsOn',
  28 + });
  29 +
  30 + const getTimeMoment = (value: number) => {
  31 + value = value / 1000;
  32 + const hour = Math.floor(value / 60 / 60);
  33 + const minute = Math.floor((value - hour * 60 * 60) / 60);
  34 + const second = value - hour * 60 * 60 - minute * 60;
  35 +
  36 + return dateUtil({ hour, minute, second });
  37 + };
  38 +
  39 + const getRangeValue = computed(() => {
  40 + const { value, startValue, endValue, startField, endField } = props;
  41 +
  42 + let startTs = startValue || value[startField];
  43 +
  44 + let endTs = endValue || value[endField];
  45 +
  46 + return {
  47 + startTs: startTs ? getTimeMoment(startTs) : null,
  48 + endTs: endTs ? getTimeMoment(endTs) : null,
  49 + };
  50 + });
  51 +
  52 + const getTimestamp = (moment: Moment | null) => {
  53 + if (!moment) return null;
  54 + const { hours, minutes, seconds } = moment.toObject();
  55 + return (hours * 60 * 60 + minutes * 60 + seconds) * 1000;
  56 + };
  57 +
  58 + const handleTimeChange = (time: Moment, type: TimePickerTypeEnum) => {
  59 + const { value, startField, endField } = props;
  60 + const { startTs, endTs } = unref(getRangeValue);
  61 +
  62 + const rangeValue = {
  63 + [startField]: getTimestamp(startTs),
  64 + [endField]: getTimestamp(endTs),
  65 + };
  66 +
  67 + Reflect.set(
  68 + rangeValue,
  69 + type === TimePickerTypeEnum.START ? startField : endField,
  70 + getTimestamp(time)
  71 + );
  72 +
  73 + if (
  74 + rangeValue[startField] &&
  75 + rangeValue[endField] &&
  76 + rangeValue[startField]! > rangeValue[endField]!
  77 + ) {
  78 + Reflect.set(rangeValue, type === TimePickerTypeEnum.START ? startField : endField, null);
  79 + }
  80 +
  81 + emit('update:startValue', rangeValue[startField]);
  82 + emit('update:endValue', rangeValue[endField]);
  83 + emit('update:value', rangeValue);
  84 + emit('change', value);
  85 + };
  86 +
  87 + const handleBasicDisabledHours = (type: TimePickerTypeEnum) => {
  88 + return () => {
  89 + const { startTs, endTs } = unref(getRangeValue);
  90 +
  91 + if (type === TimePickerTypeEnum.START && !endTs) return [];
  92 + if (type === TimePickerTypeEnum.END && !startTs) return [];
  93 +
  94 + const selectedHour = type === TimePickerTypeEnum.END ? startTs!.hour() : endTs!.hour();
  95 +
  96 + return type === TimePickerTypeEnum.START
  97 + ? Array.from({ length: 24 - selectedHour }, (_, index) => selectedHour + index + 1)
  98 + : Array.from({ length: selectedHour }, (_, index) => index);
  99 + };
  100 + };
  101 +
  102 + const handleBasicDisabledMinutes = (type: TimePickerTypeEnum) => {
  103 + return (currentSelectedHour: number) => {
  104 + const { startTs, endTs } = unref(getRangeValue);
  105 +
  106 + if (type === TimePickerTypeEnum.START && !endTs) return [];
  107 + if (type === TimePickerTypeEnum.END && !startTs) return [];
  108 +
  109 + const selectedTime = type === TimePickerTypeEnum.END ? startTs : endTs;
  110 + const selectedHour = selectedTime!.hour();
  111 + const selectedMinute = selectedTime!.minute();
  112 +
  113 + if (type == TimePickerTypeEnum.START) {
  114 + return selectedHour > currentSelectedHour
  115 + ? []
  116 + : Array.from({ length: 60 - selectedMinute }, (_, index) => selectedMinute + index + 1);
  117 + } else {
  118 + return selectedHour < currentSelectedHour
  119 + ? []
  120 + : Array.from({ length: selectedMinute }, (_, index) => index);
  121 + }
  122 + };
  123 + };
  124 +
  125 + const handleBasicDisabledSeconds = (type: TimePickerTypeEnum) => {
  126 + return (currentSelectedHour: number, currentSelectedMinute: number) => {
  127 + const { startTs, endTs } = unref(getRangeValue);
  128 +
  129 + if (type === TimePickerTypeEnum.START && !endTs) return [];
  130 + if (type === TimePickerTypeEnum.END && !startTs) return [];
  131 +
  132 + const selectedTime = type === TimePickerTypeEnum.END ? startTs : endTs;
  133 + const selectedHour = selectedTime!.hour();
  134 + const selectedMinute = selectedTime!.minute();
  135 + const selectedSeconds = selectedTime!.seconds();
  136 +
  137 + if (type === TimePickerTypeEnum.START) {
  138 + return selectedHour >= currentSelectedHour && selectedMinute > currentSelectedMinute
  139 + ? []
  140 + : Array.from({ length: 60 - selectedSeconds }, (_, index) => selectedSeconds + index + 1);
  141 + } else {
  142 + return selectedHour <= currentSelectedHour && selectedMinute < currentSelectedMinute
  143 + ? []
  144 + : Array.from({ length: selectedSeconds }, (_, index) => index);
  145 + }
  146 + };
  147 + };
  148 +</script>
  149 +
  150 +<template>
  151 + <main class="time-range-picker flex items-center">
  152 + <TimePicker
  153 + :value="getRangeValue.startTs"
  154 + format="HH:mm"
  155 + :disabled="disabled"
  156 + @change="(time) => handleTimeChange(time, TimePickerTypeEnum.START)"
  157 + :disabled-hours="handleBasicDisabledHours(TimePickerTypeEnum.START)"
  158 + :disabled-minutes="handleBasicDisabledMinutes(TimePickerTypeEnum.START)"
  159 + :disabled-seconds="handleBasicDisabledSeconds(TimePickerTypeEnum.START)"
  160 + />
  161 + <slot v-if="$slots?.separator" name="separator"></slot>
  162 + <span v-else class="mx-4">~</span>
  163 + <TimePicker
  164 + :value="getRangeValue.endTs"
  165 + format="HH:mm"
  166 + :disabled="disabled"
  167 + @change="(time) => handleTimeChange(time, TimePickerTypeEnum.END)"
  168 + :disabled-hours="handleBasicDisabledHours(TimePickerTypeEnum.END)"
  169 + :disabled-minutes="handleBasicDisabledMinutes(TimePickerTypeEnum.END)"
  170 + :disabled-seconds="handleBasicDisabledSeconds(TimePickerTypeEnum.END)"
  171 + />
  172 + </main>
  173 +</template>
  174 +
  175 +<style scoped lang="less">
  176 + .time-range-picker {
  177 + :deep(.ant-time-picker) {
  178 + width: auto;
  179 + }
  180 + }
  181 +</style>
... ...
  1 +import TimeRangePicker from './TimeRangePicker.vue';
  2 +import { FormSchema, useComponentRegister } from '/@/components/Form';
  3 +import { ScheduleTypeEnum, ScheduleTypeNameEnum } from '/@/enums/linkedgeEnum';
  4 +
  5 +export enum FormFieldsEnum {
  6 + SCHEDULE_TYPE = 'type',
  7 + TIMEZONE = 'timezone',
  8 + STARTS_ON = 'startsOn',
  9 + ENDS_ON = 'endsOn',
  10 + DAYS_OF_WEEK = 'daysOfWeek',
  11 + SCHEDULE_ITEMS = 'items',
  12 + TIME_RANGE = 'timeRange',
  13 +}
  14 +
  15 +export enum FormFieldsNameEnum {
  16 + SCHEDULE_TYPE = '启用类型',
  17 + TIMEZONE = '时区',
  18 + DAYS_OF_WEEK = '启用日',
  19 + TIME_RANGE = '启用时间',
  20 +}
  21 +
  22 +export interface EnableRuleFormType {
  23 + [FormFieldsEnum.DAYS_OF_WEEK]: number[];
  24 + [FormFieldsEnum.SCHEDULE_TYPE]: ScheduleTypeEnum;
  25 + [FormFieldsEnum.TIMEZONE]: string;
  26 + [FormFieldsEnum.TIME_RANGE]: {
  27 + [FormFieldsEnum.STARTS_ON]: number;
  28 + [FormFieldsEnum.ENDS_ON]: number;
  29 + };
  30 +}
  31 +
  32 +useComponentRegister('TimeRangePicker', TimeRangePicker);
  33 +
  34 +export const WeekName = ['一', '二', '三', '四', '五', '六', '日'];
  35 +
  36 +enum TimezoneEnum {
  37 + SHANG_HAI = 'Asia/Shanghai',
  38 +}
  39 +
  40 +export const getFormSchemas = (): FormSchema[] => {
  41 + return [
  42 + {
  43 + field: FormFieldsEnum.SCHEDULE_TYPE,
  44 + label: FormFieldsNameEnum.SCHEDULE_TYPE,
  45 + component: 'Select',
  46 + required: true,
  47 + defaultValue: ScheduleTypeEnum.ANY_TIME,
  48 + componentProps: () => {
  49 + return {
  50 + options: Object.keys(ScheduleTypeEnum).map((value) => ({
  51 + label: ScheduleTypeNameEnum[value],
  52 + value,
  53 + })),
  54 + allowClear: false,
  55 + };
  56 + },
  57 + },
  58 + {
  59 + field: FormFieldsEnum.TIMEZONE,
  60 + label: FormFieldsNameEnum.TIMEZONE,
  61 + component: 'Select',
  62 + required: true,
  63 + defaultValue: TimezoneEnum.SHANG_HAI,
  64 + ifShow: ({ model }) => model[FormFieldsEnum.SCHEDULE_TYPE] !== ScheduleTypeEnum.ANY_TIME,
  65 + componentProps: () => {
  66 + return {
  67 + options: [{ label: TimezoneEnum.SHANG_HAI, value: TimezoneEnum.SHANG_HAI }],
  68 + allowClear: false,
  69 + };
  70 + },
  71 + },
  72 + {
  73 + field: FormFieldsEnum.SCHEDULE_ITEMS,
  74 + label: '规则',
  75 + component: 'Input',
  76 + ifShow: ({ model }) => model[FormFieldsEnum.SCHEDULE_TYPE] === ScheduleTypeEnum.CUSTOM,
  77 + slot: 'custom',
  78 + },
  79 + {
  80 + field: FormFieldsEnum.DAYS_OF_WEEK,
  81 + label: FormFieldsNameEnum.DAYS_OF_WEEK,
  82 + component: 'CheckboxGroup',
  83 + ifShow: ({ model }) => model[FormFieldsEnum.SCHEDULE_TYPE] === ScheduleTypeEnum.SPECIFIC_TIME,
  84 + rules: [{ required: true, type: 'array' }],
  85 + componentProps: () => {
  86 + return {
  87 + options: Array.from({ length: 7 }, (_, index) => ({
  88 + label: `星期${WeekName[index]}`,
  89 + value: index + 1,
  90 + })),
  91 + };
  92 + },
  93 + },
  94 + {
  95 + field: FormFieldsEnum.TIME_RANGE,
  96 + label: FormFieldsNameEnum.TIME_RANGE,
  97 + component: 'TimeRangePicker',
  98 + changeEvent: 'update:value',
  99 + valueField: 'value',
  100 + ifShow: ({ model }) => model[FormFieldsEnum.SCHEDULE_TYPE] === ScheduleTypeEnum.SPECIFIC_TIME,
  101 + rules: [
  102 + {
  103 + required: true,
  104 + validator(_rule, value: Record<'startsOn' | 'endsOn', number>, _callback) {
  105 + if (!value?.endsOn) return Promise.reject('请选择结束时间');
  106 + if (!value?.startsOn) return Promise.reject('请选择开始时间');
  107 + return Promise.resolve();
  108 + },
  109 + },
  110 + ],
  111 + },
  112 + ];
  113 +};
... ...
  1 +export { default as EnablingRule } from './index.vue';
... ...
  1 +<script setup lang="ts">
  2 + import { BasicModal, useModalInner } from '/@/components/Modal';
  3 + import { BasicForm, useForm } from '/@/components/Form';
  4 + import { EnableRuleFormType, getFormSchemas } from './config';
  5 + import { nextTick, ref, unref } from 'vue';
  6 + import CustomRule from './CustomRule.vue';
  7 + import { ScheduleTypeEnum } from '/@/enums/linkedgeEnum';
  8 + import { EnableRuleFormModalParamsType, ScheduleType } from './type';
  9 + import { useSceneLinkageDrawerContext } from '../SceneLinkageDrawer/sceneLinkageDrawerContext';
  10 +
  11 + const { disabledDrawer } = useSceneLinkageDrawerContext();
  12 +
  13 + const emit = defineEmits(['register', 'settingComplete']);
  14 +
  15 + const scheduleItemKey = ref<string>();
  16 + const [registerModal, { closeModal }] = useModalInner(
  17 + ({ record }: EnableRuleFormModalParamsType) => {
  18 + resetFields();
  19 + const { type, schedule, key } = record;
  20 + scheduleItemKey.value = key;
  21 + handleSetFieldsValue({ ...schedule, type });
  22 + clearValidate();
  23 + }
  24 + );
  25 +
  26 + const [register, { getFieldsValue, validate, setFieldsValue, resetFields, clearValidate }] =
  27 + useForm({
  28 + schemas: getFormSchemas(),
  29 + showActionButtonGroup: false,
  30 + layout: 'vertical',
  31 + });
  32 +
  33 + const customRuleElRef = ref<Nullable<InstanceType<typeof CustomRule>>>();
  34 +
  35 + const handleGetFieldsValue = async (): Promise<ScheduleType> => {
  36 + await unref(customRuleElRef)?.validate?.();
  37 + await validate();
  38 + const { type, timeRange, timezone, daysOfWeek } = getFieldsValue() as EnableRuleFormType;
  39 + const customValue = unref(customRuleElRef)?.getFieldsValue();
  40 +
  41 + if (type === ScheduleTypeEnum.ANY_TIME) return { type };
  42 +
  43 + if (type === ScheduleTypeEnum.SPECIFIC_TIME)
  44 + return {
  45 + type,
  46 + timezone,
  47 + daysOfWeek,
  48 + ...timeRange,
  49 + };
  50 +
  51 + return {
  52 + type,
  53 + timezone,
  54 + items: customValue,
  55 + };
  56 + };
  57 +
  58 + const handleSetFieldsValue = async (schedule: ScheduleType) => {
  59 + const { type, startsOn, endsOn, timezone, daysOfWeek } = schedule;
  60 +
  61 + if (type === ScheduleTypeEnum.ANY_TIME) return setFieldsValue({ type });
  62 +
  63 + if (type === ScheduleTypeEnum.SPECIFIC_TIME)
  64 + return setFieldsValue({ type, timezone, daysOfWeek, timeRange: { startsOn, endsOn } });
  65 +
  66 + setFieldsValue(schedule);
  67 + await nextTick();
  68 + unref(customRuleElRef)?.setFieldsValue(schedule.items || []);
  69 + };
  70 +
  71 + const handleOk = async () => {
  72 + const result = await handleGetFieldsValue();
  73 + emit('settingComplete', result, unref(scheduleItemKey));
  74 + closeModal();
  75 + };
  76 +</script>
  77 +
  78 +<template>
  79 + <BasicModal @register="registerModal" title="启用规则" :width="600" @ok="handleOk">
  80 + <BasicForm @register="register" :disabled="disabledDrawer">
  81 + <template #custom="{ model, field }">
  82 + <CustomRule ref="customRuleElRef" v-model:value="model[field]" />
  83 + </template>
  84 + </BasicForm>
  85 + </BasicModal>
  86 +</template>
... ...
  1 +import { ScheduleTypeEnum } from '/@/enums/linkedgeEnum';
  2 +
  3 +export interface CustomRuleItemType {
  4 + dayOfWeek: number;
  5 + enabled: boolean;
  6 + endsOn: number;
  7 + startsOn: number;
  8 +}
  9 +
  10 +export interface ScheduleType {
  11 + type: ScheduleTypeEnum;
  12 + timezone?: string;
  13 + items?: CustomRuleItemType[];
  14 + daysOfWeek?: number[];
  15 + endsOn?: number;
  16 + startsOn?: number;
  17 +}
  18 +
  19 +interface EnableRuleFormModalParams {
  20 + type: ScheduleTypeEnum;
  21 + schedule: ScheduleType;
  22 + key: string;
  23 +}
  24 +
  25 +export type EnableRuleFormModalParamsType = ModalParamsType<EnableRuleFormModalParams>;
... ...
  1 +<script setup lang="ts">
  2 + import { ApiSelect } from '/@/components/Form';
  3 + import { Divider } from 'ant-design-vue';
  4 + import { Icon } from '/@/components/Icon';
  5 + import { VNode } from 'vue';
  6 + import AlarmConfigDrawer from '/@/views/alarm/config/ContactDrawer.vue';
  7 + import { useDrawer } from '/@/components/Drawer';
  8 +
  9 + const [registerAlarmContactDrawer, { openDrawer }] = useDrawer();
  10 +
  11 + const Options = (props: { menuNode: VNode }) => {
  12 + return props.menuNode;
  13 + };
  14 +
  15 + const handleOpenAlarmConfig = () => {
  16 + openDrawer();
  17 + };
  18 +
  19 + const handleSuccess = () => {};
  20 +</script>
  21 +
  22 +<template>
  23 + <section>
  24 + <ApiSelect v-bind="$attrs">
  25 + <template #dropdownRender="{ menuNode }">
  26 + <Options :menuNode="menuNode" />
  27 + <Divider class="!my-2" />
  28 + <div class="cursor-pointer text-gray-500" @click="handleOpenAlarmConfig">
  29 + <Icon icon="ant-design:plus-outlined" class="mx-2" />
  30 + <span>新增告警配置</span>
  31 + </div>
  32 + </template>
  33 + </ApiSelect>
  34 + <AlarmConfigDrawer
  35 + @register="registerAlarmContactDrawer"
  36 + default-enable
  37 + @success="handleSuccess"
  38 + />
  39 + </section>
  40 +</template>
... ...
  1 +import { FormSchema, useComponentRegister } from '/@/components/Form';
  2 +import {
  3 + CommandTypeEnum,
  4 + CommandTypeNameEnum,
  5 + ExecutionActionEnum,
  6 + ExecutionActionNameEnum,
  7 + TriggerEntityTypeEnum,
  8 + TriggerEntityTypeNameEnum,
  9 +} from '/@/enums/linkedgeEnum';
  10 +import AlarmProfileSelect from './AlarmProfileSelect.vue';
  11 +import {
  12 + byOrganizationIdGetMasterDevice,
  13 + getOrganizationAlarmConfig,
  14 +} from '/@/api/ruleengine/ruleengineApi';
  15 +import { useSceneLinkageDrawerContext } from '../SceneLinkageDrawer/sceneLinkageDrawerContext';
  16 +import { Ref, h, toRaw, unref } from 'vue';
  17 +import { AlarmLevelEnum, AlarmLevelNameEnum } from '/@/enums/alarmEnum';
  18 +import Icon from '/@/components/Icon';
  19 +import { Tooltip } from 'ant-design-vue';
  20 +import { findDictItemByCode } from '/@/api/system/dict';
  21 +import { DictEnum } from '/@/enums/dictEnum';
  22 +import { getDeviceProfile } from '/@/api/alarm/position';
  23 +import { createPickerSearch } from '/@/utils/pickerSearch';
  24 +import { ServiceCallTypeEnum, ServiceCallTypeNameEnum } from '/@/enums/toolEnum';
  25 +import { DeviceTypeEnum } from '../../../dataFlow/cpns/config';
  26 +import { getModelServices } from '/@/api/device/modelOfMatter';
  27 +import { ModelOfMatterParams, StructJSON } from '/@/api/device/model/modelOfMatterModel';
  28 +import { DeviceProfileModel } from '/@/api/device/model/deviceModel';
  29 +import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const';
  30 +import { JSONEditor } from '/@/components/CodeEditor';
  31 +import { useJsonParse } from '/@/hooks/business/useJsonParse';
  32 +import { validateTCPCustomCommand } from '/@/components/Form/src/externalCompns/components/ThingsModelForm';
  33 +
  34 +export enum FormFieldsEnum {
  35 + OUT_TARGET = 'outTarget',
  36 + ALARM_PROFILED = 'alarmProfileId',
  37 + ALARM_LEVEL = 'alarmLevel',
  38 + DEVICE_TYPE = 'deviceType',
  39 + DEVICE_PROFILE_ID = 'deviceProfileId',
  40 + ENTITY_TYPE = 'entityType',
  41 + ENTITY_ID = 'entityId',
  42 + COMMAND_TYPE = 'commandType',
  43 + CALL_TYPE = 'callType',
  44 + CALL_SERVICE_IDENTIFIER = 'callServiceIdentifier',
  45 +
  46 + TCP_CUSTOM_COMMAND = 'tcpCustomCommand',
  47 + CUSTOM_COMMAND = 'customCommand',
  48 + CALL_SERVICE = 'callService',
  49 + SERVICE_COMMAND = 'serviceCommand',
  50 + SERVICE_ID = 'serviceId',
  51 + TRANSPORT_TYPE = 'transportType',
  52 + ENABLE_CLEAR_RULE = 'enableClearRule',
  53 + CLEAR_RULE = 'clearRule',
  54 +}
  55 +
  56 +export enum FormFieldsNameEnum {
  57 + OUT_TARGET = '执行动作',
  58 + ALARM_PROFILED = '告警配置',
  59 + ALARM_LEVEL = '告警等级',
  60 + DEVICE_TYPE = '设备类型',
  61 + DEVICE_PROFILE_ID = '产品',
  62 + ENTITY_TYPE = '触发设备类型',
  63 + ENTITY_ID = '设备',
  64 + COMMAND_TYPE = '类型',
  65 + CALL_TYPE = '命令下发类型',
  66 + CALL_SERVICE_IDENTIFIER = '服务',
  67 +
  68 + ENABLE_CLEAR_RULE = '清除告警',
  69 + TCP_CUSTOM_COMMAND = '自定义命令',
  70 + CUSTOM_COMMAND = '自定义命令',
  71 +}
  72 +
  73 +useComponentRegister('AlarmProfileSelect', AlarmProfileSelect);
  74 +useComponentRegister('JSONEditor', JSONEditor);
  75 +
  76 +export const getFormSchemas = (hasAlarmNotify: Ref<boolean>): FormSchema[] => {
  77 + const { organizationId } = useSceneLinkageDrawerContext();
  78 + return [
  79 + {
  80 + field: FormFieldsEnum.OUT_TARGET,
  81 + label: '',
  82 + component: 'Select',
  83 + rules: [{ required: true, message: `请选择${FormFieldsNameEnum.OUT_TARGET}` }],
  84 + defaultValue: ExecutionActionEnum.DEVICE_OUT,
  85 + componentProps: () => {
  86 + return {
  87 + options: [
  88 + { label: ExecutionActionNameEnum.DEVICE_OUT, value: ExecutionActionEnum.DEVICE_OUT },
  89 + {
  90 + label: ExecutionActionNameEnum.MSG_NOTIFY,
  91 + value: ExecutionActionEnum.MSG_NOTIFY,
  92 + disabled: unref(hasAlarmNotify),
  93 + },
  94 + ],
  95 + labelField: 'name',
  96 + valueField: 'id',
  97 + placeholder: `请选择${FormFieldsNameEnum.OUT_TARGET}`,
  98 + onChange(value: ExecutionActionEnum) {
  99 + hasAlarmNotify.value = value === ExecutionActionEnum.MSG_NOTIFY;
  100 + },
  101 + };
  102 + },
  103 + },
  104 + {
  105 + field: FormFieldsEnum.ALARM_PROFILED,
  106 + label: '',
  107 + component: 'AlarmProfileSelect',
  108 + rules: [{ required: true, message: `请选择${FormFieldsEnum.ALARM_PROFILED}` }],
  109 + ifShow: ({ model }) => model[FormFieldsEnum.OUT_TARGET] === ExecutionActionEnum.MSG_NOTIFY,
  110 + componentProps: () => {
  111 + return {
  112 + api: async () => {
  113 + if (!unref(organizationId)) return [];
  114 + return await getOrganizationAlarmConfig({ organizationId: unref(organizationId) });
  115 + },
  116 + labelField: 'name',
  117 + valueField: 'id',
  118 + placeholder: `请选择${FormFieldsNameEnum.ALARM_PROFILED}`,
  119 + };
  120 + },
  121 + },
  122 + {
  123 + field: FormFieldsEnum.ALARM_LEVEL,
  124 + label: '',
  125 + component: 'Select',
  126 + ifShow: ({ model }) => model[FormFieldsEnum.OUT_TARGET] === ExecutionActionEnum.MSG_NOTIFY,
  127 + rules: [{ required: true, message: `请选择${FormFieldsEnum.ALARM_LEVEL}` }],
  128 + componentProps: () => {
  129 + return {
  130 + options: Object.keys(AlarmLevelEnum).map((value) => ({
  131 + label: AlarmLevelNameEnum[value],
  132 + value,
  133 + })),
  134 + placeholder: `请选择${FormFieldsNameEnum.ALARM_LEVEL}`,
  135 + };
  136 + },
  137 + },
  138 + {
  139 + field: FormFieldsEnum.ENABLE_CLEAR_RULE,
  140 + label: '',
  141 + component: 'Checkbox',
  142 + ifShow: ({ model }) => model[FormFieldsEnum.OUT_TARGET] === ExecutionActionEnum.MSG_NOTIFY,
  143 + renderComponentContent: () => ({
  144 + default: () => [
  145 + h('span', FormFieldsNameEnum.ENABLE_CLEAR_RULE),
  146 + h(Tooltip, { title: '清除告警与触发器一一对应' }, () =>
  147 + h(Icon, {
  148 + icon: 'ant-design:question-circle-outlined',
  149 + class: ' cursor-pointer ml-1',
  150 + })
  151 + ),
  152 + ],
  153 + }),
  154 + },
  155 + {
  156 + field: FormFieldsEnum.CLEAR_RULE,
  157 + label: '',
  158 + component: 'Input',
  159 + colProps: { span: 24, xxl: 24, xl: 24, lg: 24, md: 24, sm: 24, xs: 24 },
  160 + slot: 'alarmClearRule',
  161 + ifShow: ({ model }) =>
  162 + model[FormFieldsEnum.ENABLE_CLEAR_RULE] &&
  163 + model[FormFieldsEnum.OUT_TARGET] === ExecutionActionEnum.MSG_NOTIFY,
  164 + },
  165 + {
  166 + field: FormFieldsEnum.DEVICE_TYPE,
  167 + label: '',
  168 + component: 'ApiSelect',
  169 + rules: [{ required: true, message: `请选择${FormFieldsNameEnum.DEVICE_TYPE}` }],
  170 + ifShow: ({ model }) => model[FormFieldsEnum.OUT_TARGET] === ExecutionActionEnum.DEVICE_OUT,
  171 + defaultValue: DeviceTypeEnum.SENSOR,
  172 + componentProps: ({ formActionType }) => {
  173 + return {
  174 + api: findDictItemByCode,
  175 + params: {
  176 + dictCode: DictEnum.DEVICE_TYPE,
  177 + },
  178 + labelField: 'itemText',
  179 + valueField: 'itemValue',
  180 + placeholder: `请选择${FormFieldsNameEnum.DEVICE_TYPE}`,
  181 + onChange() {
  182 + const { setFieldsValue } = formActionType;
  183 + setFieldsValue({
  184 + [FormFieldsEnum.DEVICE_PROFILE_ID]: null,
  185 + [FormFieldsEnum.ENTITY_ID]: [],
  186 + [FormFieldsEnum.CALL_SERVICE_IDENTIFIER]: null,
  187 + [FormFieldsEnum.SERVICE_COMMAND]: null,
  188 + [FormFieldsEnum.CALL_SERVICE]: null,
  189 + [FormFieldsEnum.TRANSPORT_TYPE]: null,
  190 + });
  191 + },
  192 + };
  193 + },
  194 + },
  195 + {
  196 + field: FormFieldsEnum.TRANSPORT_TYPE,
  197 + label: '传输协议',
  198 + component: 'Input',
  199 + ifShow: false,
  200 + },
  201 + {
  202 + field: FormFieldsEnum.DEVICE_PROFILE_ID,
  203 + label: '',
  204 + component: 'ApiSelect',
  205 + rules: [{ required: true, message: `请选择${FormFieldsNameEnum.DEVICE_PROFILE_ID}` }],
  206 + ifShow: ({ model }) => model[FormFieldsEnum.OUT_TARGET] === ExecutionActionEnum.DEVICE_OUT,
  207 + componentProps: ({ formModel, formActionType }) => {
  208 + const deviceType = formModel[FormFieldsEnum.DEVICE_TYPE];
  209 + const { setFieldsValue } = formActionType;
  210 + return {
  211 + api: async () => {
  212 + return await getDeviceProfile(deviceType);
  213 + },
  214 + labelField: 'name',
  215 + valueField: 'id',
  216 + placeholder: `请选择${FormFieldsNameEnum.DEVICE_PROFILE_ID}`,
  217 + ...createPickerSearch(),
  218 + onChange(_value: string, option: DeviceProfileModel) {
  219 + setFieldsValue({
  220 + [FormFieldsEnum.ENTITY_ID]: [],
  221 + [FormFieldsEnum.TRANSPORT_TYPE]: option?.transportType,
  222 + });
  223 + },
  224 + onOptionsChange(options: (DeviceProfileModel & Record<'label' | 'value', string>)[]) {
  225 + const deviceProfileId = formModel[FormFieldsEnum.DEVICE_PROFILE_ID];
  226 + const res = options.find((item) => item.value === deviceProfileId);
  227 + res &&
  228 + setFieldsValue({
  229 + [FormFieldsEnum.TRANSPORT_TYPE]: res.transportType,
  230 + });
  231 + },
  232 + };
  233 + },
  234 + },
  235 + {
  236 + field: FormFieldsEnum.ENTITY_TYPE,
  237 + label: '',
  238 + component: 'Select',
  239 + rules: [{ required: true, message: `请选择${FormFieldsNameEnum.ENTITY_TYPE}` }],
  240 + ifShow: ({ model }) => model[FormFieldsEnum.OUT_TARGET] === ExecutionActionEnum.DEVICE_OUT,
  241 + componentProps: () => {
  242 + return {
  243 + options: Object.keys(TriggerEntityTypeEnum).map((value) => ({
  244 + label: TriggerEntityTypeNameEnum[value],
  245 + value,
  246 + })),
  247 + placeholder: `请选择${FormFieldsNameEnum.ENTITY_TYPE}`,
  248 + };
  249 + },
  250 + },
  251 + {
  252 + field: FormFieldsEnum.ENTITY_ID,
  253 + label: '',
  254 + component: 'ApiSelect',
  255 + rules: [{ required: true, type: 'array', message: `请选择${FormFieldsNameEnum.ENTITY_ID}` }],
  256 + ifShow: ({ model }) =>
  257 + model[FormFieldsEnum.OUT_TARGET] === ExecutionActionEnum.DEVICE_OUT &&
  258 + model[FormFieldsEnum.ENTITY_TYPE] === TriggerEntityTypeEnum.PART,
  259 + componentProps: ({ formModel }) => {
  260 + return {
  261 + api: async (params: Record<'organizationId' | 'deviceProfileId', string>) => {
  262 + if (params.deviceProfileId && params.organizationId) {
  263 + const result = await byOrganizationIdGetMasterDevice(params);
  264 + if (result) {
  265 + return result.map((item) => ({
  266 + ...item,
  267 + label: item.alias || item.name,
  268 + value: item.tbDeviceId,
  269 + }));
  270 + }
  271 + }
  272 + return [];
  273 + },
  274 + mode: 'multiple',
  275 + maxTagCount: 3,
  276 + params: {
  277 + organizationId: unref(organizationId),
  278 + deviceProfileId: formModel[FormFieldsEnum.DEVICE_PROFILE_ID],
  279 + },
  280 + placeholder: `请选择${FormFieldsNameEnum.ENTITY_ID}`,
  281 + ...createPickerSearch(),
  282 + };
  283 + },
  284 + },
  285 + {
  286 + field: FormFieldsEnum.COMMAND_TYPE,
  287 + label: '',
  288 + component: 'Select',
  289 + rules: [
  290 + { required: true, message: `请选择${FormFieldsNameEnum.COMMAND_TYPE}`, type: 'number' },
  291 + ],
  292 + defaultValue: CommandTypeEnum.CUSTOM,
  293 + ifShow: ({ model }) => model[FormFieldsEnum.OUT_TARGET] === ExecutionActionEnum.DEVICE_OUT,
  294 + componentProps: ({ formActionType }) => {
  295 + return {
  296 + options: [
  297 + { label: CommandTypeNameEnum.CUSTOM, value: CommandTypeEnum.CUSTOM },
  298 + { label: CommandTypeNameEnum.SERVICE, value: CommandTypeEnum.SERVICE },
  299 + ],
  300 + placeholder: `请选择${FormFieldsNameEnum.COMMAND_TYPE}`,
  301 + onChange() {
  302 + const { setFieldsValue } = formActionType;
  303 + setFieldsValue({
  304 + [FormFieldsEnum.CALL_SERVICE]: null,
  305 + [FormFieldsEnum.CALL_SERVICE_IDENTIFIER]: null,
  306 + [FormFieldsEnum.SERVICE_COMMAND]: null,
  307 + });
  308 + },
  309 + };
  310 + },
  311 + },
  312 + {
  313 + field: FormFieldsEnum.CALL_TYPE,
  314 + label: '',
  315 + component: 'Select',
  316 + defaultValue: ServiceCallTypeEnum.ASYNC,
  317 + rules: [{ required: true, message: `请选择${FormFieldsNameEnum.CALL_TYPE}` }],
  318 + ifShow: ({ model }) =>
  319 + model[FormFieldsEnum.OUT_TARGET] === ExecutionActionEnum.DEVICE_OUT &&
  320 + model[FormFieldsEnum.COMMAND_TYPE] === CommandTypeEnum.CUSTOM,
  321 + componentProps: () => {
  322 + return {
  323 + options: [
  324 + { label: ServiceCallTypeNameEnum.ASYNC, value: ServiceCallTypeEnum.ASYNC },
  325 + { label: ServiceCallTypeNameEnum.SYNC, value: ServiceCallTypeEnum.SYNC },
  326 + ],
  327 + placeholder: `请选择${FormFieldsNameEnum.CALL_TYPE}`,
  328 + };
  329 + },
  330 + },
  331 + {
  332 + field: FormFieldsEnum.SERVICE_ID,
  333 + label: '',
  334 + component: 'ApiSelect',
  335 + rules: [{ required: true, message: `请选择${FormFieldsNameEnum.CALL_SERVICE_IDENTIFIER}` }],
  336 + ifShow: ({ model }) =>
  337 + model[FormFieldsEnum.OUT_TARGET] === ExecutionActionEnum.DEVICE_OUT &&
  338 + model[FormFieldsEnum.COMMAND_TYPE] === CommandTypeEnum.SERVICE,
  339 + componentProps: ({ formModel, formActionType }) => {
  340 + return {
  341 + api: async () => {
  342 + const deviceProfileId = formModel[FormFieldsEnum.DEVICE_PROFILE_ID];
  343 + if (!deviceProfileId) return [];
  344 + return await getModelServices({ deviceProfileId });
  345 + },
  346 + labelField: 'functionName',
  347 + valueField: 'id',
  348 + placeholder: `请选择${FormFieldsNameEnum.CALL_SERVICE_IDENTIFIER}`,
  349 + onChange(
  350 + value: string,
  351 + option: ModelOfMatterParams & Record<'label' | 'value' | 'id', string>
  352 + ) {
  353 + const { setFieldsValue } = formActionType;
  354 + setFieldsValue({
  355 + [FormFieldsEnum.CALL_TYPE]: value ? option.callType : null,
  356 + [FormFieldsEnum.CALL_SERVICE_IDENTIFIER]: value ? option.identifier : null,
  357 + [FormFieldsEnum.CALL_SERVICE]: value
  358 + ? { ...toRaw(option), id: option?.value, functionName: option?.label }
  359 + : null,
  360 + [FormFieldsEnum.SERVICE_COMMAND]: {},
  361 + });
  362 + },
  363 + onOptionsChange(options: (StructJSON & Record<'label' | 'value' | 'id', string>)[]) {
  364 + const { setFieldsValue } = formActionType;
  365 + const serviceId = formModel[FormFieldsEnum.SERVICE_ID];
  366 + const res = options.find((item) => item.value === serviceId);
  367 + res &&
  368 + setFieldsValue({
  369 + [FormFieldsEnum.CALL_SERVICE_IDENTIFIER]: res.identifier,
  370 + [FormFieldsEnum.CALL_SERVICE]: {
  371 + ...toRaw(res),
  372 + id: res?.value,
  373 + functionName: res?.label,
  374 + },
  375 + });
  376 + },
  377 + };
  378 + },
  379 + },
  380 + {
  381 + field: FormFieldsEnum.CALL_SERVICE_IDENTIFIER,
  382 + label: '服务标识符',
  383 + component: 'Input',
  384 + ifShow: false,
  385 + },
  386 + {
  387 + field: FormFieldsEnum.CALL_SERVICE,
  388 + label: '服务详情',
  389 + component: 'Input',
  390 + ifShow: false,
  391 + },
  392 + {
  393 + field: FormFieldsEnum.SERVICE_COMMAND,
  394 + label: '',
  395 + component: 'Input',
  396 + colProps: { span: 24, xxl: 24, xl: 24, lg: 24, md: 24, sm: 24, xs: 24 },
  397 + ifShow: ({ model }) =>
  398 + model[FormFieldsEnum.OUT_TARGET] === ExecutionActionEnum.DEVICE_OUT &&
  399 + model[FormFieldsEnum.COMMAND_TYPE] === CommandTypeEnum.SERVICE &&
  400 + model[FormFieldsEnum.CALL_SERVICE],
  401 + colSlot: 'serviceCommand',
  402 + },
  403 + {
  404 + field: FormFieldsEnum.CUSTOM_COMMAND,
  405 + label: '',
  406 + component: 'JSONEditor',
  407 + valueField: 'value',
  408 + changeEvent: 'update:value',
  409 + colProps: { span: 24, xxl: 24, xl: 24, lg: 24, md: 24, sm: 24, xs: 24 },
  410 + rules: [
  411 + {
  412 + required: true,
  413 + validator(_rule, value: string) {
  414 + const { flag } = useJsonParse(value);
  415 + return flag ? Promise.resolve() : Promise.reject('请检查自定义命令');
  416 + },
  417 + },
  418 + ],
  419 + ifShow: ({ model }) =>
  420 + model[FormFieldsEnum.OUT_TARGET] === ExecutionActionEnum.DEVICE_OUT &&
  421 + model[FormFieldsEnum.TRANSPORT_TYPE] !== TransportTypeEnum.TCP &&
  422 + model[FormFieldsEnum.COMMAND_TYPE] === CommandTypeEnum.CUSTOM,
  423 + componentProps: () => {
  424 + return {
  425 + title: FormFieldsNameEnum.CUSTOM_COMMAND,
  426 + };
  427 + },
  428 + },
  429 + {
  430 + field: FormFieldsEnum.TCP_CUSTOM_COMMAND,
  431 + label: FormFieldsNameEnum.TCP_CUSTOM_COMMAND,
  432 + component: 'Input',
  433 + colProps: { span: 24, xxl: 24, xl: 24, lg: 24, md: 24, sm: 24, xs: 24 },
  434 + rules: [
  435 + { required: true, message: `请输入${FormFieldsNameEnum.TCP_CUSTOM_COMMAND}` },
  436 + { validator: validateTCPCustomCommand },
  437 + ],
  438 + ifShow: ({ model }) =>
  439 + model[FormFieldsEnum.OUT_TARGET] === ExecutionActionEnum.DEVICE_OUT &&
  440 + model[FormFieldsEnum.TRANSPORT_TYPE] === TransportTypeEnum.TCP &&
  441 + model[FormFieldsEnum.COMMAND_TYPE] === CommandTypeEnum.CUSTOM,
  442 + componentProps: () => {
  443 + return {
  444 + placeholder: `请输入${FormFieldsNameEnum.TCP_CUSTOM_COMMAND}`,
  445 + };
  446 + },
  447 + // labelWidth: 120,
  448 + },
  449 + ];
  450 +};
... ...
  1 +import { ExecutionActionListRefItemType } from './type';
  2 +import { buildUUID } from '/@/utils/uuid';
  3 +
  4 +export { default as ExecutionAction } from './index.vue';
  5 +
  6 +export function createNewExecutionActionItem(): ExecutionActionListRefItemType {
  7 + return { key: buildUUID() };
  8 +}
... ...
  1 +<script setup lang="ts">
  2 + import { CollapseContainer } from '/@/components/Container';
  3 + import { Icon } from '/@/components/Icon';
  4 + import { Tooltip, Button } from 'ant-design-vue';
  5 + import { BasicForm, FormActionType, ThingsModelForm } from '/@/components/Form';
  6 + import { getFormSchemas, FormFieldsEnum } from './config';
  7 + import { FlipFlop } from '../FlipFlop';
  8 + import { ComponentPublicInstance, ref, unref } from 'vue';
  9 + import { ExecutionActionListRefItemType } from './type';
  10 + import { useExecutionActionData } from './useExecutionActionData';
  11 + import { createNewExecutionActionItem } from '.';
  12 + import { useSceneLinkageDrawerContext } from '../SceneLinkageDrawer/sceneLinkageDrawerContext';
  13 +
  14 + const { disabledDrawer } = useSceneLinkageDrawerContext();
  15 +
  16 + const hasAlarmNotify = ref(false);
  17 +
  18 + const formSchemas = getFormSchemas(hasAlarmNotify);
  19 +
  20 + const executionActionListRef = ref<ExecutionActionListRefItemType[]>([
  21 + createNewExecutionActionItem(),
  22 + ]);
  23 +
  24 + const handleAdd = () => {
  25 + executionActionListRef.value.push(createNewExecutionActionItem());
  26 + };
  27 +
  28 + const handleDelete = (executionActionItem: ExecutionActionListRefItemType) => {
  29 + const index = unref(executionActionListRef).findIndex(
  30 + (item) => item.key === executionActionItem.key
  31 + );
  32 + ~index && unref(executionActionListRef).splice(index, 1);
  33 + };
  34 +
  35 + const setExecutionActionRef = (
  36 + el: Nullable<Element | ComponentPublicInstance>,
  37 + executionActionItem: ExecutionActionListRefItemType
  38 + ) => {
  39 + executionActionItem.ref = el as unknown as FormActionType;
  40 + };
  41 +
  42 + const setAlarmClearRuleRef = (
  43 + el: Nullable<Element | ComponentPublicInstance>,
  44 + executionActionItem: ExecutionActionListRefItemType
  45 + ) => {
  46 + executionActionItem.alarmClearRuleElRef = el as unknown as InstanceType<typeof FlipFlop>;
  47 + };
  48 +
  49 + const handleClearRuleDelete = (_executionActionItem: ExecutionActionListRefItemType) => {
  50 + //
  51 + };
  52 +
  53 + const setThingsModelFormRef = (
  54 + el: Nullable<Element | ComponentPublicInstance>,
  55 + executionActionItem: ExecutionActionListRefItemType
  56 + ) => {
  57 + executionActionItem.thingsModelFormRefl = el as unknown as InstanceType<typeof ThingsModelForm>;
  58 + };
  59 +
  60 + const { getFieldsValue, setFieldsValue, validate, resetFieldsValue } = useExecutionActionData(
  61 + executionActionListRef,
  62 + hasAlarmNotify
  63 + );
  64 +
  65 + defineExpose({
  66 + getFieldsValue,
  67 + setFieldsValue,
  68 + validate,
  69 + resetFieldsValue,
  70 + });
  71 +</script>
  72 +
  73 +<template>
  74 + <section>
  75 + <CollapseContainer
  76 + class="mb-4"
  77 + v-for="(executionActionItem, index) in executionActionListRef"
  78 + :title="`执行动作${index + 1}`"
  79 + :key="executionActionItem.key"
  80 + >
  81 + <template #action>
  82 + <Tooltip title="删除">
  83 + <Icon
  84 + v-if="!disabledDrawer"
  85 + class="ml-2 cursor-pointer"
  86 + icon="fluent:delete-off-20-regular"
  87 + size="20"
  88 + @click="handleDelete(executionActionItem)"
  89 + />
  90 + </Tooltip>
  91 + </template>
  92 + <BasicForm
  93 + :ref="(el) => setExecutionActionRef(el, executionActionItem)"
  94 + :disabled="disabledDrawer"
  95 + :show-action-button-group="false"
  96 + :baseColProps="{ span: 6, xxl: 6, xl: 8, lg: 12, sm: 24 }"
  97 + :schemas="formSchemas"
  98 + >
  99 + <template #alarmClearRule>
  100 + <FlipFlop
  101 + :ref="(el) => setAlarmClearRuleRef(el, executionActionItem)"
  102 + :panel-title="(index) => `清除告警${index}`"
  103 + :disabled="disabledDrawer"
  104 + addButtonName="新增清除告警"
  105 + @delete="handleClearRuleDelete(executionActionItem)"
  106 + />
  107 + </template>
  108 +
  109 + <template #serviceCommand="{ field, model }">
  110 + <ThingsModelForm
  111 + :ref="(el) => setThingsModelFormRef(el, executionActionItem)"
  112 + :value="model[field]"
  113 + :identifier="model[FormFieldsEnum.CALL_SERVICE_IDENTIFIER]"
  114 + :input-data="model[FormFieldsEnum.CALL_SERVICE]?.functionJson?.inputData"
  115 + :title="model[FormFieldsEnum.CALL_SERVICE]?.functionName"
  116 + :disabled="disabledDrawer"
  117 + :transport-type="model[FormFieldsEnum.TRANSPORT_TYPE]"
  118 + />
  119 + </template>
  120 + </BasicForm>
  121 + </CollapseContainer>
  122 + <Button v-if="!disabledDrawer" class="w-full" type="primary" @click="handleAdd">
  123 + <Icon icon="ant-design:plus-outlined" />
  124 + 执行动作
  125 + </Button>
  126 + </section>
  127 +</template>
... ...
  1 +export { default as ExecutionAction } from './index.vue';
  2 +import { FlipFlop } from '../FlipFlop';
  3 +import { FormFieldsEnum } from './config';
  4 +import { FlipFlopConditionType } from '../FlipFlop/types';
  5 +import { DeviceModelOfMatterAttrs, DeviceTypeEnum } from '/@/api/device/model/deviceModel';
  6 +import { FormActionType, ThingsModelForm } from '/@/components/Form';
  7 +import { AlarmLevelEnum } from '/@/enums/alarmEnum';
  8 +import { CommandTypeEnum, ExecutionActionEnum, TriggerEntityTypeEnum } from '/@/enums/linkedgeEnum';
  9 +import { ServiceCallTypeEnum } from '/@/enums/toolEnum';
  10 +import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const';
  11 +
  12 +export interface ExecutionActionListRefItemType {
  13 + key: string;
  14 + alarmClearRuleElRef?: InstanceType<typeof FlipFlop>;
  15 + ref?: FormActionType;
  16 + thingsModelFormRefl?: InstanceType<typeof ThingsModelForm>;
  17 +}
  18 +
  19 +export interface ExecutionActionDataItemType {
  20 + callType: ServiceCallTypeEnum;
  21 + commandType: CommandTypeEnum;
  22 + deviceProfileId: string;
  23 + deviceType: DeviceTypeEnum;
  24 + entityType: TriggerEntityTypeEnum;
  25 + outTarget: ExecutionActionEnum;
  26 + deviceId?: string[];
  27 + thingsModelId?: string;
  28 + doContext: DoContextType | DoContextAlarmType;
  29 + alarmProfileId: string;
  30 +}
  31 +
  32 +export interface DoContextType {
  33 + method?: string;
  34 + additionalInfo?: { cmdType: string | number };
  35 + params?: string | Recordable;
  36 +}
  37 +
  38 +export interface DoContextAlarmType {
  39 + alarmLevel: AlarmLevelEnum;
  40 + clearRule: FlipFlopConditionType[];
  41 +}
  42 +
  43 +export interface ExecutionActionFormItemRecordType {
  44 + [FormFieldsEnum.ALARM_LEVEL]?: AlarmLevelEnum;
  45 + [FormFieldsEnum.ALARM_PROFILED]?: string;
  46 + [FormFieldsEnum.CALL_TYPE]?: ServiceCallTypeEnum;
  47 + [FormFieldsEnum.COMMAND_TYPE]?: CommandTypeEnum;
  48 + [FormFieldsEnum.DEVICE_TYPE]?: DeviceTypeEnum;
  49 + [FormFieldsEnum.DEVICE_PROFILE_ID]?: string;
  50 + [FormFieldsEnum.OUT_TARGET]?: ExecutionActionEnum;
  51 + [FormFieldsEnum.TCP_CUSTOM_COMMAND]?: string;
  52 + [FormFieldsEnum.CUSTOM_COMMAND]?: string;
  53 + [FormFieldsEnum.TRANSPORT_TYPE]?: TransportTypeEnum;
  54 + [FormFieldsEnum.ENTITY_TYPE]?: TriggerEntityTypeEnum;
  55 + [FormFieldsEnum.ENTITY_ID]?: string[];
  56 + [FormFieldsEnum.CALL_SERVICE_IDENTIFIER]?: string;
  57 + [FormFieldsEnum.CALL_SERVICE]?: DeviceModelOfMatterAttrs;
  58 + [FormFieldsEnum.SERVICE_ID]?: string;
  59 +}
... ...
  1 +import { Ref, unref, nextTick, toRaw } from 'vue';
  2 +import { DefineComponentsBasicExpose } from '/#/utils';
  3 +import {
  4 + DoContextAlarmType,
  5 + DoContextType,
  6 + ExecutionActionDataItemType,
  7 + ExecutionActionFormItemRecordType,
  8 + ExecutionActionListRefItemType,
  9 +} from './type';
  10 +import { CommandTypeEnum, ExecutionActionEnum } from '/@/enums/linkedgeEnum';
  11 +import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const';
  12 +import { useJsonParse } from '/@/hooks/business/useJsonParse';
  13 +import { createNewExecutionActionItem } from '.';
  14 +import { FormFieldsEnum } from './config';
  15 +import { isObject, isString } from '/@/utils/is';
  16 +
  17 +export function useExecutionActionData(
  18 + executionActionListRef: Ref<ExecutionActionListRefItemType[]>,
  19 + hasAlarmNotify: Ref<boolean>
  20 +): DefineComponentsBasicExpose<ExecutionActionDataItemType[]> {
  21 + const getExecutionActionItemData = (executionActionItem: ExecutionActionListRefItemType) => {
  22 + const { ref, alarmClearRuleElRef, thingsModelFormRefl } = executionActionItem;
  23 + const {
  24 + callType,
  25 + commandType,
  26 + deviceType,
  27 + deviceProfileId,
  28 + entityType,
  29 + entityId,
  30 + outTarget,
  31 + transportType,
  32 + alarmLevel,
  33 + callServiceIdentifier,
  34 + serviceId,
  35 + tcpCustomCommand,
  36 + customCommand,
  37 + alarmProfileId,
  38 + } = (ref?.getFieldsValue?.() || {}) as Required<ExecutionActionFormItemRecordType>;
  39 +
  40 + const thingsModelFormRecord = thingsModelFormRefl?.getFieldsValue?.();
  41 +
  42 + const clearRule = alarmClearRuleElRef?.getFieldsValue();
  43 +
  44 + const record: ExecutionActionDataItemType = {
  45 + callType,
  46 + commandType,
  47 + alarmProfileId,
  48 + deviceType,
  49 + deviceProfileId,
  50 + entityType,
  51 + deviceId: entityId,
  52 + outTarget,
  53 + thingsModelId: serviceId,
  54 + doContext: {},
  55 + };
  56 +
  57 + if (outTarget === ExecutionActionEnum.DEVICE_OUT) {
  58 + record.doContext = {
  59 + method: 'methodThingskit',
  60 + additionalInfo: { cmdType: commandType },
  61 + params:
  62 + commandType === CommandTypeEnum.CUSTOM
  63 + ? transportType === TransportTypeEnum.TCP
  64 + ? tcpCustomCommand
  65 + : useJsonParse(customCommand).value
  66 + : {
  67 + [callServiceIdentifier]: thingsModelFormRecord,
  68 + },
  69 + };
  70 + } else {
  71 + record.doContext = {
  72 + alarmLevel,
  73 + clearRule: clearRule || [],
  74 + };
  75 + }
  76 +
  77 + return record;
  78 + };
  79 +
  80 + const getFieldsValue = () => {
  81 + const dataList: ExecutionActionDataItemType[] = [];
  82 + for (const executionActionItem of unref(executionActionListRef)) {
  83 + dataList.push(getExecutionActionItemData(executionActionItem));
  84 + }
  85 +
  86 + return dataList;
  87 + };
  88 +
  89 + const setFieldsValue = (executionActionData: ExecutionActionDataItemType[]) => {
  90 + executionActionListRef.value = Array.from({ length: executionActionData.length }, () =>
  91 + createNewExecutionActionItem()
  92 + );
  93 +
  94 + nextTick(() => {
  95 + unref(executionActionListRef).forEach((executionActionItem, index) => {
  96 + const result = executionActionData[index];
  97 +
  98 + const { doContext, outTarget, commandType } = result;
  99 +
  100 + const { thingsModelId, deviceId } = result;
  101 +
  102 + const record = {
  103 + ...result,
  104 + [FormFieldsEnum.SERVICE_ID]: thingsModelId,
  105 + [FormFieldsEnum.ENTITY_ID]: deviceId,
  106 + };
  107 +
  108 + if (outTarget === ExecutionActionEnum.MSG_NOTIFY) {
  109 + const { alarmLevel, clearRule } = doContext as DoContextAlarmType;
  110 + Object.assign(record, {
  111 + [FormFieldsEnum.ALARM_LEVEL]: alarmLevel,
  112 + [FormFieldsEnum.ENABLE_CLEAR_RULE]: !!(clearRule && clearRule.length),
  113 + });
  114 + hasAlarmNotify.value = true;
  115 + }
  116 +
  117 + if (outTarget == ExecutionActionEnum.DEVICE_OUT && (doContext as DoContextType)?.params) {
  118 + const { params } = doContext as DoContextType;
  119 + Object.assign(record, {
  120 + [isString(params) ? FormFieldsEnum.TCP_CUSTOM_COMMAND : FormFieldsEnum.CUSTOM_COMMAND]:
  121 + params,
  122 + });
  123 + }
  124 +
  125 + if (
  126 + outTarget === ExecutionActionEnum.DEVICE_OUT &&
  127 + commandType === CommandTypeEnum.SERVICE
  128 + ) {
  129 + const { params } = doContext as DoContextType;
  130 + if (isObject(params)) {
  131 + const [identifier] = Object.keys(params);
  132 + const result = params[identifier];
  133 + Object.assign(record, { [FormFieldsEnum.SERVICE_COMMAND]: toRaw(result || {}) });
  134 + executionActionItem.thingsModelFormRefl?.setFieldsValue(result || {});
  135 + }
  136 + }
  137 +
  138 + executionActionItem.ref?.setFieldsValue(record);
  139 +
  140 + if (
  141 + outTarget === ExecutionActionEnum.MSG_NOTIFY &&
  142 + (doContext as DoContextAlarmType).clearRule &&
  143 + (doContext as DoContextAlarmType).clearRule.length
  144 + ) {
  145 + nextTick(() => {
  146 + executionActionItem.alarmClearRuleElRef?.setFieldsValue(
  147 + (result.doContext as DoContextAlarmType).clearRule
  148 + );
  149 + });
  150 + }
  151 + });
  152 + });
  153 + };
  154 +
  155 + const validate = async () => {
  156 + for (const executionActionItem of unref(executionActionListRef)) {
  157 + const { ref, alarmClearRuleElRef, thingsModelFormRefl } = executionActionItem;
  158 + await ref?.validate?.();
  159 + await alarmClearRuleElRef?.validate?.();
  160 + await thingsModelFormRefl?.validate?.();
  161 + }
  162 + };
  163 +
  164 + const resetFieldsValue = () => {
  165 + executionActionListRef.value = [createNewExecutionActionItem()];
  166 + hasAlarmNotify.value = false;
  167 + };
  168 +
  169 + return {
  170 + getFieldsValue,
  171 + setFieldsValue,
  172 + validate,
  173 + resetFieldsValue,
  174 + };
  175 +}
... ...
  1 +<script setup lang="ts">
  2 + import { InputGroup, InputNumber, Select } from 'ant-design-vue';
  3 + import { TriggerUnitEnum } from '/@/enums/linkedgeEnum';
  4 +
  5 + withDefaults(
  6 + defineProps<{
  7 + value?: number;
  8 + unit?: TriggerUnitEnum;
  9 + unitOptions: Record<'label' | 'value', any>[];
  10 + disabled?: boolean;
  11 + }>(),
  12 + {
  13 + value: undefined,
  14 + unit: TriggerUnitEnum.SECONDS,
  15 + unitOptions: () => [],
  16 + }
  17 + );
  18 + const emit = defineEmits(['unitChange', 'update:unit', 'update:value']);
  19 +
  20 + const handleUnitChange = (value: TriggerUnitEnum) => {
  21 + emit('update:unit', value);
  22 + emit('unitChange', value);
  23 + };
  24 +</script>
  25 +
  26 +<template>
  27 + <InputGroup class="!flex" compact>
  28 + <InputNumber
  29 + :disabled="disabled"
  30 + :value="value"
  31 + :min="0"
  32 + @change="$emit('update:value', $event)"
  33 + v-bind="$attrs"
  34 + />
  35 + <Select
  36 + :disabled="disabled"
  37 + :value="unit"
  38 + :options="unitOptions"
  39 + :allow-clear="false"
  40 + @change="handleUnitChange"
  41 + />
  42 + </InputGroup>
  43 +</template>
... ...
  1 +import { unref } from 'vue';
  2 +import { useSceneLinkageDrawerContext } from '../SceneLinkageDrawer/sceneLinkageDrawerContext';
  3 +import { getDeviceProfile } from '/@/api/alarm/position';
  4 +import { byOrganizationIdGetMasterDevice } from '/@/api/ruleengine/ruleengineApi';
  5 +import { findDictItemByCode } from '/@/api/system/dict';
  6 +import { FormSchema, useComponentRegister } from '/@/components/Form';
  7 +import { DictEnum } from '/@/enums/dictEnum';
  8 +import { createPickerSearch } from '/@/utils/pickerSearch';
  9 +import { DeviceTypeEnum } from '../../../dataFlow/cpns/config';
  10 +import { getDeviceAttributes } from '/@/api/dataBoard';
  11 +import { DeviceAttributeParams } from '/@/api/dataBoard/model';
  12 +import {
  13 + TriggerValueTypeEnum,
  14 + TriggerValueTypeNameEnum,
  15 + TriggerEntityTypeEnum,
  16 + TriggerEntityTypeNameEnum,
  17 + TriggerTypeEnum,
  18 + TriggerTypeNameEnum,
  19 + DeviceTriggerTypeEum,
  20 + DeviceTriggerTypeNameEum,
  21 + FlipFlopTypeEnum,
  22 + FlipFlopTypeNameEnum,
  23 + TriggerUnitEnum,
  24 + TriggerUnitNameEnum,
  25 +} from '/@/enums/linkedgeEnum';
  26 +import { DeviceModelOfMatterAttrs } from '/@/api/device/model/deviceModel';
  27 +import TriggerDurationInput from './TriggerDurationInput.vue';
  28 +import { DataTypeEnum } from '/@/enums/objectModelEnum';
  29 +
  30 +export enum FormFieldEnum {
  31 + FLIP_FLOP_TYPE = 'flipFlopType',
  32 + DEVICE_TYPE = 'deviceType',
  33 + DEVICE_PROFILE_ID = 'deviceProfileId',
  34 + ENTITY_TYPE = 'entityType',
  35 + ENTITY_ID = 'entityId',
  36 + TRIGGER_TYPE = 'triggerType',
  37 + CONDITION_TYPE = 'conditionType',
  38 + CONDITION_KEY = 'conditionKey',
  39 + CONDITION_VALUE_TYPE = 'conditionValueType',
  40 + TRIGGER_DURATION_UNIT = 'triggerDurationUnit',
  41 + TRIGGER_DURATION_VALUE = 'triggerDurationValue',
  42 + TRIGGER_REPEAT_COUNT = 'triggerRepeatCount',
  43 +
  44 + CONDITION_KEY_DETAIL = 'conditionKeyDetail',
  45 +}
  46 +
  47 +export enum FormFieldNameEnum {
  48 + FLIP_FLOP_TYPE = '触发类型',
  49 + DEVICE_TYPE = '设备类型',
  50 + DEVICE_PROFILE_ID = '产品',
  51 + ENTITY_TYPE = '触发设备类型',
  52 + ENTITY_ID = '设备',
  53 + TRIGGER_TYPE = '触发方式',
  54 + CONDITION_TYPE = '触发条件',
  55 + CONDITION_KEY = '属性',
  56 + CONDITION_VALUE_TYPE = '比较类型',
  57 + TRIGGER_DURATION_VALUE = '持续时长',
  58 + TRIGGER_REPEAT_COUNT = '重复次数',
  59 +}
  60 +
  61 +useComponentRegister('TriggerDurationInput', TriggerDurationInput);
  62 +
  63 +function getTriggerValueTypeByThingsModelType(type: DataTypeEnum) {
  64 + const map = {
  65 + [DataTypeEnum.BOOL]: TriggerValueTypeEnum.BOOLEAN,
  66 + [DataTypeEnum.NUMBER_DOUBLE]: TriggerValueTypeEnum.NUMERIC,
  67 + [DataTypeEnum.NUMBER_INT]: TriggerValueTypeEnum.NUMERIC,
  68 + [DataTypeEnum.STRING]: TriggerValueTypeEnum.STRING,
  69 + [DataTypeEnum.STRUCT]: TriggerValueTypeEnum.STRING,
  70 + };
  71 +
  72 + return map[type];
  73 +}
  74 +
  75 +export const getFormSchemas = (): FormSchema[] => {
  76 + const { organizationId } = useSceneLinkageDrawerContext();
  77 +
  78 + return [
  79 + {
  80 + field: FormFieldEnum.FLIP_FLOP_TYPE,
  81 + label: '',
  82 + component: 'Select',
  83 + rules: [{ required: true, message: `请选择${FormFieldNameEnum.FLIP_FLOP_TYPE}` }],
  84 + defaultValue: FlipFlopTypeEnum.SIMPLE,
  85 + componentProps: () => {
  86 + return {
  87 + options: Object.keys(FlipFlopTypeEnum).map((value) => ({
  88 + label: FlipFlopTypeNameEnum[value],
  89 + value,
  90 + })),
  91 + placeholder: `请选择${FormFieldNameEnum.FLIP_FLOP_TYPE}`,
  92 + };
  93 + },
  94 + },
  95 + {
  96 + field: FormFieldEnum.DEVICE_TYPE,
  97 + label: '',
  98 + component: 'ApiSelect',
  99 + defaultValue: DeviceTypeEnum.SENSOR,
  100 + rules: [{ required: true, message: `请选择${FormFieldNameEnum.DEVICE_TYPE}` }],
  101 + componentProps: ({ formActionType }) => {
  102 + const { setFieldsValue } = formActionType;
  103 + return {
  104 + api: findDictItemByCode,
  105 + params: {
  106 + dictCode: DictEnum.DEVICE_TYPE,
  107 + },
  108 + labelField: 'itemText',
  109 + valueField: 'itemValue',
  110 + placeholder: `请选择${FormFieldNameEnum.DEVICE_TYPE}`,
  111 + onChange() {
  112 + setFieldsValue({
  113 + [FormFieldEnum.DEVICE_PROFILE_ID]: null,
  114 + [FormFieldEnum.ENTITY_ID]: [],
  115 + [FormFieldEnum.CONDITION_KEY]: null,
  116 + });
  117 + },
  118 + };
  119 + },
  120 + },
  121 + {
  122 + field: FormFieldEnum.DEVICE_PROFILE_ID,
  123 + label: '',
  124 + component: 'ApiSelect',
  125 + rules: [{ required: true, message: `请选择${FormFieldNameEnum.DEVICE_PROFILE_ID}` }],
  126 + componentProps: ({ formModel, formActionType }) => {
  127 + const deviceType = formModel[FormFieldEnum.DEVICE_TYPE];
  128 + const { setFieldsValue } = formActionType;
  129 + return {
  130 + api: async () => {
  131 + return await getDeviceProfile(deviceType);
  132 + },
  133 + labelField: 'name',
  134 + valueField: 'id',
  135 + placeholder: `请选择${FormFieldNameEnum.DEVICE_PROFILE_ID}`,
  136 + ...createPickerSearch(),
  137 + onChange() {
  138 + setFieldsValue({
  139 + [FormFieldEnum.ENTITY_ID]: [],
  140 + [FormFieldEnum.CONDITION_KEY]: null,
  141 + });
  142 + },
  143 + };
  144 + },
  145 + },
  146 + {
  147 + field: FormFieldEnum.ENTITY_TYPE,
  148 + label: '',
  149 + component: 'Select',
  150 + rules: [{ required: true, message: `请选择${FormFieldNameEnum.ENTITY_TYPE}` }],
  151 + componentProps: () => {
  152 + return {
  153 + options: Object.keys(TriggerEntityTypeEnum).map((value) => ({
  154 + label: TriggerEntityTypeNameEnum[value],
  155 + value,
  156 + })),
  157 + placeholder: `请选择${FormFieldNameEnum.ENTITY_TYPE}`,
  158 + };
  159 + },
  160 + },
  161 + {
  162 + field: FormFieldEnum.TRIGGER_DURATION_UNIT,
  163 + label: '',
  164 + component: 'Input',
  165 + ifShow: false,
  166 + defaultValue: TriggerUnitEnum.SECONDS,
  167 + },
  168 + {
  169 + field: FormFieldEnum.TRIGGER_DURATION_VALUE,
  170 + label: '',
  171 + component: 'TriggerDurationInput',
  172 + rules: [
  173 + {
  174 + required: true,
  175 + message: `请输入${FormFieldNameEnum.TRIGGER_DURATION_VALUE}`,
  176 + type: 'number',
  177 + },
  178 + ],
  179 + changeEvent: 'update:value',
  180 + ifShow: ({ model }) => model[FormFieldEnum.FLIP_FLOP_TYPE] === FlipFlopTypeEnum.DURATION,
  181 + componentProps: ({ formActionType, formModel }) => {
  182 + const { setFieldsValue } = formActionType;
  183 + return {
  184 + placeholder: `请输入${FormFieldNameEnum.TRIGGER_DURATION_VALUE}`,
  185 + unit: formModel[FormFieldEnum.TRIGGER_DURATION_UNIT],
  186 + unitOptions: Object.values(TriggerUnitEnum).map((value) => ({
  187 + label: TriggerUnitNameEnum[value],
  188 + value,
  189 + })),
  190 + onUnitChange(value: TriggerUnitEnum) {
  191 + setFieldsValue({ [FormFieldEnum.TRIGGER_DURATION_UNIT]: value });
  192 + },
  193 + };
  194 + },
  195 + },
  196 + {
  197 + field: FormFieldEnum.TRIGGER_REPEAT_COUNT,
  198 + label: '',
  199 + component: 'InputNumber',
  200 + rules: [{ required: true, message: `请输入${FormFieldNameEnum.TRIGGER_REPEAT_COUNT}` }],
  201 + ifShow: ({ model }) => model[FormFieldEnum.FLIP_FLOP_TYPE] === FlipFlopTypeEnum.REPEATING,
  202 + componentProps: {
  203 + placeholder: `请输入${FormFieldNameEnum.TRIGGER_REPEAT_COUNT}`,
  204 + min: 0,
  205 + },
  206 + },
  207 + {
  208 + field: FormFieldEnum.ENTITY_ID,
  209 + label: '',
  210 + component: 'ApiSelect',
  211 + rules: [{ required: true, type: 'array', message: `请选择${FormFieldNameEnum.ENTITY_ID}` }],
  212 + ifShow: ({ model }) => model[FormFieldEnum.ENTITY_TYPE] === TriggerEntityTypeEnum.PART,
  213 + componentProps: ({ formModel }) => {
  214 + return {
  215 + api: async (params: Record<'organizationId' | 'deviceProfileId', string>) => {
  216 + if (params.deviceProfileId && params.organizationId) {
  217 + const result = await byOrganizationIdGetMasterDevice(params);
  218 + if (result) {
  219 + return result.map((item) => ({
  220 + ...item,
  221 + label: item.alias || item.name,
  222 + value: item.tbDeviceId,
  223 + }));
  224 + }
  225 + }
  226 + return [];
  227 + },
  228 + mode: 'multiple',
  229 + maxTagCount: 3,
  230 + params: {
  231 + organizationId: unref(organizationId),
  232 + deviceProfileId: formModel[FormFieldEnum.DEVICE_PROFILE_ID],
  233 + },
  234 + placeholder: `请选择${FormFieldNameEnum.ENTITY_ID}`,
  235 + ...createPickerSearch(),
  236 + };
  237 + },
  238 + },
  239 + {
  240 + field: FormFieldEnum.TRIGGER_TYPE,
  241 + label: '',
  242 + component: 'Select',
  243 + rules: [{ required: true, message: `请选择${FormFieldNameEnum.TRIGGER_TYPE}` }],
  244 + defaultValue: TriggerTypeEnum.DEVICE_TRIGGER,
  245 + componentProps: () => {
  246 + return {
  247 + options: [
  248 + { label: TriggerTypeNameEnum.DEVICE_TRIGGER, value: TriggerTypeEnum.DEVICE_TRIGGER },
  249 + ],
  250 + };
  251 + },
  252 + },
  253 + {
  254 + field: FormFieldEnum.CONDITION_TYPE,
  255 + label: '',
  256 + component: 'Select',
  257 + rules: [{ required: true, message: `请选择${FormFieldNameEnum.CONDITION_TYPE}` }],
  258 + defaultValue: DeviceTriggerTypeEum.TIME_SERIES,
  259 + componentProps: () => {
  260 + return {
  261 + options: [
  262 + {
  263 + label: DeviceTriggerTypeNameEum.TIME_SERIES,
  264 + value: DeviceTriggerTypeEum.TIME_SERIES,
  265 + },
  266 + ],
  267 + };
  268 + },
  269 + },
  270 + {
  271 + field: FormFieldEnum.CONDITION_KEY_DETAIL,
  272 + label: '',
  273 + component: 'Input',
  274 + ifShow: false,
  275 + },
  276 + {
  277 + field: FormFieldEnum.CONDITION_KEY,
  278 + label: '',
  279 + component: 'ApiSelect',
  280 + rules: [{ required: true, message: `请选择${FormFieldNameEnum.CONDITION_KEY}` }],
  281 + componentProps: ({ formModel, formActionType }) => {
  282 + const { setFieldsValue } = formActionType;
  283 + return {
  284 + api: async (params: DeviceAttributeParams) => {
  285 + if (params.deviceProfileId) {
  286 + return await getDeviceAttributes(params);
  287 + }
  288 + return [];
  289 + },
  290 + labelField: 'name',
  291 + valueField: 'identifier',
  292 + params: {
  293 + deviceProfileId: formModel[FormFieldEnum.DEVICE_PROFILE_ID],
  294 + },
  295 + placeholder: `请选择${FormFieldNameEnum.CONDITION_KEY}`,
  296 + onChange(
  297 + value: string,
  298 + option: DeviceModelOfMatterAttrs & Record<'label' | 'value', string>
  299 + ) {
  300 + setFieldsValue({
  301 + [FormFieldEnum.CONDITION_KEY_DETAIL]: value
  302 + ? { ...option, identifier: option?.value, name: option?.label }
  303 + : {},
  304 + [FormFieldEnum.CONDITION_VALUE_TYPE]:
  305 + value && option?.detail?.dataType?.type
  306 + ? getTriggerValueTypeByThingsModelType(option.detail.dataType?.type)
  307 + : null,
  308 + });
  309 + },
  310 + onOptionsChange(
  311 + options: (DeviceModelOfMatterAttrs & Record<'label' | 'value', string>)[]
  312 + ) {
  313 + const conditionKey = formModel[FormFieldEnum.CONDITION_KEY];
  314 + const res = options.find((item) => item.value === conditionKey);
  315 + res &&
  316 + setFieldsValue({
  317 + [FormFieldEnum.CONDITION_KEY_DETAIL]: {
  318 + ...res,
  319 + identifier: res?.value,
  320 + name: res?.label,
  321 + },
  322 + });
  323 + },
  324 + };
  325 + },
  326 + },
  327 + {
  328 + field: FormFieldEnum.CONDITION_VALUE_TYPE,
  329 + label: '',
  330 + component: 'Select',
  331 + rules: [{ required: true, message: `请选择${FormFieldNameEnum.CONDITION_VALUE_TYPE}` }],
  332 + componentProps: () => {
  333 + return {
  334 + options: Object.keys(TriggerValueTypeEnum).map((value) => ({
  335 + label: TriggerValueTypeNameEnum[value],
  336 + value,
  337 + })),
  338 + placeholder: `请选择${FormFieldNameEnum.CONDITION_VALUE_TYPE}`,
  339 + };
  340 + },
  341 + },
  342 + {
  343 + field: 'conditionFilter',
  344 + label: '',
  345 + component: 'Input',
  346 + colProps: { span: 24, xxl: 24, xl: 24, lg: 24, md: 24, sm: 24, xs: 24 },
  347 + ifShow: ({ model }) =>
  348 + model[FormFieldEnum.CONDITION_KEY] && model[FormFieldEnum.CONDITION_VALUE_TYPE],
  349 + colSlot: 'conditionFilter',
  350 + },
  351 + ];
  352 +};
... ...
  1 +import { FlipFlopItemType } from './types';
  2 +import { ScheduleTypeEnum } from '/@/enums/linkedgeEnum';
  3 +import { buildUUID } from '/@/utils/uuid';
  4 +
  5 +export { default as FlipFlop } from './index.vue';
  6 +
  7 +export function createNewFlipFlopItem(): FlipFlopItemType {
  8 + return { key: buildUUID(), schedule: { type: ScheduleTypeEnum.ANY_TIME } };
  9 +}
... ...
  1 +<script setup lang="ts">
  2 + import { ComponentPublicInstance, ref, unref, watch } from 'vue';
  3 + import { Tooltip, Button } from 'ant-design-vue';
  4 + import { CollapseContainer } from '/@/components/Container';
  5 + import { Icon } from '/@/components/Icon';
  6 + import { FlipFlopItemType, ScheduleOptionItemType } from './types';
  7 + import { BasicForm, FormActionType } from '/@/components/Form';
  8 + import { getFormSchemas, FormFieldEnum } from './config';
  9 + import { useSceneLinkageDrawerContext } from '../SceneLinkageDrawer/sceneLinkageDrawerContext';
  10 + import { ScheduleTypeEnum, ScheduleTypeNameEnum } from '/@/enums/linkedgeEnum';
  11 + import { ConditionFilter } from '../ConditionFilter';
  12 + import { EnablingRule } from '../EnablingRule';
  13 + import { useModal } from '/@/components/Modal';
  14 + import { DataActionModeEnum } from '/@/enums/toolEnum';
  15 + import { EnableRuleFormModalParamsType, ScheduleType } from '../EnablingRule/type';
  16 + import { useFlipFlopData } from './useFlipFlopData';
  17 + import { createNewFlipFlopItem } from '.';
  18 +
  19 + const { disabledDrawer } = useSceneLinkageDrawerContext();
  20 +
  21 + const emit = defineEmits(['delete']);
  22 +
  23 + const props = withDefaults(
  24 + defineProps<{
  25 + addButtonName?: string;
  26 + defaultNull?: boolean;
  27 + panelTitle?: (index: number) => string;
  28 + showAddButton?: boolean;
  29 + disabled?: boolean;
  30 + }>(),
  31 + {
  32 + addButtonName: '触发器 (OR)',
  33 + defaultNull: false,
  34 + panelTitle: (index: number) => `触发器${index}`,
  35 + showAddButton: true,
  36 + disabled: false,
  37 + }
  38 + );
  39 +
  40 + const formSchemas = getFormSchemas();
  41 +
  42 + const [registerModal, { openModal }] = useModal();
  43 +
  44 + const handleOpenModal = (type: ScheduleTypeEnum, flipFlopItem: FlipFlopItemType) => {
  45 + if (unref(disabledDrawer) && type !== flipFlopItem.schedule.type) return;
  46 +
  47 + if (type === ScheduleTypeEnum.ANY_TIME) {
  48 + flipFlopItem.schedule = { type };
  49 + return;
  50 + }
  51 +
  52 + openModal(true, {
  53 + mode: DataActionModeEnum.CREATE,
  54 + record: {
  55 + type,
  56 + schedule: flipFlopItem.schedule,
  57 + key: flipFlopItem.key,
  58 + },
  59 + } as EnableRuleFormModalParamsType);
  60 + };
  61 +
  62 + const handleRuleSetting = (params: ScheduleType, key: string) => {
  63 + const index = unref(flipFlopListElRef).findIndex((flipFlopItem) => flipFlopItem.key === key);
  64 +
  65 + ~index && (unref(flipFlopListElRef)[index].schedule = params);
  66 + };
  67 +
  68 + const scheduleOptions: ScheduleOptionItemType[] = Object.keys(ScheduleTypeEnum).map((value) => ({
  69 + label: ScheduleTypeNameEnum[value],
  70 + value: value as ScheduleTypeEnum,
  71 + }));
  72 +
  73 + const { organizationId } = useSceneLinkageDrawerContext();
  74 +
  75 + const flipFlopListElRef = ref<FlipFlopItemType[]>(
  76 + props.defaultNull ? [] : [createNewFlipFlopItem()]
  77 + );
  78 +
  79 + /**
  80 + * @description on organization change
  81 + */
  82 + watch(organizationId, () => {
  83 + unref(flipFlopListElRef).forEach((flipFlopItem) =>
  84 + flipFlopItem.ref?.setFieldsValue({ [FormFieldEnum.ENTITY_ID]: [] })
  85 + );
  86 + });
  87 +
  88 + const setFlopFlipFormRef = (
  89 + flipFlopItem: FlipFlopItemType,
  90 + el: Nullable<Element | ComponentPublicInstance>
  91 + ) => {
  92 + flipFlopItem.ref = el as unknown as FormActionType;
  93 + };
  94 +
  95 + const setFlopFlipConditionRef = (
  96 + flipFlopItem: FlipFlopItemType,
  97 + el: Nullable<Element | ComponentPublicInstance>
  98 + ) => {
  99 + flipFlopItem.conditionRef = el as unknown as InstanceType<typeof ConditionFilter>;
  100 + };
  101 +
  102 + const handleDelete = (flipFlopItem: FlipFlopItemType) => {
  103 + const index = unref(flipFlopListElRef).findIndex((item) => item.key === flipFlopItem.key);
  104 + ~index && unref(flipFlopListElRef).splice(index, 1);
  105 + emit('delete');
  106 + };
  107 +
  108 + const handleAdd = () => {
  109 + flipFlopListElRef.value.push(createNewFlipFlopItem());
  110 + };
  111 +
  112 + const { getFieldsValue, setFieldsValue, validate, resetFieldsValue } =
  113 + useFlipFlopData(flipFlopListElRef);
  114 +
  115 + defineExpose({ getFieldsValue, setFieldsValue, validate, resetFieldsValue });
  116 +</script>
  117 +
  118 +<template>
  119 + <section>
  120 + <CollapseContainer
  121 + v-for="(flipFlopItem, index) in flipFlopListElRef"
  122 + :key="flipFlopItem.key"
  123 + :title="panelTitle(index + 1)"
  124 + class="mb-4"
  125 + >
  126 + <template #action>
  127 + <header class="flex items-center justify-between">
  128 + <div class="flex items-center">
  129 + <div class="flex">
  130 + <span class="mr-2">启用规则:</span>
  131 + <div
  132 + class="px-1 cursor-pointer"
  133 + v-for="schedule in scheduleOptions"
  134 + :key="schedule.value"
  135 + type="text"
  136 + :class="flipFlopItem.schedule.type === schedule.value ? 'text-blue-400' : ''"
  137 + @click="handleOpenModal(schedule.value, flipFlopItem)"
  138 + >
  139 + {{ schedule.label }}
  140 + </div>
  141 + </div>
  142 + <Tooltip title="删除">
  143 + <Icon
  144 + v-if="!disabledDrawer"
  145 + class="ml-2 cursor-pointer"
  146 + icon="fluent:delete-off-20-regular"
  147 + size="20"
  148 + @click="handleDelete(flipFlopItem)"
  149 + />
  150 + </Tooltip>
  151 + </div>
  152 + </header>
  153 + </template>
  154 + <BasicForm
  155 + :ref="(el) => setFlopFlipFormRef(flipFlopItem, el)"
  156 + :key="flipFlopItem.key"
  157 + class="trigger-form"
  158 + layout="horizontal"
  159 + :disabled="disabledDrawer"
  160 + :schemas="formSchemas"
  161 + :showActionButtonGroup="false"
  162 + :baseColProps="{ span: 6, xxl: 6, xl: 8, lg: 12, sm: 24 }"
  163 + >
  164 + <template #conditionFilter="{ model }">
  165 + <ConditionFilter
  166 + :ref="(el) => setFlopFlipConditionRef(flipFlopItem, el)"
  167 + :triggerType="model[FormFieldEnum.CONDITION_VALUE_TYPE]"
  168 + :object-model="model[FormFieldEnum.CONDITION_KEY_DETAIL]"
  169 + :condition-type="model[FormFieldEnum.CONDITION_TYPE]"
  170 + />
  171 + </template>
  172 + </BasicForm>
  173 + </CollapseContainer>
  174 + <Button
  175 + v-if="showAddButton && !disabledDrawer"
  176 + type="primary"
  177 + class="w-full"
  178 + @click="handleAdd"
  179 + >
  180 + <Icon icon="ant-design:plus-outlined" />
  181 + {{ addButtonName }}
  182 + </Button>
  183 +
  184 + <EnablingRule @register="registerModal" @setting-complete="handleRuleSetting" />
  185 + </section>
  186 +</template>
  187 +
  188 +<style lang="less" scoped>
  189 + .trigger-form {
  190 + :deep(.ant-form-item-control-input-content) {
  191 + width: 100%;
  192 +
  193 + > div > div {
  194 + width: 100%;
  195 + }
  196 +
  197 + .ant-input-number {
  198 + width: 100% !important;
  199 + }
  200 + // .ant-col {
  201 + // .ant-row{
  202 + // .ant-col {
  203 + // .
  204 + // }
  205 + // }
  206 + // }
  207 + }
  208 + }
  209 +</style>
... ...
  1 +import { ConditionFilter } from '../ConditionFilter';
  2 +import { TriggerCondition } from '../ConditionFilter/type';
  3 +import { ScheduleType } from '../EnablingRule/type';
  4 +import { FormFieldEnum } from './config';
  5 +import { DeviceTypeEnum } from '/@/api/device/model/deviceModel';
  6 +import { FormActionType } from '/@/components/Form';
  7 +import {
  8 + DeviceTriggerTypeEum,
  9 + FlipFlopTypeEnum,
  10 + ScheduleTypeEnum,
  11 + TriggerEntityTypeEnum,
  12 + TriggerTypeEnum,
  13 + TriggerUnitEnum,
  14 + TriggerValueTypeNameEnum,
  15 +} from '/@/enums/linkedgeEnum';
  16 +
  17 +export interface ScheduleOptionItemType {
  18 + label: string;
  19 + value: ScheduleTypeEnum;
  20 +}
  21 +
  22 +export interface FlipFlopItemType {
  23 + key: string;
  24 + ref?: FormActionType;
  25 + schedule: ScheduleType;
  26 + conditionRef?: InstanceType<typeof ConditionFilter>;
  27 +}
  28 +
  29 +export interface FlipFlopConditionType {
  30 + deviceProfileId: string;
  31 + deviceType: DeviceTypeEnum;
  32 + entityId: string[];
  33 + entityType: TriggerEntityTypeEnum;
  34 + triggerCondition: TriggerCondition;
  35 + triggerType: TriggerTypeEnum;
  36 +}
  37 +
  38 +export interface FlipFlopFormRecordType {
  39 + [FormFieldEnum.FLIP_FLOP_TYPE]: FlipFlopTypeEnum;
  40 + [FormFieldEnum.DEVICE_TYPE]: DeviceTypeEnum;
  41 + [FormFieldEnum.DEVICE_PROFILE_ID]: string;
  42 + [FormFieldEnum.ENTITY_TYPE]: TriggerEntityTypeEnum;
  43 + [FormFieldEnum.ENTITY_ID]: string[];
  44 + [FormFieldEnum.TRIGGER_TYPE]: TriggerTypeEnum;
  45 + [FormFieldEnum.CONDITION_TYPE]: DeviceTriggerTypeEum;
  46 + [FormFieldEnum.CONDITION_KEY]: string;
  47 + [FormFieldEnum.CONDITION_VALUE_TYPE]: TriggerValueTypeNameEnum;
  48 + [FormFieldEnum.TRIGGER_DURATION_VALUE]: number;
  49 + [FormFieldEnum.TRIGGER_REPEAT_COUNT]: number;
  50 + [FormFieldEnum.TRIGGER_DURATION_UNIT]: TriggerUnitEnum;
  51 +}
... ...