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,7 +25,7 @@ module.exports = defineConfig({
25 'plugin:jest/recommended', 25 'plugin:jest/recommended',
26 ], 26 ],
27 rules: { 27 rules: {
28 - 'no-console': 'off', 28 + 'no-console': 'error',
29 'vue/script-setup-uses-vars': 'error', 29 'vue/script-setup-uses-vars': 'error',
30 '@typescript-eslint/ban-ts-ignore': 'off', 30 '@typescript-eslint/ban-ts-ignore': 'off',
31 '@typescript-eslint/explicit-function-return-type': 'off', 31 '@typescript-eslint/explicit-function-return-type': 'off',
1 import { DataType } from '../../device/model/modelOfMatterModel'; 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 import { DataSource } from '/@/views/visual/palette/types'; 3 import { DataSource } from '/@/views/visual/palette/types';
4 4
5 export interface AddDataBoardParams { 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 export interface Specs { 4 export interface Specs {
7 min: string; 5 min: string;
@@ -26,7 +24,7 @@ export interface DataType { @@ -26,7 +24,7 @@ export interface DataType {
26 24
27 export interface StructJSON { 25 export interface StructJSON {
28 functionName?: string; 26 functionName?: string;
29 - identifier?: string; 27 + identifier: string;
30 remark?: string; 28 remark?: string;
31 dataType?: DataType; 29 dataType?: DataType;
32 serviceCommand?: string; 30 serviceCommand?: string;
1 import { BasicPageParams } from '/@/api/model/baseModel'; 1 import { BasicPageParams } from '/@/api/model/baseModel';
  2 +import { SceneLinkageDataType } from '/@/views/rule/linkedge/components/SceneLinkageDrawer/type';
2 3
3 export type ScreenLinkPageQueryParam = BasicPageParams & ScreenParams; 4 export type ScreenLinkPageQueryParam = BasicPageParams & ScreenParams;
4 5
@@ -14,97 +15,7 @@ export type ScreenByDeptIdParams = { @@ -14,97 +15,7 @@ export type ScreenByDeptIdParams = {
14 // organizationId: '2f5c8f2a-196c-4941-8771-290f9da76219'; 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 export interface IChangeStatus { 20 export interface IChangeStatus {
110 status?: number; 21 status?: number;
@@ -6,6 +6,7 @@ import { @@ -6,6 +6,7 @@ import {
6 ScreenByDeptIdParams, 6 ScreenByDeptIdParams,
7 IChangeStatus, 7 IChangeStatus,
8 } from '/@/api/ruleengine/model/ruleengineModel'; 8 } from '/@/api/ruleengine/model/ruleengineModel';
  9 +import { DeviceModel } from '../device/model/deviceModel';
9 10
10 enum ScreenManagerApi { 11 enum ScreenManagerApi {
11 /** 12 /**
@@ -126,7 +127,7 @@ export const byOrganizationIdGetMasterDevice = (params: { @@ -126,7 +127,7 @@ export const byOrganizationIdGetMasterDevice = (params: {
126 deviceProfileId?: string; 127 deviceProfileId?: string;
127 }) => { 128 }) => {
128 const { organizationId, deviceProfileId } = params; 129 const { organizationId, deviceProfileId } = params;
129 - return defHttp.get({ 130 + return defHttp.get<DeviceModel[]>({
130 url: `${ScreenManagerApi.MASTER_GET_DEVICE}`, 131 url: `${ScreenManagerApi.MASTER_GET_DEVICE}`,
131 params: { deviceProfileId, organizationId }, 132 params: { deviceProfileId, organizationId },
132 }); 133 });
@@ -191,6 +191,10 @@ @@ -191,6 +191,10 @@
191 <slot name="afterFullScreen"></slot> 191 <slot name="afterFullScreen"></slot>
192 </div> 192 </div>
193 </div> 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 </div> 199 </div>
196 </template> 200 </template>
@@ -15,6 +15,8 @@ export { default as ApiUpload } from './src/components/ApiUpload.vue'; @@ -15,6 +15,8 @@ export { default as ApiUpload } from './src/components/ApiUpload.vue';
15 export { default as StructForm } from './src/externalCompns/components/StructForm/StructForm.vue'; 15 export { default as StructForm } from './src/externalCompns/components/StructForm/StructForm.vue';
16 export { default as JavaScriptFunctionEditor } from './src/components/JavaScriptFunctionEditor.vue'; 16 export { default as JavaScriptFunctionEditor } from './src/components/JavaScriptFunctionEditor.vue';
17 17
  18 +export { ThingsModelForm } from './src/externalCompns/components/ThingsModelForm';
  19 +
18 //注册自定义组件 20 //注册自定义组件
19 export { 21 export {
20 JEasyCron, 22 JEasyCron,
@@ -69,7 +69,7 @@ @@ -69,7 +69,7 @@
69 name: 'BasicForm', 69 name: 'BasicForm',
70 components: { FormItem, Form, Row, FormAction }, 70 components: { FormItem, Form, Row, FormAction },
71 props: basicProps, 71 props: basicProps,
72 - emits: ['advanced-change', 'reset', 'submit', 'register'], 72 + emits: ['advanced-change', 'reset', 'submit', 'register', 'fieldValueChange'],
73 setup(props, { emit, attrs }) { 73 setup(props, { emit, attrs }) {
74 const formModel = reactive<Recordable>({}); 74 const formModel = reactive<Recordable>({});
75 const modalFn = useModalContext(); 75 const modalFn = useModalContext();
@@ -230,6 +230,7 @@ @@ -230,6 +230,7 @@
230 230
231 function setFormModel(key: string, value: any) { 231 function setFormModel(key: string, value: any) {
232 formModel[key] = value; 232 formModel[key] = value;
  233 + emit('fieldValueChange', key, value);
233 const { validateTrigger } = unref(getBindValue); 234 const { validateTrigger } = unref(getBindValue);
234 if (!validateTrigger || validateTrigger === 'change') { 235 if (!validateTrigger || validateTrigger === 'change') {
235 validateFields([key]).catch((_) => {}); 236 validateFields([key]).catch((_) => {});
@@ -5,7 +5,8 @@ @@ -5,7 +5,8 @@
5 import { BasicModal } from '/@/components/Modal'; 5 import { BasicModal } from '/@/components/Modal';
6 import { PlusCircleOutlined } from '@ant-design/icons-vue'; 6 import { PlusCircleOutlined } from '@ant-design/icons-vue';
7 import { FormFieldsEnum, formSchemas } from './config'; 7 import { FormFieldsEnum, formSchemas } from './config';
8 - import { DataTypeEnum } from '../StructForm/config'; 8 + import { DataTypeEnum } from '/@/enums/objectModelEnum';
  9 +
9 const show = ref(false); 10 const show = ref(false);
10 11
11 const props = withDefaults( 12 const props = withDefaults(
@@ -50,7 +51,7 @@ @@ -50,7 +51,7 @@
50 updateSchema([ 51 updateSchema([
51 { 52 {
52 field: FormFieldsEnum.ZOOM_FACTOR, 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 <script lang="ts" setup> 1 <script lang="ts" setup>
2 import { Card } from 'ant-design-vue'; 2 import { Card } from 'ant-design-vue';
3 import { computed, nextTick, onMounted, onUpdated, unref, watch } from 'vue'; 3 import { computed, nextTick, onMounted, onUpdated, unref, watch } from 'vue';
4 - import { DataTypeEnum } from '../StructForm/config';  
5 import { BasicCreateFormParams } from './type'; 4 import { BasicCreateFormParams } from './type';
6 import { DynamicProps } from '/#/utils'; 5 import { DynamicProps } from '/#/utils';
7 import { Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel'; 6 import { Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel';
8 import { BasicForm, FormProps, FormSchema, useForm } from '/@/components/Form'; 7 import { BasicForm, FormProps, FormSchema, useForm } from '/@/components/Form';
  8 + import { DataTypeEnum } from '/@/enums/objectModelEnum';
9 import { ReadAndWriteEnum } from '/@/enums/toolEnum'; 9 import { ReadAndWriteEnum } from '/@/enums/toolEnum';
10 10
11 const props = withDefaults( 11 const props = withDefaults(
@@ -86,7 +86,7 @@ @@ -86,7 +86,7 @@
86 // step: step, 86 // step: step,
87 // formatter: (value: string) => value, 87 // formatter: (value: string) => value,
88 // parser: (string: string) => { 88 // parser: (string: string) => {
89 - // if (dataType === DataTypeEnum.IS_NUMBER_INT) { 89 + // if (dataType === DataTypeEnum.NUMBER_INT) {
90 // return Number(Number(string).toFixed()); 90 // return Number(Number(string).toFixed());
91 // } 91 // }
92 // return Number(string); 92 // return Number(string);
@@ -188,19 +188,19 @@ @@ -188,19 +188,19 @@
188 dataType: dataType! as unknown as DataTypeEnum, 188 dataType: dataType! as unknown as DataTypeEnum,
189 specs: specs as Partial<Specs>, 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 schemas.push(createInputNumber(params)); 192 schemas.push(createInputNumber(params));
193 } 193 }
194 194
195 - if (type === DataTypeEnum.IS_BOOL) { 195 + if (type === DataTypeEnum.BOOL) {
196 schemas.push(createSelect(params)); 196 schemas.push(createSelect(params));
197 } 197 }
198 198
199 - if (type === DataTypeEnum.IS_STRING) { 199 + if (type === DataTypeEnum.STRING) {
200 schemas.push(createInput(params)); 200 schemas.push(createInput(params));
201 } 201 }
202 202
203 - if (type === DataTypeEnum.IS_STRUCT) { 203 + if (type === DataTypeEnum.STRUCT) {
204 schemas.push(createInputJson(params)); 204 schemas.push(createInputJson(params));
205 } 205 }
206 } 206 }
1 -import { DataTypeEnum } from '../StructForm/config';  
2 import { Specs } from '/@/api/device/model/modelOfMatterModel'; 1 import { Specs } from '/@/api/device/model/modelOfMatterModel';
  2 +import { DataTypeEnum } from '/@/enums/objectModelEnum';
3 3
4 export interface BasicCreateFormParams { 4 export interface BasicCreateFormParams {
5 identifier: string; 5 identifier: string;
1 import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; 1 import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel';
2 import { findDictItemByCode } from '/@/api/system/dict'; 2 import { findDictItemByCode } from '/@/api/system/dict';
3 import { FormSchema } from '/@/components/Table'; 3 import { FormSchema } from '/@/components/Table';
  4 +import { DataTypeEnum } from '/@/enums/objectModelEnum';
4 import { isNullOrUnDef } from '/@/utils/is'; 5 import { isNullOrUnDef } from '/@/utils/is';
5 import { FormField } from '/@/views/device/profiles/step/cpns/physical/cpns/config'; 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 export const validateValueRange = (_rule, value: Record<'min' | 'max', number>, _callback) => { 8 export const validateValueRange = (_rule, value: Record<'min' | 'max', number>, _callback) => {
16 value = value || {}; 9 value = value || {};
17 const { min, max } = value; 10 const { min, max } = value;
@@ -77,10 +70,9 @@ export const formSchemas = ( @@ -77,10 +70,9 @@ export const formSchemas = (
77 try { 70 try {
78 const record = await findDictItemByCode(params); 71 const record = await findDictItemByCode(params);
79 return hasStructForm 72 return hasStructForm
80 - ? record.filter((item) => item.itemValue !== DataTypeEnum.IS_STRUCT) 73 + ? record.filter((item) => item.itemValue !== DataTypeEnum.STRUCT)
81 : record; 74 : record;
82 } catch (error) { 75 } catch (error) {
83 - console.log(error);  
84 return []; 76 return [];
85 } 77 }
86 }, 78 },
@@ -91,7 +83,7 @@ export const formSchemas = ( @@ -91,7 +83,7 @@ export const formSchemas = (
91 valueField: 'itemValue', 83 valueField: 'itemValue',
92 getPopupContainer: () => document.body, 84 getPopupContainer: () => document.body,
93 onChange: (value: string) => { 85 onChange: (value: string) => {
94 - if (value == DataTypeEnum.IS_STRUCT) { 86 + if (value == DataTypeEnum.STRUCT) {
95 updateSchema({ 87 updateSchema({
96 field: FormField.SPECS_LIST, 88 field: FormField.SPECS_LIST,
97 componentProps: { 89 componentProps: {
@@ -114,8 +106,8 @@ export const formSchemas = ( @@ -114,8 +106,8 @@ export const formSchemas = (
114 span: 18, 106 span: 18,
115 }, 107 },
116 ifShow: ({ values }) => 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 rules: [{ validator: validateValueRange }], 111 rules: [{ validator: validateValueRange }],
120 }, 112 },
121 { 113 {
@@ -134,8 +126,8 @@ export const formSchemas = ( @@ -134,8 +126,8 @@ export const formSchemas = (
134 }, 126 },
135 }, 127 },
136 ifShow: ({ values }) => 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 dynamicRules: ({ model }) => { 131 dynamicRules: ({ model }) => {
140 const valueRange = model[FormField.VALUE_RANGE] || {}; 132 const valueRange = model[FormField.VALUE_RANGE] || {};
141 const { min, max } = valueRange; 133 const { min, max } = valueRange;
@@ -199,8 +191,8 @@ export const formSchemas = ( @@ -199,8 +191,8 @@ export const formSchemas = (
199 }; 191 };
200 }, 192 },
201 ifShow: ({ values }) => 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 field: FormField.BOOL_CLOSE, 198 field: FormField.BOOL_CLOSE,
@@ -214,7 +206,7 @@ export const formSchemas = ( @@ -214,7 +206,7 @@ export const formSchemas = (
214 placeholder: '如:关', 206 placeholder: '如:关',
215 }, 207 },
216 defaultValue: '关', 208 defaultValue: '关',
217 - ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.IS_BOOL, 209 + ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.BOOL,
218 dynamicRules: ({ model }) => { 210 dynamicRules: ({ model }) => {
219 const close = model[FormField.BOOL_CLOSE]; 211 const close = model[FormField.BOOL_CLOSE];
220 const open = model[FormField.BOOL_OPEN]; 212 const open = model[FormField.BOOL_OPEN];
@@ -243,7 +235,7 @@ export const formSchemas = ( @@ -243,7 +235,7 @@ export const formSchemas = (
243 placeholder: '如:开', 235 placeholder: '如:开',
244 }, 236 },
245 defaultValue: '开', 237 defaultValue: '开',
246 - ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.IS_BOOL, 238 + ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.BOOL,
247 dynamicRules: ({ model }) => { 239 dynamicRules: ({ model }) => {
248 const close = model[FormField.BOOL_CLOSE]; 240 const close = model[FormField.BOOL_CLOSE];
249 const open = model[FormField.BOOL_OPEN]; 241 const open = model[FormField.BOOL_OPEN];
@@ -277,7 +269,7 @@ export const formSchemas = ( @@ -277,7 +269,7 @@ export const formSchemas = (
277 suffix: () => '字节', 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 field: FormField.EXTENSION_DESC, 275 field: FormField.EXTENSION_DESC,
@@ -322,7 +314,7 @@ export const formSchemas = ( @@ -322,7 +314,7 @@ export const formSchemas = (
322 valueField: 'value', 314 valueField: 'value',
323 changeEvent: 'update:value', 315 changeEvent: 'update:value',
324 colProps: { span: 24 }, 316 colProps: { span: 24 },
325 - ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.IS_STRUCT, 317 + ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.STRUCT,
326 rules: [{ required: true, validator: validateJSON }], 318 rules: [{ required: true, validator: validateJSON }],
327 }, 319 },
328 { 320 {
1 -import { DataTypeEnum } from './config';  
2 import { StructJSON } from '/@/api/device/model/modelOfMatterModel'; 1 import { StructJSON } from '/@/api/device/model/modelOfMatterModel';
  2 +import { DataTypeEnum } from '/@/enums/objectModelEnum';
3 import { FormField } from '/@/views/device/profiles/step/cpns/physical/cpns/config'; 3 import { FormField } from '/@/views/device/profiles/step/cpns/physical/cpns/config';
4 4
5 export enum OpenModalMode { 5 export enum OpenModalMode {
1 import { cloneDeep } from 'lodash-es'; 1 import { cloneDeep } from 'lodash-es';
2 -import { DataTypeEnum } from './config';  
3 import { StructFormValue } from './type'; 2 import { StructFormValue } from './type';
4 import { DataType, ModelOfMatterParams, StructJSON } from '/@/api/device/model/modelOfMatterModel'; 3 import { DataType, ModelOfMatterParams, StructJSON } from '/@/api/device/model/modelOfMatterModel';
5 import { isArray } from '/@/utils/is'; 4 import { isArray } from '/@/utils/is';
  5 +import { DataTypeEnum } from '/@/enums/objectModelEnum';
6 6
7 export function transfromToStructJSON(value: StructFormValue): StructJSON { 7 export function transfromToStructJSON(value: StructFormValue): StructJSON {
8 const { 8 const {
@@ -24,21 +24,21 @@ export function transfromToStructJSON(value: StructFormValue): StructJSON { @@ -24,21 +24,21 @@ export function transfromToStructJSON(value: StructFormValue): StructJSON {
24 let dataType = {} as unknown as DataType; 24 let dataType = {} as unknown as DataType;
25 25
26 switch (type) { 26 switch (type) {
27 - case DataTypeEnum.IS_NUMBER_INT: 27 + case DataTypeEnum.NUMBER_INT:
28 dataType = { 28 dataType = {
29 type, 29 type,
30 specs: { valueRange, step, unit, unitName }, 30 specs: { valueRange, step, unit, unitName },
31 }; 31 };
32 break; 32 break;
33 33
34 - case DataTypeEnum.IS_NUMBER_DOUBLE: 34 + case DataTypeEnum.NUMBER_DOUBLE:
35 dataType = { 35 dataType = {
36 type, 36 type,
37 specs: { valueRange, step, unit, unitName }, 37 specs: { valueRange, step, unit, unitName },
38 }; 38 };
39 break; 39 break;
40 40
41 - case DataTypeEnum.IS_BOOL: 41 + case DataTypeEnum.BOOL:
42 dataType = { 42 dataType = {
43 type, 43 type,
44 specs: { 44 specs: {
@@ -48,14 +48,14 @@ export function transfromToStructJSON(value: StructFormValue): StructJSON { @@ -48,14 +48,14 @@ export function transfromToStructJSON(value: StructFormValue): StructJSON {
48 }; 48 };
49 break; 49 break;
50 50
51 - case DataTypeEnum.IS_STRING: 51 + case DataTypeEnum.STRING:
52 dataType = { 52 dataType = {
53 type, 53 type,
54 specs: { length }, 54 specs: { length },
55 }; 55 };
56 break; 56 break;
57 57
58 - case DataTypeEnum.IS_STRUCT: 58 + case DataTypeEnum.STRUCT:
59 dataType = { 59 dataType = {
60 type, 60 type,
61 specs: specs! as unknown as ModelOfMatterParams[], 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,6 +123,7 @@ export type ComponentType =
123 | 'TransferModal' 123 | 'TransferModal'
124 | 'TransferTableModal' 124 | 'TransferTableModal'
125 | 'ObjectModelValidateForm' 125 | 'ObjectModelValidateForm'
  126 + | 'ThingsModelForm'
126 | 'DevicePicker' 127 | 'DevicePicker'
127 | 'ProductPicker' 128 | 'ProductPicker'
128 | 'PollCommandInput' 129 | 'PollCommandInput'
@@ -138,4 +139,8 @@ export type ComponentType = @@ -138,4 +139,8 @@ export type ComponentType =
138 | 'RelationsQuery' 139 | 'RelationsQuery'
139 | 'CredentialsCard' 140 | 'CredentialsCard'
140 | 'ApiComplete' 141 | 'ApiComplete'
141 - | 'DeviceProfileForm'; 142 + | 'DeviceProfileForm'
  143 + | 'ConditionFilter'
  144 + | 'TimeRangePicker'
  145 + | 'TriggerDurationInput'
  146 + | 'AlarmProfileSelect';
@@ -11,3 +11,19 @@ export enum AlarmStatusMean { @@ -11,3 +11,19 @@ export enum AlarmStatusMean {
11 CLEARED_ACK = '清除已确认', 11 CLEARED_ACK = '清除已确认',
12 ACTIVE_ACK = '激活已确认', 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 +7,7 @@ export enum DataActionModeEnum {
7 } 7 }
8 8
9 export enum DataActionModeNameEnum { 9 export enum DataActionModeNameEnum {
10 - CREATE = '创建', 10 + CREATE = '新增',
11 READ = '查看', 11 READ = '查看',
12 UPDATE = '编辑', 12 UPDATE = '编辑',
13 DELETE = '删除', 13 DELETE = '删除',
@@ -40,6 +40,11 @@ export enum ServiceCallTypeEnum { @@ -40,6 +40,11 @@ export enum ServiceCallTypeEnum {
40 SYNC = 'SYNC', 40 SYNC = 'SYNC',
41 } 41 }
42 42
  43 +export enum ServiceCallTypeNameEnum {
  44 + ASYNC = '异步',
  45 + SYNC = '同步',
  46 +}
  47 +
43 export enum CommandDeliveryWayEnum { 48 export enum CommandDeliveryWayEnum {
44 ONE_WAY = 'oneway', 49 ONE_WAY = 'oneway',
45 TWO_WAY = 'twoway', 50 TWO_WAY = 'twoway',
@@ -16,15 +16,17 @@ @@ -16,15 +16,17 @@
16 value?: string | string[]; 16 value?: string | string[];
17 apiTreeSelectProps?: Recordable; 17 apiTreeSelectProps?: Recordable;
18 showCreate?: boolean; 18 showCreate?: boolean;
  19 + disabled?: boolean;
19 }>(), 20 }>(),
20 { 21 {
21 showCreate: true, 22 showCreate: true,
  23 + disabled: false,
22 } 24 }
23 ); 25 );
24 26
25 const needReload = ref(true); 27 const needReload = ref(true);
26 28
27 - const emit = defineEmits(['change']); 29 + const emit = defineEmits(['change', 'optionsChange']);
28 30
29 const handleOpenCreate = () => { 31 const handleOpenCreate = () => {
30 openDrawer(true, { isUpdate: false }); 32 openDrawer(true, { isUpdate: false });
@@ -35,13 +37,14 @@ @@ -35,13 +37,14 @@
35 const orgList = ref<Recordable[]>([]); 37 const orgList = ref<Recordable[]>([]);
36 38
37 const getBindProps = computed<Recordable>(() => { 39 const getBindProps = computed<Recordable>(() => {
38 - const { value, apiTreeSelectProps = {} } = props; 40 + const { value, apiTreeSelectProps = {}, disabled } = props;
39 const { params = {} } = apiTreeSelectProps; 41 const { params = {} } = apiTreeSelectProps;
40 return { 42 return {
41 replaceFields: { children: 'children', key: 'id', title: 'name', value: 'id' }, 43 replaceFields: { children: 'children', key: 'id', title: 'name', value: 'id' },
42 getPopupContainer: () => document.body, 44 getPopupContainer: () => document.body,
43 placeholder: '请选择所属组织', 45 placeholder: '请选择所属组织',
44 maxLength: 250, 46 maxLength: 250,
  47 + disabled,
45 ...apiTreeSelectProps, 48 ...apiTreeSelectProps,
46 value, 49 value,
47 dropdownStyle: { maxHeight: '300px' }, 50 dropdownStyle: { maxHeight: '300px' },
@@ -63,6 +66,7 @@ @@ -63,6 +66,7 @@
63 onChange: (...args: any[]) => { 66 onChange: (...args: any[]) => {
64 emit('change', ...args); 67 emit('change', ...args);
65 }, 68 },
  69 + onOptionsChange: (...args: any[]) => emit('optionsChange', ...args),
66 }; 70 };
67 }); 71 });
68 72
@@ -80,7 +84,9 @@ @@ -80,7 +84,9 @@
80 <template> 84 <template>
81 <section class="flex"> 85 <section class="flex">
82 <ApiTreeSelect v-bind="getBindProps" /> 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 <OrganizationDrawer v-if="getShowCreate" @register="registerDrawer" @success="handleReload" /> 90 <OrganizationDrawer v-if="getShowCreate" @register="registerDrawer" @success="handleReload" />
85 </section> 91 </section>
86 </template> 92 </template>
@@ -15,13 +15,13 @@ @@ -15,13 +15,13 @@
15 import { useHistoryData } from '../../hook/useHistoryData'; 15 import { useHistoryData } from '../../hook/useHistoryData';
16 import { formatToDateTime } from '/@/utils/dateUtil'; 16 import { formatToDateTime } from '/@/utils/dateUtil';
17 import { useTable, BasicTable, BasicColumn } from '/@/components/Table'; 17 import { useTable, BasicTable, BasicColumn } from '/@/components/Table';
18 - import { DataTypeEnum } from '/@/components/Form/src/externalCompns/components/StructForm/config';  
19 import { 18 import {
20 ModeSwitchButton, 19 ModeSwitchButton,
21 TABLE_CHART_MODE_LIST, 20 TABLE_CHART_MODE_LIST,
22 EnumTableChartMode, 21 EnumTableChartMode,
23 } from '/@/components/Widget'; 22 } from '/@/components/Widget';
24 import { SchemaFiled } from '/@/views/visual/palette/components/HistoryTrendModal/config'; 23 import { SchemaFiled } from '/@/views/visual/palette/components/HistoryTrendModal/config';
  24 + import { DataTypeEnum } from '/@/enums/objectModelEnum';
25 25
26 interface DeviceDetail { 26 interface DeviceDetail {
27 tbDeviceId: string; 27 tbDeviceId: string;
@@ -188,7 +188,7 @@ @@ -188,7 +188,7 @@
188 method.setFieldsValue({ keys: props.attr }); 188 method.setFieldsValue({ keys: props.attr });
189 const attrInfo = unref(deviceAttrs).find((item) => item.identifier === props.attr); 189 const attrInfo = unref(deviceAttrs).find((item) => item.identifier === props.attr);
190 if ( 190 if (
191 - [DataTypeEnum.IS_STRING, DataTypeEnum.IS_STRUCT].includes( 191 + [DataTypeEnum.STRING, DataTypeEnum.STRUCT].includes(
192 attrInfo?.detail.dataType.type as unknown as DataTypeEnum 192 attrInfo?.detail.dataType.type as unknown as DataTypeEnum
193 ) 193 )
194 ) { 194 ) {
@@ -16,7 +16,6 @@ @@ -16,7 +16,6 @@
16 import { DeviceModelOfMatterAttrs, DeviceRecord } from '/@/api/device/model/deviceModel'; 16 import { DeviceModelOfMatterAttrs, DeviceRecord } from '/@/api/device/model/deviceModel';
17 import { Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel'; 17 import { Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel';
18 import { isArray, isNull, isObject } from '/@/utils/is'; 18 import { isArray, isNull, isObject } from '/@/utils/is';
19 - import { DataTypeEnum } from '/@/components/Form/src/externalCompns/components/StructForm/config';  
20 import { useGlobSetting } from '/@/hooks/setting'; 19 import { useGlobSetting } from '/@/hooks/setting';
21 import { ModeSwitchButton, EnumTableCardMode } from '/@/components/Widget'; 20 import { ModeSwitchButton, EnumTableCardMode } from '/@/components/Widget';
22 import { toRaw } from 'vue'; 21 import { toRaw } from 'vue';
@@ -25,6 +24,7 @@ @@ -25,6 +24,7 @@
25 import { ModalParamsType } from '/#/utils'; 24 import { ModalParamsType } from '/#/utils';
26 import { AreaChartOutlined } from '@ant-design/icons-vue'; 25 import { AreaChartOutlined } from '@ant-design/icons-vue';
27 import { SvgIcon } from '/@/components/Icon'; 26 import { SvgIcon } from '/@/components/Icon';
  27 + import { DataTypeEnum } from '/@/enums/objectModelEnum';
28 28
29 interface ReceiveMessage { 29 interface ReceiveMessage {
30 data: { 30 data: {
@@ -220,7 +220,7 @@ @@ -220,7 +220,7 @@
220 }; 220 };
221 221
222 const isStructAndTextType = (type: DataTypeEnum) => { 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 const setDataSource = () => { 226 const setDataSource = () => {
@@ -311,7 +311,7 @@ @@ -311,7 +311,7 @@
311 }); 311 });
312 312
313 const formatValue = (item: DataSource) => { 313 const formatValue = (item: DataSource) => {
314 - return item.type === DataTypeEnum.IS_BOOL 314 + return item.type === DataTypeEnum.BOOL
315 ? !isNull(item.value) 315 ? !isNull(item.value)
316 ? !!Number(item.value) 316 ? !!Number(item.value)
317 ? item.boolOpen 317 ? item.boolOpen
@@ -6,7 +6,6 @@ @@ -6,7 +6,6 @@
6 import { StructJSON } from '/@/api/device/model/modelOfMatterModel'; 6 import { StructJSON } from '/@/api/device/model/modelOfMatterModel';
7 import { BasicForm, useForm } from '/@/components/Form'; 7 import { BasicForm, useForm } from '/@/components/Form';
8 import { BasicModal, useModalInner } from '/@/components/Modal'; 8 import { BasicModal, useModalInner } from '/@/components/Modal';
9 - import { DataTypeEnum } from '/@/views/device/profiles/step/cpns/physical/cpns/config';  
10 import { sendCommandOneway } from '/@/api/dataBoard'; 9 import { sendCommandOneway } from '/@/api/dataBoard';
11 import { useMessage } from '/@/hooks/web/useMessage'; 10 import { useMessage } from '/@/hooks/web/useMessage';
12 import { unref } from 'vue'; 11 import { unref } from 'vue';
@@ -14,6 +13,7 @@ @@ -14,6 +13,7 @@
14 import { TaskTypeEnum } from '/@/views/task/center/config'; 13 import { TaskTypeEnum } from '/@/views/task/center/config';
15 import { SingleToHex, formSchemasConfig } from './config'; 14 import { SingleToHex, formSchemasConfig } from './config';
16 import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const'; 15 import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const';
  16 + import { DataTypeEnum } from '/@/enums/objectModelEnum';
17 17
18 defineEmits(['register']); 18 defineEmits(['register']);
19 const props = defineProps<{ deviceId: string; deviceName: string }>(); 19 const props = defineProps<{ deviceId: string; deviceName: string }>();
@@ -51,7 +51,7 @@ @@ -51,7 +51,7 @@
51 51
52 let schemas = [{ dataType: dataType, identifier, functionName: name } as StructJSON]; 52 let schemas = [{ dataType: dataType, identifier, functionName: name } as StructJSON];
53 53
54 - if (type === DataTypeEnum.IS_STRUCT) { 54 + if (type === DataTypeEnum.STRUCT) {
55 schemas = dataType?.specs as StructJSON[]; 55 schemas = dataType?.specs as StructJSON[];
56 } 56 }
57 57
1 import { DataType, Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel'; 1 import { DataType, Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel';
2 import { JSONEditor } from '/@/components/CodeEditor'; 2 import { JSONEditor } from '/@/components/CodeEditor';
3 import { FormSchema, useComponentRegister } from '/@/components/Form'; 3 import { FormSchema, useComponentRegister } from '/@/components/Form';
  4 +import { DataTypeEnum } from '/@/enums/objectModelEnum';
4 import { useJsonParse } from '/@/hooks/business/useJsonParse'; 5 import { useJsonParse } from '/@/hooks/business/useJsonParse';
5 -import { DataTypeEnum } from '/@/views/device/profiles/step/cpns/physical/cpns/config';  
6 6
7 export interface BasicCreateFormParams { 7 export interface BasicCreateFormParams {
8 identifier: string; 8 identifier: string;
@@ -19,9 +19,9 @@ const validateDouble = ( @@ -19,9 +19,9 @@ const validateDouble = (
19 max?: number | string 19 max?: number | string
20 ) => { 20 ) => {
21 min = 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 max = 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 return { 26 return {
27 flag: value < min || value > max, 27 flag: value < min || value > max,
@@ -56,13 +56,11 @@ export const useGenDynamicForm = () => { @@ -56,13 +56,11 @@ export const useGenDynamicForm = () => {
56 }, 56 },
57 ], 57 ],
58 componentProps: { 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 step, 61 step,
64 placeholder: `请输入${functionName}`, 62 placeholder: `请输入${functionName}`,
65 - precision: type === DataTypeEnum.IS_NUMBER_INT ? 0 : 2, 63 + precision: type === DataTypeEnum.NUMBER_INT ? 0 : 2,
66 }, 64 },
67 } as FormSchema; 65 } as FormSchema;
68 }; 66 };
@@ -141,11 +139,11 @@ export const useGenDynamicForm = () => { @@ -141,11 +139,11 @@ export const useGenDynamicForm = () => {
141 }; 139 };
142 140
143 const schemaMethod = { 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 const fieldTypeMap = new Map<string, DataTypeEnum>(); 149 const fieldTypeMap = new Map<string, DataTypeEnum>();
@@ -176,7 +174,7 @@ export const useGenDynamicForm = () => { @@ -176,7 +174,7 @@ export const useGenDynamicForm = () => {
176 const dataType = fieldTypeMap.get(next)!; 174 const dataType = fieldTypeMap.get(next)!;
177 175
178 let itemValue = value[next]; 176 let itemValue = value[next];
179 - if (dataType === DataTypeEnum.IS_STRUCT) { 177 + if (dataType === DataTypeEnum.STRUCT) {
180 const { value } = useJsonParse(itemValue); 178 const { value } = useJsonParse(itemValue);
181 itemValue = value; 179 itemValue = value;
182 } 180 }
1 import { FormSchema } from '/@/components/Table'; 1 import { FormSchema } from '/@/components/Table';
2 import { findDictItemByCode } from '/@/api/system/dict'; 2 import { findDictItemByCode } from '/@/api/system/dict';
  3 +import { DataTypeEnum } from '/@/enums/objectModelEnum';
3 4
4 export enum FormField { 5 export enum FormField {
5 FUNCTION_NAME = 'functionName', 6 FUNCTION_NAME = 'functionName',
@@ -40,30 +41,19 @@ export enum AssessMode { @@ -40,30 +41,19 @@ export enum AssessMode {
40 WRITE = 'w', 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 const isNumber = (type: string) => { 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 const isString = (type: string) => { 48 const isString = (type: string) => {
59 - return type === DataTypeEnum.IS_STRING; 49 + return type === DataTypeEnum.STRING;
60 }; 50 };
61 const isStruct = (type: string) => { 51 const isStruct = (type: string) => {
62 - return type === DataTypeEnum.IS_STRUCT; 52 + return type === DataTypeEnum.STRUCT;
63 }; 53 };
64 54
65 const isBool = (type: string) => { 55 const isBool = (type: string) => {
66 - return type === DataTypeEnum.IS_BOOL; 56 + return type === DataTypeEnum.BOOL;
67 }; 57 };
68 58
69 export const serviceSchemas = (tcpDeviceFlag: boolean): FormSchema[] => { 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 +}