Showing
129 changed files
with
5651 additions
and
3388 deletions
@@ -52,7 +52,7 @@ VITE_GLOB_ALARM_NOTIFY_POLLING_INTERVAL_TIME = 500000 | @@ -52,7 +52,7 @@ VITE_GLOB_ALARM_NOTIFY_POLLING_INTERVAL_TIME = 500000 | ||
52 | VITE_GLOB_ALARM_NOTIFY_DURATION = 5 | 52 | VITE_GLOB_ALARM_NOTIFY_DURATION = 5 |
53 | 53 | ||
54 | # Should Disabled Task Center Execute Interval Unit (Second) | 54 | # Should Disabled Task Center Execute Interval Unit (Second) |
55 | -VITE_GLOB_DISABLED_TASK_CENTER_EXECUTE_INTERVAL_UNIT_SECOND = true | 55 | +VITE_GLOB_DISABLED_TASK_CENTER_EXECUTE_INTERVAL_UNIT_SECOND = false |
56 | 56 | ||
57 | # Software version number | 57 | # Software version number |
58 | VITE_GLOB_SOFTWARE_VERSION_NUMBER = ThingsKit v1.2.0_release | 58 | VITE_GLOB_SOFTWARE_VERSION_NUMBER = ThingsKit v1.2.0_release |
@@ -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 { |
src/api/device/classModal.ts
0 → 100644
1 | +import { defHttp } from '/@/utils/http/axios'; | ||
2 | +// import { PaginationResult } from '/#/axios'; | ||
3 | + | ||
4 | +enum Api { | ||
5 | + DEVICE_PROFILE_CATEGORY = '/device_profile/category', | ||
6 | +} | ||
7 | + | ||
8 | +/** | ||
9 | + * 新增产品分类 | ||
10 | + */ | ||
11 | +export const deviceProfileCategory = (params) => { | ||
12 | + return defHttp.post<any>({ | ||
13 | + url: `${Api.DEVICE_PROFILE_CATEGORY}`, | ||
14 | + params, | ||
15 | + }); | ||
16 | +}; | ||
17 | + | ||
18 | +/** | ||
19 | + * 获取列表 | ||
20 | + */ | ||
21 | +export const getDeviceClassList = (params) => { | ||
22 | + return defHttp.get<any>({ | ||
23 | + url: Api.DEVICE_PROFILE_CATEGORY, | ||
24 | + params, | ||
25 | + }); | ||
26 | +}; | ||
27 | + | ||
28 | +/** | ||
29 | + * 删除产品分类 | ||
30 | + */ | ||
31 | +export const deleteDeviceClass = (params) => { | ||
32 | + return defHttp.delete<any>({ | ||
33 | + url: Api.DEVICE_PROFILE_CATEGORY, | ||
34 | + params, | ||
35 | + }); | ||
36 | +}; |
@@ -194,6 +194,7 @@ export interface DeviceRecord { | @@ -194,6 +194,7 @@ export interface DeviceRecord { | ||
194 | customerAdditionalInfo?: { | 194 | customerAdditionalInfo?: { |
195 | isPublic?: boolean; | 195 | isPublic?: boolean; |
196 | }; | 196 | }; |
197 | + ifShowClass?: Boolean; | ||
197 | } | 198 | } |
198 | 199 | ||
199 | export interface DeviceModelOfMatterAttrs { | 200 | export interface DeviceModelOfMatterAttrs { |
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; |
@@ -41,13 +39,14 @@ export interface FunctionJson { | @@ -41,13 +39,14 @@ export interface FunctionJson { | ||
41 | } | 39 | } |
42 | 40 | ||
43 | export interface ModelOfMatterParams { | 41 | export interface ModelOfMatterParams { |
44 | - deviceProfileId: string; | 42 | + deviceProfileId?: string; |
45 | functionJson: FunctionJson; | 43 | functionJson: FunctionJson; |
46 | functionName: string; | 44 | functionName: string; |
47 | functionType: FunctionType; | 45 | functionType: FunctionType; |
48 | identifier: string; | 46 | identifier: string; |
49 | remark: string; | 47 | remark: string; |
50 | id?: string; | 48 | id?: string; |
49 | + categoryId?: string; | ||
51 | callType?: string; | 50 | callType?: string; |
52 | eventType?: string; | 51 | eventType?: string; |
53 | accessMode?: string; | 52 | accessMode?: string; |
@@ -57,10 +56,12 @@ export interface ModelOfMatterParams { | @@ -57,10 +56,12 @@ export interface ModelOfMatterParams { | ||
57 | export interface GetModelTslParams { | 56 | export interface GetModelTslParams { |
58 | functionType: FunctionType; | 57 | functionType: FunctionType; |
59 | deviceProfileId: string; | 58 | deviceProfileId: string; |
59 | + ifShowClass?: string | Boolean; | ||
60 | } | 60 | } |
61 | 61 | ||
62 | export interface ImportModelOfMatterType { | 62 | export interface ImportModelOfMatterType { |
63 | data: Recordable; | 63 | data: Recordable; |
64 | functionType: string; | 64 | functionType: string; |
65 | - tkDeviceProfileId: string; | 65 | + tkDeviceProfileId?: string; |
66 | + categoryId?: string; | ||
66 | } | 67 | } |
@@ -18,13 +18,21 @@ enum ModelOfMatter { | @@ -18,13 +18,21 @@ enum ModelOfMatter { | ||
18 | IMPORT = '/things_model/import', | 18 | IMPORT = '/things_model/import', |
19 | 19 | ||
20 | GET_MODEL_SERVICE = '/things_model/get_services', | 20 | GET_MODEL_SERVICE = '/things_model/get_services', |
21 | + CATEGORY = '/things_model/category', | ||
22 | + CATEGORY_IMPORT = '/things_model/categoryImport', | ||
23 | + | ||
24 | + CATEGORY_EXPORT = '/things_model/categoryGetExport', | ||
25 | + | ||
26 | + IMPORT_CSV = '/things_model/csvImport', | ||
21 | } | 27 | } |
22 | 28 | ||
23 | export const getModelList = ( | 29 | export const getModelList = ( |
24 | params: BasicPageParams & { | 30 | params: BasicPageParams & { |
25 | - deviceProfileId: string; | 31 | + deviceProfileId?: string; |
26 | functionTyp?: FunctionType; | 32 | functionTyp?: FunctionType; |
27 | nameOrIdentifier?: string; | 33 | nameOrIdentifier?: string; |
34 | + selectType?: string | undefined; | ||
35 | + id?: string; | ||
28 | } | 36 | } |
29 | ) => { | 37 | ) => { |
30 | return defHttp.get({ | 38 | return defHttp.get({ |
@@ -82,3 +90,82 @@ export const importModelOfMatter = (data: ImportModelOfMatterType) => { | @@ -82,3 +90,82 @@ export const importModelOfMatter = (data: ImportModelOfMatterType) => { | ||
82 | data, | 90 | data, |
83 | }); | 91 | }); |
84 | }; | 92 | }; |
93 | + | ||
94 | +// 产品品类接口 | ||
95 | + | ||
96 | +// 新增 | ||
97 | +export const createModelCategory = (params: Partial<ModelOfMatterParams>) => { | ||
98 | + return defHttp.post({ | ||
99 | + url: ModelOfMatter.CATEGORY, | ||
100 | + params, | ||
101 | + }); | ||
102 | +}; | ||
103 | + | ||
104 | +// 修改 | ||
105 | +export const updateModelCategory = (params: Partial<ModelOfMatterParams>) => { | ||
106 | + return defHttp.put({ | ||
107 | + url: ModelOfMatter.CATEGORY, | ||
108 | + params, | ||
109 | + }); | ||
110 | +}; | ||
111 | + | ||
112 | +// 删除 | ||
113 | +export const deleteModelCategory = (params: string[]) => { | ||
114 | + return defHttp.delete({ | ||
115 | + url: ModelOfMatter.CATEGORY, | ||
116 | + params: { | ||
117 | + ids: params, | ||
118 | + }, | ||
119 | + }); | ||
120 | +}; | ||
121 | + | ||
122 | +// 导入 | ||
123 | +export const importModelCategory = (data: ImportModelOfMatterType) => { | ||
124 | + return defHttp.post({ | ||
125 | + url: ModelOfMatter.CATEGORY_IMPORT, | ||
126 | + data, | ||
127 | + }); | ||
128 | +}; | ||
129 | + | ||
130 | +// 导出 | ||
131 | +export const ExportModelCategory = (params: any) => { | ||
132 | + const { functionType, deviceProfileId } = params; | ||
133 | + return defHttp.get({ | ||
134 | + url: `${ModelOfMatter.CATEGORY_EXPORT}/${functionType}/${deviceProfileId}`, | ||
135 | + params, | ||
136 | + }); | ||
137 | +}; | ||
138 | + | ||
139 | +/** | ||
140 | + * 物模型产品品类界面excel导入 | ||
141 | + */ | ||
142 | + | ||
143 | +export const importCsvCategory = (params: { | ||
144 | + categoryId?: string; | ||
145 | + deviceProfileId?: string; | ||
146 | + file: FormData; | ||
147 | +}) => { | ||
148 | + const { categoryId } = params || {}; | ||
149 | + | ||
150 | + return defHttp.post<any>({ | ||
151 | + url: `${ModelOfMatter.IMPORT_CSV}?categoryId=${categoryId}`, | ||
152 | + params: params.file, | ||
153 | + }); | ||
154 | +}; | ||
155 | + | ||
156 | +/** | ||
157 | + * 物模型产品物模型界面excel导入 | ||
158 | + */ | ||
159 | + | ||
160 | +export const importCsvDeviceProfileId = (params: { | ||
161 | + categoryId?: string; | ||
162 | + deviceProfileId?: string; | ||
163 | + file: FormData; | ||
164 | +}) => { | ||
165 | + const { deviceProfileId } = params || {}; | ||
166 | + | ||
167 | + return defHttp.post<any>({ | ||
168 | + url: `${ModelOfMatter.IMPORT_CSV}?deviceProfileId=${deviceProfileId}`, | ||
169 | + params: params.file, | ||
170 | + }); | ||
171 | +}; |
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 | }); |
@@ -37,10 +37,11 @@ export interface AccountListItem { | @@ -37,10 +37,11 @@ export interface AccountListItem { | ||
37 | } | 37 | } |
38 | 38 | ||
39 | export interface OrganizationListItem { | 39 | export interface OrganizationListItem { |
40 | - id: string; | ||
41 | - name: string; | 40 | + id?: string; |
41 | + name?: string; | ||
42 | parentId?: string; | 42 | parentId?: string; |
43 | - remark: string; | 43 | + remark?: string; |
44 | + organizationId?: string; | ||
44 | } | 45 | } |
45 | 46 | ||
46 | export interface MenuListItem { | 47 | export interface MenuListItem { |
src/assets/svg/JSON.svg
0 → 100644
1 | +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1699588251601" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1411" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M145.621959 0c-44.79888 0-79.998 36.81188-79.998 81.61076v860.77848c0 44.79888 35.19912 81.61076 79.998 81.61076h732.781681a81.969151 81.969151 0 0 0 81.61076-81.61076V324.80468L657.60916 0h-511.987201z" fill="#F17F53" p-id="1412"></path><path d="M657.60916 0v233.59416c0 25.59936 17.61236 92.79768 97.61036 92.79768h204.79488L657.60916 0z" fill="#FFFFFF" p-id="1413"></path><path d="M109.117272 659.874703c1.61276 0 2.867128 0.614385 3.814305 1.868753s1.663958 2.764731 2.175945 4.582286 0.81918 3.737507 0.972776 5.862253 0.230394 4.044699 0.230394 5.862254v5.708657c2.099148 3.302317 4.78708 6.886228 8.0126 10.726132s6.963026 7.449414 11.18692 10.80293 8.78058 6.143846 13.721257 8.39659 10.214145 3.379116 15.820405 3.379115c5.99025 0 11.622109-1.407965 16.869978-4.198295s10.137347-6.527837 14.617235-11.18692 8.652584-9.98375 12.441288-15.974001 7.21902-12.236494 10.265344-18.661933 5.759856-12.876478 8.089398-19.276318 4.377491-12.287693 6.067048-17.689158 3.046324-10.086148 4.044699-14.028449 1.689558-6.681433 2.099147-8.166196c2.611135-10.598135 4.81268-20.761081 6.681433-30.437639s2.764731-19.250719 2.764731-28.645684c0-2.303942-0.691183-3.60951-2.099147-3.891103l-37.19587 10.495738a19.916302 19.916302 0 0 1-5.094273 0.895978c-3.788705 0-6.963026-1.20317-9.522962-3.60951s-3.814305-5.862253-3.814305-10.342142 0.435189-8.114997 1.279968-10.879728 2.201545-4.81268 4.044699-6.220644 4.275093-2.329542 7.270218-2.764731 6.655834-0.665583 10.956527-0.665583h2.099147c2.40634-0.511987 5.478263-1.20317 9.21577-2.099148s7.935802-1.919952 12.518087-3.071923l14.694032-3.737507 15.743607-4.044699c5.299068-1.356766 10.572536-2.662333 15.820404-3.9679a1637.283868 1637.283868 0 0 1 23.474613-5.63186l5.990251-1.279968a22.01545 22.01545 0 0 1 3.737506-0.588785c1.20317 0 2.303942 0.358391 3.302318 1.049574s1.817555 1.58716 2.483138 2.636734a14.514837 14.514837 0 0 1 2.175945 7.423814c0 2.995125-1.151971 5.60626-3.455913 7.807805s-5.19667 4.121497-8.703783 5.785456-7.321417 3.097523-11.468513 4.351891l-11.62211 3.532711c-3.60951 1.100772-6.681433 2.175946-9.215769 3.22552s-3.967901 2.227144-4.275093 3.532711c-0.511987 4.40309-0.998375 9.292568-1.510363 14.694033s-1.049574 10.546936-1.638359 15.462014c-0.204795 1.20317-0.614385 4.095898-1.279968 8.703782s-1.561561 10.188545-2.687932 16.79318-2.559936 13.823654-4.198295 21.682658-3.532712 15.564411-5.63186 23.167421c-2.303942 8.294193-5.324667 17.151571-9.062173 26.546536s-8.191795 18.559536-13.337267 27.442514-10.982125 17.228369-17.484363 24.984976-13.542061 14.130847-21.145071 19.122721a58.929727 58.929727 0 0 1-23.19302 8.089398 61.336067 61.336067 0 0 1-9.369366 0.742382c-5.19667 0-10.828529-0.691183-16.869978-2.099148s-12.134097-3.404715-18.226744-5.99025-11.9805-5.734257-17.61236-9.369366-10.674933-7.705407-15.078023-12.159696-7.910202-9.21577-10.495738-14.335641-3.891103-10.444539-3.891102-16.050799c0-4.300692 0.435189-8.0126 1.279968-11.110122s2.227144-5.60626 4.121496-7.500613 4.428689-3.302317 7.577411-4.198295 7.014225-1.484763 11.519712-1.484763zM305.61796 693.333067c3.404715 1.20317 6.886228 2.611135 10.418939 4.198295s7.21902 3.123122 11.033324 4.582285 7.782205 2.662333 11.929302 3.686308 8.575786 1.510362 13.286068 1.510362c5.503862 0 11.058924-0.639984 16.639584-1.945551s11.084523-3.225519 16.434789-5.785455 10.470138-5.657459 15.385215-9.292568 9.394965-7.884603 13.490863-12.671683c4.505487-5.094273 8.242994-9.727757 11.238119-13.874854s5.427064-7.859004 7.270218-11.110122 3.174321-6.041449 3.967901-8.39659 1.20317-4.377491 1.20317-6.067048c0-2.508737-0.844779-4.684683-2.559936-6.527837s-3.967901-3.379116-6.835029-4.582285-6.067048-2.073548-9.676558-2.636734-7.347016-0.81918-11.238119-0.81918c-2.303942 0-4.991875 0.076798-8.089398 0.230394l-9.446164 0.460789-9.292567 0.38399a150.39624 150.39624 0 0 1-7.807805 0.153596c-3.302317 0-6.732632-0.38399-10.265344-1.126372s-7.014225-1.971151-10.418939-3.686307-6.553436-3.9935-9.446164-6.911828a35.403915 35.403915 0 0 1-8.831779-15.743606 24.626584 24.626584 0 0 1-0.742382-6.143846c0-2.40634 0.179196-4.684683 0.537587-6.83503s0.767981-4.223894 1.279968-6.220644c1.407965-4.991875 3.737507-10.086148 6.963026-15.231619s7.142221-10.162946 11.698907-15.078023 9.497363-9.650959 14.847629-14.258844 10.854129-8.934177 16.511587-12.978875 11.21252-7.705407 16.639584-10.956526 10.521337-6.01585 15.23162-8.319792c6.604635-3.19992 13.337267-5.529462 20.172295-6.963026s13.61886-2.175946 20.325892-2.175946c3.9935 0 7.807805 0.537587 11.391715 1.58716s6.78383 2.585535 9.522962 4.582286 4.915077 4.479888 6.527837 7.423814 2.40634 6.323042 2.40634 10.137347c0 2.201545-0.255994 4.044699-0.742381 5.555061s-1.151971 2.713532-1.945552 3.686308-1.663958 1.61276-2.636734 2.022349-1.919952 0.588785-2.918327 0.588785c-2.687933 0-5.171071-0.307192-7.423814-0.895977s-4.428689-1.279968-6.527837-2.02235-4.198295-1.459164-6.297443-2.099147-4.40309-0.972776-6.911827-0.972776c-7.398215 0-14.924427 1.484763-22.578635 4.428689s-15.103622 6.707032-22.348242 11.23812-14.105247 9.625359-20.556286 15.231619-12.236494 11.058924-17.330767 16.357991c-2.687933 2.79033-4.684683 5.60626-5.913452 8.39659s-1.868753 5.452664-1.868753 7.961401c0 3.711907 1.61276 6.604635 4.81268 8.703782s7.091023 3.148721 11.698907 3.148722c2.687933 0 5.759856-0.051199 9.138972-0.153597a796.140096 796.140096 0 0 0 20.761081-0.844779c3.353516-0.153596 6.323042-0.230394 8.934176-0.230394 6.195045 0 12.159696 0.870378 17.842754 2.636734s10.751731 4.326292 15.154821 7.731007 7.935802 7.628609 10.572536 12.671683 3.967901 10.828529 3.967901 17.330767c0 3.9935-0.588785 8.038199-1.791955 12.159696s-2.841529 8.217395-4.940677 12.364491-4.505487 8.242994-7.19342 12.287693-5.555061 8.0126-8.550186 11.929301c-5.401465 6.988625-11.417315 13.59326-18.073148 19.788306s-13.721257 11.59651-21.22187 16.204395-15.436414 8.242994-23.781805 10.956526-16.921177 4.044699-25.727357 4.044699a74.724532 74.724532 0 0 1-23.551411-4.121497c-3.891103-1.356766-7.577411-3.046324-11.033325-5.094273s-6.476638-4.377491-9.062173-6.963026-4.684683-5.478263-6.220644-8.626984-2.329542-6.579036-2.329542-10.265344c0-1.791955 0.281593-3.532712 0.819179-5.17107s1.868753-2.559936 4.070299-2.559936zM497.152371 651.631709c1.791955-7.500612 4.40309-15.18042 7.807805-23.013824s7.398215-15.718007 12.0061-23.62821 9.727757-15.666808 15.385215-23.321017a316.868878 316.868878 0 0 1 57.982551-59.928102c3.19992-2.201545 6.143846-3.660708 8.857378-4.351891s5.555061-1.049574 8.550186-1.049573c3.19992 0 6.041449 0.486388 8.550187 1.433564s4.991875 2.662333 7.500612 5.17107c0.79358 0.895978 1.638359 1.689558 2.559936 2.40634s1.740756 1.049574 2.559936 1.049574c0.204795 0 0.460788-0.230394 0.742382-0.665583l0.972775-1.587161c0.358391-0.614385 0.767981-1.228769 1.279968-1.868753s1.100772-1.126372 1.791956-1.433564c1.689558-0.79358 3.737507-1.331167 6.067048-1.58716s4.735882-0.38399 7.116622-0.383991c4.505487 0 8.857379 0.81918 13.055674 2.483138s7.910202 3.967901 11.110122 6.963026c7.60301 7.091023 13.362866 15.59001 17.330767 25.496963s5.913452 20.556286 5.913452 31.948001c0 10.188545-1.510362 21.19627-4.505488 32.997575-2.611135 10.39334-6.323042 20.505087-11.18692 30.309642s-10.546936 18.994725-17.100373 27.59611-13.849254 16.460388-21.887452 23.551412-16.613985 13.20927-25.727357 18.303542-18.610735 9.062173-28.492088 11.929302-19.941901 4.275093-30.156046 4.275093c-9.497363 0-18.021949-1.459164-25.573761-4.351891s-13.951651-6.988625-19.19952-12.287693-9.241369-11.59651-12.006099-18.892328-4.121497-15.410815-4.121497-24.293793c0-3.788705 0.230394-7.628609 0.665583-11.468513s1.151971-7.807805 2.150346-11.801305z m25.343367 10.060549a63.230419 63.230419 0 0 0-1.638359 13.490862c0 5.60626 0.972776 10.521337 2.918327 14.770831s4.710282 7.807805 8.242994 10.649334 7.807805 4.991875 12.748481 6.451039 10.367741 2.175946 16.281193 2.175945c6.988625 0 14.130847-1.100772 21.375465-3.302317s14.335642-5.401465 21.22187-9.59976 13.542061-9.241369 19.941901-15.154821 12.313292-12.594885 17.765956-20.095498 10.290943-15.641209 14.540437-24.447389 7.679808-18.149946 10.265343-28.056898c0.998375-3.60951 1.715157-7.347016 2.175946-11.238119s0.665583-7.654209 0.665583-11.238119c0-5.811055-0.639984-11.340516-1.945551-16.639584s-3.455914-10.00935-6.451039-14.105248-6.937427-7.372616-11.852504-9.830154-10.905327-3.686308-17.99635-3.686308c-0.998375 0-1.766356 0.435189-2.329542 1.279968s-1.126372 1.919952-1.715157 3.225519c-0.511987 1.100772-1.075173 2.252744-1.715157 3.455914a11.878103 11.878103 0 0 1-6.451039 5.555061 15.794805 15.794805 0 0 1-5.785455 0.895978c-1.689558 0-3.19992-0.38399-4.505487-1.126372s-2.611135-1.766356-3.891103-3.071923l-1.510362-1.433564a1.971151 1.971151 0 0 0-1.356766-0.537587c-0.895978 0-2.073548 0.40959-3.532712 1.20317s-2.943926 1.740756-4.505487 2.841529-3.046324 2.252744-4.505488 3.455914-2.585535 2.252744-3.379115 3.148721c-6.80943 6.988625-13.567661 14.617235-20.325892 22.885828s-12.978876 16.767581-18.661934 25.57376-10.674933 17.663558-14.924427 26.623335-7.270218 17.561161-9.16457 25.880953zM760.083398 546.495138c0.588785-2.611135 1.356766-5.375866 2.252744-8.319792s2.099148-5.657459 3.609509-8.089398 3.455914-4.505487 5.862254-6.143847 5.452664-2.483138 9.138971-2.483137c3.507112 0 6.630234 0.972776 9.369366 2.918327s5.222269 3.916702 7.423814 5.913452c2.611135 1.689558 5.094273 5.145471 7.500613 10.342141s4.863878 11.59651 7.423814 19.19952 5.299068 16.204395 8.242994 25.804155 6.195045 19.660308 9.753356 30.156046 7.526212 21.221869 11.929302 32.178396 9.394965 21.631459 15.001225 32.024799l3.148721 5.631859a95.076023 95.076023 0 0 0 7.142222 10.879728c1.049574 1.305567 1.817555 1.945551 2.329542 1.945552 0.307192 0 0.639984-0.742381 1.049573-2.252744s0.844779-3.302317 1.356767-5.401465l1.58716-6.451039a59.902502 59.902502 0 0 1 1.58716-5.401465 476.736882 476.736882 0 0 0 13.721257-37.426264c2.739132-8.447789 5.785455-18.482738 9.062174-30.079248a1560.127397 1560.127397 0 0 0 14.182045-54.526637c1.151971-4.966276 2.252744-10.00935 3.302317-15.154821s1.971151-10.162946 2.764731-15.078023 1.356766-9.138972 1.638359-12.748481a77.540461 77.540461 0 0 1 0.691183-8.242994c0.255994-1.894353 0.563186-3.865503 0.972776-5.913452s0.921577-3.916702 1.58716-5.63186 1.484763-3.097523 2.483138-4.198295 2.201545-1.638359 3.60951-1.638359 3.123122 0.204795 5.171071 0.588786 3.916702 0.998375 5.631859 1.791955c1.894353 2.611135 3.635109 5.734257 5.17107 9.369366s2.329542 8.575786 2.329542 14.77083c0 3.891103-0.40959 8.575786-1.20317 14.02845s-1.817555 11.289318-3.071923 17.561161a634.864128 634.864128 0 0 1-8.934177 38.245443c-1.61276 6.041449-3.123122 11.59651-4.582285 16.639584s-2.662333 9.21577-3.686308 12.518087c-1.791955 5.811055-3.686308 11.775706-5.631859 17.919552s-4.095898 12.594885-6.451039 19.353117-4.889478 13.900452-7.654208 21.452263-5.836654 15.61561-9.21577 24.216995c-1.510362 3.891103-3.225519 7.807805-5.171071 11.698907s-4.172696 7.423814-6.681433 10.572536-5.324667 5.708657-8.473388 7.654209-6.681433 2.918327-10.572536 2.918327c-2.303942 0-4.684683-0.332792-7.116622-0.972776s-4.531087-1.971151-6.220644-3.967901c-8.191795-9.497363-15.487613-20.274693-21.887453-32.331991s-12.134097-24.652184-17.17717-37.810255-9.522962-26.444139-13.414065-39.909402-7.449414-26.316142-10.649334-38.629435l-0.895978-3.455913c-0.40959-1.407965-0.767981-2.81593-1.126371-4.275093s-0.691183-2.764731-1.049574-3.967901-0.742381-2.175946-1.20317-2.918327-0.972776-1.126372-1.58716-1.126372-1.356766 1.100772-2.252744 3.302317-1.791955 4.684683-2.687933 7.423815-1.715157 5.427064-2.483138 8.012599l-1.433564 4.940677c-0.691183 2.611135-1.740756 6.323042-3.148721 11.18692l-4.735882 16.434789c-1.766356 6.118247-3.660708 12.543686-5.708657 19.353117l-5.913452 19.737106a1165.026874 1165.026874 0 0 1-12.902078 40.728582l-3.891102 12.364491c-1.279968 4.147096-2.508737 7.935802-3.60951 11.314917s-1.791955 5.657459-2.099148 6.758231c-1.20317 3.788705-2.969526 6.681433-5.324667 8.626984s-4.735882 2.918327-7.116622 2.918327c-1.510362 0-2.995125-0.537587-4.505487-1.58716s-2.81593-2.329542-3.967901-3.814305-2.099148-3.123122-2.841529-4.863878-1.126372-3.327917-1.126372-4.735882c0-0.79358 0.255994-2.073548 0.742382-3.814304s1.305567-4.735882 2.40634-8.934177l3.891102-15.462013 44.338092-143.638009z" fill="#FFFFFF" p-id="1414"></path></svg> |
src/assets/svg/excel.svg
0 → 100644
1 | +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1699933658783" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1443" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M145.621959 0c-44.79888 0-79.998 36.81188-79.998 81.61076v860.77848c0 44.79888 35.19912 81.61076 79.998 81.61076h732.781681a81.969151 81.969151 0 0 0 81.61076-81.61076V324.80468L657.60916 0h-511.987201z" fill="#25B39E" p-id="1444"></path><path d="M657.60916 0v233.59416c0 25.59936 17.61236 92.79768 97.61036 92.79768h204.79488L657.60916 0z" fill="#FFFFFF" p-id="1445"></path><path d="M158.498438 694.485038c0-3.507112 0.511987-7.19342 1.510362-11.110122 3.404715-13.59326 7.372616-26.930527 11.929302-39.986201s8.678183-26.034549 12.36449-38.936626 6.78383-23.781805 9.21577-32.613585 4.633484-16.460388 6.527837-22.80903 3.660708-11.826904 5.247869-16.434789 3.353516-9.138972 5.247868-13.644459c1.20317-2.687933 2.534337-5.503862 3.967901-8.39659s3.020724-5.58066 4.735882-8.0126 3.481513-4.454289 5.324667-5.99025 3.763106-2.329542 5.785455-2.329542c4.095898 0 7.21902 0.665583 9.369366 2.02235s3.481513 2.585535 3.9935 3.686308l1.356766 6.143846c1.510362-0.102397 3.455914-0.38399 5.862254-0.819179s4.81268-0.921577 7.270218-1.433565 4.735882-0.998375 6.835029-1.510362 3.711907-0.947176 4.81268-1.356766c6.604635-1.791955 12.697283-3.353516 18.303542-4.659083s10.930927-2.38074 15.974001-3.22552 9.881353-1.484763 14.463638-1.868753 9.241369-0.588785 13.951651-0.588785c4.607885 0 8.345391 0.255994 11.238119 0.742381s5.19667 1.177571 6.911828 2.02235 2.867128 1.843154 3.532711 2.995125 0.972776 2.38074 0.972776 3.686308c0 1.20317-0.332792 2.483138-0.972776 3.814304s-1.715157 2.636734-3.225519 3.814305-3.455914 2.252744-5.862254 3.148721-5.401465 1.510362-9.010974 1.791955c-0.895978 0.204795-2.969526 0.563186-6.220645 1.126372l-10.572535 1.791955-10.80293 1.868754a129.60956 129.60956 0 0 0-7.039824 1.356766h0.153596c-0.588785 0.102397-2.022349 0.486388-4.275093 1.126372s-4.966276 1.407965-8.166196 2.252743-6.681433 1.817555-10.41894 2.918327l-11.110122 3.22552a687.163621 687.163621 0 0 0-17.637959 5.324667c-6.092648 2.40634-10.879728 5.58066-14.335642 9.522961s-6.169446 8.370991-8.166195 13.286068c-0.40959 0.895978-0.998375 2.431939-1.791956 4.582286l-2.636734 7.347016-3.071923 8.780581-3.148721 8.857378c-0.998375 2.81593-1.868753 5.350266-2.636734 7.654209s-1.279968 3.9935-1.587161 5.094272c-0.307192 0.998375-0.511987 1.894353-0.588785 2.687933s-0.153596 1.407965-0.153596 1.791955c0 0.588785 0.153596 0.895978 0.460788 0.895978 1.510362 0 4.633484-0.665583 9.369366-2.022349s10.290943-3.020724 16.639584-5.017475l20.40269-6.451039c7.244619-2.303942 14.207645-4.454289 20.837879-6.451038s12.569286-3.686308 17.765956-5.017475 8.959776-2.022349 11.238119-2.022349c3.507112 0 5.862253 0.563186 7.039824 1.715157s1.791955 2.431939 1.791955 3.814304c0 3.60951-1.279968 6.758231-3.814304 9.446164s-5.734257 5.043074-9.522962 7.039824c-1.20317 0.588785-3.430314 1.484763-6.681433 2.636734s-6.758231 2.329542-10.495738 3.532712l-10.726132 3.455914-7.347016 2.406339c-9.804555 3.788705-17.765956 6.937427-23.935402 9.369366s-11.135722 4.454289-14.924427 5.99025l-8.78058 3.532712c-2.047949 0.81918-3.942301 1.484763-5.708657 2.02235s-3.686308 1.075173-5.785456 1.58716-4.940676 1.254369-8.652583 2.150346c-0.895978 2.099148-1.945551 5.273468-3.148722 9.522962s-2.355141 8.652584-3.455913 13.20927-2.022349 8.78058-2.764731 12.671683-1.126372 6.604635-1.126372 8.089398c0 1.791955 0.255994 3.123122 0.742381 3.967901s1.100772 1.433564 1.791956 1.715157 1.407965 0.460788 2.099147 0.460788h1.638359c4.81268 0 10.572536-0.691183 17.330767-2.099147s13.414065-3.251119 20.018699-5.555061c6.297443-2.201545 11.801305-4.607885 16.511588-7.193421s9.113372-5.017475 13.286068-7.270218 8.191795-4.147096 12.159696-5.708657 8.268593-2.38074 12.978875-2.483138c0.998375 0 2.175946 0.102397 3.532712 0.307192s2.636734 0.563186 3.814304 1.126372 2.175946 1.279968 2.918327 2.175946 1.023974 2.047949 0.81918 3.455913c-0.40959 2.892728-1.843154 5.759856-4.351891 8.550186s-5.657459 5.503862-9.446164 8.089398-8.038199 5.094273-12.748481 7.500613-9.497363 4.684683-14.41244 6.835029-9.702157 4.095898-14.41244 5.862253-8.908577 3.276718-12.594885 4.582286c-9.19017 3.19992-18.149946 5.555061-26.853729 7.039824s-16.460388 2.252744-23.372215 2.252743c-7.60301 0-13.414065-1.433564-17.484363-4.275093s-6.067048-7.577411-6.067048-14.182045zM336.541986 699.579311a14.489238 14.489238 0 0 1-13.721257 1.638359c-1.356766-0.691183-2.508737-1.61276-3.455913-2.764731a12.902077 12.902077 0 0 1-2.918327-8.319792c0-2.099148 0.435189-3.865503 1.279968-5.324667s1.894353-2.687933 3.148721-3.737507 2.611135-1.919952 4.044699-2.636734 2.764731-1.305567 3.967901-1.791955c3.9935-2.611135 8.498988-5.811055 13.490863-9.59976s10.137347-7.884603 15.462013-12.236494 10.572536-8.78058 15.820404-13.286068l14.924427-12.902077 6.758231-5.990251a121.59696 121.59696 0 0 0-13.59326-31.128821l-5.017474-8.166196-3.686308-5.990251a7.782205 7.782205 0 0 1-1.279968-4.198295 14.258844 14.258844 0 0 1 7.116622-11.340516 11.417315 11.417315 0 0 1 5.785455-1.433564c3.19992 0 6.01585 0.665583 8.473388 2.022349s4.633484 3.071923 6.527837 5.171071 3.558311 4.377491 4.940677 6.835029 2.662333 4.78708 3.737506 6.963026c0.588785 1.20317 1.254369 2.79033 1.945552 4.81268s1.382365 4.121497 2.022349 6.37424l1.868753 6.604635 1.510363 5.631859c4.40309-4.198295 8.678183-8.601385 12.825279-13.209269a587.556511 587.556511 0 0 0 18.892328-22.194646c1.740756-2.201545 3.58391-4.223894 5.478263-6.067048s3.839904-3.379116 5.862253-4.582285 4.147096-1.791955 6.451039-1.791955c3.891103 0 6.681433 0.998375 8.319792 2.995125s2.483138 4.607885 2.483138 7.807804c0 3.404715-0.614385 6.527837-1.868753 9.369366s-2.867128 5.068673-4.863879 6.681433c-2.892728 2.303942-6.067048 5.068673-9.522962 8.319792s-6.937427 6.681433-10.495737 10.265344l-10.572536 10.649333c-3.507112 3.507112-6.707032 6.655834-9.59976 9.446164-1.20317 1.100772-2.227144 2.201545-3.071923 3.302318s-1.279968 2.508737-1.279968 4.198295c0 0.998375 0.281593 2.252744 0.819179 3.737506s1.254369 3.097523 2.099148 4.81268l2.559936 5.171071c0.870378 1.740756 1.58716 3.327917 2.175945 4.735881 1.20317 2.687933 2.867128 5.887853 5.017475 9.59976s4.300692 7.577411 6.451039 11.62211 4.0191 8.166196 5.631859 12.364491 2.40634 8.191795 2.40634 12.006099a20.761081 20.761081 0 0 1-3.737507 12.082898c-1.075173 1.561561-2.329542 2.764731-3.686308 3.686308s-2.636734 1.356766-3.814304 1.356766c-2.611135 0-4.684683-0.307192-6.220645-0.895977s-2.867128-1.510362-3.967901-2.687933-2.022349-2.662333-2.764731-4.428689-1.58716-3.763106-2.483137-6.067049c-1.305567-3.711907-2.995125-7.961401-5.094273-12.748481s-4.275093-9.548561-6.527837-14.258844-4.377491-9.062173-6.374241-13.132471-3.558311-7.116622-4.659083-9.21577l-8.626984 6.835029-11.468514 9.062174-17.919552 14.105247c-7.244619 5.708657-16.588385 12.953276-28.0057 21.861854zM490.752531 646.921427a158.588035 158.588035 0 0 1 22.041049-48.510787c5.299068-7.833404 11.314917-15.103622 18.073148-21.759456s14.130847-12.082898 22.117847-16.281193c2.611135-1.407965 5.452664-2.457539 8.550187-3.148722s6.246244-1.049574 9.446163-1.049573c3.302317 0 6.374241 0.307192 9.21577 0.895977s5.299068 1.61276 7.347016 2.995125 3.686308 3.19992 4.863879 5.401465 1.791955 4.889478 1.791955 8.089398c0 2.40634-0.358391 4.81268-1.049574 7.270218s-1.740756 4.659084-3.148721 6.604635-3.148721 3.532712-5.247869 4.735882-4.556686 1.791955-7.347016 1.791955c-2.201545 0-4.223894-0.40959-6.067048-1.20317a47.102822 47.102822 0 0 1-7.884603-4.275093 19.916302 19.916302 0 0 0-2.841529-1.279968c-3.9935 1.791955-7.782205 4.172696-11.314917 7.116622s-6.860628 6.220644-9.906953 9.830154-5.862253 7.449414-8.39659 11.545312-4.838279 8.191795-6.911827 12.287693-3.788705 8.114997-5.247869 12.082897-2.636734 7.577411-3.532712 10.879728c-0.691183 2.40634-1.331167 5.119872-1.868753 8.166196s-0.81918 6.01585-0.819179 8.934177c0 1.99675 0.179196 3.891103 0.537586 5.708657s0.972776 3.404715 1.868753 4.81268 2.099148 2.508737 3.60951 3.302317 3.353516 1.20317 5.555061 1.20317c3.507112 0 6.963026-0.460788 10.41894-1.356766s6.835029-2.047949 10.137346-3.455913 6.527837-3.020724 9.676559-4.863879a314.129747 314.129747 0 0 0 14.924426-9.369366c1.919952-1.279968 3.711907-2.457539 5.401465-3.455913s3.276718-1.817555 4.735882-2.483138 2.713532-0.972776 3.814305-0.972776c1.510362 0 2.892728 0.38399 4.198295 1.126372s1.945551 2.022349 1.945551 3.814305a12.79968 12.79968 0 0 1-0.895978 4.351891c-1.407965 3.891103-3.302317 7.398215-5.708657 10.495738s-5.068673 5.862253-8.0126 8.319792-6.092648 4.633484-9.446163 6.527836-6.681433 3.660708-9.983751 5.247869c-6.195045 3.302317-12.466888 5.785455-18.815529 7.423815s-11.673308 2.483138-15.974001 2.483137c-1.407965 0-3.046324-0.153596-4.940677-0.460788s-3.558311-0.742381-4.940676-1.356766c-8.089398-3.507112-14.00285-8.191795-17.689158-14.105247s-5.555061-12.902077-5.555061-20.991476c0-3.507112 0.281593-7.167821 0.81918-11.033324s1.356766-7.807805 2.457538-12.031699zM618.698133 647.689408c0-4.300692 1.151971-7.961401 3.455913-10.956526s5.350266-4.991875 9.138972-5.990251a138.287743 138.287743 0 0 1 13.644459-21.17067c3.404715-4.40309 7.270218-8.831779 11.622109-13.286068s9.138972-8.498988 14.41244-12.159696 10.905327-6.630234 16.946776-8.934177 12.466888-3.455914 19.276318-3.455913c5.811055 0 10.854129 0.691183 15.154821 2.099147s7.859004 3.353516 10.649334 5.862254 4.889478 5.478263 6.297443 8.934176 2.099148 7.167821 2.099147 11.186921c0 3.404715-0.460788 6.655834-1.356766 9.753356-1.407965 5.60626-3.9935 10.495738-7.807805 14.694032s-8.268593 7.884603-13.414064 11.033325-10.751731 5.785455-16.793181 7.884602-12.108497 3.839904-18.149946 5.247869-11.878103 2.483138-17.484363 3.22552-10.444539 1.331167-14.540436 1.715157c-0.895978 1.791955-1.663958 4.070298-2.329542 6.835029s-0.972776 5.427064-0.972776 8.012599c0 5.811055 2.303942 10.290943 6.911828 13.490863s10.956526 4.81268 19.045923 4.81268c4.710282 0 9.19017-0.537587 13.490863-1.58716s8.370991-2.40634 12.236494-4.044699 7.475013-3.455914 10.879728-5.401465 6.502237-3.865503 9.292568-5.785456c3.60951-2.40634 7.014225-4.428689 10.265343-6.067048s6.118247-2.483138 8.626985-2.483138 4.095898 0.614385 4.812679 1.868753 1.049574 2.585535 1.049574 3.967901a34.251944 34.251944 0 0 1-0.307192 4.198295 25.804155 25.804155 0 0 1-8.703783 14.566036c-10.39334 8.191795-21.068273 14.054049-32.024799 17.561161s-22.629834 5.247869-35.019925 5.247869c-2.40634 0-5.222269-0.153596-8.473388-0.460789s-6.630234-0.895978-10.137346-1.791955-6.937427-2.201545-10.342142-3.891103a31.538412 31.538412 0 0 1-15.513212-17.100372 40.037399 40.037399 0 0 1-2.483138-14.847629c0-4.710282 0.639984-9.958151 1.945552-15.743606-1.305567-0.204795-2.534337-0.870378-3.686308-2.02235s-1.715157-2.81593-1.715157-5.017474z m93.898452-41.547762c0.204795-0.511987 0.307192-0.947176 0.307192-1.356766v-1.356766c0-2.687933-0.998375-4.735882-2.995125-6.067048s-4.40309-2.022349-7.19342-2.022349c-4.40309 0-8.729382 1.151971-12.978875 3.455913s-8.242994 5.171071-12.0061 8.626984-7.065423 7.142221-9.983751 11.110123-5.145471 7.526212-6.758231 10.726132c7.19342-0.588785 13.798055-1.58716 19.788306-2.918327s11.21252-3.046324 15.666808-5.094273 8.038199-4.351891 10.80293-6.911827 4.556686-5.299068 5.350266-8.191796zM818.347541 499.929902c0.511987-3.9935 1.305567-7.705407 2.40634-11.110122s2.585535-6.220644 4.428689-8.473389 4.223894-3.379116 7.116623-3.379115c1.61276 0 3.379116 0.307192 5.324666 0.895977s3.788705 1.433564 5.555062 2.483138 3.225519 2.329542 4.428689 3.814305 1.791955 3.148721 1.791955 4.940676c0 0.79358-0.153596 1.715157-0.460788 2.764731s-0.537587 2.073548-0.742382 3.071924l-37.503062 150.293842c-1.510362 5.99025-2.739132 11.724507-3.737507 17.177171s-1.740756 9.932552-2.252744 13.414064v3.60951c0 2.995125-0.204795 6.271843-0.588785 9.830154s-1.151971 6.860628-2.252744 9.906953-2.636734 5.60626-4.582285 7.654208-4.479888 3.071923-7.577411 3.071924c-2.687933 0-5.043074-0.563186-7.039824-1.715158s-3.660708-2.636734-4.940676-4.428689-2.252744-3.737507-2.841529-5.785455-0.895978-3.967901-0.895978-5.785456c0-1.61276 0.153596-3.58391 0.460789-5.913452s0.665583-4.81268 1.126372-7.347016 0.921577-5.094273 1.433564-7.654209 0.998375-4.78708 1.510362-6.681433l38.706232-153.59616a193.275168 193.275168 0 0 1 1.126372-11.058923z" fill="#FFFFFF" p-id="1446"></path></svg> |
@@ -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> |
@@ -47,6 +47,7 @@ | @@ -47,6 +47,7 @@ | ||
47 | isClose: { type: Boolean, default: true }, | 47 | isClose: { type: Boolean, default: true }, |
48 | title: { type: String, default: '' }, | 48 | title: { type: String, default: '' }, |
49 | loading: { type: Boolean }, | 49 | loading: { type: Boolean }, |
50 | + defaultExpand: { type: Boolean, default: true }, | ||
50 | /** | 51 | /** |
51 | * Can it be expanded | 52 | * Can it be expanded |
52 | */ | 53 | */ |
@@ -75,7 +76,7 @@ | @@ -75,7 +76,7 @@ | ||
75 | 76 | ||
76 | const emit = defineEmits(['expand', 'change', 'hchange']); | 77 | const emit = defineEmits(['expand', 'change', 'hchange']); |
77 | 78 | ||
78 | - const show = ref(true); | 79 | + const show = ref(props.defaultExpand); |
79 | 80 | ||
80 | const { prefixCls } = useDesign('collapse-container'); | 81 | const { prefixCls } = useDesign('collapse-container'); |
81 | 82 |
@@ -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((_) => {}); |
@@ -4,77 +4,102 @@ | @@ -4,77 +4,102 @@ | ||
4 | <!-- 待完善封装InputGroup --> | 4 | <!-- 待完善封装InputGroup --> |
5 | <InputGroup compact> | 5 | <InputGroup compact> |
6 | <Select | 6 | <Select |
7 | - v-if="type !== '2'" | 7 | + v-if="type !== RequestMethodTypeEnum.WEBSOCKET" |
8 | placeholder="请求类型" | 8 | placeholder="请求类型" |
9 | - :style="{ width: type !== '2' ? 15 + '%' : 0 + '%' }" | ||
10 | - v-model:value="valueObj.requestHttpType" | 9 | + :style="{ width: type !== RequestMethodTypeEnum.WEBSOCKET ? 15 + '%' : 0 + '%' }" |
10 | + v-model:value="requestTypeUrlValue.requestHttpType" | ||
11 | :options="selectOptions" | 11 | :options="selectOptions" |
12 | allowClear | 12 | allowClear |
13 | @change="emitChange" | 13 | @change="emitChange" |
14 | /> | 14 | /> |
15 | <Input | 15 | <Input |
16 | - @change="emitChange" | ||
17 | placeholder="请输入接口地址" | 16 | placeholder="请输入接口地址" |
18 | - v-model:value="valueObj.requestUrl" | ||
19 | - :style="{ width: type !== '2' ? 85 + '%' : 100 + '%' }" | 17 | + v-model:value="requestTypeUrlValue.requestUrl" |
18 | + :style="{ width: type !== RequestMethodTypeEnum.WEBSOCKET ? 85 + '%' : 100 + '%' }" | ||
19 | + @change="emitChange" | ||
20 | /> | 20 | /> |
21 | </InputGroup> | 21 | </InputGroup> |
22 | </div> | 22 | </div> |
23 | </template> | 23 | </template> |
24 | <script lang="ts" setup> | 24 | <script lang="ts" setup> |
25 | - import { reactive, ref, watchEffect } from 'vue'; | 25 | + import { reactive, ref, PropType, watch } from 'vue'; |
26 | import { InputGroup, Select, Input } from 'ant-design-vue'; | 26 | import { InputGroup, Select, Input } from 'ant-design-vue'; |
27 | - import type { SelectValue } from 'ant-design-vue/lib/select'; | ||
28 | import { findDictItemByCode } from '/@/api/system/dict'; | 27 | import { findDictItemByCode } from '/@/api/system/dict'; |
29 | - import { propTypes } from '/@/utils/propTypes'; | 28 | + import { RequestMethodTypeEnum } from '/@/views/dataview/publicApi/config/enum'; |
30 | 29 | ||
31 | - type TypeInputGroup = { | ||
32 | - requestHttpType: SelectValue | undefined; | 30 | + interface requestTypeUrlConfig { |
31 | + requestHttpType: undefined; | ||
33 | requestUrl?: string; | 32 | requestUrl?: string; |
34 | disabled?: boolean; | 33 | disabled?: boolean; |
35 | - }; | 34 | + } |
36 | 35 | ||
37 | - type selectType = { label: string; value: string; disabled?: boolean }; | 36 | + type selectType = { |
37 | + label: string; | ||
38 | + value: string; | ||
39 | + }; | ||
38 | 40 | ||
39 | const props = defineProps({ | 41 | const props = defineProps({ |
40 | type: { | 42 | type: { |
41 | type: String, | 43 | type: String, |
44 | + default: RequestMethodTypeEnum.COMMOM, | ||
42 | }, | 45 | }, |
43 | - value: propTypes.object.def({}), | 46 | + value: Object as PropType<requestTypeUrlConfig>, |
44 | }); | 47 | }); |
45 | 48 | ||
46 | const emits = defineEmits(['change', 'update:value']); | 49 | const emits = defineEmits(['change', 'update:value']); |
47 | 50 | ||
48 | const selectOptions = ref<selectType[]>([]); | 51 | const selectOptions = ref<selectType[]>([]); |
49 | 52 | ||
50 | - const getOptions = async (e) => { | ||
51 | - const res = await findDictItemByCode({ | ||
52 | - dictCode: e === '1' ? 'dataview_select_sql_request' : 'dataview_select_request', | 53 | + const getOptions = async (requestType) => { |
54 | + // 暂且排除SQL和WEBSOCKET | ||
55 | + const resItem = await findDictItemByCode({ | ||
56 | + dictCode: 'dataview_select_request', | ||
53 | }); | 57 | }); |
54 | - if (e === '1' || e === '0') { | ||
55 | - selectOptions.value = res.map((m) => ({ label: m.itemText, value: m.itemValue })); | 58 | + if (requestType === RequestMethodTypeEnum.COMMOM) { |
59 | + selectOptions.value = resItem.map((m) => ({ label: m.itemText, value: m.itemValue })); | ||
56 | } else { | 60 | } else { |
57 | selectOptions.value = []; | 61 | selectOptions.value = []; |
58 | } | 62 | } |
59 | }; | 63 | }; |
60 | 64 | ||
61 | - const valueObj = reactive<TypeInputGroup>({ | 65 | + const requestTypeUrlValue = reactive<requestTypeUrlConfig>({ |
62 | requestHttpType: undefined, | 66 | requestHttpType: undefined, |
63 | requestUrl: '', | 67 | requestUrl: '', |
64 | }); | 68 | }); |
65 | 69 | ||
66 | - watchEffect(() => { | ||
67 | - initVal(); | ||
68 | - }); | 70 | + watch( |
71 | + () => props.type, | ||
72 | + () => { | ||
73 | + initOption(); | ||
74 | + }, | ||
75 | + { | ||
76 | + immediate: true, | ||
77 | + } | ||
78 | + ); | ||
79 | + | ||
80 | + watch( | ||
81 | + () => props.value, | ||
82 | + () => { | ||
83 | + initConfig(); | ||
84 | + }, | ||
85 | + { | ||
86 | + immediate: true, | ||
87 | + } | ||
88 | + ); | ||
89 | + | ||
90 | + function initOption() { | ||
91 | + if (props.type) { | ||
92 | + getOptions(props.type); | ||
93 | + } | ||
94 | + } | ||
69 | 95 | ||
70 | - async function initVal() { | ||
71 | - if (props?.type) await getOptions(props?.type); | ||
72 | - if (props?.value) for (let i in props.value) Reflect.set(valueObj, i, props.value[i]); | 96 | + function initConfig() { |
97 | + if (props?.value) | ||
98 | + for (let i in props.value) Reflect.set(requestTypeUrlValue, i, props.value[i]); | ||
73 | } | 99 | } |
74 | 100 | ||
75 | function emitChange() { | 101 | function emitChange() { |
76 | - emits('change', valueObj); | ||
77 | - emits('update:value', valueObj); | 102 | + emits('change', requestTypeUrlValue); |
103 | + emits('update:value', requestTypeUrlValue); | ||
78 | } | 104 | } |
79 | </script> | 105 | </script> |
80 | -<style scoped></style> |
1 | <script lang="ts" setup> | 1 | <script lang="ts" setup> |
2 | - import { Button, Transfer, Tag } from 'ant-design-vue'; | 2 | + import { Transfer, Select } from 'ant-design-vue'; |
3 | import { cloneDeep, get } from 'lodash-es'; | 3 | import { cloneDeep, get } from 'lodash-es'; |
4 | import { computed, CSSProperties, ExtractPropTypes, onMounted, ref, unref, watch } from 'vue'; | 4 | import { computed, CSSProperties, ExtractPropTypes, onMounted, ref, unref, watch } from 'vue'; |
5 | import { BasicModal, useModal } from '/@/components/Modal'; | 5 | import { BasicModal, useModal } from '/@/components/Modal'; |
@@ -29,7 +29,7 @@ | @@ -29,7 +29,7 @@ | ||
29 | buttonName?: string; | 29 | buttonName?: string; |
30 | modalProps?: ExtractPropTypes<InstanceType<typeof BasicModal>['$props']>; | 30 | modalProps?: ExtractPropTypes<InstanceType<typeof BasicModal>['$props']>; |
31 | transferProps?: ExtractPropTypes<TransferType['$props']>; | 31 | transferProps?: ExtractPropTypes<TransferType['$props']>; |
32 | - buttonProps?: ExtractPropTypes<InstanceType<typeof Button>['$props']>; | 32 | + selectProps?: ExtractPropTypes<InstanceType<typeof Select>['$props']>; |
33 | disabled?: any; | 33 | disabled?: any; |
34 | }>(), | 34 | }>(), |
35 | { | 35 | { |
@@ -52,24 +52,15 @@ | @@ -52,24 +52,15 @@ | ||
52 | return unref(getOptions).filter((item) => unref(targetKeys).includes(item[valueField])); | 52 | return unref(getOptions).filter((item) => unref(targetKeys).includes(item[valueField])); |
53 | }); | 53 | }); |
54 | 54 | ||
55 | - const getShowTagOptions = computed(() => { | ||
56 | - const { maxTagLength } = props; | ||
57 | - return unref(targetOptions).slice(0, maxTagLength); | 55 | + const getSelectOptions = computed(() => { |
56 | + return unref(targetOptions).map((item) => ({ ...item, label: item.title, value: item.key })); | ||
58 | }); | 57 | }); |
59 | 58 | ||
60 | - const getSurplusOptionsLength = computed(() => { | ||
61 | - const { maxTagLength } = props; | ||
62 | - const surplusValue = unref(targetKeys).length - maxTagLength; | ||
63 | - return surplusValue < 0 ? 0 : surplusValue; | ||
64 | - }); | ||
65 | - | ||
66 | - const count = computed(() => { | ||
67 | - return unref(targetKeys).length; | ||
68 | - }); | 59 | + const getSelectValue = computed(() => unref(getSelectOptions).map((item) => item.value)); |
69 | 60 | ||
70 | const targetKeys = ref<string[]>(props.value || []); | 61 | const targetKeys = ref<string[]>(props.value || []); |
71 | 62 | ||
72 | - const getOptions = computed<Recordable[]>(() => { | 63 | + const getOptions = computed<Record<'key' | 'title', string>[]>(() => { |
73 | const { labelField, valueField } = props; | 64 | const { labelField, valueField } = props; |
74 | return unref(options).map((item) => ({ | 65 | return unref(options).map((item) => ({ |
75 | ...item, | 66 | ...item, |
@@ -99,13 +90,14 @@ | @@ -99,13 +90,14 @@ | ||
99 | }; | 90 | }; |
100 | }); | 91 | }); |
101 | 92 | ||
102 | - const getBindButtonProps = computed(() => { | ||
103 | - const { buttonProps = {} } = props; | 93 | + const getBindSelectProps = computed(() => { |
94 | + const { selectProps = {} } = props; | ||
104 | return { | 95 | return { |
105 | - ...buttonProps, | ||
106 | - type: 'link', | ||
107 | - onClick: handleOpenModal, | ||
108 | - } as ExtractPropTypes<InstanceType<typeof Button>['$props']>; | 96 | + maxTagCount: 3, |
97 | + ...selectProps, | ||
98 | + open: false, | ||
99 | + onDropdownVisibleChange: handleOpenModal, | ||
100 | + } as ExtractPropTypes<InstanceType<typeof Select>['$props']>; | ||
109 | }); | 101 | }); |
110 | 102 | ||
111 | const handleChange = (_targetKeys: string[], direction: 'left' | 'right', moveKeys: string[]) => { | 103 | const handleChange = (_targetKeys: string[], direction: 'left' | 'right', moveKeys: string[]) => { |
@@ -165,22 +157,11 @@ | @@ -165,22 +157,11 @@ | ||
165 | <Transfer v-bind="getBindProps" /> | 157 | <Transfer v-bind="getBindProps" /> |
166 | </section> | 158 | </section> |
167 | </BasicModal> | 159 | </BasicModal> |
168 | - <Button v-bind="getBindButtonProps" class="!flex !justify-center !items-center min-h-8"> | ||
169 | - <span v-if="!count">{{ $props.buttonName }}</span> | ||
170 | - <div v-if="!!count"> | ||
171 | - <Tag | ||
172 | - class="!px-2 !py-1 !bg-gray-50 !border-gray-100" | ||
173 | - v-for="item in getShowTagOptions" | ||
174 | - :key="item.key" | ||
175 | - > | ||
176 | - <span> | ||
177 | - {{ item.title }} | ||
178 | - </span> | ||
179 | - </Tag> | ||
180 | - <Tag class="!px-2 !py-1 !bg-gray-50 !border-gray-100" v-if="getSurplusOptionsLength"> | ||
181 | - <span> +{{ getSurplusOptionsLength }}... </span> | ||
182 | - </Tag> | ||
183 | - </div> | ||
184 | - </Button> | 160 | + <Select |
161 | + v-bind="getBindSelectProps" | ||
162 | + :options="getSelectOptions" | ||
163 | + :value="getSelectValue" | ||
164 | + mode="multiple" | ||
165 | + /> | ||
185 | </div> | 166 | </div> |
186 | </template> | 167 | </template> |
src/components/Form/src/components/TransferTableModal.vue
deleted
100644 → 0
1 | -<script lang="ts" setup> | ||
2 | - import { Button, Tabs, Badge, ButtonProps, Tag } from 'ant-design-vue'; | ||
3 | - import { get, isFunction, set, uniqBy } from 'lodash-es'; | ||
4 | - import { ExtractPropTypes, computed, unref, ref, nextTick, onMounted, watch } from 'vue'; | ||
5 | - import { DynamicProps } from '/#/utils'; | ||
6 | - import { BasicModal, useModal } from '/@/components/Modal'; | ||
7 | - import { BasicTable, BasicTableProps, TableRowSelection, useTable } from '/@/components/Table'; | ||
8 | - import { FETCH_SETTING } from '/@/components/Table/src/const'; | ||
9 | - import { useDesign } from '/@/hooks/web/useDesign'; | ||
10 | - | ||
11 | - interface Options extends Recordable { | ||
12 | - primaryKey?: string; | ||
13 | - disabled?: boolean; | ||
14 | - } | ||
15 | - | ||
16 | - enum Active { | ||
17 | - PENDING = 'pending', | ||
18 | - SELECTED = 'selected', | ||
19 | - } | ||
20 | - | ||
21 | - interface ActionType { | ||
22 | - setSelectedOptions: (options: Recordable[]) => void; | ||
23 | - } | ||
24 | - | ||
25 | - const emit = defineEmits(['change', 'update:value']); | ||
26 | - | ||
27 | - const props = withDefaults( | ||
28 | - defineProps<{ | ||
29 | - value?: string[]; | ||
30 | - labelField?: string; | ||
31 | - valueField?: string; | ||
32 | - primaryKey?: string; | ||
33 | - params?: Recordable; | ||
34 | - buttonName?: string; | ||
35 | - pendingTableProps?: BasicTableProps; | ||
36 | - selectedTableProps?: BasicTableProps; | ||
37 | - maxTagLength?: number; | ||
38 | - modalProps?: ExtractPropTypes<InstanceType<typeof BasicModal>['$props']>; | ||
39 | - buttonProps?: ExtractPropTypes<InstanceType<typeof Button>['$props']>; | ||
40 | - initSelectedOptions?: (actionType: ActionType) => Promise<Recordable[]>; | ||
41 | - transformValue?: (selectedRowKeys: string[], selectedRows: Options[]) => any[]; | ||
42 | - onValueChange?: (selectedRowkeys: string[]) => any[]; | ||
43 | - onRemoveAfter?: (actionType: ActionType) => Promise<any>; | ||
44 | - onSelectedAfter?: (actionType: ActionType) => Promise<any>; | ||
45 | - disabled?: any; | ||
46 | - }>(), | ||
47 | - { | ||
48 | - buttonName: '选择设备', | ||
49 | - primaryKey: 'id', | ||
50 | - maxTagLength: 2, | ||
51 | - labelField: 'label', | ||
52 | - valueField: 'value', | ||
53 | - disabled: false, | ||
54 | - } | ||
55 | - ); | ||
56 | - | ||
57 | - const { prefixCls } = useDesign('transfer-table-modal'); | ||
58 | - | ||
59 | - const activeKey = ref<Active>(Active.PENDING); | ||
60 | - | ||
61 | - const selectedRows = ref<Options[]>([]); | ||
62 | - | ||
63 | - const selectedRowKeys = ref<string[]>(props.value || []); | ||
64 | - | ||
65 | - const pendingOptions = ref<Options[]>([]); | ||
66 | - | ||
67 | - const selectedConfirmQueue = ref<Options[]>([]); | ||
68 | - | ||
69 | - const pendingConfirmQueue = ref<Options[]>([]); | ||
70 | - | ||
71 | - const selectedTotal = ref(0); | ||
72 | - | ||
73 | - const pendingTotal = ref(0); | ||
74 | - | ||
75 | - const getFetchSetting = computed(() => { | ||
76 | - const { pendingTableProps } = props; | ||
77 | - return pendingTableProps?.fetchSetting || FETCH_SETTING; | ||
78 | - }); | ||
79 | - | ||
80 | - const getPendingRowSelection = computed<TableRowSelection>(() => { | ||
81 | - const rowKeys = unref(selectedRowKeys); | ||
82 | - return { | ||
83 | - type: 'checkbox', | ||
84 | - getCheckboxProps: (record: Recordable) => { | ||
85 | - const { primaryKey } = props; | ||
86 | - return { | ||
87 | - ...record, | ||
88 | - disabled: rowKeys.includes(record[primaryKey]), | ||
89 | - }; | ||
90 | - }, | ||
91 | - onSelect: (_record: Recordable, _selected: boolean, selectedRows: Object[]) => { | ||
92 | - pendingConfirmQueue.value = selectedRows; | ||
93 | - }, | ||
94 | - onSelectAll: (_selected: boolean, selectedRows: Recordable[]) => { | ||
95 | - pendingConfirmQueue.value = selectedRows; | ||
96 | - }, | ||
97 | - }; | ||
98 | - }); | ||
99 | - | ||
100 | - const getPendingTableBindProps = computed<Partial<DynamicProps<BasicTableProps>>>(() => { | ||
101 | - const { pendingTableProps, primaryKey } = props; | ||
102 | - return { | ||
103 | - ...pendingTableProps, | ||
104 | - rowKey: primaryKey, | ||
105 | - api: handlePendingApiIntercept, | ||
106 | - clickToRowSelect: false, | ||
107 | - rowSelection: getPendingRowSelection, | ||
108 | - }; | ||
109 | - }); | ||
110 | - | ||
111 | - const getSelectedTableBindProps = computed<Partial<DynamicProps<BasicTableProps>>>(() => { | ||
112 | - const { selectedTableProps, primaryKey } = props; | ||
113 | - return { | ||
114 | - ...selectedTableProps, | ||
115 | - dataSource: selectedRows, | ||
116 | - clickToRowSelect: false, | ||
117 | - rowKey: primaryKey, | ||
118 | - api: selectedTableProps!.api ? handleSelectedApiIntercept : undefined, | ||
119 | - rowSelection: { | ||
120 | - type: 'checkbox', | ||
121 | - onSelect: (_record: Recordable, _selected: boolean, selectedRows: Object[]) => { | ||
122 | - selectedConfirmQueue.value = selectedRows; | ||
123 | - }, | ||
124 | - onSelectAll: (_selected: boolean, selectedRows: Recordable[]) => { | ||
125 | - selectedConfirmQueue.value = selectedRows; | ||
126 | - }, | ||
127 | - }, | ||
128 | - }; | ||
129 | - }); | ||
130 | - | ||
131 | - const getModalBindProps = computed(() => { | ||
132 | - const { modalProps = {} } = props; | ||
133 | - return { | ||
134 | - width: '60%', | ||
135 | - title: '穿梭表格', | ||
136 | - wrapClassName: prefixCls, | ||
137 | - ...modalProps, | ||
138 | - showOkBtn: false, | ||
139 | - }; | ||
140 | - }); | ||
141 | - | ||
142 | - const getBindButtonProps = computed<ButtonProps>(() => { | ||
143 | - const { buttonProps = {} } = props; | ||
144 | - return { | ||
145 | - type: 'link', | ||
146 | - ...buttonProps, | ||
147 | - }; | ||
148 | - }); | ||
149 | - | ||
150 | - const getShowTagOptions = computed(() => { | ||
151 | - const { maxTagLength } = props; | ||
152 | - return unref(selectedRows).slice(0, maxTagLength); | ||
153 | - }); | ||
154 | - | ||
155 | - const getSurplusOptionsLength = computed(() => { | ||
156 | - const { maxTagLength } = props; | ||
157 | - const surplusValue = unref(selectedRows).length - maxTagLength; | ||
158 | - return surplusValue < 0 ? 0 : surplusValue; | ||
159 | - }); | ||
160 | - | ||
161 | - const [registerModal, { openModal }] = useModal(); | ||
162 | - | ||
163 | - const [ | ||
164 | - regsterPendingTable, | ||
165 | - { | ||
166 | - getSelectRows: getPendingSelectRows, | ||
167 | - getSelectRowKeys: getPendingSelectRowKeys, | ||
168 | - reload: reloadPending, | ||
169 | - clearSelectedRowKeys: clearPendingSelectedRowKeys, | ||
170 | - }, | ||
171 | - ] = useTable(unref(getPendingTableBindProps)); | ||
172 | - | ||
173 | - const [ | ||
174 | - registerSelectedTable, | ||
175 | - { getSelectRowKeys, setProps, clearSelectedRowKeys, reload: reloadSelected }, | ||
176 | - ] = useTable(unref(getSelectedTableBindProps)); | ||
177 | - | ||
178 | - async function handlePendingApiIntercept(params?: Recordable) { | ||
179 | - try { | ||
180 | - const { api } = props.pendingTableProps || {}; | ||
181 | - if (api && isFunction(api)) { | ||
182 | - let options = await api(params); | ||
183 | - pendingOptions.value = options; | ||
184 | - const { totalField, listField } = unref(getFetchSetting); | ||
185 | - const total = get(options, totalField!); | ||
186 | - if (unref(selectedTotal) + unref(pendingTotal) !== total) { | ||
187 | - pendingTotal.value = total; | ||
188 | - } | ||
189 | - let list: Recordable[] = get(options, listField!); | ||
190 | - list = getSelectedRows(list); | ||
191 | - options = set(options, listField!, list); | ||
192 | - return options; | ||
193 | - } | ||
194 | - } catch (error) { | ||
195 | - console.error(error); | ||
196 | - return []; | ||
197 | - } | ||
198 | - return []; | ||
199 | - } | ||
200 | - | ||
201 | - async function handleSelectedApiIntercept(params?: Recordable) { | ||
202 | - try { | ||
203 | - const { api } = props.selectedTableProps || {}; | ||
204 | - if (api && isFunction(api)) { | ||
205 | - let options = await api(params); | ||
206 | - pendingOptions.value = options; | ||
207 | - const { totalField, listField } = unref(getFetchSetting); | ||
208 | - selectedTotal.value = get(options, totalField!); | ||
209 | - let list: Recordable[] = get(options, listField!); | ||
210 | - list = getSelectedRows(list); | ||
211 | - options = set(options, listField!, list); | ||
212 | - return options; | ||
213 | - } | ||
214 | - } catch (error) { | ||
215 | - console.error(error); | ||
216 | - return []; | ||
217 | - } | ||
218 | - return []; | ||
219 | - } | ||
220 | - | ||
221 | - const handleOpenModal = async () => { | ||
222 | - const { disabled } = props; | ||
223 | - if (disabled) return; | ||
224 | - openModal(true); | ||
225 | - await nextTick(); | ||
226 | - if (props.value && !props.value.length) { | ||
227 | - activeKey.value = Active.PENDING; | ||
228 | - reloadPending(); | ||
229 | - } | ||
230 | - }; | ||
231 | - | ||
232 | - const handleTriggerEmit = (selectedRowKeys: string[], selectedRows: Options[]) => { | ||
233 | - const { transformValue } = props; | ||
234 | - let value = selectedRowKeys; | ||
235 | - if (transformValue && isFunction(transformValue)) { | ||
236 | - value = transformValue(selectedRowKeys, selectedRows); | ||
237 | - } | ||
238 | - emit('change', unref(selectedRowKeys), unref(selectedRows)); | ||
239 | - emit('update:value', unref(value)); | ||
240 | - }; | ||
241 | - | ||
242 | - const handleSelected = async () => { | ||
243 | - const { onSelectedAfter } = props; | ||
244 | - const currentPageSelectRows = getPendingSelectRows(); | ||
245 | - const currentPageSelectRowKeys = getPendingSelectRowKeys(); | ||
246 | - const { primaryKey } = props; | ||
247 | - selectedRows.value = uniqBy([...unref(selectedRows), ...currentPageSelectRows], primaryKey); | ||
248 | - selectedRowKeys.value = [...new Set([...unref(selectedRowKeys), ...currentPageSelectRowKeys])]; | ||
249 | - pendingConfirmQueue.value = []; | ||
250 | - // selectedTotal.value = unref(selectedRowKeys).length; | ||
251 | - pendingTotal.value = unref(pendingTotal) - currentPageSelectRows.length; | ||
252 | - selectedTotal.value = unref(selectedTotal) + currentPageSelectRows.length; | ||
253 | - | ||
254 | - clearPendingSelectedRowKeys(); | ||
255 | - handleTriggerEmit(unref(selectedRowKeys), unref(selectedRows)); | ||
256 | - | ||
257 | - if (onSelectedAfter && isFunction(onSelectedAfter)) { | ||
258 | - await onSelectedAfter(actionType); | ||
259 | - } | ||
260 | - reloadPending(); | ||
261 | - }; | ||
262 | - | ||
263 | - const handleRemoveSelected = async () => { | ||
264 | - const { onRemoveAfter } = props; | ||
265 | - const removeRowKeys = getSelectRowKeys(); | ||
266 | - selectedRowKeys.value = unref(selectedRowKeys).filter((key) => !removeRowKeys.includes(key)); | ||
267 | - selectedRows.value = unref(selectedRows).filter((item) => { | ||
268 | - const { primaryKey } = props; | ||
269 | - return unref(selectedRowKeys).includes(item[primaryKey]); | ||
270 | - }); | ||
271 | - pendingTotal.value = unref(pendingTotal) + removeRowKeys.length; | ||
272 | - selectedTotal.value = unref(selectedTotal) - removeRowKeys.length; | ||
273 | - | ||
274 | - clearSelectedRowKeys(); | ||
275 | - selectedConfirmQueue.value = []; | ||
276 | - setProps({ dataSource: unref(selectedRows) }); | ||
277 | - handleTriggerEmit(unref(selectedRowKeys), unref(selectedRows)); | ||
278 | - | ||
279 | - if (onRemoveAfter && isFunction(onRemoveAfter)) { | ||
280 | - await onRemoveAfter(actionType); | ||
281 | - } | ||
282 | - }; | ||
283 | - | ||
284 | - const actionType = { | ||
285 | - setSelectedOptions, | ||
286 | - setSelectedTotal, | ||
287 | - reloadPending, | ||
288 | - reloadSelected, | ||
289 | - }; | ||
290 | - | ||
291 | - const getSelectedRows = (options: Recordable[]) => { | ||
292 | - const { labelField, valueField } = props; | ||
293 | - return options.map((item) => ({ ...item, label: item[labelField], value: item[valueField] })); | ||
294 | - }; | ||
295 | - | ||
296 | - const getSelectedKeys = (options: Recordable[]) => { | ||
297 | - const { primaryKey } = props; | ||
298 | - return options.map((item) => item[primaryKey]); | ||
299 | - }; | ||
300 | - | ||
301 | - function setSelectedOptions(options: Recordable[]) { | ||
302 | - selectedRows.value = getSelectedRows(options); | ||
303 | - selectedRowKeys.value = getSelectedKeys(options); | ||
304 | - } | ||
305 | - | ||
306 | - function setSelectedTotal(number: number) { | ||
307 | - selectedTotal.value = number; | ||
308 | - } | ||
309 | - | ||
310 | - const handleCheckoutPanel = async (keys: Active) => { | ||
311 | - await nextTick(); | ||
312 | - if (keys === Active.PENDING) { | ||
313 | - reloadPending(); | ||
314 | - } else { | ||
315 | - reloadSelected(); | ||
316 | - setProps({ | ||
317 | - dataSource: unref(selectedRows), | ||
318 | - }); | ||
319 | - } | ||
320 | - }; | ||
321 | - | ||
322 | - watch( | ||
323 | - () => props.value, | ||
324 | - () => { | ||
325 | - if (props.value && !props.value.length) { | ||
326 | - selectedRowKeys.value = []; | ||
327 | - selectedRows.value = []; | ||
328 | - // pendingTotal.value = 0; | ||
329 | - selectedTotal.value = 0; | ||
330 | - } | ||
331 | - } | ||
332 | - ); | ||
333 | - | ||
334 | - onMounted(async () => { | ||
335 | - const { initSelectedOptions } = props; | ||
336 | - if (initSelectedOptions && isFunction(initSelectedOptions)) { | ||
337 | - const options = await initSelectedOptions(actionType); | ||
338 | - setSelectedOptions(options); | ||
339 | - } | ||
340 | - }); | ||
341 | -</script> | ||
342 | - | ||
343 | -<template> | ||
344 | - <section> | ||
345 | - <BasicModal @register="registerModal" v-bind="getModalBindProps"> | ||
346 | - <section class="bg-gray-100"> | ||
347 | - <Tabs v-model:active-key="activeKey" type="card" @change="handleCheckoutPanel"> | ||
348 | - <Tabs.TabPane :key="Active.PENDING"> | ||
349 | - <template #tab> | ||
350 | - <div class="flex items-center justify-center"> | ||
351 | - <span>待选设备</span> | ||
352 | - <Badge show-zero :count="pendingTotal" /> | ||
353 | - </div> | ||
354 | - </template> | ||
355 | - <BasicTable @register="regsterPendingTable"> | ||
356 | - <template #toolbar> | ||
357 | - <section class="flex w-full justify-end items-center"> | ||
358 | - <!-- <Button type="primary">全选</Button> --> | ||
359 | - <div class="text-blue-400"> | ||
360 | - <span class="mr-2">选择设备:</span> | ||
361 | - <span>{{ pendingConfirmQueue.length }}</span> | ||
362 | - </div> | ||
363 | - </section> | ||
364 | - </template> | ||
365 | - </BasicTable> | ||
366 | - <section class="flex justify-end px-4 pb-4"> | ||
367 | - <Button | ||
368 | - type="primary" | ||
369 | - @click="handleSelected" | ||
370 | - :disabled="!pendingConfirmQueue.length" | ||
371 | - > | ||
372 | - <span>确定已选</span> | ||
373 | - </Button> | ||
374 | - </section> | ||
375 | - </Tabs.TabPane> | ||
376 | - <Tabs.TabPane :key="Active.SELECTED"> | ||
377 | - <template #tab> | ||
378 | - <div class="flex items-center justify-center"> | ||
379 | - <span>已选设备</span> | ||
380 | - <Badge show-zero :count="selectedTotal" /> | ||
381 | - </div> | ||
382 | - </template> | ||
383 | - <BasicTable @register="registerSelectedTable"> | ||
384 | - <template #toolbar> | ||
385 | - <section class="flex w-full justify-end items-center"> | ||
386 | - <!-- <Button type="primary">全选</Button> --> | ||
387 | - <div class="text-blue-400"> | ||
388 | - <span class="mr-2">选择设备:</span> | ||
389 | - <span>{{ selectedConfirmQueue.length }}</span> | ||
390 | - </div> | ||
391 | - </section> | ||
392 | - </template> | ||
393 | - </BasicTable> | ||
394 | - <section class="flex justify-end px-4 pb-4"> | ||
395 | - <Button | ||
396 | - type="primary" | ||
397 | - :disabled="!selectedConfirmQueue.length" | ||
398 | - @click="handleRemoveSelected" | ||
399 | - > | ||
400 | - <span>移除已选</span> | ||
401 | - </Button> | ||
402 | - </section> | ||
403 | - </Tabs.TabPane> | ||
404 | - </Tabs> | ||
405 | - </section> | ||
406 | - </BasicModal> | ||
407 | - <Button @click="handleOpenModal" v-bind="getBindButtonProps"> | ||
408 | - <span v-if="!selectedRowKeys.length">选择设备</span> | ||
409 | - <div v-if="selectedRowKeys.length"> | ||
410 | - <Tag | ||
411 | - class="!px-2 !py-1 !bg-gray-50 !border-gray-100" | ||
412 | - v-for="item in getShowTagOptions" | ||
413 | - :key="item.value" | ||
414 | - > | ||
415 | - <span> | ||
416 | - {{ item.alias || item.name }} | ||
417 | - </span> | ||
418 | - </Tag> | ||
419 | - <Tag class="!px-2 !py-1 !bg-gray-50 !border-gray-100" v-if="getSurplusOptionsLength"> | ||
420 | - <span> +{{ getSurplusOptionsLength }}... </span> | ||
421 | - </Tag> | ||
422 | - </div> | ||
423 | - </Button> | ||
424 | - </section> | ||
425 | -</template> | ||
426 | - | ||
427 | -<style lang="less"> | ||
428 | - @prefix-cls: ~'@{namespace}-transfer-table-modal'; | ||
429 | - | ||
430 | - .@{prefix-cls} { | ||
431 | - .vben-basic-table { | ||
432 | - padding-top: 0; | ||
433 | - } | ||
434 | - | ||
435 | - .vben-basic-form > .ant-row { | ||
436 | - width: 100%; | ||
437 | - } | ||
438 | - | ||
439 | - .ant-tabs-top-bar { | ||
440 | - background-color: #fff; | ||
441 | - } | ||
442 | - } | ||
443 | -</style> |
@@ -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 = ({ serviceCommand }: StructJSON): FormSchema => { | ||
120 | + return { | ||
121 | + field: 'serviceCommand', | ||
122 | + label: '服务命令', | ||
123 | + component: 'Input', | ||
124 | + required, | ||
125 | + defaultValue: serviceCommand, | ||
126 | + componentProps: { | ||
127 | + placeholder: `请输入服务命令`, | ||
128 | + }, | ||
129 | + }; | ||
130 | + }; | ||
131 | + | ||
132 | + const schemas: FormSchema[] = []; | ||
133 | + | ||
134 | + for (const item of structJson) { | ||
135 | + const { dataType } = item; | ||
136 | + const { type } = dataType || {}; | ||
137 | + if (transportType === TransportTypeEnum.TCP) { | ||
138 | + item.serviceCommand && schemas.push(createTCPServiceCommandInput(item)); | ||
139 | + break; | ||
140 | + } | ||
141 | + | ||
142 | + if (type === DataTypeEnum.BOOL) schemas.push(createSelect(item)); | ||
143 | + else if (type === DataTypeEnum.NUMBER_INT) schemas.push(createInputNumber(item)); | ||
144 | + else if (type === DataTypeEnum.NUMBER_DOUBLE) schemas.push(createInputNumber(item)); | ||
145 | + else if (type === DataTypeEnum.STRING) schemas.push(createInput(item)); | ||
146 | + else if (type === DataTypeEnum.STRUCT) schemas.push(createStructJson(item)); | ||
147 | + } | ||
148 | + | ||
149 | + return schemas; | ||
150 | +}; |
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(/(?!^)(?=(\w{2})+$)/g, ''); | ||
11 | + | ||
12 | +export const formatCommandString = (string = '') => string.replace(/(?!^)(?=(\w{2})+$)/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-row) { | ||
155 | + width: 100%; | ||
156 | + } | ||
157 | + | ||
158 | + :deep(.ant-input-number) { | ||
159 | + width: 100%; | ||
160 | + } | ||
161 | + | ||
162 | + :deep(.ant-form-item-label) { | ||
163 | + label { | ||
164 | + width: 100%; | ||
165 | + display: block; | ||
166 | + overflow: hidden; | ||
167 | + text-overflow: ellipsis; | ||
168 | + } | ||
169 | + } | ||
170 | + } | ||
171 | + | ||
172 | + :deep(.ant-form-item-control) { | ||
173 | + margin-left: 6px; | ||
174 | + } | ||
175 | +</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'; |
@@ -25,19 +25,18 @@ export function usePagination(refProps: ComputedRef<BasicTableProps>) { | @@ -25,19 +25,18 @@ export function usePagination(refProps: ComputedRef<BasicTableProps>) { | ||
25 | const { t } = useI18n(); | 25 | const { t } = useI18n(); |
26 | 26 | ||
27 | const configRef = ref<PaginationProps>({ | 27 | const configRef = ref<PaginationProps>({ |
28 | - hideOnSinglePage:true | 28 | + hideOnSinglePage: true, |
29 | }); | 29 | }); |
30 | const show = ref(true); | 30 | const show = ref(true); |
31 | 31 | ||
32 | watchEffect(() => { | 32 | watchEffect(() => { |
33 | const { pagination } = unref(refProps); | 33 | const { pagination } = unref(refProps); |
34 | - | 34 | + |
35 | if (!isBoolean(pagination) && pagination) { | 35 | if (!isBoolean(pagination) && pagination) { |
36 | configRef.value = { | 36 | configRef.value = { |
37 | ...unref(configRef), | 37 | ...unref(configRef), |
38 | ...(pagination ?? {}), | 38 | ...(pagination ?? {}), |
39 | - }; | ||
40 | - | 39 | + }; |
41 | } | 40 | } |
42 | }); | 41 | }); |
43 | 42 |
@@ -88,7 +88,7 @@ export function useTableScroll( | @@ -88,7 +88,7 @@ export function useTableScroll( | ||
88 | 88 | ||
89 | bodyEl!.style.height = 'unset'; | 89 | bodyEl!.style.height = 'unset'; |
90 | 90 | ||
91 | - if (!unref(getCanResize) || tableData.length === 0) return; | 91 | + if (!unref(getCanResize)) return; |
92 | 92 | ||
93 | await nextTick(); | 93 | await nextTick(); |
94 | //Add a delay to get the correct bottomIncludeBody paginationHeight footerHeight headerHeight | 94 | //Add a delay to get the correct bottomIncludeBody paginationHeight footerHeight headerHeight |
@@ -143,7 +143,12 @@ export function useTableScroll( | @@ -143,7 +143,12 @@ export function useTableScroll( | ||
143 | height = (height > maxHeight! ? (maxHeight as number) : height) ?? height; | 143 | height = (height > maxHeight! ? (maxHeight as number) : height) ?? height; |
144 | setHeight(height); | 144 | setHeight(height); |
145 | 145 | ||
146 | - bodyEl!.style.height = `${height}px`; | 146 | + if (tableData.length) { |
147 | + bodyEl!.style.height = `${height}px`; | ||
148 | + } else { | ||
149 | + const emptyPlaceholder: HTMLDivElement = tableEl.querySelector('.ant-table-placeholder')!; | ||
150 | + emptyPlaceholder && (emptyPlaceholder.style.height = `${height}px`); | ||
151 | + } | ||
147 | } | 152 | } |
148 | useWindowSizeFn(calcTableHeight, 280); | 153 | useWindowSizeFn(calcTableHeight, 280); |
149 | onMountedOrActivated(() => { | 154 | onMountedOrActivated(() => { |
@@ -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 | +} |
@@ -23,4 +23,7 @@ export enum DictEnum { | @@ -23,4 +23,7 @@ export enum DictEnum { | ||
23 | MESSAGE_TYPES_FILTER = 'message_types_filter', | 23 | MESSAGE_TYPES_FILTER = 'message_types_filter', |
24 | // 实体类型 规则节点 Filter originator types switch | 24 | // 实体类型 规则节点 Filter originator types switch |
25 | ORIGINATOR_TYPES = 'originator_types', | 25 | ORIGINATOR_TYPES = 'originator_types', |
26 | + | ||
27 | + // 产品品类领域 | ||
28 | + CATEGORY_FIELD = 'category_field', | ||
26 | } | 29 | } |
src/enums/linkedgeEnum.ts
0 → 100644
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 | +} |
src/enums/objectModelEnum.ts
0 → 100644
src/enums/operationEnum.ts
deleted
100644 → 0
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', |
@@ -5,4 +5,5 @@ | @@ -5,4 +5,5 @@ | ||
5 | export const enum UserDropDownItemEnum { | 5 | export const enum UserDropDownItemEnum { |
6 | FORGOT_PASSWORD = 'system:password:view', //忘记密码权限标识key | 6 | FORGOT_PASSWORD = 'system:password:view', //忘记密码权限标识key |
7 | ABOUT_SOFTWARE = 'system:about_software:view', //关于软件权限标识key | 7 | ABOUT_SOFTWARE = 'system:about_software:view', //关于软件权限标识key |
8 | + PERSONAL_CENTER = 'system:personal_center:view', //个人中心权限标识key | ||
8 | } | 9 | } |
@@ -12,6 +12,7 @@ | @@ -12,6 +12,7 @@ | ||
12 | <template #overlay> | 12 | <template #overlay> |
13 | <Menu @click="handleMenuClick"> | 13 | <Menu @click="handleMenuClick"> |
14 | <MenuItem | 14 | <MenuItem |
15 | + v-if="hasPermission(UserDropDownItemEnum.PERSONAL_CENTER)" | ||
15 | key="personal" | 16 | key="personal" |
16 | :text="t('layout.header.dropdownItemPersonal')" | 17 | :text="t('layout.header.dropdownItemPersonal')" |
17 | icon="ion:document-text-outline" | 18 | icon="ion:document-text-outline" |
1 | export const createPickerSearch = (searchValue = false) => { | 1 | export const createPickerSearch = (searchValue = false) => { |
2 | return { | 2 | return { |
3 | showSearch: true, | 3 | showSearch: true, |
4 | + getPopupContainer: (triggerNode) => triggerNode.parentNode, | ||
4 | filterOption: (inputValue: string, option: Record<'label' | 'value', string>) => { | 5 | filterOption: (inputValue: string, option: Record<'label' | 'value', string>) => { |
5 | let { label, value } = option; | 6 | let { label, value } = option; |
6 | label = label.toLowerCase(); | 7 | label = label.toLowerCase(); |
@@ -120,6 +120,22 @@ export const emailRule: Rule[] = [ | @@ -120,6 +120,22 @@ export const emailRule: Rule[] = [ | ||
120 | }, | 120 | }, |
121 | ]; | 121 | ]; |
122 | 122 | ||
123 | +// 钉钉验证 | ||
124 | +export const dingRule: Rule[] = [ | ||
125 | + { | ||
126 | + validator: (_, value: string) => { | ||
127 | + const reg = /^[1][3,4,5,6,7,8,9][0-9]{9}$/; | ||
128 | + if (!value) { | ||
129 | + return Promise.resolve(); | ||
130 | + } else if (!reg.test(value)) { | ||
131 | + return Promise.reject('手机号格式不正确'); | ||
132 | + } | ||
133 | + return Promise.resolve(); | ||
134 | + }, | ||
135 | + validateTrigger: 'blur', | ||
136 | + }, | ||
137 | +]; | ||
138 | + | ||
123 | // 中文正则 | 139 | // 中文正则 |
124 | export const ChineseRegexp = /[\u4E00-\u9FA5]/; | 140 | export const ChineseRegexp = /[\u4E00-\u9FA5]/; |
125 | 141 |
1 | import { BasicColumn, FormSchema } from '/@/components/Table'; | 1 | import { BasicColumn, FormSchema } from '/@/components/Table'; |
2 | -import { emailRule, phoneRule } from '/@/utils/rules'; | 2 | +import { dingRule, emailRule, phoneRule } from '/@/utils/rules'; |
3 | import { useComponentRegister } from '/@/components/Form'; | 3 | import { useComponentRegister } from '/@/components/Form'; |
4 | import { OrgTreeSelect } from '../../common/OrgTreeSelect'; | 4 | import { OrgTreeSelect } from '../../common/OrgTreeSelect'; |
5 | useComponentRegister('OrgTreeSelect', OrgTreeSelect); | 5 | useComponentRegister('OrgTreeSelect', OrgTreeSelect); |
@@ -31,6 +31,11 @@ export const columns: BasicColumn[] = [ | @@ -31,6 +31,11 @@ export const columns: BasicColumn[] = [ | ||
31 | width: 180, | 31 | width: 180, |
32 | }, | 32 | }, |
33 | { | 33 | { |
34 | + title: '钉钉', | ||
35 | + dataIndex: 'dingtalk', | ||
36 | + width: 160, | ||
37 | + }, | ||
38 | + { | ||
34 | title: '备注', | 39 | title: '备注', |
35 | dataIndex: 'remark', | 40 | dataIndex: 'remark', |
36 | width: 120, | 41 | width: 120, |
@@ -109,6 +114,17 @@ export const formSchema: FormSchema[] = [ | @@ -109,6 +114,17 @@ export const formSchema: FormSchema[] = [ | ||
109 | }, | 114 | }, |
110 | }, | 115 | }, |
111 | { | 116 | { |
117 | + field: 'dingtalk', | ||
118 | + label: '钉钉', | ||
119 | + component: 'Input', | ||
120 | + helpMessage: '请输入与钉钉相关联的手机号', | ||
121 | + rules: dingRule, | ||
122 | + componentProps: { | ||
123 | + placeholder: '不填默认为手机号码', | ||
124 | + maxLength: 11, | ||
125 | + }, | ||
126 | + }, | ||
127 | + { | ||
112 | field: 'remark', | 128 | field: 'remark', |
113 | label: '备注', | 129 | label: '备注', |
114 | component: 'InputTextArea', | 130 | component: 'InputTextArea', |
@@ -127,10 +127,19 @@ export function useAlarmNotify(params: UseAlarmNotifyParams = {}) { | @@ -127,10 +127,19 @@ export function useAlarmNotify(params: UseAlarmNotifyParams = {}) { | ||
127 | clearInterval(timeout as NodeJS.Timer); | 127 | clearInterval(timeout as NodeJS.Timer); |
128 | timeout = null; | 128 | timeout = null; |
129 | }; | 129 | }; |
130 | + | ||
130 | onMounted(() => { | 131 | onMounted(() => { |
131 | if (getRoleHasNotifyFlag()) polling(); | 132 | if (getRoleHasNotifyFlag()) polling(); |
132 | }); | 133 | }); |
133 | 134 | ||
135 | + document.addEventListener('visibilitychange', () => { | ||
136 | + if (document.hidden) { | ||
137 | + clearTimeout(); | ||
138 | + } else { | ||
139 | + polling(); | ||
140 | + } | ||
141 | + }); | ||
142 | + | ||
134 | onUnmounted(() => { | 143 | onUnmounted(() => { |
135 | clearTimeout(); | 144 | clearTimeout(); |
136 | }); | 145 | }); |
@@ -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> |
@@ -222,6 +222,14 @@ | @@ -222,6 +222,14 @@ | ||
222 | </div> | 222 | </div> |
223 | </template> | 223 | </template> |
224 | <template class="ant-card-actions" #actions> | 224 | <template class="ant-card-actions" #actions> |
225 | + <Tooltip title="预览"> | ||
226 | + <AuthIcon | ||
227 | + :auth="ConfigurationTemplatePermission.PREVIEW" | ||
228 | + class="!text-lg" | ||
229 | + icon="ant-design:eye-outlined" | ||
230 | + @click="handlePreview(item)" | ||
231 | + /> | ||
232 | + </Tooltip> | ||
225 | <Tooltip v-if="!isCustomerUser" title="设计"> | 233 | <Tooltip v-if="!isCustomerUser" title="设计"> |
226 | <AuthIcon | 234 | <AuthIcon |
227 | :auth="ConfigurationTemplatePermission.DESIGN" | 235 | :auth="ConfigurationTemplatePermission.DESIGN" |
@@ -96,6 +96,10 @@ | @@ -96,6 +96,10 @@ | ||
96 | top: '48%', | 96 | top: '48%', |
97 | z: 10, | 97 | z: 10, |
98 | style: { | 98 | style: { |
99 | + width: 35, | ||
100 | + textAlign: 'left', | ||
101 | + overflow: 'truncate', | ||
102 | + textVerticalAlign: 'middle', | ||
99 | fill: '#1a1a1a', | 103 | fill: '#1a1a1a', |
100 | text: value + '个', | 104 | text: value + '个', |
101 | font: '0.85rem Microsoft YaHei', | 105 | font: '0.85rem Microsoft YaHei', |
@@ -232,12 +232,6 @@ export const schemas = (): FormSchema[] => { | @@ -232,12 +232,6 @@ export const schemas = (): FormSchema[] => { | ||
232 | component: 'InputGroup', | 232 | component: 'InputGroup', |
233 | required: true, | 233 | required: true, |
234 | colProps: { span: 24 }, | 234 | colProps: { span: 24 }, |
235 | - componentProps: ({ formActionType }) => { | ||
236 | - const { getFieldsValue } = formActionType; | ||
237 | - return { | ||
238 | - type: getFieldsValue().requestContentType, | ||
239 | - }; | ||
240 | - }, | ||
241 | }, | 235 | }, |
242 | { | 236 | { |
243 | field: 'fillAddress', | 237 | field: 'fillAddress', |
@@ -127,6 +127,15 @@ | @@ -127,6 +127,15 @@ | ||
127 | showActionButtonGroup: false, | 127 | showActionButtonGroup: false, |
128 | }); | 128 | }); |
129 | 129 | ||
130 | + const updatePartFormScheme = (requestType: string) => { | ||
131 | + updateSchema({ | ||
132 | + field: 'requestHttpTypeAndUrl', | ||
133 | + componentProps: { | ||
134 | + type: requestType, | ||
135 | + }, | ||
136 | + }); | ||
137 | + }; | ||
138 | + | ||
130 | const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => { | 139 | const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => { |
131 | await resetFields(); | 140 | await resetFields(); |
132 | await nextTick(); | 141 | await nextTick(); |
@@ -152,13 +161,12 @@ | @@ -152,13 +161,12 @@ | ||
152 | requestUrl: data.record?.requestUrl, | 161 | requestUrl: data.record?.requestUrl, |
153 | }, | 162 | }, |
154 | }); | 163 | }); |
155 | - await nextTick(() => simpleRequestRef.value?.setValue(data.record, false, [])); | ||
156 | - updateSchema({ | ||
157 | - field: 'requestHttpTypeAndUrl', | ||
158 | - componentProps: { | ||
159 | - type: String(data.record?.requestContentType), | ||
160 | - }, | 164 | + await nextTick(() => { |
165 | + simpleRequestRef.value?.setValue(data.record, false, []); | ||
166 | + updatePartFormScheme(String(data.record?.requestContentType)); | ||
161 | }); | 167 | }); |
168 | + } else { | ||
169 | + await updatePartFormScheme(RequestMethodTypeEnum.COMMOM); | ||
162 | } | 170 | } |
163 | }); | 171 | }); |
164 | 172 |
1 | +<script setup lang="ts"> | ||
2 | + import { computed, unref } from 'vue'; | ||
3 | + import { ref } from 'vue'; | ||
4 | + import { BasicModal, useModalInner } from '/@/components/Modal'; | ||
5 | + import { schemas } from '../index'; | ||
6 | + import { useForm, BasicForm } from '/@/components/Form'; | ||
7 | + | ||
8 | + import { deviceProfileCategory } from '/@/api/device/classModal'; | ||
9 | + import { useMessage } from '/@/hooks/web/useMessage'; | ||
10 | + | ||
11 | + const emit = defineEmits(['handleReload', 'register']); | ||
12 | + | ||
13 | + const isUpdate = ref<Boolean>(false); | ||
14 | + const getTitle = computed(() => (!unref(isUpdate) ? '新增品类' : '编辑品类')); | ||
15 | + const { createMessage } = useMessage(); | ||
16 | + | ||
17 | + const [registerForm, { getFieldsValue, setFieldsValue, validate }] = useForm({ | ||
18 | + labelWidth: 140, | ||
19 | + schemas, | ||
20 | + actionColOptions: { | ||
21 | + span: 14, | ||
22 | + }, | ||
23 | + showActionButtonGroup: false, | ||
24 | + }); | ||
25 | + const recordInfo = ref<any>({}); | ||
26 | + | ||
27 | + const [register, { closeModal, setModalProps }] = useModalInner(async (data) => { | ||
28 | + console.log(123131, data); | ||
29 | + setModalProps({ confirmLoading: false, loading: true }); | ||
30 | + isUpdate.value = data?.isUpdate; | ||
31 | + recordInfo.value = data?.record; | ||
32 | + if (data?.record) { | ||
33 | + setFieldsValue(data?.record); | ||
34 | + } | ||
35 | + setModalProps({ loading: false }); | ||
36 | + }); | ||
37 | + | ||
38 | + const handleCancel = () => { | ||
39 | + closeModal(); | ||
40 | + }; | ||
41 | + | ||
42 | + const handleOk = async () => { | ||
43 | + await validate(); | ||
44 | + let values = getFieldsValue(); | ||
45 | + if (unref(isUpdate)) { | ||
46 | + values = { ...values, id: unref(recordInfo).id }; | ||
47 | + } | ||
48 | + await deviceProfileCategory(values); | ||
49 | + createMessage.success('操作成功'); | ||
50 | + emit('handleReload'); | ||
51 | + handleCancel(); | ||
52 | + }; | ||
53 | +</script> | ||
54 | + | ||
55 | +<template> | ||
56 | + <div> | ||
57 | + <BasicModal | ||
58 | + v-bind="$attrs" | ||
59 | + width="30rem" | ||
60 | + :title="getTitle" | ||
61 | + @register="register" | ||
62 | + @cancel="handleCancel" | ||
63 | + @ok="handleOk" | ||
64 | + destroyOnClose | ||
65 | + > | ||
66 | + <div> | ||
67 | + <BasicForm @register="registerForm" /> | ||
68 | + </div> | ||
69 | + </BasicModal> | ||
70 | + </div> | ||
71 | +</template> |
src/views/device/classIfication/index.ts
0 → 100644
1 | +import { BasicColumn, FormSchema } from '/@/components/Table'; | ||
2 | +import { findDictItemByCode } from '/@/api/system/dict'; | ||
3 | + | ||
4 | +import { DictEnum } from '/@/enums/dictEnum'; | ||
5 | + | ||
6 | +export const columns: BasicColumn[] = [ | ||
7 | + { | ||
8 | + title: '名称', | ||
9 | + dataIndex: 'name', | ||
10 | + }, | ||
11 | + { | ||
12 | + title: '领域', | ||
13 | + dataIndex: 'dictItemName', | ||
14 | + }, | ||
15 | + { | ||
16 | + title: '状态', | ||
17 | + dataIndex: 'status', | ||
18 | + slots: { customRender: 'status' }, | ||
19 | + }, | ||
20 | +]; | ||
21 | + | ||
22 | +export const searchFormSchema: FormSchema[] = [ | ||
23 | + { | ||
24 | + field: 'name', | ||
25 | + label: '名称', | ||
26 | + component: 'Input', | ||
27 | + colProps: { span: 6 }, | ||
28 | + componentProps: { | ||
29 | + maxLength: 255, | ||
30 | + placeholder: '请输入品类名称', | ||
31 | + }, | ||
32 | + }, | ||
33 | + { | ||
34 | + field: 'status', | ||
35 | + label: '状态', | ||
36 | + component: 'Select', | ||
37 | + colProps: { span: 6 }, | ||
38 | + componentProps: { | ||
39 | + options: [ | ||
40 | + { label: '启用', value: '1' }, | ||
41 | + { label: '禁用', value: '0' }, | ||
42 | + ], | ||
43 | + placeholder: '请选择品类状态', | ||
44 | + }, | ||
45 | + }, | ||
46 | + { | ||
47 | + field: 'dictItemId', | ||
48 | + label: '领域', | ||
49 | + component: 'ApiSelect', | ||
50 | + colProps: { span: 6 }, | ||
51 | + componentProps() { | ||
52 | + return { | ||
53 | + api: findDictItemByCode, | ||
54 | + params: { | ||
55 | + dictCode: DictEnum.CATEGORY_FIELD, | ||
56 | + }, | ||
57 | + labelField: 'itemText', | ||
58 | + valueField: 'itemValue', | ||
59 | + placeholder: '请选择领域', | ||
60 | + getPopupContainer: () => document.body, | ||
61 | + }; | ||
62 | + }, | ||
63 | + }, | ||
64 | +]; | ||
65 | + | ||
66 | +export const schemas: FormSchema[] = [ | ||
67 | + { | ||
68 | + field: 'name', | ||
69 | + label: '品类名称', | ||
70 | + component: 'Input', | ||
71 | + colProps: { span: 21 }, | ||
72 | + required: true, | ||
73 | + componentProps: { | ||
74 | + maxLength: 20, | ||
75 | + }, | ||
76 | + }, | ||
77 | + { | ||
78 | + field: 'dictItemId', | ||
79 | + label: '领域', | ||
80 | + component: 'ApiSelect', | ||
81 | + colProps: { span: 21 }, | ||
82 | + required: true, | ||
83 | + componentProps({ formActionType }) { | ||
84 | + return { | ||
85 | + api: findDictItemByCode, | ||
86 | + params: { | ||
87 | + dictCode: DictEnum.CATEGORY_FIELD, | ||
88 | + }, | ||
89 | + onChange(value, options) { | ||
90 | + formActionType.setFieldsValue({ dictItemName: value ? options.label : '' }); | ||
91 | + }, | ||
92 | + labelField: 'itemText', | ||
93 | + valueField: 'itemValue', | ||
94 | + placeholder: '请选择领域', | ||
95 | + getPopupContainer: () => document.body, | ||
96 | + }; | ||
97 | + }, | ||
98 | + }, | ||
99 | + { | ||
100 | + field: 'dictItemName', | ||
101 | + label: '领域名称', | ||
102 | + ifShow: false, | ||
103 | + component: 'Input', | ||
104 | + }, | ||
105 | + { | ||
106 | + field: 'status', | ||
107 | + label: '状态', | ||
108 | + component: 'RadioButtonGroup', | ||
109 | + defaultValue: 1, | ||
110 | + componentProps: { | ||
111 | + options: [ | ||
112 | + { label: '启用', value: 1 }, | ||
113 | + { label: '禁用', value: 0 }, | ||
114 | + ], | ||
115 | + }, | ||
116 | + }, | ||
117 | +]; |
src/views/device/classIfication/index.vue
0 → 100644
1 | +<script setup lang="ts"> | ||
2 | + import { ref } from 'vue'; | ||
3 | + import { columns, searchFormSchema } from '.'; | ||
4 | + import { BasicTable, useTable, TableAction } from '/@/components/Table'; | ||
5 | + import { Button, Popconfirm, Switch } from 'ant-design-vue'; | ||
6 | + import { useBatchOperation } from '/@/utils/useBatchOperation'; | ||
7 | + import { useMessage } from '/@/hooks/web/useMessage'; | ||
8 | + import { Authority } from '/@/components/Authority'; | ||
9 | + import { useModal } from '/@/components/Modal'; | ||
10 | + import { classModal } from './components/index'; | ||
11 | + import { useDrawer } from '/@/components/Drawer'; | ||
12 | + import { USER_INFO_KEY } from '/@/enums/cacheEnum'; | ||
13 | + import { getAuthCache } from '/@/utils/auth'; | ||
14 | + import { authBtn } from '/@/enums/roleEnum'; | ||
15 | + import { | ||
16 | + getDeviceClassList, | ||
17 | + deleteDeviceClass, | ||
18 | + deviceProfileCategory, | ||
19 | + } from '/@/api/device/classModal'; | ||
20 | + | ||
21 | + import { BasicDrawer } from '/@/components/Drawer'; | ||
22 | + import PhysicalModelManagementStep from '/@/views/device/profiles/step/PhysicalModelManagementStep.vue'; | ||
23 | + const [ | ||
24 | + registerTable, | ||
25 | + { reload, setLoading, getSelectRowKeys, setSelectedRowKeys, getRowSelection }, | ||
26 | + ] = useTable({ | ||
27 | + title: '产品品类列表', | ||
28 | + api: getDeviceClassList, | ||
29 | + columns, | ||
30 | + formConfig: { | ||
31 | + labelWidth: 100, | ||
32 | + schemas: searchFormSchema, | ||
33 | + }, | ||
34 | + immediate: true, | ||
35 | + useSearchForm: true, | ||
36 | + showTableSetting: true, | ||
37 | + bordered: true, | ||
38 | + showIndexColumn: false, | ||
39 | + clickToRowSelect: false, | ||
40 | + rowKey: 'id', | ||
41 | + // searchInfo: searchInfo, | ||
42 | + actionColumn: { | ||
43 | + width: 230, | ||
44 | + title: '操作', | ||
45 | + slots: { customRender: 'action' }, | ||
46 | + fixed: 'right', | ||
47 | + }, | ||
48 | + rowSelection: { | ||
49 | + type: 'checkbox', | ||
50 | + getCheckboxProps: (record: any) => { | ||
51 | + return { disabled: !!record.status }; | ||
52 | + }, | ||
53 | + }, | ||
54 | + }); | ||
55 | + | ||
56 | + const [registerModal, { openModal }] = useModal(); | ||
57 | + const [registerDetailDrawer, { openDrawer }] = useDrawer(); | ||
58 | + | ||
59 | + const { createMessage } = useMessage(); | ||
60 | + const { isExistOption } = useBatchOperation(getRowSelection, setSelectedRowKeys); | ||
61 | + | ||
62 | + const switchLoading = ref<boolean>(false); | ||
63 | + | ||
64 | + const userInfo: any = getAuthCache(USER_INFO_KEY); | ||
65 | + const role: string = userInfo.roles[0]; | ||
66 | + | ||
67 | + const handleReload = () => { | ||
68 | + setSelectedRowKeys([]); | ||
69 | + reload(); | ||
70 | + }; | ||
71 | + | ||
72 | + // 新增 | ||
73 | + const handleCreate = () => { | ||
74 | + openModal(true, { | ||
75 | + isUpdate: false, | ||
76 | + }); | ||
77 | + }; | ||
78 | + | ||
79 | + // 编辑 | ||
80 | + const handleEdit = (record?: any) => { | ||
81 | + openModal(true, { | ||
82 | + isUpdate: true, | ||
83 | + record, | ||
84 | + }); | ||
85 | + }; | ||
86 | + | ||
87 | + // 详情 | ||
88 | + const registerDetailRecord = ref<any>({}); | ||
89 | + const handleDetail = (record?: any) => { | ||
90 | + openDrawer(true); | ||
91 | + registerDetailRecord.value = { | ||
92 | + ...record, | ||
93 | + ifShowClass: true, | ||
94 | + }; | ||
95 | + }; | ||
96 | + | ||
97 | + // 状态->编辑 | ||
98 | + const handleSwitch = async (e: any, record: any) => { | ||
99 | + switchLoading.value = true; | ||
100 | + console.log(e, record); | ||
101 | + await deviceProfileCategory({ ...record, status: e }); | ||
102 | + switchLoading.value = false; | ||
103 | + createMessage.success('操作成功'); | ||
104 | + handleReload(); | ||
105 | + }; | ||
106 | + | ||
107 | + // 删除 | ||
108 | + const handleDelete = async (record?: any) => { | ||
109 | + let ids: string[] = []; | ||
110 | + if (record) { | ||
111 | + ids = [record.id]; | ||
112 | + } else { | ||
113 | + ids = getSelectRowKeys(); | ||
114 | + } | ||
115 | + try { | ||
116 | + setLoading(true); | ||
117 | + await deleteDeviceClass({ ids }); | ||
118 | + createMessage.success('删除成功'); | ||
119 | + handleReload(); | ||
120 | + } catch (error) { | ||
121 | + throw error; | ||
122 | + } finally { | ||
123 | + setLoading(false); | ||
124 | + } | ||
125 | + }; | ||
126 | +</script> | ||
127 | +<template> | ||
128 | + <div> | ||
129 | + <BasicTable style="flex: auto" @register="registerTable"> | ||
130 | + <template #toolbar> | ||
131 | + <Authority value="api:yt:product:category:post"> | ||
132 | + <Button type="primary" @click="handleCreate"> 新增品类 </Button> | ||
133 | + </Authority> | ||
134 | + <Authority value="api:yt:product:category:delete"> | ||
135 | + <Popconfirm | ||
136 | + title="您确定要批量删除数据" | ||
137 | + ok-text="确定" | ||
138 | + cancel-text="取消" | ||
139 | + @confirm="handleDelete()" | ||
140 | + > | ||
141 | + <a-button color="error" :disabled="!isExistOption"> 批量删除 </a-button> | ||
142 | + </Popconfirm> | ||
143 | + </Authority> | ||
144 | + </template> | ||
145 | + <template #status="{ record }"> | ||
146 | + <Switch | ||
147 | + @click="(e) => handleSwitch(e, record)" | ||
148 | + :loading="switchLoading" | ||
149 | + v-model:checked="record.status" | ||
150 | + checked-children="启用" | ||
151 | + un-checked-children="禁用" | ||
152 | + :checkedValue="1" | ||
153 | + :unCheckedValue="0" | ||
154 | + /> | ||
155 | + </template> | ||
156 | + <template #action="{ record }"> | ||
157 | + <TableAction | ||
158 | + :actions="[ | ||
159 | + { | ||
160 | + label: '详情', | ||
161 | + icon: 'ant-design:eye-outlined', | ||
162 | + auth: 'api:yt:product:category:get', | ||
163 | + onClick: handleDetail.bind(null, record), | ||
164 | + }, | ||
165 | + { | ||
166 | + label: '编辑', | ||
167 | + auth: 'api:yt:product:category:update', | ||
168 | + icon: 'clarity:note-edit-line', | ||
169 | + ifShow: authBtn(role), | ||
170 | + onClick: handleEdit.bind(null, record), | ||
171 | + }, | ||
172 | + { | ||
173 | + label: '删除', | ||
174 | + auth: 'api:yt:product:category:delete', | ||
175 | + icon: 'ant-design:delete-outlined', | ||
176 | + ifShow: authBtn(role) && !record.status, | ||
177 | + color: 'error', | ||
178 | + popConfirm: { | ||
179 | + title: '是否确认删除', | ||
180 | + confirm: handleDelete.bind(null, record), | ||
181 | + }, | ||
182 | + }, | ||
183 | + ]" | ||
184 | + /> | ||
185 | + </template> | ||
186 | + </BasicTable> | ||
187 | + <classModal @register="registerModal" @handleReload="handleReload" /> | ||
188 | + <!-- <physicalModel @register="registerDetailDrawer" :record /> --> | ||
189 | + <BasicDrawer title="物模型" @register="registerDetailDrawer" width="60%" destroy-on-close> | ||
190 | + <PhysicalModelManagementStep :record="registerDetailRecord" /> | ||
191 | + </BasicDrawer> | ||
192 | + </div> | ||
193 | +</template> |
@@ -16,6 +16,9 @@ import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue'; | @@ -16,6 +16,9 @@ import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue'; | ||
16 | import { createImgPreview } from '/@/components/Preview'; | 16 | import { createImgPreview } from '/@/components/Preview'; |
17 | import { uploadThumbnail } from '/@/api/configuration/center/configurationCenter'; | 17 | import { uploadThumbnail } from '/@/api/configuration/center/configurationCenter'; |
18 | 18 | ||
19 | +import { getOrganizationList } from '/@/api/system/system'; | ||
20 | +import { copyTransFun } from '/@/utils/fnUtils'; | ||
21 | + | ||
19 | useComponentRegister('JSONEditor', JSONEditor); | 22 | useComponentRegister('JSONEditor', JSONEditor); |
20 | useComponentRegister('ObjectModelValidateForm', ObjectModelValidateForm); | 23 | useComponentRegister('ObjectModelValidateForm', ObjectModelValidateForm); |
21 | 24 | ||
@@ -263,22 +266,15 @@ export const step1Schemas: FormSchema[] = [ | @@ -263,22 +266,15 @@ export const step1Schemas: FormSchema[] = [ | ||
263 | ifShow: ({ values }) => isGateWay(values.deviceType), | 266 | ifShow: ({ values }) => isGateWay(values.deviceType), |
264 | }, | 267 | }, |
265 | { | 268 | { |
266 | - field: 'organizationId', | ||
267 | - label: '所属组织', | ||
268 | - component: 'Input', | ||
269 | - required: true, | ||
270 | - slot: 'addOrg', | ||
271 | - }, | ||
272 | - { | ||
273 | field: 'gatewayId', | 269 | field: 'gatewayId', |
274 | label: '网关设备', | 270 | label: '网关设备', |
275 | required: true, | 271 | required: true, |
276 | component: 'ApiSelect', | 272 | component: 'ApiSelect', |
277 | - ifShow: ({ values }) => values.deviceType === 'SENSOR' && values.organizationId, | 273 | + ifShow: ({ values }) => values.deviceType === 'SENSOR', |
278 | componentProps: ({ formModel, formActionType }) => { | 274 | componentProps: ({ formModel, formActionType }) => { |
279 | - const { organizationId, transportType } = formModel; | 275 | + const { transportType } = formModel; |
280 | const { validateFields } = formActionType; | 276 | const { validateFields } = formActionType; |
281 | - if (![organizationId, transportType].every(Boolean)) return {}; | 277 | + if (!transportType) return {}; |
282 | return { | 278 | return { |
283 | api: async (params: Recordable) => { | 279 | api: async (params: Recordable) => { |
284 | try { | 280 | try { |
@@ -291,19 +287,36 @@ export const step1Schemas: FormSchema[] = [ | @@ -291,19 +287,36 @@ export const step1Schemas: FormSchema[] = [ | ||
291 | }, | 287 | }, |
292 | showSearch: true, | 288 | showSearch: true, |
293 | params: { | 289 | params: { |
294 | - organizationId, | ||
295 | transportType, | 290 | transportType, |
296 | }, | 291 | }, |
297 | valueField: 'tbDeviceId', | 292 | valueField: 'tbDeviceId', |
298 | labelField: 'alias', | 293 | labelField: 'alias', |
299 | - onChange: async () => { | 294 | + onChange: async (value, options) => { |
300 | await nextTick(); | 295 | await nextTick(); |
296 | + if (value) { | ||
297 | + const data = await getOrganizationList({ organizationId: options?.organizationId }); | ||
298 | + copyTransFun(data as any as any[]); | ||
299 | + formModel.organizationList = data; | ||
300 | + } | ||
301 | validateFields(['gatewayId']); | 301 | validateFields(['gatewayId']); |
302 | }, | 302 | }, |
303 | }; | 303 | }; |
304 | }, | 304 | }, |
305 | }, | 305 | }, |
306 | { | 306 | { |
307 | + field: 'organizationList', | ||
308 | + label: '依据网关设备请求的组织数组', | ||
309 | + component: 'Input', | ||
310 | + ifShow: false, | ||
311 | + }, | ||
312 | + { | ||
313 | + field: 'organizationId', | ||
314 | + label: '所属组织', | ||
315 | + component: 'Select', | ||
316 | + required: true, | ||
317 | + slot: 'addOrg', | ||
318 | + }, | ||
319 | + { | ||
307 | field: 'label', | 320 | field: 'label', |
308 | label: '设备标签', | 321 | label: '设备标签', |
309 | component: 'Input', | 322 | component: 'Input', |
@@ -14,7 +14,7 @@ | @@ -14,7 +14,7 @@ | ||
14 | placeholder="请选择组织" | 14 | placeholder="请选择组织" |
15 | allow-clear | 15 | allow-clear |
16 | tree-default-expand-all | 16 | tree-default-expand-all |
17 | - :tree-data="treeData" | 17 | + :tree-data="model?.['organizationList'] || treeData" |
18 | /> | 18 | /> |
19 | </div> | 19 | </div> |
20 | <div> | 20 | <div> |
@@ -49,44 +49,46 @@ | @@ -49,44 +49,46 @@ | ||
49 | centered | 49 | centered |
50 | > | 50 | > |
51 | <div> | 51 | <div> |
52 | - <Form :label-col="labelCol" :colon="false" :rules="rules" :model="positionState"> | ||
53 | - <Row :gutter="20" class="mt-4"> | ||
54 | - <Col :span="20"> | ||
55 | - <FormItem label="搜索位置"> | ||
56 | - <AutoComplete | ||
57 | - v-model:value="positionState.address" | ||
58 | - :options="dataSource" | ||
59 | - style="width: 100%" | ||
60 | - placeholder="搜索位置" | ||
61 | - @search="debounceSearch" | ||
62 | - @select="handleSelect" | ||
63 | - backfill | ||
64 | - /> | ||
65 | - </FormItem> | ||
66 | - </Col> | ||
67 | - </Row> | ||
68 | - <Row :gutter="20" class=""> | ||
69 | - <Col :span="10"> | ||
70 | - <FormItem label="经度" name="longitude"> | ||
71 | - <Input | ||
72 | - @blur="redirectPosition" | ||
73 | - @change="redirectPosition" | ||
74 | - v-model:value="positionState.longitude" | ||
75 | - /> | ||
76 | - </FormItem> | ||
77 | - </Col> | ||
78 | - <Col :span="10"> | ||
79 | - <FormItem label="纬度" name="latitude"> | ||
80 | - <Input | ||
81 | - @blur="redirectPosition" | ||
82 | - @change="redirectPosition" | ||
83 | - v-model:value="positionState.latitude" | ||
84 | - /> | ||
85 | - </FormItem> | ||
86 | - </Col> | ||
87 | - </Row> | ||
88 | - </Form> | ||
89 | - <div ref="wrapRef" style="height: 300px; width: 90%" class="ml-6"></div> | 52 | + <Spin :spinning="spinning"> |
53 | + <Form :label-col="labelCol" :colon="false" :rules="rules" :model="positionState"> | ||
54 | + <Row :gutter="20" class="mt-4"> | ||
55 | + <Col :span="20"> | ||
56 | + <FormItem label="搜索位置"> | ||
57 | + <AutoComplete | ||
58 | + v-model:value="positionState.address" | ||
59 | + :options="dataSource" | ||
60 | + style="width: 100%" | ||
61 | + placeholder="搜索位置" | ||
62 | + @search="debounceSearch" | ||
63 | + @select="handleSelect" | ||
64 | + backfill | ||
65 | + /> | ||
66 | + </FormItem> | ||
67 | + </Col> | ||
68 | + </Row> | ||
69 | + <Row :gutter="20" class=""> | ||
70 | + <Col :span="10"> | ||
71 | + <FormItem label="经度" name="longitude"> | ||
72 | + <Input | ||
73 | + @blur="redirectPosition" | ||
74 | + @change="redirectPosition" | ||
75 | + v-model:value="positionState.longitude" | ||
76 | + /> | ||
77 | + </FormItem> | ||
78 | + </Col> | ||
79 | + <Col :span="10"> | ||
80 | + <FormItem label="纬度" name="latitude"> | ||
81 | + <Input | ||
82 | + @blur="redirectPosition" | ||
83 | + @change="redirectPosition" | ||
84 | + v-model:value="positionState.latitude" | ||
85 | + /> | ||
86 | + </FormItem> | ||
87 | + </Col> | ||
88 | + </Row> | ||
89 | + </Form> | ||
90 | + <div ref="wrapRef" style="height: 300px; width: 90%" class="ml-6"></div> | ||
91 | + </Spin> | ||
90 | </div> | 92 | </div> |
91 | </Modal> | 93 | </Modal> |
92 | <DeptDrawer @register="registerModal" @success="handleSuccess" /> | 94 | <DeptDrawer @register="registerModal" @success="handleSuccess" /> |
@@ -97,7 +99,7 @@ | @@ -97,7 +99,7 @@ | ||
97 | import { BasicForm, useForm } from '/@/components/Form'; | 99 | import { BasicForm, useForm } from '/@/components/Form'; |
98 | import { step1Schemas } from '../../config/data'; | 100 | import { step1Schemas } from '../../config/data'; |
99 | import { useScript } from '/@/hooks/web/useScript'; | 101 | import { useScript } from '/@/hooks/web/useScript'; |
100 | - import { Input, message, Modal, Form, Row, Col, AutoComplete } from 'ant-design-vue'; | 102 | + import { Input, message, Modal, Form, Row, Col, AutoComplete, Spin } from 'ant-design-vue'; |
101 | import { EnvironmentTwoTone } from '@ant-design/icons-vue'; | 103 | import { EnvironmentTwoTone } from '@ant-design/icons-vue'; |
102 | import { upload } from '/@/api/oss/ossFileUploader'; | 104 | import { upload } from '/@/api/oss/ossFileUploader'; |
103 | import { BAI_DU_MAP_URL } from '/@/utils/fnUtils'; | 105 | import { BAI_DU_MAP_URL } from '/@/utils/fnUtils'; |
@@ -113,6 +115,8 @@ | @@ -113,6 +115,8 @@ | ||
113 | import { toRaw } from 'vue'; | 115 | import { toRaw } from 'vue'; |
114 | import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue'; | 116 | import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue'; |
115 | import { buildUUID } from '/@/utils/uuid'; | 117 | import { buildUUID } from '/@/utils/uuid'; |
118 | + import { useMessage } from '/@/hooks/web/useMessage'; | ||
119 | + import { computed } from 'vue'; | ||
116 | 120 | ||
117 | export default defineComponent({ | 121 | export default defineComponent({ |
118 | components: { | 122 | components: { |
@@ -126,6 +130,7 @@ | @@ -126,6 +130,7 @@ | ||
126 | Row, | 130 | Row, |
127 | Col, | 131 | Col, |
128 | DeptDrawer, | 132 | DeptDrawer, |
133 | + Spin, | ||
129 | }, | 134 | }, |
130 | props: { | 135 | props: { |
131 | isUpdate: { | 136 | isUpdate: { |
@@ -134,9 +139,14 @@ | @@ -134,9 +139,14 @@ | ||
134 | }, | 139 | }, |
135 | emits: ['next'], | 140 | emits: ['next'], |
136 | setup(props, { emit }) { | 141 | setup(props, { emit }) { |
142 | + const { createMessage } = useMessage(); | ||
137 | const orgData: any = reactive({ | 143 | const orgData: any = reactive({ |
138 | treeData: [], | 144 | treeData: [], |
139 | }); | 145 | }); |
146 | + const isUpdate1 = computed(() => { | ||
147 | + const { isUpdate } = props; | ||
148 | + return isUpdate; | ||
149 | + }); | ||
140 | const getOrganizationListFunc = async () => { | 150 | const getOrganizationListFunc = async () => { |
141 | const data = await getOrganizationList(); | 151 | const data = await getOrganizationList(); |
142 | copyTransFun(data as any as any[]); | 152 | copyTransFun(data as any as any[]); |
@@ -168,17 +178,27 @@ | @@ -168,17 +178,27 @@ | ||
168 | const devicePic = ref(''); | 178 | const devicePic = ref(''); |
169 | const loading = ref(false); | 179 | const loading = ref(false); |
170 | 180 | ||
171 | - const [register, { validate, resetFields, setFieldsValue, getFieldsValue, updateSchema }] = | ||
172 | - useForm({ | ||
173 | - labelWidth: 140, | ||
174 | - schemas: step1Schemas, | ||
175 | - actionColOptions: { | ||
176 | - span: 14, | ||
177 | - }, | ||
178 | - labelAlign: 'right', | ||
179 | - showResetButton: false, | ||
180 | - showSubmitButton: false, | ||
181 | - }); | 181 | + const [ |
182 | + register, | ||
183 | + { | ||
184 | + validate, | ||
185 | + resetFields, | ||
186 | + setFieldsValue, | ||
187 | + getFieldsValue, | ||
188 | + updateSchema, | ||
189 | + clearValidate, | ||
190 | + validateFields, | ||
191 | + }, | ||
192 | + ] = useForm({ | ||
193 | + labelWidth: 140, | ||
194 | + schemas: step1Schemas, | ||
195 | + actionColOptions: { | ||
196 | + span: 14, | ||
197 | + }, | ||
198 | + labelAlign: 'right', | ||
199 | + showResetButton: false, | ||
200 | + showSubmitButton: false, | ||
201 | + }); | ||
182 | async function nextStep() { | 202 | async function nextStep() { |
183 | try { | 203 | try { |
184 | let values = await validate(); | 204 | let values = await validate(); |
@@ -220,21 +240,53 @@ | @@ -220,21 +240,53 @@ | ||
220 | }; | 240 | }; |
221 | 241 | ||
222 | // 地图的弹框 | 242 | // 地图的弹框 |
243 | + const spinning = ref<boolean>(false); | ||
223 | const visible = ref(false); | 244 | const visible = ref(false); |
224 | - const selectPosition = () => { | 245 | + const selectPosition = async () => { |
225 | visible.value = true; | 246 | visible.value = true; |
226 | - if (!positionState.longitude) { | ||
227 | - positionState.longitude = '104.05326410962411'; | ||
228 | - positionState.latitude = '30.54855093076791'; | ||
229 | - | ||
230 | - // 根据经纬度获取详细位置 | ||
231 | - if (positionState.longitude && positionState.latitude) { | ||
232 | - var pt = new BMap.Point(positionState.longitude, positionState.latitude); | ||
233 | - getAddrByPoint(pt); | 247 | + if ( |
248 | + !unref(isUpdate1) && | ||
249 | + unref(devicePositionState).longitude && | ||
250 | + unref(devicePositionState).latitude | ||
251 | + ) { | ||
252 | + positionState.longitude = unref(devicePositionState).longitude; | ||
253 | + positionState.latitude = unref(devicePositionState).latitude; | ||
254 | + } | ||
255 | + if (!positionState.longitude || !positionState.latitude) { | ||
256 | + let getLocalCity = new BMap.LocalCity(); | ||
257 | + let getLocation = new BMap.Geolocation(); | ||
258 | + const userAgent = navigator.userAgent; | ||
259 | + try { | ||
260 | + spinning.value = true; | ||
261 | + if ( | ||
262 | + (userAgent.indexOf('Chrome') > -1 || userAgent.indexOf('WebKit') > -1) && | ||
263 | + userAgent.indexOf('Edg') == -1 | ||
264 | + ) { | ||
265 | + //判断是否是谷歌 或则是谷歌的内核 '104.05326410962411'; '30.54855093076791'; | ||
266 | + await getLocalCity.get(function (res) { | ||
267 | + positionState.longitude = res?.center.lng || '104.05326410962411'; | ||
268 | + positionState.latitude = res?.center.lat || '30.54855093076791'; | ||
269 | + var pt = new BMap.Point(positionState.longitude, positionState.latitude); | ||
270 | + getAddrByPoint(pt); | ||
271 | + spinning.value = false; | ||
272 | + }); | ||
273 | + } else { | ||
274 | + await getLocation.getCurrentPosition(function (res) { | ||
275 | + positionState.longitude = res?.longitude || '104.05326410962411'; | ||
276 | + positionState.latitude = res?.latitude || '30.54855093076791'; | ||
277 | + var pt = new BMap.Point(positionState.longitude, positionState.latitude); | ||
278 | + getAddrByPoint(pt); | ||
279 | + spinning.value = false; | ||
280 | + }); | ||
281 | + } | ||
282 | + } catch (err) { | ||
283 | + createMessage.error('获取定位失败'); | ||
234 | } | 284 | } |
235 | initMap(positionState.longitude, positionState.latitude); | 285 | initMap(positionState.longitude, positionState.latitude); |
236 | } else { | 286 | } else { |
237 | initMap(positionState.longitude, positionState.latitude); | 287 | initMap(positionState.longitude, positionState.latitude); |
288 | + var pt = new BMap.Point(positionState.longitude, positionState.latitude); | ||
289 | + getAddrByPoint(pt); | ||
238 | } | 290 | } |
239 | }; | 291 | }; |
240 | 292 | ||
@@ -447,11 +499,11 @@ | @@ -447,11 +499,11 @@ | ||
447 | }); | 499 | }); |
448 | } | 500 | } |
449 | 501 | ||
450 | - const handleTreeOrg = () => { | ||
451 | - const clearGatewayId = { | ||
452 | - gatewayId: '', | ||
453 | - }; | ||
454 | - setFieldsValue(clearGatewayId); | 502 | + const handleTreeOrg = (option: string) => { |
503 | + if (option) clearValidate('organizationId'); | ||
504 | + else { | ||
505 | + validateFields(['organizationId']); | ||
506 | + } | ||
455 | }; | 507 | }; |
456 | 508 | ||
457 | return { | 509 | return { |
@@ -488,6 +540,7 @@ | @@ -488,6 +540,7 @@ | ||
488 | handleSuccess, | 540 | handleSuccess, |
489 | handleTreeOrg, | 541 | handleTreeOrg, |
490 | devicePositionState, | 542 | devicePositionState, |
543 | + spinning, | ||
491 | }; | 544 | }; |
492 | }, | 545 | }, |
493 | }); | 546 | }); |
@@ -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 | } |
@@ -4,6 +4,7 @@ | @@ -4,6 +4,7 @@ | ||
4 | :maskClosable="false" | 4 | :maskClosable="false" |
5 | v-bind="$attrs" | 5 | v-bind="$attrs" |
6 | :width="dynamicWidth" | 6 | :width="dynamicWidth" |
7 | + :destroy-on-close="true" | ||
7 | @register="register" | 8 | @register="register" |
8 | @ok="handleSubmit" | 9 | @ok="handleSubmit" |
9 | @cancel="handleCancel" | 10 | @cancel="handleCancel" |
@@ -125,6 +126,8 @@ | @@ -125,6 +126,8 @@ | ||
125 | const title = unref(isUpdate) ? '编辑产品' : '新增产品'; | 126 | const title = unref(isUpdate) ? '编辑产品' : '新增产品'; |
126 | setModalProps({ title, showOkBtn: true, showCancelBtn: true }); | 127 | setModalProps({ title, showOkBtn: true, showCancelBtn: true }); |
127 | if (unref(isUpdate)) { | 128 | if (unref(isUpdate)) { |
129 | + const { deviceType } = res || {}; | ||
130 | + deviceTypeStr.value = deviceType; | ||
128 | await setDeviceConfEditFormData(res); | 131 | await setDeviceConfEditFormData(res); |
129 | await setTransConfEditFormData(res); | 132 | await setTransConfEditFormData(res); |
130 | handleStepNext(false, res); | 133 | handleStepNext(false, res); |
@@ -157,7 +160,6 @@ | @@ -157,7 +160,6 @@ | ||
157 | }; | 160 | }; |
158 | const deviceTypeStr = ref(''); | 161 | const deviceTypeStr = ref(''); |
159 | const handleGetDeviceType = async (e) => { | 162 | const handleGetDeviceType = async (e) => { |
160 | - console.log(e); | ||
161 | deviceTypeStr.value = e; | 163 | deviceTypeStr.value = e; |
162 | await nextTick(); | 164 | await nextTick(); |
163 | TransConStRef.value?.updateDisabled(e); | 165 | TransConStRef.value?.updateDisabled(e); |
1 | +<script lang="ts" setup> | ||
2 | + import { columns, searchFormSchema } from './config'; | ||
3 | + import { BasicDrawer, useDrawerInner } from '/@/components/Drawer'; | ||
4 | + import { BasicTable } from '/@/components/Table'; | ||
5 | + import { useTable } from '/@/components/Table'; | ||
6 | + import { Button } from 'ant-design-vue'; | ||
7 | + import { ExclamationCircleOutlined } from '@ant-design/icons-vue'; | ||
8 | + import { getDeviceClassList } from '/@/api/device/classModal'; | ||
9 | + import { ref } from 'vue'; | ||
10 | + const emits = defineEmits([ | ||
11 | + 'handleOpenListDrawer', | ||
12 | + 'handleSelectCategory', | ||
13 | + 'handleClose', | ||
14 | + 'register', | ||
15 | + ]); | ||
16 | + | ||
17 | + const categoryId = ref<string | undefined | null>(); | ||
18 | + const [registerDrawer] = useDrawerInner((data) => { | ||
19 | + categoryId.value = data.categoryId || {}; | ||
20 | + }); | ||
21 | + const [registerTable] = useTable({ | ||
22 | + title: '', | ||
23 | + api: getDeviceClassList, | ||
24 | + columns, | ||
25 | + formConfig: { | ||
26 | + schemas: searchFormSchema, | ||
27 | + }, | ||
28 | + searchInfo: { | ||
29 | + status: 1, | ||
30 | + }, | ||
31 | + resizeHeightOffset: 50, | ||
32 | + useSearchForm: true, | ||
33 | + showTableSetting: false, | ||
34 | + bordered: true, | ||
35 | + showIndexColumn: false, | ||
36 | + clickToRowSelect: false, | ||
37 | + rowKey: 'id', | ||
38 | + actionColumn: { | ||
39 | + width: 100, | ||
40 | + title: '操作', | ||
41 | + slots: { customRender: 'action' }, | ||
42 | + fixed: 'right', | ||
43 | + }, | ||
44 | + }); | ||
45 | + | ||
46 | + const closeDrawer = () => { | ||
47 | + emits('handleClose'); | ||
48 | + }; | ||
49 | + | ||
50 | + const handleChecked = (item?: any) => { | ||
51 | + emits('handleSelectCategory', item); | ||
52 | + }; | ||
53 | + | ||
54 | + const handleOpenListDrawer = (record?: any) => { | ||
55 | + emits('handleOpenListDrawer', record); | ||
56 | + }; | ||
57 | +</script> | ||
58 | + | ||
59 | +<template> | ||
60 | + <BasicDrawer | ||
61 | + :zIndex="1100" | ||
62 | + v-bind="$attrs" | ||
63 | + @register="registerDrawer" | ||
64 | + :showFooter="true" | ||
65 | + destroyOnClose | ||
66 | + @close="closeDrawer" | ||
67 | + title="选择品类" | ||
68 | + width="32%" | ||
69 | + > | ||
70 | + <BasicTable @register="registerTable"> | ||
71 | + <template #name="{ record }"> | ||
72 | + <span>{{ record.name }}</span> | ||
73 | + <ExclamationCircleOutlined | ||
74 | + style="color: #377dff" | ||
75 | + @click.stop="handleOpenListDrawer(record)" | ||
76 | + class="ml-1" | ||
77 | + /> | ||
78 | + </template> | ||
79 | + <template #action="{ record }"> | ||
80 | + <Button type="link" :disabled="categoryId === record.id" @click="handleChecked(record)">{{ | ||
81 | + categoryId === record.id ? '已选择' : '选择' | ||
82 | + }}</Button> | ||
83 | + </template> | ||
84 | + </BasicTable> | ||
85 | + </BasicDrawer> | ||
86 | +</template> |
1 | +<script lang="ts" setup> | ||
2 | + import { columnsDrawer } from './config'; | ||
3 | + import { unref, ref, watch, nextTick } from 'vue'; | ||
4 | + import { BasicDrawer, useDrawerInner } from '/@/components/Drawer'; | ||
5 | + import { BasicTable } from '/@/components/Table'; | ||
6 | + import { useTable } from '/@/components/Table'; | ||
7 | + import { ExclamationCircleOutlined } from '@ant-design/icons-vue'; | ||
8 | + import { getModelList } from '/@/api/device/modelOfMatter'; | ||
9 | + | ||
10 | + defineEmits(['register']); | ||
11 | + | ||
12 | + const props = defineProps<{ cateforyListInfo: any }>(); | ||
13 | + | ||
14 | + const recordInfo = ref<any>({}); | ||
15 | + const secondVisible = ref<Boolean>(false); | ||
16 | + | ||
17 | + watch( | ||
18 | + () => props.cateforyListInfo, | ||
19 | + async () => { | ||
20 | + recordInfo.value = props.cateforyListInfo.record || {}; | ||
21 | + secondVisible.value = props.cateforyListInfo.isRight || false; | ||
22 | + await nextTick(); | ||
23 | + reload(); | ||
24 | + } | ||
25 | + ); | ||
26 | + const [registerListDrawer, { closeDrawer }] = useDrawerInner(); | ||
27 | + const [registerTable, { reload }] = useTable({ | ||
28 | + title: '', | ||
29 | + api: async (params: Record<'page' | 'pageSize', number>) => { | ||
30 | + return await getModelList({ | ||
31 | + ...params, | ||
32 | + id: unref(recordInfo).id, | ||
33 | + selectType: 'category', | ||
34 | + }); | ||
35 | + }, | ||
36 | + columns: columnsDrawer, | ||
37 | + useSearchForm: false, | ||
38 | + showTableSetting: false, | ||
39 | + bordered: true, | ||
40 | + showIndexColumn: false, | ||
41 | + clickToRowSelect: false, | ||
42 | + rowKey: 'id', | ||
43 | + }); | ||
44 | + | ||
45 | + const handleClose = () => { | ||
46 | + closeDrawer(); | ||
47 | + }; | ||
48 | +</script> | ||
49 | + | ||
50 | +<template> | ||
51 | + <BasicDrawer | ||
52 | + :zIndex="1100" | ||
53 | + @register="registerListDrawer" | ||
54 | + wrap-class-name="secondDrawer" | ||
55 | + :wrap-style="{ | ||
56 | + right: secondVisible ? '32%' : 0, | ||
57 | + transition: 'none', | ||
58 | + }" | ||
59 | + v-bind="$attrs" | ||
60 | + destroyOnClose | ||
61 | + @close="handleClose" | ||
62 | + title="物模型" | ||
63 | + width="36%" | ||
64 | + > | ||
65 | + <BasicTable @register="registerTable"> | ||
66 | + <template #name="{ record }"> | ||
67 | + <span>{{ record.name }}</span> | ||
68 | + <ExclamationCircleOutlined class="ml-1" /> | ||
69 | + </template> | ||
70 | + </BasicTable> | ||
71 | + </BasicDrawer> | ||
72 | +</template> |
1 | +<template> | ||
2 | + <BasicModal | ||
3 | + title="导入物模型" | ||
4 | + :maskClosable="false" | ||
5 | + destroyOnClose | ||
6 | + v-bind="$attrs" | ||
7 | + width="25rem" | ||
8 | + @register="register" | ||
9 | + @cancel="handleCancel" | ||
10 | + :showOkBtn="false" | ||
11 | + > | ||
12 | + <div class="w-full h-full" ref="loadingRef"> | ||
13 | + <!-- <div class="flex justify-end"> | ||
14 | + <Button @click="handleTemplateDownload" type="link">EXCEL模板下载</Button> | ||
15 | + </div> --> | ||
16 | + <div class="flex justify-evenly items-center h-50 !w-full"> | ||
17 | + <Upload | ||
18 | + accept=".json," | ||
19 | + :show-upload-list="false" | ||
20 | + :customRequest="handleImportModel" | ||
21 | + class="flex justify-center items-center" | ||
22 | + > | ||
23 | + <div class="flex flex-col justify-center items-center"> | ||
24 | + <img :src="JSONImage" alt="avatar" class="w-20 h-20" /> | ||
25 | + </div> | ||
26 | + </Upload> | ||
27 | + <Upload | ||
28 | + accept=".csv,.xls,.xlsx" | ||
29 | + :show-upload-list="false" | ||
30 | + :customRequest="handleCSVImport" | ||
31 | + class="flex justify-center items-center" | ||
32 | + > | ||
33 | + <div class="flex flex-col justify-center items-center"> | ||
34 | + <img :src="CSVImage" alt="avatar" class="w-20 h-20" /> | ||
35 | + </div> | ||
36 | + </Upload> | ||
37 | + </div> | ||
38 | + </div> | ||
39 | + </BasicModal> | ||
40 | +</template> | ||
41 | +<script lang="ts" setup> | ||
42 | + import { ref, unref } from 'vue'; | ||
43 | + import { Upload } from 'ant-design-vue'; | ||
44 | + import { BasicModal, useModalInner } from '/@/components/Modal'; | ||
45 | + import { DeviceRecord } from '/@/api/device/model/deviceModel'; | ||
46 | + import { useMessage } from '/@/hooks/web/useMessage'; | ||
47 | + import { isObject, isString } from '/@/utils/is'; | ||
48 | + import { | ||
49 | + importCsvCategory, | ||
50 | + importModelCategory, | ||
51 | + importModelOfMatter, | ||
52 | + importCsvDeviceProfileId, | ||
53 | + } from '/@/api/device/modelOfMatter'; | ||
54 | + // import XLSX, { CellObject } from 'xlsx'; | ||
55 | + import { useLoading } from '/@/components/Loading'; | ||
56 | + import JSONImage from '/@/assets/svg/JSON.svg'; | ||
57 | + import CSVImage from '/@/assets/svg/excel.svg'; | ||
58 | + | ||
59 | + const emits = defineEmits(['register', 'handleImportCSV', 'handleReload']); | ||
60 | + | ||
61 | + defineProps<{ | ||
62 | + record: DeviceRecord; | ||
63 | + }>(); | ||
64 | + | ||
65 | + const { createMessage } = useMessage(); | ||
66 | + const loadingRef = ref(); | ||
67 | + const [openLoading, closeLoading] = useLoading({ | ||
68 | + target: loadingRef, | ||
69 | + props: { | ||
70 | + tip: '加载中', | ||
71 | + absolute: true, | ||
72 | + }, | ||
73 | + }); | ||
74 | + | ||
75 | + const isEmptyObject = (value: any) => isObject(value) && !Object.keys(value).length; | ||
76 | + | ||
77 | + const ImportInfo = ref<{ id?: string; isCateGory?: string | Boolean }>({}); | ||
78 | + | ||
79 | + const [register, { closeModal }] = useModalInner(async (data) => { | ||
80 | + ImportInfo.value = data; | ||
81 | + }); | ||
82 | + // 导入loading | ||
83 | + const importLoading = ref(false); | ||
84 | + | ||
85 | + const paseJSON = (string: string) => { | ||
86 | + let data = null; | ||
87 | + let flag = false; | ||
88 | + try { | ||
89 | + if (!isString(string)) return { flag: false, data }; | ||
90 | + data = JSON.parse(string); | ||
91 | + flag = true; | ||
92 | + if (!isObject(data)) flag = false; | ||
93 | + } catch (error) {} | ||
94 | + return { flag, data }; | ||
95 | + }; | ||
96 | + | ||
97 | + // JSON导入 | ||
98 | + const handleImportModel = async (data: { file: File }) => { | ||
99 | + const fileReader = new FileReader(); | ||
100 | + const { isCateGory, id } = unref(ImportInfo); | ||
101 | + | ||
102 | + fileReader.onload = async () => { | ||
103 | + const { flag, data } = paseJSON(fileReader.result as string); | ||
104 | + if (!flag) { | ||
105 | + createMessage.warning('JSON解析失败,请导入正确的JSON'); | ||
106 | + return; | ||
107 | + } | ||
108 | + openLoading(); | ||
109 | + try { | ||
110 | + importLoading.value = true; | ||
111 | + | ||
112 | + Object.keys(data || {}).forEach((key) => { | ||
113 | + const value = (data || {})[key]; | ||
114 | + if (value && isEmptyObject(value)) { | ||
115 | + (data || {})[key] = []; | ||
116 | + } | ||
117 | + }); | ||
118 | + | ||
119 | + const result = isCateGory | ||
120 | + ? await importModelCategory({ | ||
121 | + categoryId: id, | ||
122 | + data: data!, | ||
123 | + functionType: 'all', | ||
124 | + }) | ||
125 | + : await importModelOfMatter({ | ||
126 | + tkDeviceProfileId: id, | ||
127 | + data: data!, | ||
128 | + functionType: 'all', | ||
129 | + }); | ||
130 | + | ||
131 | + result | ||
132 | + ? createMessage.success('导入成功~') | ||
133 | + : createMessage.error('JSON解析失败,请导入正确的JSON'); | ||
134 | + | ||
135 | + result && emits('handleReload'); | ||
136 | + } catch (error) { | ||
137 | + throw error; | ||
138 | + } finally { | ||
139 | + importLoading.value = false; | ||
140 | + closeLoading(); | ||
141 | + closeModal(); | ||
142 | + } | ||
143 | + }; | ||
144 | + | ||
145 | + fileReader.readAsText(data.file, 'utf-8'); | ||
146 | + }; | ||
147 | + | ||
148 | + // CSV导入 | ||
149 | + const handleCSVImport = async ({ file }) => { | ||
150 | + const { isCateGory, id } = unref(ImportInfo); | ||
151 | + | ||
152 | + try { | ||
153 | + openLoading(); | ||
154 | + const formData = new FormData(); | ||
155 | + formData.set('file', file); | ||
156 | + const flag = isCateGory | ||
157 | + ? await importCsvCategory({ categoryId: id, deviceProfileId: undefined, file: formData }) | ||
158 | + : await importCsvDeviceProfileId({ | ||
159 | + categoryId: undefined, | ||
160 | + deviceProfileId: id, | ||
161 | + file: formData, | ||
162 | + }); | ||
163 | + flag && createMessage.info(flag?.message); | ||
164 | + } catch (msg) { | ||
165 | + console.log(msg, 'msg'); | ||
166 | + } finally { | ||
167 | + closeLoading(); | ||
168 | + closeModal(); | ||
169 | + emits('handleReload'); | ||
170 | + } | ||
171 | + }; | ||
172 | + | ||
173 | + // const downloadFile = (data: string, fileName: string, type: string, ext: string) => { | ||
174 | + // const blob = new Blob([data], { type: type }); | ||
175 | + // const objectURL = URL.createObjectURL(blob); | ||
176 | + // const element = document.createElement('a'); | ||
177 | + // element.href = objectURL; | ||
178 | + // element.download = `${fileName}.${ext}`; | ||
179 | + // element.style.display = 'none'; | ||
180 | + // document.body.appendChild(element); | ||
181 | + // element.click(); | ||
182 | + // element.remove(); | ||
183 | + // URL.revokeObjectURL(objectURL); | ||
184 | + // }; | ||
185 | + | ||
186 | + // 模板下载 | ||
187 | + // const handleTemplateDownload = () => { | ||
188 | + | ||
189 | + // }; | ||
190 | + | ||
191 | + const handleCancel = () => { | ||
192 | + closeModal(); | ||
193 | + }; | ||
194 | +</script> | ||
195 | + | ||
196 | +<style lang="less" scope></style> |
1 | +import { BasicColumn, FormSchema } from '/@/components/Table'; | ||
2 | +import { findDictItemByCode } from '/@/api/system/dict'; | ||
3 | +import { h, unref } from 'vue'; | ||
4 | +import { Tooltip } from 'ant-design-vue'; | ||
5 | + | ||
6 | +import { FunctionType } from '/@/views/device/profiles/step/cpns/physical/cpns/config'; | ||
7 | +import { useMessage } from '/@/hooks/web/useMessage'; | ||
8 | +import { useClipboard } from '@vueuse/core'; | ||
9 | +import { DictEnum } from '/@/enums/dictEnum'; | ||
10 | + | ||
11 | +// import { | ||
12 | +// EventType, | ||
13 | +// EventTypeColor, | ||
14 | +// EventTypeName, | ||
15 | +// } from '/@/views/device/list/cpns/tabs/EventManage/config'; | ||
16 | + | ||
17 | +export const columns: BasicColumn[] = [ | ||
18 | + { | ||
19 | + title: '品类名称', | ||
20 | + dataIndex: 'name', | ||
21 | + slots: { customRender: 'name' }, | ||
22 | + }, | ||
23 | + { | ||
24 | + title: '领域', | ||
25 | + dataIndex: 'dictItemName', | ||
26 | + }, | ||
27 | +]; | ||
28 | + | ||
29 | +export const formatFunctionType: Record<FunctionType, string> = { | ||
30 | + [FunctionType.PROPERTIES]: '属性', | ||
31 | + [FunctionType.EVENTS]: '事件', | ||
32 | + [FunctionType.SERVICE]: '服务', | ||
33 | +}; | ||
34 | + | ||
35 | +const handleCopy = async (value: string) => { | ||
36 | + const { createMessage } = useMessage(); | ||
37 | + const { copied, copy } = useClipboard({ legacy: true }); | ||
38 | + await copy(value); | ||
39 | + if (unref(copied)) createMessage.success('复制成功~'); | ||
40 | + else createMessage.error('复制失败~'); | ||
41 | +}; | ||
42 | + | ||
43 | +export const columnsDrawer: BasicColumn[] = [ | ||
44 | + { | ||
45 | + title: '功能类型', | ||
46 | + dataIndex: 'functionType', | ||
47 | + width: 90, | ||
48 | + format: (text: FunctionType) => { | ||
49 | + return formatFunctionType[text]; | ||
50 | + }, | ||
51 | + }, | ||
52 | + { | ||
53 | + title: '功能名称', | ||
54 | + dataIndex: 'functionName', | ||
55 | + width: 90, | ||
56 | + ellipsis: true, | ||
57 | + }, | ||
58 | + { | ||
59 | + title: '标识符', | ||
60 | + dataIndex: 'identifier', | ||
61 | + width: 90, | ||
62 | + ellipsis: true, | ||
63 | + customRender: ({ text }: Record<'text', string>) => { | ||
64 | + return h(Tooltip, { title: text }, () => | ||
65 | + h('span', { class: 'cursor-pointer', onClick: () => handleCopy(text) }, text) | ||
66 | + ); | ||
67 | + }, | ||
68 | + }, | ||
69 | + { | ||
70 | + title: '数据类型', | ||
71 | + dataIndex: 'functionJson.dataType.type', | ||
72 | + width: 100, | ||
73 | + format: (text: string) => { | ||
74 | + return text || '--'; | ||
75 | + }, | ||
76 | + ellipsis: true, | ||
77 | + }, | ||
78 | + // { | ||
79 | + // title: '事件类型', | ||
80 | + // dataIndex: 'eventType', | ||
81 | + // customRender({ text }) { | ||
82 | + // return h( | ||
83 | + // Tag, | ||
84 | + // { | ||
85 | + // color: EventTypeColor[text as EventType], | ||
86 | + // }, | ||
87 | + // () => EventTypeName[text as EventType] | ||
88 | + // ); | ||
89 | + // }, | ||
90 | + // ellipsis: true, | ||
91 | + // }, | ||
92 | + // { | ||
93 | + // title: '状态', | ||
94 | + // dataIndex: 'status', | ||
95 | + // width: 100, | ||
96 | + // customRender: (value: Record<'text', number>) => { | ||
97 | + // const { text } = value; | ||
98 | + // return h( | ||
99 | + // Tag, | ||
100 | + // { | ||
101 | + // color: text ? 'green' : 'red', | ||
102 | + // }, | ||
103 | + // () => (text ? '已发布' : '待发布') | ||
104 | + // ); | ||
105 | + // }, | ||
106 | + // }, | ||
107 | +]; | ||
108 | + | ||
109 | +export const searchFormSchema: FormSchema[] = [ | ||
110 | + { | ||
111 | + field: 'dictItemId', | ||
112 | + label: '', | ||
113 | + component: 'ApiSelect', | ||
114 | + colProps: { span: 7 }, | ||
115 | + componentProps() { | ||
116 | + return { | ||
117 | + api: findDictItemByCode, | ||
118 | + params: { | ||
119 | + dictCode: DictEnum.CATEGORY_FIELD, | ||
120 | + }, | ||
121 | + labelField: 'itemText', | ||
122 | + valueField: 'itemValue', | ||
123 | + placeholder: '请选择领域', | ||
124 | + // getPopupContainer: () => document.body, | ||
125 | + }; | ||
126 | + }, | ||
127 | + }, | ||
128 | + { | ||
129 | + field: 'name', | ||
130 | + label: '', | ||
131 | + component: 'Input', | ||
132 | + colProps: { span: 9 }, | ||
133 | + componentProps: { | ||
134 | + placeholder: '请输入品类名称', | ||
135 | + }, | ||
136 | + }, | ||
137 | +]; |
@@ -42,6 +42,12 @@ export enum ModelOfMatterPermission { | @@ -42,6 +42,12 @@ export enum ModelOfMatterPermission { | ||
42 | DELETE = 'api:yt:things_model:delete', | 42 | DELETE = 'api:yt:things_model:delete', |
43 | RELEASE = 'api:yt:things_model:release', | 43 | RELEASE = 'api:yt:things_model:release', |
44 | } | 44 | } |
45 | +export enum ModelCategoryPermission { | ||
46 | + CREATE = 'api:yt:things_model:category:post', | ||
47 | + UPDATE = 'api:yt:things_model:category:put', | ||
48 | + DELETE = 'api:yt:things_model:category:delete', | ||
49 | + RELEASE = 'api:yt:things_model:category:release', | ||
50 | +} | ||
45 | 51 | ||
46 | const handleCopy = async (value: string) => { | 52 | const handleCopy = async (value: string) => { |
47 | const { createMessage } = useMessage(); | 53 | const { createMessage } = useMessage(); |
@@ -212,6 +218,41 @@ export const step1Schemas: FormSchema[] = [ | @@ -212,6 +218,41 @@ export const step1Schemas: FormSchema[] = [ | ||
212 | }, | 218 | }, |
213 | }, | 219 | }, |
214 | { | 220 | { |
221 | + field: 'category', | ||
222 | + label: '产品品类', | ||
223 | + component: 'RadioGroup', | ||
224 | + defaultValue: 2, | ||
225 | + required: true, | ||
226 | + componentProps({ formModel }) { | ||
227 | + return { | ||
228 | + options: [ | ||
229 | + { label: '自定义品类', value: 2 }, | ||
230 | + { label: '标准品类', value: 1 }, | ||
231 | + ], | ||
232 | + onChange() { | ||
233 | + formModel.categoryId = null; | ||
234 | + formModel.categoryName = undefined; | ||
235 | + }, | ||
236 | + }; | ||
237 | + }, | ||
238 | + }, | ||
239 | + { | ||
240 | + field: 'categoryId', | ||
241 | + label: '产品品类', | ||
242 | + component: 'Input', | ||
243 | + defaultValue: undefined, | ||
244 | + ifShow: false, | ||
245 | + }, | ||
246 | + { | ||
247 | + field: 'categoryName', | ||
248 | + label: ' ', | ||
249 | + component: 'ApiSelect', | ||
250 | + colProps: { span: 14 }, | ||
251 | + slot: 'categoryName', | ||
252 | + required: true, | ||
253 | + ifShow: ({ model }) => model.category === 1, | ||
254 | + }, | ||
255 | + { | ||
215 | field: 'name', | 256 | field: 'name', |
216 | label: '产品名称', | 257 | label: '产品名称', |
217 | required: true, | 258 | required: true, |
@@ -325,6 +366,14 @@ export const columns: BasicColumn[] = [ | @@ -325,6 +366,14 @@ export const columns: BasicColumn[] = [ | ||
325 | width: 120, | 366 | width: 120, |
326 | }, | 367 | }, |
327 | { | 368 | { |
369 | + title: '产品品类', | ||
370 | + dataIndex: 'categoryName', | ||
371 | + width: 120, | ||
372 | + format: (text) => { | ||
373 | + return text ? text : '自定义品类'; | ||
374 | + }, | ||
375 | + }, | ||
376 | + { | ||
328 | title: '设备类型', | 377 | title: '设备类型', |
329 | dataIndex: 'deviceType', | 378 | dataIndex: 'deviceType', |
330 | width: 90, | 379 | width: 90, |
1 | <template> | 1 | <template> |
2 | <div class="step1"> | 2 | <div class="step1"> |
3 | - <BasicForm @register="register" /> | 3 | + <BasicForm @register="register"> |
4 | + <template #categoryName="{ model }"> | ||
5 | + <div class="flex"> | ||
6 | + <Input | ||
7 | + v-model:value="model.categoryName" | ||
8 | + placeholder="请选择所属品类" | ||
9 | + style="width: 300px" | ||
10 | + :disabled="cateGoryDisabled" | ||
11 | + @focus="handleFocus" | ||
12 | + /> | ||
13 | + <Button type="link" :disabled="!model.categoryName" @click="handleCategoryId" | ||
14 | + >查看功能</Button | ||
15 | + > | ||
16 | + </div> | ||
17 | + </template> | ||
18 | + </BasicForm> | ||
19 | + <CategoryList | ||
20 | + @register="registerDrawer" | ||
21 | + @handleOpenListDrawer="handleOpenListDrawer" | ||
22 | + @handleSelectCategory="handleSelectCategory" | ||
23 | + @handleClose="handleClose" | ||
24 | + /> | ||
25 | + <CategoryListDrawer @register="registerListDrawer" :cateforyListInfo="cateforyListInfo" /> | ||
4 | </div> | 26 | </div> |
5 | </template> | 27 | </template> |
6 | <script lang="ts" setup> | 28 | <script lang="ts" setup> |
7 | - import { nextTick } from 'vue'; | 29 | + import { nextTick, ref } from 'vue'; |
8 | import { BasicForm, useForm } from '/@/components/Form'; | 30 | import { BasicForm, useForm } from '/@/components/Form'; |
9 | import { step1Schemas } from '../device.profile.data'; | 31 | import { step1Schemas } from '../device.profile.data'; |
10 | import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue'; | 32 | import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue'; |
11 | import { buildUUID } from '/@/utils/uuid'; | 33 | import { buildUUID } from '/@/utils/uuid'; |
34 | + import { Input, Button } from 'ant-design-vue'; | ||
35 | + import { useDrawer } from '/@/components/Drawer'; | ||
36 | + import CategoryList from '../components/CategoryList.vue'; | ||
37 | + import CategoryListDrawer from '../components/CategoryListDrawer.vue'; | ||
12 | 38 | ||
13 | const emits = defineEmits(['next', 'emitDeviceType']); | 39 | const emits = defineEmits(['next', 'emitDeviceType']); |
14 | const props = defineProps({ | 40 | const props = defineProps({ |
15 | ifShowBtn: { type: Boolean, default: true }, | 41 | ifShowBtn: { type: Boolean, default: true }, |
16 | }); | 42 | }); |
43 | + const [registerDrawer, { openDrawer, closeDrawer }] = useDrawer(); | ||
44 | + const [registerListDrawer, { openDrawer: openListDrawer, closeDrawer: closeListDrawer }] = | ||
45 | + useDrawer(); | ||
17 | 46 | ||
18 | const [register, { validate, setFieldsValue, resetFields, updateSchema, getFieldsValue }] = | 47 | const [register, { validate, setFieldsValue, resetFields, updateSchema, getFieldsValue }] = |
19 | useForm({ | 48 | useForm({ |
@@ -29,6 +58,9 @@ | @@ -29,6 +58,9 @@ | ||
29 | }, | 58 | }, |
30 | submitFunc: customSubmitFunc, | 59 | submitFunc: customSubmitFunc, |
31 | }); | 60 | }); |
61 | + | ||
62 | + const cateGoryDisabled = ref<boolean>(false); | ||
63 | + | ||
32 | const editOrAddNameStatus = (nameStatus) => | 64 | const editOrAddNameStatus = (nameStatus) => |
33 | updateSchema({ | 65 | updateSchema({ |
34 | field: 'name', | 66 | field: 'name', |
@@ -48,19 +80,20 @@ | @@ -48,19 +80,20 @@ | ||
48 | emits('emitDeviceType', values?.deviceType); | 80 | emits('emitDeviceType', values?.deviceType); |
49 | } | 81 | } |
50 | //回显数据 | 82 | //回显数据 |
51 | - const setFormData = (v) => { | 83 | + const setFormData = async (v) => { |
52 | if (v.image) { | 84 | if (v.image) { |
53 | setFieldsValue({ | 85 | setFieldsValue({ |
54 | image: [{ uid: buildUUID(), name: 'name', url: v.image } as FileItem], | 86 | image: [{ uid: buildUUID(), name: 'name', url: v.image } as FileItem], |
55 | }); | 87 | }); |
56 | } | 88 | } |
57 | const { image, ...params } = v; | 89 | const { image, ...params } = v; |
58 | - console.log(image); | ||
59 | - setFieldsValue({ ...params }); | 90 | + console.log(image, props.isUpdate); |
91 | + setFieldsValue({ ...params, category: params?.categoryId ? 1 : 2 }); | ||
60 | }; | 92 | }; |
61 | //获取数据 | 93 | //获取数据 |
62 | async function getFormData() { | 94 | async function getFormData() { |
63 | - const values = await validate(); | 95 | + await validate(); |
96 | + const values = getFieldsValue(); | ||
64 | if (!values) return; | 97 | if (!values) return; |
65 | if (Reflect.has(values, 'image')) { | 98 | if (Reflect.has(values, 'image')) { |
66 | const file = (values.image || []).at(0) || {}; | 99 | const file = (values.image || []).at(0) || {}; |
@@ -74,12 +107,65 @@ | @@ -74,12 +107,65 @@ | ||
74 | }; | 107 | }; |
75 | 108 | ||
76 | const editOrAddDeviceTypeStatus = (status: boolean) => { | 109 | const editOrAddDeviceTypeStatus = (status: boolean) => { |
77 | - updateSchema({ | ||
78 | - field: 'deviceType', | ||
79 | - componentProps: { | ||
80 | - disabled: status, | 110 | + updateSchema([ |
111 | + { | ||
112 | + field: 'deviceType', | ||
113 | + componentProps: { | ||
114 | + disabled: status, | ||
115 | + }, | ||
81 | }, | 116 | }, |
82 | - }); | 117 | + { |
118 | + field: 'category', | ||
119 | + componentProps({ formModel }) { | ||
120 | + return { | ||
121 | + options: [ | ||
122 | + { label: '自定义品类', value: 2 }, | ||
123 | + { label: '标准品类', value: 1 }, | ||
124 | + ], | ||
125 | + onChange() { | ||
126 | + formModel.categoryId = null; | ||
127 | + formModel.categoryName = undefined; | ||
128 | + }, | ||
129 | + disabled: status, | ||
130 | + }; | ||
131 | + }, | ||
132 | + }, | ||
133 | + ]); | ||
134 | + | ||
135 | + cateGoryDisabled.value = status; | ||
136 | + }; | ||
137 | + | ||
138 | + // 查看功能 | ||
139 | + const handleCategoryId = () => { | ||
140 | + const { categoryId } = getFieldsValue() || {}; | ||
141 | + if (!categoryId) return; | ||
142 | + cateforyListInfo.value = { record: { id: categoryId }, isRight: false }; | ||
143 | + openListDrawer(true); | ||
144 | + }; | ||
145 | + | ||
146 | + // 获取焦点 | ||
147 | + const handleFocus = () => { | ||
148 | + const { categoryId } = getFieldsValue() || {}; | ||
149 | + openDrawer(true, { categoryId }); | ||
150 | + }; | ||
151 | + | ||
152 | + //打开品类名称 | ||
153 | + const cateforyListInfo = ref<any>({}); | ||
154 | + const handleOpenListDrawer = (record?: any) => { | ||
155 | + cateforyListInfo.value = { record, isRight: true }; | ||
156 | + openListDrawer(true); | ||
157 | + }; | ||
158 | + | ||
159 | + // 选择产品品类 | ||
160 | + const handleSelectCategory = (item) => { | ||
161 | + closeDrawer(); | ||
162 | + closeListDrawer(); | ||
163 | + item.id && setFieldsValue({ categoryId: item.id, categoryName: item?.name }); | ||
164 | + }; | ||
165 | + | ||
166 | + // 关闭抽屉 | ||
167 | + const handleClose = () => { | ||
168 | + closeListDrawer(); | ||
83 | }; | 169 | }; |
84 | 170 | ||
85 | defineExpose({ | 171 | defineExpose({ |
@@ -27,25 +27,33 @@ | @@ -27,25 +27,33 @@ | ||
27 | </div> | 27 | </div> |
28 | <div class="flex justify-between items-end"> | 28 | <div class="flex justify-between items-end"> |
29 | <div class="flex gap-2"> | 29 | <div class="flex gap-2"> |
30 | - <Authority :value="ModelOfMatterPermission.CREATE"> | 30 | + <Authority :value="[ModelOfMatterPermission.CREATE, ModelCategoryPermission.CREATE]"> |
31 | <Button v-if="isShowBtn" type="primary" @click="handleCreateOrEdit()"> | 31 | <Button v-if="isShowBtn" type="primary" @click="handleCreateOrEdit()"> |
32 | 新增物模型 | 32 | 新增物模型 |
33 | </Button> | 33 | </Button> |
34 | </Authority> | 34 | </Authority> |
35 | - <Button type="primary" @click="handleOpenTsl"> 物模型TSL </Button> | ||
36 | - <Authority value="api:yt:things_model:import"> | ||
37 | - <Upload | 35 | + <Button v-if="!record.ifShowClass" type="primary" @click="handleOpenTsl"> |
36 | + 物模型TSL</Button | ||
37 | + > | ||
38 | + <Button v-else type="primary" @click="handleExport" :loading="loading" | ||
39 | + >导出物模型</Button | ||
40 | + > | ||
41 | + <Authority | ||
42 | + :value="['api:yt:things_model:import', 'api:yt:things_model:category:import']" | ||
43 | + > | ||
44 | + <!-- <Upload | ||
38 | v-if="isShowBtn" | 45 | v-if="isShowBtn" |
39 | accept=".json," | 46 | accept=".json," |
40 | :show-upload-list="false" | 47 | :show-upload-list="false" |
41 | :customRequest="handleImportModel" | 48 | :customRequest="handleImportModel" |
42 | > | 49 | > |
43 | <Button type="primary" :loading="importLoading"> 导入物模型 </Button> | 50 | <Button type="primary" :loading="importLoading"> 导入物模型 </Button> |
44 | - </Upload> | 51 | + </Upload> --> |
52 | + <Button type="primary" @click="handleSelectImport">导入物模型</Button> | ||
45 | </Authority> | 53 | </Authority> |
46 | </div> | 54 | </div> |
47 | <div class="flex gap-2"> | 55 | <div class="flex gap-2"> |
48 | - <Authority :value="ModelOfMatterPermission.RELEASE"> | 56 | + <Authority :value="[ModelOfMatterPermission.RELEASE]"> |
49 | <Popconfirm | 57 | <Popconfirm |
50 | title="是否需要发布上线?" | 58 | title="是否需要发布上线?" |
51 | ok-text="确定" | 59 | ok-text="确定" |
@@ -61,7 +69,7 @@ | @@ -61,7 +69,7 @@ | ||
61 | <Button v-if="isShowBtn" class="!bg-gray-200" type="text" @click="handleReturn"> | 69 | <Button v-if="isShowBtn" class="!bg-gray-200" type="text" @click="handleReturn"> |
62 | 返回 | 70 | 返回 |
63 | </Button> | 71 | </Button> |
64 | - <Authority :value="ModelOfMatterPermission.DELETE"> | 72 | + <Authority :value="[ModelOfMatterPermission.DELETE, ModelCategoryPermission.DELETE]"> |
65 | <Popconfirm | 73 | <Popconfirm |
66 | title="您确定要批量删除数据" | 74 | title="您确定要批量删除数据" |
67 | ok-text="确定" | 75 | ok-text="确定" |
@@ -94,14 +102,14 @@ | @@ -94,14 +102,14 @@ | ||
94 | { | 102 | { |
95 | label: '编辑', | 103 | label: '编辑', |
96 | icon: 'clarity:note-edit-line', | 104 | icon: 'clarity:note-edit-line', |
97 | - auth: ModelOfMatterPermission.UPDATE, | 105 | + auth: [ModelOfMatterPermission.UPDATE, ModelCategoryPermission.UPDATE], |
98 | onClick: handleCreateOrEdit.bind(null, record), | 106 | onClick: handleCreateOrEdit.bind(null, record), |
99 | ifShow: !isShowBtn ? false : true, | 107 | ifShow: !isShowBtn ? false : true, |
100 | }, | 108 | }, |
101 | { | 109 | { |
102 | label: '删除', | 110 | label: '删除', |
103 | icon: 'ant-design:delete-outlined', | 111 | icon: 'ant-design:delete-outlined', |
104 | - auth: ModelOfMatterPermission.DELETE, | 112 | + auth: [ModelOfMatterPermission.DELETE, ModelCategoryPermission.DELETE], |
105 | color: 'error', | 113 | color: 'error', |
106 | ifShow: !isShowBtn ? false : true, | 114 | ifShow: !isShowBtn ? false : true, |
107 | popConfirm: { | 115 | popConfirm: { |
@@ -119,6 +127,11 @@ | @@ -119,6 +127,11 @@ | ||
119 | @success="handleSuccess" | 127 | @success="handleSuccess" |
120 | /> | 128 | /> |
121 | <PhysicalModelTsl :record="$props.record" @register="registerModalTsl" /> | 129 | <PhysicalModelTsl :record="$props.record" @register="registerModalTsl" /> |
130 | + <SelectImport | ||
131 | + :record="$props.record" | ||
132 | + @register="registerModalSelect" | ||
133 | + @handleReload="handleReload" | ||
134 | + /> | ||
122 | </div> | 135 | </div> |
123 | </template> | 136 | </template> |
124 | <script lang="ts" setup> | 137 | <script lang="ts" setup> |
@@ -127,25 +140,32 @@ | @@ -127,25 +140,32 @@ | ||
127 | import { | 140 | import { |
128 | modelOfMatterForm, | 141 | modelOfMatterForm, |
129 | ModelOfMatterPermission, | 142 | ModelOfMatterPermission, |
143 | + ModelCategoryPermission, | ||
130 | physicalColumn, | 144 | physicalColumn, |
131 | } from '../device.profile.data'; | 145 | } from '../device.profile.data'; |
132 | import { useBatchDelete } from '/@/hooks/web/useBatchDelete'; | 146 | import { useBatchDelete } from '/@/hooks/web/useBatchDelete'; |
133 | import { Authority } from '/@/components/Authority'; | 147 | import { Authority } from '/@/components/Authority'; |
134 | import PhysicalModelModal from './cpns/physical/PhysicalModelModal.vue'; | 148 | import PhysicalModelModal from './cpns/physical/PhysicalModelModal.vue'; |
135 | import PhysicalModelTsl from './cpns/physical/PhysicalModelTsl.vue'; | 149 | import PhysicalModelTsl from './cpns/physical/PhysicalModelTsl.vue'; |
136 | - import { Popconfirm, Button, Alert, Upload } from 'ant-design-vue'; | 150 | + import { Popconfirm, Button, Alert } from 'ant-design-vue'; |
137 | import { useMessage } from '/@/hooks/web/useMessage'; | 151 | import { useMessage } from '/@/hooks/web/useMessage'; |
138 | import { DeviceRecord } from '/@/api/device/model/deviceModel'; | 152 | import { DeviceRecord } from '/@/api/device/model/deviceModel'; |
139 | import { | 153 | import { |
140 | deleteModel, | 154 | deleteModel, |
155 | + deleteModelCategory, | ||
141 | getModelList, | 156 | getModelList, |
142 | - importModelOfMatter, | ||
143 | releaseModel, | 157 | releaseModel, |
144 | } from '/@/api/device/modelOfMatter'; | 158 | } from '/@/api/device/modelOfMatter'; |
145 | import { OpenModelOfMatterModelParams, OpenModelMode } from './cpns/physical/types'; | 159 | import { OpenModelOfMatterModelParams, OpenModelMode } from './cpns/physical/types'; |
146 | import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; | 160 | import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; |
147 | - import { ref } from 'vue'; | ||
148 | - import { isObject, isString } from '/@/utils/is'; | 161 | + import { ref, unref } from 'vue'; |
162 | + import { isObject } from '/@/utils/is'; | ||
163 | + import { useRole } from '/@/hooks/business/useRole'; | ||
164 | + import { ExportModelCategory } from '/@/api/device/modelOfMatter'; | ||
165 | + import { FunctionType } from './cpns/physical/cpns/config'; | ||
166 | + import SelectImport from '../components/SelectImport.vue'; | ||
167 | + | ||
168 | + const { isPlatformAdmin, isSysadmin } = useRole(); | ||
149 | defineEmits(['register']); | 169 | defineEmits(['register']); |
150 | 170 | ||
151 | const props = defineProps<{ | 171 | const props = defineProps<{ |
@@ -156,10 +176,15 @@ | @@ -156,10 +176,15 @@ | ||
156 | const isShowBtn = ref(false); | 176 | const isShowBtn = ref(false); |
157 | const [registerModal, { openModal }] = useModal(); | 177 | const [registerModal, { openModal }] = useModal(); |
158 | const [registerModalTsl, { openModal: openModalTsl }] = useModal(); | 178 | const [registerModalTsl, { openModal: openModalTsl }] = useModal(); |
179 | + const [registerModalSelect, { openModal: openModalSelect }] = useModal(); | ||
159 | 180 | ||
160 | const [registerTable, { reload, setProps }] = useTable({ | 181 | const [registerTable, { reload, setProps }] = useTable({ |
161 | api: async (params: Record<'page' | 'pageSize', number>) => { | 182 | api: async (params: Record<'page' | 'pageSize', number>) => { |
162 | - return await getModelList({ ...params, deviceProfileId: props.record.id }); | 183 | + return await getModelList({ |
184 | + ...params, | ||
185 | + id: props.record.id, | ||
186 | + selectType: props.record.ifShowClass ? 'category' : undefined, | ||
187 | + }); | ||
163 | }, | 188 | }, |
164 | columns: physicalColumn, | 189 | columns: physicalColumn, |
165 | showIndexColumn: false, | 190 | showIndexColumn: false, |
@@ -184,9 +209,10 @@ | @@ -184,9 +209,10 @@ | ||
184 | const handleSuccess = () => { | 209 | const handleSuccess = () => { |
185 | reload(); | 210 | reload(); |
186 | }; | 211 | }; |
187 | - | ||
188 | const { hasBatchDelete, handleDeleteOrBatchDelete, selectionOptions } = useBatchDelete( | 212 | const { hasBatchDelete, handleDeleteOrBatchDelete, selectionOptions } = useBatchDelete( |
189 | - deleteModel, | 213 | + props.record.ifShowClass && (unref(isPlatformAdmin) || unref(isSysadmin)) |
214 | + ? deleteModelCategory | ||
215 | + : deleteModel, | ||
190 | handleSuccess, | 216 | handleSuccess, |
191 | setProps | 217 | setProps |
192 | ); | 218 | ); |
@@ -242,59 +268,76 @@ | @@ -242,59 +268,76 @@ | ||
242 | } | 268 | } |
243 | }; | 269 | }; |
244 | 270 | ||
245 | - const paseJSON = (string: string) => { | ||
246 | - let data = null; | ||
247 | - let flag = false; | ||
248 | - try { | ||
249 | - if (!isString(string)) return { flag: false, data }; | ||
250 | - data = JSON.parse(string); | ||
251 | - flag = true; | ||
252 | - if (!isObject(data)) flag = false; | ||
253 | - } catch (error) {} | ||
254 | - return { flag, data }; | ||
255 | - }; | ||
256 | - | ||
257 | const isEmptyObject = (value: any) => isObject(value) && !Object.keys(value).length; | 271 | const isEmptyObject = (value: any) => isObject(value) && !Object.keys(value).length; |
258 | 272 | ||
259 | - const importLoading = ref(false); | ||
260 | - const handleImportModel = async (data: { file: File }) => { | ||
261 | - const fileReader = new FileReader(); | ||
262 | - | ||
263 | - fileReader.onload = async () => { | ||
264 | - const { flag, data } = paseJSON(fileReader.result as string); | ||
265 | - if (!flag) { | ||
266 | - createMessage.warning('JSON解析失败,请导入正确的JSON~'); | ||
267 | - return; | ||
268 | - } | ||
269 | - try { | ||
270 | - importLoading.value = true; | 273 | + // 选择导入物模型的方式 |
274 | + const handleSelectImport = () => { | ||
275 | + openModalSelect(true, { | ||
276 | + id: props.record.id, | ||
277 | + isCateGory: (unref(isPlatformAdmin) || unref(isSysadmin)) && props.record.ifShowClass, | ||
278 | + }); | ||
279 | + }; | ||
271 | 280 | ||
272 | - Object.keys(data || {}).forEach((key) => { | ||
273 | - const value = (data || {})[key]; | ||
274 | - if (value && isEmptyObject(value)) { | ||
275 | - (data || {})[key] = []; | ||
276 | - } | ||
277 | - }); | 281 | + const handleReload = () => { |
282 | + reload(); | ||
283 | + }; | ||
278 | 284 | ||
279 | - const result = await importModelOfMatter({ | ||
280 | - tkDeviceProfileId: props.record.id, | ||
281 | - data: data!, | ||
282 | - functionType: 'all', | ||
283 | - }); | 285 | + // 导出物模型 |
286 | + const loading = ref<boolean>(false); | ||
284 | 287 | ||
285 | - result | ||
286 | - ? createMessage.success('导入成功~') | ||
287 | - : createMessage.error('JSON解析失败,请导入正确的JSON~'); | 288 | + const getAllCategory = () => { |
289 | + const { id: deviceProfileId } = props.record; | ||
290 | + return Promise.all([ | ||
291 | + ExportModelCategory({ | ||
292 | + deviceProfileId, | ||
293 | + functionType: FunctionType.EVENTS, | ||
294 | + }), | ||
295 | + ExportModelCategory({ | ||
296 | + deviceProfileId, | ||
297 | + functionType: FunctionType.PROPERTIES, | ||
298 | + }), | ||
299 | + ExportModelCategory({ | ||
300 | + deviceProfileId, | ||
301 | + functionType: FunctionType.SERVICE, | ||
302 | + }), | ||
303 | + ]); | ||
304 | + }; | ||
305 | + const exportJSONFile = (value: Recordable) => { | ||
306 | + const blob = new Blob([JSON.stringify(value, null, 2)], { type: 'text/json' }); | ||
307 | + const objectURL = URL.createObjectURL(blob); | ||
308 | + const element = document.createElement('a'); | ||
309 | + element.href = objectURL; | ||
310 | + element.download = `${props.record.name}-model.json`; | ||
311 | + element.style.display = 'none'; | ||
312 | + document.body.appendChild(element); | ||
313 | + element.click(); | ||
314 | + element.remove(); | ||
315 | + URL.revokeObjectURL(objectURL); | ||
316 | + }; | ||
288 | 317 | ||
289 | - result && reload(); | ||
290 | - } catch (error) { | ||
291 | - throw error; | ||
292 | - } finally { | ||
293 | - importLoading.value = false; | ||
294 | - } | ||
295 | - }; | 318 | + const isEmptyStr = (s: any) => { |
319 | + if (s == undefined || s == null || s == '') { | ||
320 | + return true; | ||
321 | + } | ||
322 | + return false; | ||
323 | + }; | ||
296 | 324 | ||
297 | - fileReader.readAsText(data.file, 'utf-8'); | 325 | + //产品品类物模型导出 |
326 | + const handleExport = async () => { | ||
327 | + loading.value = true; | ||
328 | + try { | ||
329 | + const [events, properties, services] = await getAllCategory(); | ||
330 | + const value = { | ||
331 | + properties: isEmptyObject(properties) || isEmptyStr(properties) ? [] : properties, | ||
332 | + services: isEmptyObject(services) || isEmptyStr(services) ? [] : services, | ||
333 | + events: isEmptyObject(events) || isEmptyStr(events) ? [] : events, | ||
334 | + }; | ||
335 | + exportJSONFile(value); | ||
336 | + } catch (error) { | ||
337 | + throw error; | ||
338 | + } finally { | ||
339 | + loading.value = false; | ||
340 | + } | ||
298 | }; | 341 | }; |
299 | 342 | ||
300 | defineExpose({}); | 343 | defineExpose({}); |
@@ -60,11 +60,19 @@ | @@ -60,11 +60,19 @@ | ||
60 | import Service from './cpns/Service.vue'; | 60 | import Service from './cpns/Service.vue'; |
61 | import Events from './cpns/Events.vue'; | 61 | import Events from './cpns/Events.vue'; |
62 | import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; | 62 | import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; |
63 | - import { createModel, updateModel } from '/@/api/device/modelOfMatter'; | 63 | + import { |
64 | + createModel, | ||
65 | + updateModel, | ||
66 | + createModelCategory, | ||
67 | + updateModelCategory, | ||
68 | + } from '/@/api/device/modelOfMatter'; | ||
64 | import { DeviceRecord, DeviceTypeEnum } from '/@/api/device/model/deviceModel'; | 69 | import { DeviceRecord, DeviceTypeEnum } from '/@/api/device/model/deviceModel'; |
65 | import { useMessage } from '/@/hooks/web/useMessage'; | 70 | import { useMessage } from '/@/hooks/web/useMessage'; |
66 | import { OpenModelMode, OpenModelOfMatterModelParams } from './types/index'; | 71 | import { OpenModelMode, OpenModelOfMatterModelParams } from './types/index'; |
67 | import { FunctionType } from './cpns/config'; | 72 | import { FunctionType } from './cpns/config'; |
73 | + import { useRole } from '/@/hooks/business/useRole'; | ||
74 | + | ||
75 | + const { isPlatformAdmin, isSysadmin } = useRole(); | ||
68 | 76 | ||
69 | const emit = defineEmits(['register', 'success']); | 77 | const emit = defineEmits(['register', 'success']); |
70 | 78 | ||
@@ -158,14 +166,28 @@ | @@ -158,14 +166,28 @@ | ||
158 | params = (await EventsRef.value?.getFormData()) || {}; | 166 | params = (await EventsRef.value?.getFormData()) || {}; |
159 | } | 167 | } |
160 | params.deviceProfileId = props.record.id; | 168 | params.deviceProfileId = props.record.id; |
169 | + | ||
161 | if (unref(openModalMode) === OpenModelMode.CREATE) { | 170 | if (unref(openModalMode) === OpenModelMode.CREATE) { |
162 | - await createModel(params); | 171 | + (unref(isSysadmin) || unref(isPlatformAdmin)) && props.record.ifShowClass |
172 | + ? await createModelCategory({ | ||
173 | + ...params, | ||
174 | + deviceProfileId: undefined, | ||
175 | + categoryId: props.record.id, | ||
176 | + }) | ||
177 | + : await createModel(params); | ||
163 | createMessage.success('创建成功'); | 178 | createMessage.success('创建成功'); |
164 | } else { | 179 | } else { |
165 | params.id = unref(openModalParams)?.record.id; | 180 | params.id = unref(openModalParams)?.record.id; |
166 | - await updateModel(params); | 181 | + (unref(isSysadmin) || unref(isPlatformAdmin)) && props.record.ifShowClass |
182 | + ? await updateModelCategory({ | ||
183 | + ...params, | ||
184 | + deviceProfileId: undefined, | ||
185 | + categoryId: props.record.id, | ||
186 | + }) | ||
187 | + : await updateModel(params); | ||
167 | createMessage.success('修改成功'); | 188 | createMessage.success('修改成功'); |
168 | } | 189 | } |
190 | + | ||
169 | closeModal(); | 191 | closeModal(); |
170 | emit('success'); | 192 | emit('success'); |
171 | } catch (error) { | 193 | } catch (error) { |
@@ -73,9 +73,18 @@ | @@ -73,9 +73,18 @@ | ||
73 | const getAllModel = () => { | 73 | const getAllModel = () => { |
74 | const { id: deviceProfileId } = props.record; | 74 | const { id: deviceProfileId } = props.record; |
75 | return Promise.all([ | 75 | return Promise.all([ |
76 | - getModelTsl({ deviceProfileId, functionType: FunctionType.EVENTS }), | ||
77 | - getModelTsl({ deviceProfileId, functionType: FunctionType.PROPERTIES }), | ||
78 | - getModelTsl({ deviceProfileId, functionType: FunctionType.SERVICE }), | 76 | + getModelTsl({ |
77 | + deviceProfileId, | ||
78 | + functionType: FunctionType.EVENTS, | ||
79 | + }), | ||
80 | + getModelTsl({ | ||
81 | + deviceProfileId, | ||
82 | + functionType: FunctionType.PROPERTIES, | ||
83 | + }), | ||
84 | + getModelTsl({ | ||
85 | + deviceProfileId, | ||
86 | + functionType: FunctionType.SERVICE, | ||
87 | + }), | ||
79 | ]); | 88 | ]); |
80 | }; | 89 | }; |
81 | 90 |
@@ -84,7 +84,10 @@ | @@ -84,7 +84,10 @@ | ||
84 | const handleSwitchTsl = async (functionType: FunctionType) => { | 84 | const handleSwitchTsl = async (functionType: FunctionType) => { |
85 | try { | 85 | try { |
86 | loading.value = true; | 86 | loading.value = true; |
87 | - const record = await getModelTsl({ deviceProfileId: props.record.id, functionType }); | 87 | + const record = await getModelTsl({ |
88 | + deviceProfileId: props.record.id, | ||
89 | + functionType, | ||
90 | + }); | ||
88 | jsonValue.value = JSON.stringify( | 91 | jsonValue.value = JSON.stringify( |
89 | { | 92 | { |
90 | [functionType]: record, | 93 | [functionType]: record, |
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[] => { |
@@ -138,13 +138,90 @@ | @@ -138,13 +138,90 @@ | ||
138 | }); | 138 | }); |
139 | } | 139 | } |
140 | 140 | ||
141 | + const cipherLength = 7; | ||
142 | + | ||
143 | + //字符串星号替代 | ||
144 | + function parseStringToStar(originalStr: string, startLength: number, endLength: number) { | ||
145 | + //字符串大于7位,前3后4明文,其他以星号替代 | ||
146 | + //字符串小于7位,前1后1明文,其他以星号替代 | ||
147 | + return originalStr?.length > startLength | ||
148 | + ? originalStr?.substr(0, startLength) + | ||
149 | + new Array(originalStr?.length - endLength)?.join('*') + | ||
150 | + originalStr?.substr(-endLength) | ||
151 | + : originalStr; | ||
152 | + } | ||
153 | + | ||
154 | + //长度 都大于或者小于7或者其中一个大于7,一个小于7个字符的处理 | ||
155 | + //二维数组优化if else if else | ||
156 | + const mapConditionConfig = [ | ||
157 | + [ | ||
158 | + (startParam, endParam) => | ||
159 | + String(startParam)?.length > cipherLength && String(endParam)?.length > cipherLength, | ||
160 | + (startParam) => parseStringToStar(startParam, 3, 4), | ||
161 | + (endParam) => parseStringToStar(endParam, 3, 4), | ||
162 | + ], | ||
163 | + [ | ||
164 | + (startParam, endParam) => | ||
165 | + String(startParam)?.length < cipherLength && String(endParam)?.length < cipherLength, | ||
166 | + (startParam) => parseStringToStar(startParam, 1, 1), | ||
167 | + (endParam) => parseStringToStar(endParam, 1, 1), | ||
168 | + ], | ||
169 | + [ | ||
170 | + (startParam, endParam) => | ||
171 | + String(startParam)?.length > cipherLength || String(endParam)?.length < cipherLength, | ||
172 | + (startParam) => parseStringToStar(startParam, 3, 4), | ||
173 | + (endParam) => parseStringToStar(endParam, 1, 1), | ||
174 | + ], | ||
175 | + [ | ||
176 | + (startParam, endParam) => | ||
177 | + String(startParam)?.length < cipherLength || String(endParam)?.length > cipherLength, | ||
178 | + (startParam) => parseStringToStar(startParam, 1, 1), | ||
179 | + (endParam) => parseStringToStar(endParam, 3, 4), | ||
180 | + ], | ||
181 | + ]; | ||
182 | + | ||
183 | + const handleStandardLength = ( | ||
184 | + startKey: string, | ||
185 | + endKey: string, | ||
186 | + startParam: string, | ||
187 | + endParam: string | ||
188 | + ) => { | ||
189 | + const findDateFlow: any = mapConditionConfig.find((item: Function[]) => | ||
190 | + item[0](startParam, endParam) | ||
191 | + ); | ||
192 | + if (!findDateFlow) return; | ||
193 | + const cipherData: Recordable = {}; | ||
194 | + cipherData[startKey] = findDateFlow[1](startParam); | ||
195 | + cipherData[endKey] = findDateFlow[2](endParam); | ||
196 | + return cipherData; | ||
197 | + }; | ||
198 | + | ||
199 | + const handleCipherTextConfig = (e) => { | ||
200 | + /** | ||
201 | + * username和password 存在则是邮件配置 | ||
202 | + * secretId和secretKey 存在则是腾讯云配置信息 | ||
203 | + * accessKeyId和accessKeySecret 存在则是阿里云配置信息 | ||
204 | + */ | ||
205 | + const { username, password, secretId, secretKey, accessKeyId, accessKeySecret } = e; | ||
206 | + return JSON.parse( | ||
207 | + JSON.stringify({ | ||
208 | + ...e, | ||
209 | + ...handleStandardLength('username', 'password', username, password), | ||
210 | + ...handleStandardLength('secretId', 'secretKey', secretId, secretKey), | ||
211 | + ...handleStandardLength('accessKeyId', 'accessKeySecret', accessKeyId, accessKeySecret), | ||
212 | + }) | ||
213 | + ); | ||
214 | + }; | ||
215 | + | ||
141 | function showData(record: Recordable) { | 216 | function showData(record: Recordable) { |
142 | Modal.info({ | 217 | Modal.info({ |
143 | title: '当前配置', | 218 | title: '当前配置', |
144 | width: 600, | 219 | width: 600, |
145 | centered: true, | 220 | centered: true, |
146 | maskClosable: true, | 221 | maskClosable: true, |
147 | - content: h(JsonPreview, { data: JSON.parse(JSON.stringify(record.config)) }), | 222 | + content: h(JsonPreview, { |
223 | + data: handleCipherTextConfig(record.config), | ||
224 | + }), | ||
148 | }); | 225 | }); |
149 | } | 226 | } |
150 | const statusChange = async (checked, record) => { | 227 | const statusChange = async (checked, record) => { |
@@ -184,10 +184,12 @@ | @@ -184,10 +184,12 @@ | ||
184 | businessText.value === BusinessReportConfigTextEnum.BUSINESS_ADD_TEXT | 184 | businessText.value === BusinessReportConfigTextEnum.BUSINESS_ADD_TEXT |
185 | ? await createOrEditReportManage(data) | 185 | ? await createOrEditReportManage(data) |
186 | : putReportConfigManage({ ...restData.data, ...data }); | 186 | : putReportConfigManage({ ...restData.data, ...data }); |
187 | - emits('success'); | ||
188 | createMessage.success(`${businessText.value}成功`); | 187 | createMessage.success(`${businessText.value}成功`); |
189 | closeDrawer(); | 188 | closeDrawer(); |
190 | handleClose(); | 189 | handleClose(); |
190 | + setTimeout(() => { | ||
191 | + emits('success'); | ||
192 | + }, 500); | ||
191 | } finally { | 193 | } finally { |
192 | setDrawerProps({ confirmLoading: false }); | 194 | setDrawerProps({ confirmLoading: false }); |
193 | } | 195 | } |
@@ -471,7 +471,6 @@ export const formSchema: BFormSchema[] = [ | @@ -471,7 +471,6 @@ export const formSchema: BFormSchema[] = [ | ||
471 | dynamicRules: () => { | 471 | dynamicRules: () => { |
472 | return [ | 472 | return [ |
473 | { | 473 | { |
474 | - // required: model[SchemaFiled.AGG] !== AggregateDataEnum.NONE, | ||
475 | required: true, | 474 | required: true, |
476 | message: '间隔时间为必填项', | 475 | message: '间隔时间为必填项', |
477 | type: 'number', | 476 | type: 'number', |
@@ -479,7 +478,11 @@ export const formSchema: BFormSchema[] = [ | @@ -479,7 +478,11 @@ export const formSchema: BFormSchema[] = [ | ||
479 | ]; | 478 | ]; |
480 | }, | 479 | }, |
481 | ifShow({ values }) { | 480 | ifShow({ values }) { |
482 | - return values[SchemaFiled.WAY] === QueryWay.TIME_PERIOD && exectueIsImmed(values.executeWay); | 481 | + return ( |
482 | + values[SchemaFiled.WAY] === QueryWay.TIME_PERIOD && | ||
483 | + exectueIsImmed(values.executeWay) && | ||
484 | + values[SchemaFiled.DATA_TYPE] !== DataTypeEnum.ORIGINAL | ||
485 | + ); | ||
483 | }, | 486 | }, |
484 | componentProps({ formModel, formActionType }) { | 487 | componentProps({ formModel, formActionType }) { |
485 | const options = | 488 | const options = |
@@ -166,6 +166,8 @@ export const useHooks = () => { | @@ -166,6 +166,8 @@ export const useHooks = () => { | ||
166 | dataRange: [value?.startTs, value?.endTs], | 166 | dataRange: [value?.startTs, value?.endTs], |
167 | dateGroupGap: value?.interval, | 167 | dateGroupGap: value?.interval, |
168 | }; | 168 | }; |
169 | + Reflect.deleteProperty(value, 'startTs'); | ||
170 | + Reflect.deleteProperty(value, 'interval'); | ||
169 | } | 171 | } |
170 | const spanDisance = executeContent?.split(' '); | 172 | const spanDisance = executeContent?.split(' '); |
171 | const cronTime = | 173 | const cronTime = |
@@ -34,7 +34,12 @@ | @@ -34,7 +34,12 @@ | ||
34 | @change="(value) => handleChangeChars(value, item.device, item)" | 34 | @change="(value) => handleChangeChars(value, item.device, item)" |
35 | v-bind="createPickerSearch()" | 35 | v-bind="createPickerSearch()" |
36 | placeholder="请选择设备属性" | 36 | placeholder="请选择设备属性" |
37 | - :options="item?.attributes?.map((item1) => ({ label: item1, value: item1 }))" | 37 | + :options=" |
38 | + item?.attributes?.map((attrItem: any) => ({ | ||
39 | + label: attrItem.label, | ||
40 | + value: attrItem.value, | ||
41 | + })) | ||
42 | + " | ||
38 | /> | 43 | /> |
39 | </div> | 44 | </div> |
40 | <div class="w-full h-full flex justify-center items-center"> | 45 | <div class="w-full h-full flex justify-center items-center"> |
@@ -64,6 +69,8 @@ | @@ -64,6 +69,8 @@ | ||
64 | import { ExecuteReportRecord } from '/@/api/export/model/exportModel'; | 69 | import { ExecuteReportRecord } from '/@/api/export/model/exportModel'; |
65 | import { Select, Spin, Empty } from 'ant-design-vue'; | 70 | import { Select, Spin, Empty } from 'ant-design-vue'; |
66 | import { createPickerSearch } from '/@/utils/pickerSearch'; | 71 | import { createPickerSearch } from '/@/utils/pickerSearch'; |
72 | + import { getDeviceDetail } from '/@/api/device/deviceManager'; | ||
73 | + import { getAttribute } from '/@/api/ruleengine/ruleengineApi'; | ||
67 | 74 | ||
68 | interface ResponsData { | 75 | interface ResponsData { |
69 | attr: string; | 76 | attr: string; |
@@ -141,20 +148,66 @@ | @@ -141,20 +148,66 @@ | ||
141 | return chartOption; | 148 | return chartOption; |
142 | }; | 149 | }; |
143 | 150 | ||
151 | + const handleDeviceProfileAttributes = async (entityId: string) => { | ||
152 | + const deviceDetailRes = await getDeviceDetail(entityId); | ||
153 | + const { deviceProfileId } = deviceDetailRes; | ||
154 | + if (!deviceProfileId) return; | ||
155 | + const attributeRes = await getAttribute(deviceProfileId); | ||
156 | + return handleDataFormat(deviceDetailRes, attributeRes); | ||
157 | + }; | ||
158 | + | ||
159 | + const handleDataFormat = (deviceDetail: any, attributes: any) => { | ||
160 | + const { name, tbDeviceId } = deviceDetail; | ||
161 | + const attribute = attributes.map((item: any) => ({ | ||
162 | + identifier: item.identifier, | ||
163 | + name: item.name, | ||
164 | + detail: item.detail, | ||
165 | + })); | ||
166 | + return { | ||
167 | + name, | ||
168 | + tbDeviceId, | ||
169 | + attribute, | ||
170 | + }; | ||
171 | + }; | ||
172 | + | ||
144 | const [register, { setModalProps }] = useModalInner( | 173 | const [register, { setModalProps }] = useModalInner( |
145 | async (data: { record: ExecuteReportRecord }) => { | 174 | async (data: { record: ExecuteReportRecord }) => { |
146 | setModalProps({ loading: true }); | 175 | setModalProps({ loading: true }); |
147 | try { | 176 | try { |
148 | currentRecord = data.record; | 177 | currentRecord = data.record; |
149 | const deviceInfo = data.record.executeCondition.executeAttributes || []; | 178 | const deviceInfo = data.record.executeCondition.executeAttributes || []; |
150 | - chartInstance.value = deviceInfo.map((item) => ({ | ||
151 | - ...item, | ||
152 | - active: item.attributes.at(0), | ||
153 | - })); | 179 | + let attributesList: Recordable[] = []; |
180 | + // 处理为物模型里的标识符名 | ||
181 | + const reflectDeviceList = deviceInfo.map(async (item) => { | ||
182 | + const { device, attributes } = item as Recordable; | ||
183 | + if (!device) return; | ||
184 | + const thingsModel = (await handleDeviceProfileAttributes(item.device)) as Recordable; | ||
185 | + const { tbDeviceId, attribute } = thingsModel as Recordable; | ||
186 | + if (!tbDeviceId) return; | ||
187 | + if (device === tbDeviceId) { | ||
188 | + attributesList = attributes.reduce((acc, curr) => { | ||
189 | + attribute.forEach((item: Recordable) => { | ||
190 | + if (item.identifier === curr) { | ||
191 | + acc.push({ | ||
192 | + label: item.name, | ||
193 | + value: item.identifier, | ||
194 | + }); | ||
195 | + } | ||
196 | + }); | ||
197 | + return [...acc]; | ||
198 | + }, []); | ||
199 | + } | ||
200 | + return { | ||
201 | + ...item, | ||
202 | + active: attributes.at(0), | ||
203 | + attributes: attributesList, | ||
204 | + }; | ||
205 | + }); | ||
206 | + chartInstance.value = (await Promise.all(reflectDeviceList)) as any as ChartInstance[]; | ||
154 | for (const item of unref(chartInstance)) { | 207 | for (const item of unref(chartInstance)) { |
155 | const { attributes, device } = item; | 208 | const { attributes, device } = item; |
156 | 209 | ||
157 | - const keys = attributes.length ? attributes.at(0) : ''; | 210 | + const keys = attributes.length ? (attributes as any[]).at(0)?.value : ''; |
158 | 211 | ||
159 | const sendParams = { | 212 | const sendParams = { |
160 | ...data.record.executeCondition.executeCondition, | 213 | ...data.record.executeCondition.executeCondition, |
1 | <script lang="ts" setup> | 1 | <script lang="ts" setup> |
2 | - import { Button, Tabs, Tag } from 'ant-design-vue'; | 2 | + import { Button, Tabs, Select } from 'ant-design-vue'; |
3 | import { remove, uniqBy, cloneDeep } from 'lodash'; | 3 | import { remove, uniqBy, cloneDeep } from 'lodash'; |
4 | - import { computed, nextTick, onMounted, ref, unref, toRaw } from 'vue'; | 4 | + import { nextTick, onMounted, ref, unref, toRaw, watch } from 'vue'; |
5 | import { | 5 | import { |
6 | deviceTableColumn, | 6 | deviceTableColumn, |
7 | deviceTableFormSchema, | 7 | deviceTableFormSchema, |
@@ -21,8 +21,7 @@ | @@ -21,8 +21,7 @@ | ||
21 | 21 | ||
22 | const props = withDefaults( | 22 | const props = withDefaults( |
23 | defineProps<{ | 23 | defineProps<{ |
24 | - getPendingTableParams: (params: Recordable) => any; | ||
25 | - getSelectedTableParams: (params: Recordable) => any; | 24 | + params?: Recordable; |
26 | value?: (Recordable & DeviceModel)[]; | 25 | value?: (Recordable & DeviceModel)[]; |
27 | maxTagLength?: number; | 26 | maxTagLength?: number; |
28 | openModalValidate?: () => boolean; | 27 | openModalValidate?: () => boolean; |
@@ -34,6 +33,7 @@ | @@ -34,6 +33,7 @@ | ||
34 | value: () => [], | 33 | value: () => [], |
35 | maxTagLength: 2, | 34 | maxTagLength: 2, |
36 | primaryKey: 'tbDeviceId', | 35 | primaryKey: 'tbDeviceId', |
36 | + params: () => ({}), | ||
37 | } | 37 | } |
38 | ); | 38 | ); |
39 | 39 | ||
@@ -56,23 +56,6 @@ | @@ -56,23 +56,6 @@ | ||
56 | 56 | ||
57 | const selectedConfirmQueue = ref<DeviceModel[]>([]); | 57 | const selectedConfirmQueue = ref<DeviceModel[]>([]); |
58 | 58 | ||
59 | - const getShowTagOptions = computed(() => { | ||
60 | - const { maxTagLength } = props; | ||
61 | - return unref(selectedTotalList).slice(0, maxTagLength); | ||
62 | - }); | ||
63 | - | ||
64 | - const getSurplusOptionsLength = computed(() => { | ||
65 | - const { maxTagLength } = props; | ||
66 | - const surplusValue = unref(selectedTotalList).length - maxTagLength; | ||
67 | - return surplusValue < 0 ? 0 : surplusValue; | ||
68 | - }); | ||
69 | - | ||
70 | - // const pendingListCount = computed(() => { | ||
71 | - // const { value } = props; | ||
72 | - // const selectedList = unref(pendingTotalList).filter((item) => value.includes(item.id)); | ||
73 | - // return unref(pendingTotalList).length - selectedList.length; | ||
74 | - // }); | ||
75 | - | ||
76 | const [registerModal, { openModal }] = useModal(); | 59 | const [registerModal, { openModal }] = useModal(); |
77 | 60 | ||
78 | const [regsterPendingTable, pendingTableActionType] = useTable({ | 61 | const [regsterPendingTable, pendingTableActionType] = useTable({ |
@@ -82,9 +65,8 @@ | @@ -82,9 +65,8 @@ | ||
82 | immediate: false, | 65 | immediate: false, |
83 | clickToRowSelect: false, | 66 | clickToRowSelect: false, |
84 | beforeFetch: (params) => { | 67 | beforeFetch: (params) => { |
85 | - const { getPendingTableParams } = props; | ||
86 | - const data = getPendingTableParams?.(params) || {}; | ||
87 | - Object.assign(params, { ...data, selected: false }); | 68 | + const { params: otherParams } = props; |
69 | + Object.assign(params, { ...otherParams, selected: false }); | ||
88 | return params; | 70 | return params; |
89 | }, | 71 | }, |
90 | afterFetch: (list: DeviceModel[]) => { | 72 | afterFetch: (list: DeviceModel[]) => { |
@@ -150,9 +132,8 @@ | @@ -150,9 +132,8 @@ | ||
150 | dataSource: selectedTotalList, | 132 | dataSource: selectedTotalList, |
151 | clickToRowSelect: false, | 133 | clickToRowSelect: false, |
152 | beforeFetch: (params) => { | 134 | beforeFetch: (params) => { |
153 | - const { getSelectedTableParams } = props; | ||
154 | - const data = getSelectedTableParams?.(params) || {}; | ||
155 | - Object.assign(params, { ...data, selected: false }); | 135 | + const { params: otherParams } = props; |
136 | + Object.assign(params, { ...otherParams, selected: false }); | ||
156 | return params; | 137 | return params; |
157 | }, | 138 | }, |
158 | rowSelection: { | 139 | rowSelection: { |
@@ -219,18 +200,31 @@ | @@ -219,18 +200,31 @@ | ||
219 | 200 | ||
220 | if (openModalValidate && isFunction(openModalValidate) && !openModalValidate()) return; | 201 | if (openModalValidate && isFunction(openModalValidate) && !openModalValidate()) return; |
221 | 202 | ||
203 | + activeKey.value = Active.PENDING; | ||
222 | openModal(true); | 204 | openModal(true); |
223 | await nextTick(); | 205 | await nextTick(); |
224 | pendingTableActionType.reload(); | 206 | pendingTableActionType.reload(); |
225 | }; | 207 | }; |
226 | 208 | ||
209 | + watch( | ||
210 | + () => props.params, | ||
211 | + () => { | ||
212 | + selectedTotalList.value = []; | ||
213 | + selectedConfirmQueue.value = []; | ||
214 | + pendingConfirmQueue.value = []; | ||
215 | + try { | ||
216 | + pendingTableActionType.clearSelectedRowKeys(); | ||
217 | + selectedTableActionType.clearSelectedRowKeys(); | ||
218 | + } catch (error) {} | ||
219 | + } | ||
220 | + ); | ||
221 | + | ||
227 | onMounted(async () => { | 222 | onMounted(async () => { |
228 | - const { getSelectedTableParams } = props; | ||
229 | - const data = getSelectedTableParams?.({}) || {}; | ||
230 | - if (!data?.convertConfigId || !data?.deviceProfileIds) { | 223 | + const { params } = props; |
224 | + if (!params?.convertConfigId || !params?.deviceProfileIds) { | ||
231 | return; | 225 | return; |
232 | } | 226 | } |
233 | - const { items } = await devicePage({ page: 1, pageSize: 10, ...data, selected: true }); | 227 | + const { items } = await devicePage({ page: 1, pageSize: 10, ...params, selected: true }); |
234 | selectedTotalList.value = items; | 228 | selectedTotalList.value = items; |
235 | }); | 229 | }); |
236 | </script> | 230 | </script> |
@@ -239,7 +233,7 @@ | @@ -239,7 +233,7 @@ | ||
239 | <section> | 233 | <section> |
240 | <BasicModal | 234 | <BasicModal |
241 | @register="registerModal" | 235 | @register="registerModal" |
242 | - title="穿梭表格" | 236 | + title="设备选择" |
243 | width="60%" | 237 | width="60%" |
244 | :wrapClassName="prefixCls" | 238 | :wrapClassName="prefixCls" |
245 | :showOkBtn="false" | 239 | :showOkBtn="false" |
@@ -251,13 +245,11 @@ | @@ -251,13 +245,11 @@ | ||
251 | <template #tab> | 245 | <template #tab> |
252 | <div class="flex items-center justify-center"> | 246 | <div class="flex items-center justify-center"> |
253 | <span>待选设备</span> | 247 | <span>待选设备</span> |
254 | - <!-- <Badge show-zero :count="pendingListCount" /> --> | ||
255 | </div> | 248 | </div> |
256 | </template> | 249 | </template> |
257 | <BasicTable @register="regsterPendingTable"> | 250 | <BasicTable @register="regsterPendingTable"> |
258 | <template #toolbar> | 251 | <template #toolbar> |
259 | <section class="flex w-full justify-end items-center"> | 252 | <section class="flex w-full justify-end items-center"> |
260 | - <!-- <Button type="primary">全选</Button> --> | ||
261 | <div class="text-blue-400"> | 253 | <div class="text-blue-400"> |
262 | <span class="mr-2">选择设备:</span> | 254 | <span class="mr-2">选择设备:</span> |
263 | <span>{{ pendingConfirmQueue.length }}</span> | 255 | <span>{{ pendingConfirmQueue.length }}</span> |
@@ -279,7 +271,6 @@ | @@ -279,7 +271,6 @@ | ||
279 | <template #tab> | 271 | <template #tab> |
280 | <div class="flex items-center justify-center"> | 272 | <div class="flex items-center justify-center"> |
281 | <span>已选设备</span> | 273 | <span>已选设备</span> |
282 | - <!-- <Badge show-zero :count="selectedTotalList.length" /> --> | ||
283 | </div> | 274 | </div> |
284 | </template> | 275 | </template> |
285 | <BasicTable @register="registerSelectedTable"> | 276 | <BasicTable @register="registerSelectedTable"> |
@@ -305,23 +296,22 @@ | @@ -305,23 +296,22 @@ | ||
305 | </Tabs> | 296 | </Tabs> |
306 | </section> | 297 | </section> |
307 | </BasicModal> | 298 | </BasicModal> |
308 | - <Button @click="handleOpenModal" type="link" :disabled="disabled"> | ||
309 | - <span v-if="!selectedTotalList.length">选择设备</span> | ||
310 | - <div v-if="selectedTotalList.length"> | ||
311 | - <Tag | ||
312 | - class="!px-2 !py-1 !bg-gray-50 !border-gray-100" | ||
313 | - v-for="item in getShowTagOptions" | ||
314 | - :key="item[primaryKey]" | ||
315 | - > | ||
316 | - <span> | ||
317 | - {{ item.alias || item.name }} | ||
318 | - </span> | ||
319 | - </Tag> | ||
320 | - <Tag class="!px-2 !py-1 !bg-gray-50 !border-gray-100" v-if="getSurplusOptionsLength"> | ||
321 | - <span> +{{ getSurplusOptionsLength }}... </span> | ||
322 | - </Tag> | ||
323 | - </div> | ||
324 | - </Button> | 299 | + <Select |
300 | + placeholder="请选择设备" | ||
301 | + :disabled="disabled" | ||
302 | + :value="selectedTotalList.map((item) => item[primaryKey])" | ||
303 | + mode="multiple" | ||
304 | + :maxTagCount="3" | ||
305 | + :open="false" | ||
306 | + @dropdownVisibleChange="handleOpenModal" | ||
307 | + :options=" | ||
308 | + selectedTotalList.map((item) => ({ | ||
309 | + ...item, | ||
310 | + label: item.alias || item.name, | ||
311 | + value: item[primaryKey], | ||
312 | + })) | ||
313 | + " | ||
314 | + /> | ||
325 | </section> | 315 | </section> |
326 | </template> | 316 | </template> |
327 | 317 |
@@ -73,6 +73,10 @@ export const modeForm = (disabled: boolean): FormSchema[] => { | @@ -73,6 +73,10 @@ export const modeForm = (disabled: boolean): FormSchema[] => { | ||
73 | labelField: 'name', | 73 | labelField: 'name', |
74 | valueField: 'tbProfileId', | 74 | valueField: 'tbProfileId', |
75 | disabled, | 75 | disabled, |
76 | + selectProps: { | ||
77 | + disabled, | ||
78 | + placeholder: '请选择产品', | ||
79 | + }, | ||
76 | transferProps: { | 80 | transferProps: { |
77 | listStyle: { height: '400px' }, | 81 | listStyle: { height: '400px' }, |
78 | showSearch: true, | 82 | showSearch: true, |
@@ -98,26 +102,14 @@ export const modeForm = (disabled: boolean): FormSchema[] => { | @@ -98,26 +102,14 @@ export const modeForm = (disabled: boolean): FormSchema[] => { | ||
98 | valueField: 'value', | 102 | valueField: 'value', |
99 | changeEvent: 'update:value', | 103 | changeEvent: 'update:value', |
100 | rules: [{ required: true, message: '数据源设备为必选项', type: 'array' }], | 104 | rules: [{ required: true, message: '数据源设备为必选项', type: 'array' }], |
101 | - componentProps: ({ formActionType }) => { | ||
102 | - const { getFieldsValue } = formActionType; | 105 | + componentProps: ({ formModel }) => { |
106 | + const convertConfigId = formModel[BasicInfoFormField.CONVERT_CONFIG_ID]; | ||
107 | + const deviceProfileIds = formModel[BasicInfoFormField.DATA_SOURCE_PRODUCT]; | ||
103 | return { | 108 | return { |
104 | disabled, | 109 | disabled, |
105 | - getPendingTableParams: () => { | ||
106 | - const values = getFieldsValue(); | ||
107 | - const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID); | ||
108 | - const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT); | ||
109 | - return { convertConfigId, deviceProfileIds }; | ||
110 | - }, | ||
111 | - getSelectedTableParams: () => { | ||
112 | - const values = getFieldsValue(); | ||
113 | - const convertConfigId = Reflect.get(values, BasicInfoFormField.CONVERT_CONFIG_ID); | ||
114 | - const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT); | ||
115 | - return { convertConfigId, deviceProfileIds }; | ||
116 | - }, | 110 | + params: { convertConfigId, deviceProfileIds }, |
117 | transformValue: handleGroupDevice, | 111 | transformValue: handleGroupDevice, |
118 | openModalValidate: () => { | 112 | openModalValidate: () => { |
119 | - const values = getFieldsValue(); | ||
120 | - const deviceProfileIds = Reflect.get(values, BasicInfoFormField.DATA_SOURCE_PRODUCT); | ||
121 | if (!deviceProfileIds || !deviceProfileIds?.length) { | 113 | if (!deviceProfileIds || !deviceProfileIds?.length) { |
122 | createMessage.warning('请选择数据源设备'); | 114 | createMessage.warning('请选择数据源设备'); |
123 | } | 115 | } |
@@ -9,6 +9,7 @@ export const formSchemas: FormSchema[] = [ | @@ -9,6 +9,7 @@ export const formSchemas: FormSchema[] = [ | ||
9 | field: CheckExistenceFieldsEnum.MESSAGE_NAMES, | 9 | field: CheckExistenceFieldsEnum.MESSAGE_NAMES, |
10 | component: 'Select', | 10 | component: 'Select', |
11 | label: CheckExistenceFieldsNameEnum.MESSAGE_NAMES, | 11 | label: CheckExistenceFieldsNameEnum.MESSAGE_NAMES, |
12 | + rules: [{ required: true, type: 'array' }], | ||
12 | componentProps: { | 13 | componentProps: { |
13 | mode: 'tags', | 14 | mode: 'tags', |
14 | open: false, | 15 | open: false, |
@@ -20,6 +21,7 @@ export const formSchemas: FormSchema[] = [ | @@ -20,6 +21,7 @@ export const formSchemas: FormSchema[] = [ | ||
20 | field: CheckExistenceFieldsEnum.METADATA_NAMES, | 21 | field: CheckExistenceFieldsEnum.METADATA_NAMES, |
21 | component: 'Select', | 22 | component: 'Select', |
22 | label: CheckExistenceFieldsNameEnum.METADATA_NAMES, | 23 | label: CheckExistenceFieldsNameEnum.METADATA_NAMES, |
24 | + rules: [{ required: true, type: 'array' }], | ||
23 | componentProps: { | 25 | componentProps: { |
24 | mode: 'tags', | 26 | mode: 'tags', |
25 | open: false, | 27 | open: false, |
@@ -25,7 +25,7 @@ export const getFormSchemas = (route: RouteLocationNormalizedLoaded): FormSchema | @@ -25,7 +25,7 @@ export const getFormSchemas = (route: RouteLocationNormalizedLoaded): FormSchema | ||
25 | component: 'ApiSearchSelect', | 25 | component: 'ApiSearchSelect', |
26 | componentProps: () => { | 26 | componentProps: () => { |
27 | return { | 27 | return { |
28 | - placeholder: '请选择所属产品', | 28 | + placeholder: '请选择规则链', |
29 | showSearch: true, | 29 | showSearch: true, |
30 | params: { | 30 | params: { |
31 | pageSize: 50, | 31 | pageSize: 50, |
@@ -62,7 +62,7 @@ export const formSchemas: FormSchema[] = [ | @@ -62,7 +62,7 @@ export const formSchemas: FormSchema[] = [ | ||
62 | { | 62 | { |
63 | field: FormFieldsEnum.STATUS, | 63 | field: FormFieldsEnum.STATUS, |
64 | label: '状态', | 64 | label: '状态', |
65 | - component: 'Input', | 65 | + component: 'Select', |
66 | componentProps: { | 66 | componentProps: { |
67 | options: Object.keys(StatusEnum).map((value) => ({ label: StatusNameEnum[value], value })), | 67 | options: Object.keys(StatusEnum).map((value) => ({ label: StatusNameEnum[value], value })), |
68 | allowClear: true, | 68 | allowClear: true, |
src/views/rule/linkedge/SceneLinkAgeDrawer.vue
deleted
100644 → 0
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 enum CheckStatusEnum { | ||
7 | + NO_VALIDATE = 'NO_VALIDATE', | ||
8 | + VALIDATE_SUCCESS = 'VALIDATE_SUCCESS', | ||
9 | + VALIDATE_FAILED = 'VALIDATE_FAILED', | ||
10 | +} | ||
11 | + | ||
12 | +export enum CheckStatusColorEnum { | ||
13 | + NO_VALIDATE, | ||
14 | + VALIDATE_SUCCESS = '!text-green-600', | ||
15 | + VALIDATE_FAILED = '!text-red-600', | ||
16 | +} | ||
17 | + | ||
18 | +export enum CheckStatusIconEnum { | ||
19 | + NO_VALIDATE = '', | ||
20 | + VALIDATE_SUCCESS = 'ant-design:check-outlined', | ||
21 | + VALIDATE_FAILED = 'ant-design:close-outlined', | ||
22 | +} | ||
23 | + | ||
24 | +export function getNewConditionFilterItem( | ||
25 | + checkStatus = CheckStatusEnum.NO_VALIDATE | ||
26 | +): ConditionListItemType { | ||
27 | + return { | ||
28 | + key: buildUUID(), | ||
29 | + checkStatus, | ||
30 | + }; | ||
31 | +} |
1 | +<script setup lang="ts"> | ||
2 | + import { ComponentPublicInstance, computed, ref, unref, watch } from 'vue'; | ||
3 | + import { Card, Tag, Button, Tooltip } 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 { | ||
13 | + CheckStatusEnum, | ||
14 | + CheckStatusIconEnum, | ||
15 | + CheckStatusColorEnum, | ||
16 | + getNewConditionFilterItem, | ||
17 | + } from '.'; | ||
18 | + import { useSceneLinkageDrawerContext } from '../SceneLinkageDrawer/sceneLinkageDrawerContext'; | ||
19 | + | ||
20 | + const { disabledDrawer } = useSceneLinkageDrawerContext(); | ||
21 | + | ||
22 | + const props = withDefaults(defineProps<ConditionFilterProps>(), { | ||
23 | + triggerType: TriggerValueTypeEnum.BOOLEAN, | ||
24 | + }); | ||
25 | + | ||
26 | + const getSchemas = computed(() => { | ||
27 | + const { triggerType } = props; | ||
28 | + return getFormSchemas({ triggerType }); | ||
29 | + }); | ||
30 | + | ||
31 | + const conditionListElRef = ref<ConditionListItemType[]>([getNewConditionFilterItem()]); | ||
32 | + | ||
33 | + const getConditionFormValues = (): Record<FormFieldsEnum, string>[] => { | ||
34 | + return unref(conditionListElRef).map( | ||
35 | + (conditionForm) => conditionForm.ref?.getFieldsValue() as Record<FormFieldsEnum, string> | ||
36 | + ); | ||
37 | + }; | ||
38 | + | ||
39 | + const conditionMessageList = ref<ConditionMessageItemType[]>([]); | ||
40 | + | ||
41 | + const handleConditionFormValueChange = () => { | ||
42 | + const condition = getConditionFormValues(); | ||
43 | + const { triggerType } = props; | ||
44 | + const message = useConditionFilterMessage({ triggerType, condition }); | ||
45 | + conditionMessageList.value = message; | ||
46 | + }; | ||
47 | + | ||
48 | + const setConditionRef = ( | ||
49 | + conditionItem: ConditionListItemType, | ||
50 | + el: Element | ComponentPublicInstance | null | ||
51 | + ) => { | ||
52 | + conditionItem.ref = el as unknown as FormActionType; | ||
53 | + }; | ||
54 | + | ||
55 | + const handleDelete = (conditionItem: ConditionListItemType) => { | ||
56 | + const { key } = conditionItem; | ||
57 | + | ||
58 | + const index = unref(conditionListElRef).findIndex((item) => item.key === key); | ||
59 | + | ||
60 | + ~index && unref(conditionListElRef).splice(index, 1); | ||
61 | + | ||
62 | + handleConditionFormValueChange(); | ||
63 | + }; | ||
64 | + | ||
65 | + const handleAdd = () => { | ||
66 | + conditionListElRef.value.push(getNewConditionFilterItem()); | ||
67 | + }; | ||
68 | + | ||
69 | + watch( | ||
70 | + () => props.triggerType, | ||
71 | + () => { | ||
72 | + conditionListElRef.value = [getNewConditionFilterItem()]; | ||
73 | + conditionMessageList.value = []; | ||
74 | + } | ||
75 | + ); | ||
76 | + | ||
77 | + const { getFieldsValue, setFieldsValue, validate } = useConditionData( | ||
78 | + props, | ||
79 | + conditionListElRef, | ||
80 | + handleConditionFormValueChange | ||
81 | + ); | ||
82 | + | ||
83 | + const handelValidate = async () => { | ||
84 | + await validate?.(); | ||
85 | + }; | ||
86 | + | ||
87 | + defineExpose({ getFieldsValue, setFieldsValue, validate }); | ||
88 | +</script> | ||
89 | + | ||
90 | +<template> | ||
91 | + <Card class="condition-filter-card !border-dashed !border-2 !border-gray-200 !rounded-lg"> | ||
92 | + <CollapseContainer class="rounded-lg" :default-expand="!disabledDrawer"> | ||
93 | + <template #title> | ||
94 | + <section class="flex !p-2"> | ||
95 | + <div class="w-20 flex-grow-0 flex-shrink-0"> 条件筛选 </div> | ||
96 | + <div class="flex flex-wrap gap-1 mr-4"> | ||
97 | + <template v-for="(item, index) in conditionMessageList" :key="index"> | ||
98 | + <Tag class="!rounded-md !m-0 !text-sky-700"> {{ objectModel?.name }} </Tag> | ||
99 | + <span>{{ item.operation }}</span> | ||
100 | + <Tag class="!rounded-md !m-0 !text-orange-300"> | ||
101 | + {{ item.defaultValue }} | ||
102 | + </Tag> | ||
103 | + <span v-if="conditionMessageList.length - 1 !== index">和</span> | ||
104 | + </template> | ||
105 | + </div> | ||
106 | + </section> | ||
107 | + </template> | ||
108 | + <!-- --> | ||
109 | + <main | ||
110 | + class="flex gap-4 items-center mb-2" | ||
111 | + v-for="(conditionItem, index) in conditionListElRef" | ||
112 | + :key="conditionItem.key" | ||
113 | + > | ||
114 | + <section | ||
115 | + class="border-2 border-dashed border-gray-200 rounded-lg px-4 py-2 flex-1 flex items-center" | ||
116 | + > | ||
117 | + <BasicForm | ||
118 | + class="flex-auto" | ||
119 | + :ref="(el) => setConditionRef(conditionItem, el)" | ||
120 | + :show-action-button-group="false" | ||
121 | + layout="horizontal" | ||
122 | + :disabled="disabledDrawer" | ||
123 | + :label-width="80" | ||
124 | + :schemas="getSchemas" | ||
125 | + :base-col-props="{ span: 24 / getSchemas.length, xl: 8, lg: 12, sm: 24 }" | ||
126 | + @field-value-change="handleConditionFormValueChange" | ||
127 | + /> | ||
128 | + <Icon | ||
129 | + :style="{ | ||
130 | + opacity: conditionItem.checkStatus !== CheckStatusEnum.NO_VALIDATE ? 100 : 0, | ||
131 | + }" | ||
132 | + :icon="CheckStatusIconEnum[conditionItem.checkStatus!]" | ||
133 | + :size="20" | ||
134 | + :class="CheckStatusColorEnum[conditionItem.checkStatus!]" | ||
135 | + class="transition-opacity duration-500 ease-linear" | ||
136 | + /> | ||
137 | + </section> | ||
138 | + <span | ||
139 | + class="w-5" | ||
140 | + :style="{ visibility: conditionListElRef.length - 1 !== index ? 'visible' : 'hidden' }" | ||
141 | + > | ||
142 | + 和 | ||
143 | + </span> | ||
144 | + <Tooltip title="删除"> | ||
145 | + <Icon | ||
146 | + v-if="!disabledDrawer" | ||
147 | + icon="fluent:delete-off-20-regular" | ||
148 | + size="24" | ||
149 | + class="cursor-pointer" | ||
150 | + @click="handleDelete(conditionItem)" | ||
151 | + /> | ||
152 | + </Tooltip> | ||
153 | + </main> | ||
154 | + <section class="flex mt-4 items-center justify-between"> | ||
155 | + <Button v-if="!disabledDrawer" type="primary" @click="handleAdd"> | ||
156 | + <Icon icon="ant-design:plus-outlined" /> | ||
157 | + 新增条件筛选 | ||
158 | + </Button> | ||
159 | + <Button v-if="!disabledDrawer" type="primary" @click="handelValidate">添加</Button> | ||
160 | + </section> | ||
161 | + </CollapseContainer> | ||
162 | + </Card> | ||
163 | +</template> | ||
164 | + | ||
165 | +<style lang="less" scoped> | ||
166 | + .condition-filter-card { | ||
167 | + :deep(.ant-card-body) { | ||
168 | + @apply p-3; | ||
169 | + | ||
170 | + .vben-collapse-container__header { | ||
171 | + height: fit-content; | ||
172 | + } | ||
173 | + | ||
174 | + .ant-calendar-picker { | ||
175 | + min-width: auto !important; | ||
176 | + width: 100%; | ||
177 | + } | ||
178 | + | ||
179 | + .ant-input-number { | ||
180 | + min-width: auto !important; | ||
181 | + } | ||
182 | + } | ||
183 | + } | ||
184 | +</style> | ||
185 | +./type |
1 | +import { CheckStatusEnum } from '.'; | ||
2 | +import { ScheduleType } from '../EnablingRule/type'; | ||
3 | +import { DeviceModelOfMatterAttrs } from '/@/api/device/model/deviceModel'; | ||
4 | +import { FormActionType } from '/@/components/Form'; | ||
5 | +import { | ||
6 | + BooleanOperationEnum, | ||
7 | + DeviceTriggerTypeEum, | ||
8 | + FlipFlopTypeEnum, | ||
9 | + NumberOperationEnum, | ||
10 | + StringOperationNameEnum, | ||
11 | + TriggerTypeEnum, | ||
12 | + TriggerValueTypeEnum, | ||
13 | +} from '/@/enums/linkedgeEnum'; | ||
14 | + | ||
15 | +export interface ConditionListItemType { | ||
16 | + key: string; | ||
17 | + ref?: FormActionType; | ||
18 | + checkStatus?: CheckStatusEnum; | ||
19 | +} | ||
20 | + | ||
21 | +export interface ConditionFilterProps { | ||
22 | + triggerType?: TriggerValueTypeEnum; | ||
23 | + objectModel?: DeviceModelOfMatterAttrs; | ||
24 | + conditionType?: DeviceTriggerTypeEum; | ||
25 | +} | ||
26 | + | ||
27 | +export interface TriggerCondition { | ||
28 | + condition: ConditionType; | ||
29 | + alarmDetails?: Nullable<any>; | ||
30 | + dashboardId?: Nullable<any>; | ||
31 | + schedule: ScheduleType; | ||
32 | + triggerType?: TriggerTypeEnum; | ||
33 | +} | ||
34 | + | ||
35 | +export interface ConditionType { | ||
36 | + condition: ConditionItemType[]; | ||
37 | + spec: { type: FlipFlopTypeEnum; unit?: string; predicate?: { defaultValue: any } }; | ||
38 | +} | ||
39 | + | ||
40 | +export type OperationType = NumberOperationEnum | StringOperationNameEnum | BooleanOperationEnum; | ||
41 | + | ||
42 | +export interface ConditionItemType { | ||
43 | + key: { type: DeviceTriggerTypeEum; key: string }; | ||
44 | + predicate: { | ||
45 | + type: TriggerValueTypeEnum; | ||
46 | + operation: OperationType; | ||
47 | + ignoreCase?: boolean; | ||
48 | + value: { | ||
49 | + defaultValue: number | string | boolean; | ||
50 | + userValue?: Nullable<number | string | boolean>; | ||
51 | + dynamicValue?: Nullable<number | string | boolean>; | ||
52 | + }; | ||
53 | + }; | ||
54 | + valueType: TriggerValueTypeEnum; | ||
55 | + value?: Nullable<any>; | ||
56 | +} |
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 { CheckStatusEnum, 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(CheckStatusEnum.VALIDATE_SUCCESS) | ||
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 | + conditionItem.checkStatus = CheckStatusEnum.VALIDATE_FAILED; | ||
83 | + await conditionItem.ref?.validate(); | ||
84 | + conditionItem.checkStatus = CheckStatusEnum.VALIDATE_SUCCESS; | ||
85 | + } | ||
86 | + }; | ||
87 | + return { | ||
88 | + getFieldsValue, | ||
89 | + setFieldsValue, | ||
90 | + validate, | ||
91 | + }; | ||
92 | +} |
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 | ||
80 | + @register="registerModal" | ||
81 | + title="启用规则" | ||
82 | + :width="600" | ||
83 | + @ok="handleOk" | ||
84 | + :cancel-text="disabledDrawer ? '关闭' : '取消'" | ||
85 | + :show-ok-btn="!disabledDrawer" | ||
86 | + > | ||
87 | + <BasicForm @register="register" :disabled="disabledDrawer"> | ||
88 | + <template #custom="{ model, field }"> | ||
89 | + <CustomRule ref="customRuleElRef" v-model:value="model[field]" /> | ||
90 | + </template> | ||
91 | + </BasicForm> | ||
92 | + </BasicModal> | ||
93 | +</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 | + [FormFieldsEnum.SERVICE_ID]: null, | ||
223 | + [FormFieldsEnum.CALL_SERVICE]: null, | ||
224 | + [FormFieldsEnum.CALL_SERVICE_IDENTIFIER]: null, | ||
225 | + [FormFieldsEnum.SERVICE_COMMAND]: null, | ||
226 | + }); | ||
227 | + }, | ||
228 | + onOptionsChange(options: (DeviceProfileModel & Record<'label' | 'value', string>)[]) { | ||
229 | + const deviceProfileId = formModel[FormFieldsEnum.DEVICE_PROFILE_ID]; | ||
230 | + const res = options.find((item) => item.value === deviceProfileId); | ||
231 | + res && | ||
232 | + setFieldsValue({ | ||
233 | + [FormFieldsEnum.TRANSPORT_TYPE]: res.transportType, | ||
234 | + }); | ||
235 | + }, | ||
236 | + }; | ||
237 | + }, | ||
238 | + }, | ||
239 | + { | ||
240 | + field: FormFieldsEnum.ENTITY_TYPE, | ||
241 | + label: '', | ||
242 | + component: 'Select', | ||
243 | + rules: [{ required: true, message: `请选择${FormFieldsNameEnum.ENTITY_TYPE}` }], | ||
244 | + ifShow: ({ model }) => model[FormFieldsEnum.OUT_TARGET] === ExecutionActionEnum.DEVICE_OUT, | ||
245 | + componentProps: () => { | ||
246 | + return { | ||
247 | + options: Object.keys(TriggerEntityTypeEnum).map((value) => ({ | ||
248 | + label: TriggerEntityTypeNameEnum[value], | ||
249 | + value, | ||
250 | + })), | ||
251 | + placeholder: `请选择${FormFieldsNameEnum.ENTITY_TYPE}`, | ||
252 | + }; | ||
253 | + }, | ||
254 | + }, | ||
255 | + { | ||
256 | + field: FormFieldsEnum.ENTITY_ID, | ||
257 | + label: '', | ||
258 | + component: 'ApiSelect', | ||
259 | + rules: [{ required: true, type: 'array', message: `请选择${FormFieldsNameEnum.ENTITY_ID}` }], | ||
260 | + ifShow: ({ model }) => | ||
261 | + model[FormFieldsEnum.OUT_TARGET] === ExecutionActionEnum.DEVICE_OUT && | ||
262 | + model[FormFieldsEnum.ENTITY_TYPE] === TriggerEntityTypeEnum.PART, | ||
263 | + componentProps: ({ formModel }) => { | ||
264 | + return { | ||
265 | + api: async (params: Record<'organizationId' | 'deviceProfileId', string>) => { | ||
266 | + if (params.deviceProfileId && params.organizationId) { | ||
267 | + const result = await byOrganizationIdGetMasterDevice(params); | ||
268 | + if (result) { | ||
269 | + return result.map((item) => ({ | ||
270 | + ...item, | ||
271 | + label: item.alias || item.name, | ||
272 | + value: item.tbDeviceId, | ||
273 | + })); | ||
274 | + } | ||
275 | + } | ||
276 | + return []; | ||
277 | + }, | ||
278 | + mode: 'multiple', | ||
279 | + maxTagCount: 3, | ||
280 | + params: { | ||
281 | + organizationId: unref(organizationId), | ||
282 | + deviceProfileId: formModel[FormFieldsEnum.DEVICE_PROFILE_ID], | ||
283 | + }, | ||
284 | + placeholder: `请选择${FormFieldsNameEnum.ENTITY_ID}`, | ||
285 | + ...createPickerSearch(), | ||
286 | + }; | ||
287 | + }, | ||
288 | + }, | ||
289 | + { | ||
290 | + field: FormFieldsEnum.COMMAND_TYPE, | ||
291 | + label: '', | ||
292 | + component: 'Select', | ||
293 | + rules: [ | ||
294 | + { required: true, message: `请选择${FormFieldsNameEnum.COMMAND_TYPE}`, type: 'number' }, | ||
295 | + ], | ||
296 | + defaultValue: CommandTypeEnum.CUSTOM, | ||
297 | + ifShow: ({ model }) => model[FormFieldsEnum.OUT_TARGET] === ExecutionActionEnum.DEVICE_OUT, | ||
298 | + componentProps: ({ formActionType }) => { | ||
299 | + return { | ||
300 | + options: [ | ||
301 | + { label: CommandTypeNameEnum.CUSTOM, value: CommandTypeEnum.CUSTOM }, | ||
302 | + { label: CommandTypeNameEnum.SERVICE, value: CommandTypeEnum.SERVICE }, | ||
303 | + ], | ||
304 | + placeholder: `请选择${FormFieldsNameEnum.COMMAND_TYPE}`, | ||
305 | + onChange() { | ||
306 | + const { setFieldsValue } = formActionType; | ||
307 | + setFieldsValue({ | ||
308 | + [FormFieldsEnum.CUSTOM_COMMAND]: null, | ||
309 | + [FormFieldsEnum.TCP_CUSTOM_COMMAND]: null, | ||
310 | + [FormFieldsEnum.CALL_SERVICE]: null, | ||
311 | + [FormFieldsEnum.CALL_SERVICE_IDENTIFIER]: null, | ||
312 | + [FormFieldsEnum.SERVICE_COMMAND]: null, | ||
313 | + }); | ||
314 | + }, | ||
315 | + }; | ||
316 | + }, | ||
317 | + }, | ||
318 | + { | ||
319 | + field: FormFieldsEnum.CALL_TYPE, | ||
320 | + label: '', | ||
321 | + component: 'Select', | ||
322 | + defaultValue: ServiceCallTypeEnum.ASYNC, | ||
323 | + rules: [{ required: true, message: `请选择${FormFieldsNameEnum.CALL_TYPE}` }], | ||
324 | + ifShow: ({ model }) => | ||
325 | + model[FormFieldsEnum.OUT_TARGET] === ExecutionActionEnum.DEVICE_OUT && | ||
326 | + model[FormFieldsEnum.COMMAND_TYPE] === CommandTypeEnum.CUSTOM, | ||
327 | + componentProps: () => { | ||
328 | + return { | ||
329 | + options: [ | ||
330 | + { label: ServiceCallTypeNameEnum.ASYNC, value: ServiceCallTypeEnum.ASYNC }, | ||
331 | + { label: ServiceCallTypeNameEnum.SYNC, value: ServiceCallTypeEnum.SYNC }, | ||
332 | + ], | ||
333 | + placeholder: `请选择${FormFieldsNameEnum.CALL_TYPE}`, | ||
334 | + }; | ||
335 | + }, | ||
336 | + }, | ||
337 | + { | ||
338 | + field: FormFieldsEnum.SERVICE_ID, | ||
339 | + label: '', | ||
340 | + component: 'ApiSelect', | ||
341 | + rules: [{ required: true, message: `请选择${FormFieldsNameEnum.CALL_SERVICE_IDENTIFIER}` }], | ||
342 | + ifShow: ({ model }) => | ||
343 | + model[FormFieldsEnum.OUT_TARGET] === ExecutionActionEnum.DEVICE_OUT && | ||
344 | + model[FormFieldsEnum.COMMAND_TYPE] === CommandTypeEnum.SERVICE, | ||
345 | + componentProps: ({ formModel, formActionType }) => { | ||
346 | + return { | ||
347 | + api: async () => { | ||
348 | + const deviceProfileId = formModel[FormFieldsEnum.DEVICE_PROFILE_ID]; | ||
349 | + if (!deviceProfileId) return []; | ||
350 | + return await getModelServices({ deviceProfileId }); | ||
351 | + }, | ||
352 | + labelField: 'functionName', | ||
353 | + valueField: 'id', | ||
354 | + placeholder: `请选择${FormFieldsNameEnum.CALL_SERVICE_IDENTIFIER}`, | ||
355 | + onChange( | ||
356 | + value: string, | ||
357 | + option: ModelOfMatterParams & Record<'label' | 'value' | 'id', string> | ||
358 | + ) { | ||
359 | + const { setFieldsValue } = formActionType; | ||
360 | + setFieldsValue({ | ||
361 | + [FormFieldsEnum.CALL_TYPE]: value ? option.callType : null, | ||
362 | + [FormFieldsEnum.CALL_SERVICE_IDENTIFIER]: value ? option.identifier : null, | ||
363 | + [FormFieldsEnum.CALL_SERVICE]: value | ||
364 | + ? { ...toRaw(option), id: option?.value, functionName: option?.label } | ||
365 | + : null, | ||
366 | + [FormFieldsEnum.SERVICE_COMMAND]: {}, | ||
367 | + }); | ||
368 | + }, | ||
369 | + onOptionsChange(options: (StructJSON & Record<'label' | 'value' | 'id', string>)[]) { | ||
370 | + const { setFieldsValue } = formActionType; | ||
371 | + const serviceId = formModel[FormFieldsEnum.SERVICE_ID]; | ||
372 | + const res = options.find((item) => item.value === serviceId); | ||
373 | + res && | ||
374 | + setFieldsValue({ | ||
375 | + [FormFieldsEnum.CALL_SERVICE_IDENTIFIER]: res.identifier, | ||
376 | + [FormFieldsEnum.CALL_SERVICE]: { | ||
377 | + ...toRaw(res), | ||
378 | + id: res?.value, | ||
379 | + functionName: res?.label, | ||
380 | + }, | ||
381 | + }); | ||
382 | + }, | ||
383 | + }; | ||
384 | + }, | ||
385 | + }, | ||
386 | + { | ||
387 | + field: FormFieldsEnum.CALL_SERVICE_IDENTIFIER, | ||
388 | + label: '服务标识符', | ||
389 | + component: 'Input', | ||
390 | + ifShow: false, | ||
391 | + }, | ||
392 | + { | ||
393 | + field: FormFieldsEnum.CALL_SERVICE, | ||
394 | + label: '服务详情', | ||
395 | + component: 'Input', | ||
396 | + ifShow: false, | ||
397 | + }, | ||
398 | + { | ||
399 | + field: FormFieldsEnum.SERVICE_COMMAND, | ||
400 | + label: '', | ||
401 | + component: 'Input', | ||
402 | + colProps: { span: 24, xxl: 24, xl: 24, lg: 24, md: 24, sm: 24, xs: 24 }, | ||
403 | + ifShow: ({ model }) => | ||
404 | + model[FormFieldsEnum.OUT_TARGET] === ExecutionActionEnum.DEVICE_OUT && | ||
405 | + model[FormFieldsEnum.COMMAND_TYPE] === CommandTypeEnum.SERVICE && | ||
406 | + model[FormFieldsEnum.CALL_SERVICE], | ||
407 | + colSlot: 'serviceCommand', | ||
408 | + }, | ||
409 | + { | ||
410 | + field: FormFieldsEnum.CUSTOM_COMMAND, | ||
411 | + label: '', | ||
412 | + component: 'JSONEditor', | ||
413 | + valueField: 'value', | ||
414 | + changeEvent: 'update:value', | ||
415 | + colProps: { span: 24, xxl: 24, xl: 24, lg: 24, md: 24, sm: 24, xs: 24 }, | ||
416 | + rules: [ | ||
417 | + { | ||
418 | + required: true, | ||
419 | + validator(_rule, value: string) { | ||
420 | + const { flag } = useJsonParse(value); | ||
421 | + return flag ? Promise.resolve() : Promise.reject('请检查自定义命令'); | ||
422 | + }, | ||
423 | + }, | ||
424 | + ], | ||
425 | + ifShow: ({ model }) => | ||
426 | + model[FormFieldsEnum.OUT_TARGET] === ExecutionActionEnum.DEVICE_OUT && | ||
427 | + model[FormFieldsEnum.TRANSPORT_TYPE] !== TransportTypeEnum.TCP && | ||
428 | + model[FormFieldsEnum.COMMAND_TYPE] === CommandTypeEnum.CUSTOM, | ||
429 | + componentProps: () => { | ||
430 | + return { | ||
431 | + title: FormFieldsNameEnum.CUSTOM_COMMAND, | ||
432 | + }; | ||
433 | + }, | ||
434 | + }, | ||
435 | + { | ||
436 | + field: FormFieldsEnum.TCP_CUSTOM_COMMAND, | ||
437 | + label: FormFieldsNameEnum.TCP_CUSTOM_COMMAND, | ||
438 | + component: 'Input', | ||
439 | + colProps: { span: 24, xxl: 24, xl: 24, lg: 24, md: 24, sm: 24, xs: 24 }, | ||
440 | + rules: [ | ||
441 | + { required: true, message: `请输入${FormFieldsNameEnum.TCP_CUSTOM_COMMAND}` }, | ||
442 | + { validator: validateTCPCustomCommand }, | ||
443 | + ], | ||
444 | + ifShow: ({ model }) => | ||
445 | + model[FormFieldsEnum.OUT_TARGET] === ExecutionActionEnum.DEVICE_OUT && | ||
446 | + model[FormFieldsEnum.TRANSPORT_TYPE] === TransportTypeEnum.TCP && | ||
447 | + model[FormFieldsEnum.COMMAND_TYPE] === CommandTypeEnum.CUSTOM, | ||
448 | + componentProps: () => { | ||
449 | + return { | ||
450 | + placeholder: `请输入${FormFieldsNameEnum.TCP_CUSTOM_COMMAND}`, | ||
451 | + }; | ||
452 | + }, | ||
453 | + // labelWidth: 120, | ||
454 | + }, | ||
455 | + ]; | ||
456 | +}; |
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 | +} |
1 | +import { Ref, nextTick, toRaw, unref } from 'vue'; | ||
2 | +import { FlipFlopConditionType, FlipFlopFormRecordType, FlipFlopItemType } from './types'; | ||
3 | +import { DefineComponentsBasicExpose } from '/#/utils'; | ||
4 | +import { FormFieldEnum } from './config'; | ||
5 | +import { FlipFlopTypeEnum } from '/@/enums/linkedgeEnum'; | ||
6 | +import { createNewFlipFlopItem } from '.'; | ||
7 | + | ||
8 | +export function useFlipFlopData( | ||
9 | + flipFlopListRef: Ref<FlipFlopItemType[]> | ||
10 | +): DefineComponentsBasicExpose<FlipFlopConditionType[]> { | ||
11 | + const crateFlipFlopCondition = (flipFlopItem: FlipFlopItemType): FlipFlopConditionType => { | ||
12 | + const { | ||
13 | + deviceProfileId, | ||
14 | + deviceType, | ||
15 | + entityId, | ||
16 | + entityType, | ||
17 | + triggerType, | ||
18 | + flipFlopType, | ||
19 | + triggerDurationUnit, | ||
20 | + triggerRepeatCount, | ||
21 | + triggerDurationValue, | ||
22 | + } = flipFlopItem.ref?.getFieldsValue() as FlipFlopFormRecordType; | ||
23 | + | ||
24 | + const condition = flipFlopItem.conditionRef?.getFieldsValue() || []; | ||
25 | + | ||
26 | + return { | ||
27 | + deviceProfileId, | ||
28 | + deviceType, | ||
29 | + entityId, | ||
30 | + entityType, | ||
31 | + triggerType, | ||
32 | + triggerCondition: { | ||
33 | + condition: { | ||
34 | + condition, | ||
35 | + spec: { | ||
36 | + type: flipFlopType, | ||
37 | + ...(flipFlopType !== FlipFlopTypeEnum.SIMPLE | ||
38 | + ? { | ||
39 | + unit: triggerDurationUnit, | ||
40 | + predicate: { | ||
41 | + defaultValue: | ||
42 | + flipFlopType === FlipFlopTypeEnum.DURATION | ||
43 | + ? triggerDurationValue | ||
44 | + : triggerRepeatCount, | ||
45 | + }, | ||
46 | + } | ||
47 | + : {}), | ||
48 | + }, | ||
49 | + }, | ||
50 | + schedule: toRaw(unref(flipFlopItem.schedule)), | ||
51 | + }, | ||
52 | + }; | ||
53 | + }; | ||
54 | + | ||
55 | + const getFieldsValue = () => { | ||
56 | + return unref(flipFlopListRef).map((flipFlopItem) => crateFlipFlopCondition(flipFlopItem)); | ||
57 | + }; | ||
58 | + | ||
59 | + const setFieldsValue = (flipFlopData: FlipFlopConditionType[]) => { | ||
60 | + flipFlopListRef.value = Array.from({ length: flipFlopData.length }, () => | ||
61 | + createNewFlipFlopItem() | ||
62 | + ); | ||
63 | + | ||
64 | + nextTick(() => { | ||
65 | + unref(flipFlopListRef).forEach((item, index) => { | ||
66 | + const data = flipFlopData[index]; | ||
67 | + const condition = data.triggerCondition.condition; | ||
68 | + const [firstItem] = condition.condition; | ||
69 | + const { key, valueType } = firstItem; | ||
70 | + const { type, unit, predicate } = condition.spec; | ||
71 | + const { defaultValue } = predicate || {}; | ||
72 | + | ||
73 | + item.ref?.setFieldsValue({ | ||
74 | + ...data, | ||
75 | + [FormFieldEnum.FLIP_FLOP_TYPE]: type, | ||
76 | + [FormFieldEnum.CONDITION_KEY]: key.key, | ||
77 | + [FormFieldEnum.CONDITION_VALUE_TYPE]: valueType, | ||
78 | + [FormFieldEnum.TRIGGER_DURATION_UNIT]: unit, | ||
79 | + [FormFieldEnum.TRIGGER_DURATION_VALUE]: | ||
80 | + type === FlipFlopTypeEnum.DURATION ? defaultValue : null, | ||
81 | + [FormFieldEnum.TRIGGER_REPEAT_COUNT]: | ||
82 | + type === FlipFlopTypeEnum.REPEATING ? defaultValue : null, | ||
83 | + }); | ||
84 | + | ||
85 | + const { | ||
86 | + daysOfWeek, | ||
87 | + startsOn = 0, | ||
88 | + endsOn = 0, | ||
89 | + timezone, | ||
90 | + type: scheduleType, | ||
91 | + items, | ||
92 | + } = data.triggerCondition.schedule; | ||
93 | + item.schedule = { | ||
94 | + type: scheduleType, | ||
95 | + timezone, | ||
96 | + daysOfWeek, | ||
97 | + startsOn, | ||
98 | + endsOn, | ||
99 | + items, | ||
100 | + }; | ||
101 | + | ||
102 | + nextTick(() => { | ||
103 | + item.conditionRef?.setFieldsValue(condition.condition); | ||
104 | + }); | ||
105 | + }); | ||
106 | + }); | ||
107 | + }; | ||
108 | + | ||
109 | + const validate = async () => { | ||
110 | + for (const flipFlopItem of unref(flipFlopListRef)) { | ||
111 | + await flipFlopItem.ref?.validate(); | ||
112 | + await flipFlopItem.conditionRef?.validate?.(); | ||
113 | + } | ||
114 | + }; | ||
115 | + | ||
116 | + const resetFieldsValue = (defaultNull?: boolean) => { | ||
117 | + flipFlopListRef.value = defaultNull ? [] : [createNewFlipFlopItem()]; | ||
118 | + }; | ||
119 | + | ||
120 | + return { | ||
121 | + getFieldsValue, | ||
122 | + setFieldsValue, | ||
123 | + validate, | ||
124 | + resetFieldsValue, | ||
125 | + }; | ||
126 | +} |
1 | +import { FormSchema, useComponentRegister } from '/@/components/Form'; | ||
2 | +import { OrgTreeSelect } from '/@/views/common/OrgTreeSelect'; | ||
3 | + | ||
4 | +useComponentRegister('OrgTreeSelect', OrgTreeSelect); | ||
5 | + | ||
6 | +export enum FormFieldsEnum { | ||
7 | + NAME = 'name', | ||
8 | + ORGANIZATION_ID = 'organizationId', | ||
9 | + DESCRIPTION = 'description', | ||
10 | +} | ||
11 | +export enum FormFieldsNameEnum { | ||
12 | + NAME = '场景联动名称', | ||
13 | + ORGANIZATION_ID = '所属组织', | ||
14 | + DESCRIPTION = '描述', | ||
15 | +} | ||
16 | +export const getFormSchemas = ( | ||
17 | + onOrgTreeSelectChange: Fn, | ||
18 | + onOrgTreeSelectOptionsChange: Fn | ||
19 | +): FormSchema[] => { | ||
20 | + return [ | ||
21 | + { | ||
22 | + field: FormFieldsEnum.NAME, | ||
23 | + component: 'Input', | ||
24 | + label: FormFieldsNameEnum.NAME, | ||
25 | + required: true, | ||
26 | + componentProps: { | ||
27 | + placeholder: `请输入${FormFieldsNameEnum.NAME}`, | ||
28 | + }, | ||
29 | + }, | ||
30 | + { | ||
31 | + field: FormFieldsEnum.ORGANIZATION_ID, | ||
32 | + component: 'OrgTreeSelect', | ||
33 | + label: FormFieldsNameEnum.ORGANIZATION_ID, | ||
34 | + required: true, | ||
35 | + componentProps: { | ||
36 | + placeholder: `请输入${FormFieldsNameEnum.NAME}`, | ||
37 | + onChange: onOrgTreeSelectChange, | ||
38 | + onOptionsChange: onOrgTreeSelectOptionsChange, | ||
39 | + }, | ||
40 | + }, | ||
41 | + { | ||
42 | + field: FormFieldsEnum.DESCRIPTION, | ||
43 | + component: 'InputTextArea', | ||
44 | + label: FormFieldsNameEnum.DESCRIPTION, | ||
45 | + componentProps: { | ||
46 | + placeholder: `请输入${FormFieldsNameEnum.DESCRIPTION}`, | ||
47 | + }, | ||
48 | + }, | ||
49 | + { | ||
50 | + field: 'flipFlop', | ||
51 | + component: 'Input', | ||
52 | + label: '', | ||
53 | + slot: 'flipFlop', | ||
54 | + }, | ||
55 | + { | ||
56 | + field: 'executionCondition', | ||
57 | + component: 'Input', | ||
58 | + label: '', | ||
59 | + slot: 'executionCondition', | ||
60 | + }, | ||
61 | + { | ||
62 | + field: 'executionAction', | ||
63 | + component: 'Input', | ||
64 | + label: '', | ||
65 | + slot: 'executionAction', | ||
66 | + }, | ||
67 | + ]; | ||
68 | +}; |
1 | +export { default as SceneLinkageDrawer } from './index.vue'; |
1 | +<script setup lang="ts"> | ||
2 | + import { computed, ref, unref } from 'vue'; | ||
3 | + import { SceneLinkageDrawerDataType } from './type'; | ||
4 | + import { BasicDrawer, useDrawerInner } from '/@/components/Drawer'; | ||
5 | + import { DataActionModeEnum, DataActionModeNameEnum } from '/@/enums/toolEnum'; | ||
6 | + import { BasicForm, useForm } from '/@/components/Form'; | ||
7 | + import { getFormSchemas, FormFieldsEnum } from './config'; | ||
8 | + import { createSceneLinkageDrawerContext } from './sceneLinkageDrawerContext'; | ||
9 | + import { FlipFlop } from '../FlipFlop'; | ||
10 | + import { Divider, Tooltip } from 'ant-design-vue'; | ||
11 | + import { Icon } from '/@/components/Icon'; | ||
12 | + import { ExecutionAction } from '../ExecutionAction'; | ||
13 | + import { useSceneLinkageData } from './useSceneLinkedgeData'; | ||
14 | + import { screenLinkPageAddApi } from '/@/api/ruleengine/ruleengineApi'; | ||
15 | + import { useMessage } from '/@/hooks/web/useMessage'; | ||
16 | + import { OrganizationListItem } from '/@/api/system/model/systemModel'; | ||
17 | + | ||
18 | + const emit = defineEmits(['success', 'register']); | ||
19 | + | ||
20 | + const drawerMode = ref(DataActionModeEnum.CREATE); | ||
21 | + | ||
22 | + const getDrawTitle = computed(() => `${DataActionModeNameEnum[unref(drawerMode)]}场景联动`); | ||
23 | + | ||
24 | + const disabledDrawer = ref(false); | ||
25 | + | ||
26 | + const currentUpdateLinkedgeId = ref<string>(); | ||
27 | + | ||
28 | + const [register, { setDrawerProps, closeDrawer }] = useDrawerInner( | ||
29 | + (data: SceneLinkageDrawerDataType) => { | ||
30 | + const { mode, record } = data; | ||
31 | + drawerMode.value = mode; | ||
32 | + disabledDrawer.value = mode === DataActionModeEnum.READ; | ||
33 | + resetFieldsValue?.(); | ||
34 | + | ||
35 | + if (mode === DataActionModeEnum.UPDATE || mode === DataActionModeEnum.READ) { | ||
36 | + currentUpdateLinkedgeId.value = record.id; | ||
37 | + setDrawerProps({ loading: true }); | ||
38 | + handleOnOrganizationChange(data.record.organizationId); | ||
39 | + setTimeout(() => { | ||
40 | + setFieldsValue(record); | ||
41 | + setDrawerProps({ loading: false }); | ||
42 | + }, 300); | ||
43 | + } | ||
44 | + | ||
45 | + setDrawerProps({ | ||
46 | + showOkBtn: !unref(disabledDrawer), | ||
47 | + cancelText: unref(disabledDrawer) ? '关闭' : '取消', | ||
48 | + }); | ||
49 | + } | ||
50 | + ); | ||
51 | + | ||
52 | + const [registerForm, basicFormActionType] = useForm({ | ||
53 | + schemas: getFormSchemas(handleOnOrganizationChange, handleOnOrganizationOptionsChange), | ||
54 | + layout: 'horizontal', | ||
55 | + labelWidth: 120, | ||
56 | + showActionButtonGroup: false, | ||
57 | + }); | ||
58 | + | ||
59 | + const organizationId = ref<string>(); | ||
60 | + | ||
61 | + function handleOnOrganizationChange(id: string) { | ||
62 | + organizationId.value = id; | ||
63 | + } | ||
64 | + | ||
65 | + function handleOnOrganizationOptionsChange(options: OrganizationListItem[]) { | ||
66 | + if (unref(drawerMode) === DataActionModeEnum.CREATE) { | ||
67 | + const [firsetItem] = options; | ||
68 | + organizationId.value = firsetItem?.id; | ||
69 | + basicFormActionType.setFieldsValue({ [FormFieldsEnum.ORGANIZATION_ID]: firsetItem?.id }); | ||
70 | + } | ||
71 | + } | ||
72 | + | ||
73 | + createSceneLinkageDrawerContext({ | ||
74 | + organizationId: computed(() => unref(organizationId)), | ||
75 | + disabledDrawer: computed(() => unref(disabledDrawer)), | ||
76 | + }); | ||
77 | + | ||
78 | + const flipFlopElRef = ref<InstanceType<typeof FlipFlop>>(); | ||
79 | + | ||
80 | + const executionConditionElRef = ref<InstanceType<typeof FlipFlop>>(); | ||
81 | + | ||
82 | + const executionActionElRef = ref<InstanceType<typeof ExecutionAction>>(); | ||
83 | + | ||
84 | + const { getFieldsValue, setFieldsValue, validate, resetFieldsValue } = useSceneLinkageData({ | ||
85 | + flipFlopElRef, | ||
86 | + executionActionElRef, | ||
87 | + executionConditionElRef, | ||
88 | + basicFormActionType, | ||
89 | + }); | ||
90 | + | ||
91 | + const { createMessage } = useMessage(); | ||
92 | + const handleSave = async () => { | ||
93 | + await validate?.(); | ||
94 | + setDrawerProps({ loading: true, confirmLoading: true }); | ||
95 | + try { | ||
96 | + const value = getFieldsValue(); | ||
97 | + | ||
98 | + if (unref(drawerMode) === DataActionModeEnum.UPDATE) { | ||
99 | + Reflect.set(value, 'id', unref(currentUpdateLinkedgeId)); | ||
100 | + } | ||
101 | + | ||
102 | + await screenLinkPageAddApi(value, unref(drawerMode) === DataActionModeEnum.UPDATE); | ||
103 | + createMessage.success(`${DataActionModeNameEnum[unref(drawerMode)]}成功`); | ||
104 | + closeDrawer(); | ||
105 | + emit('success'); | ||
106 | + } finally { | ||
107 | + setDrawerProps({ loading: false, confirmLoading: false }); | ||
108 | + } | ||
109 | + }; | ||
110 | +</script> | ||
111 | + | ||
112 | +<template> | ||
113 | + <BasicDrawer | ||
114 | + @register="register" | ||
115 | + :title="getDrawTitle" | ||
116 | + width="50%" | ||
117 | + @ok="handleSave" | ||
118 | + show-cancel-btn | ||
119 | + show-footer | ||
120 | + > | ||
121 | + <BasicForm @register="registerForm" :disabled="disabledDrawer"> | ||
122 | + <template #flipFlop> | ||
123 | + <Divider orientation="left"> | ||
124 | + <Tooltip> | ||
125 | + <template #title> 触发器不可为空,消息只要满足触发条件中任意一个即可触发。 </template> | ||
126 | + <label><span class="text-red-400 mr-1">*</span>触发器</label> | ||
127 | + <Icon icon="ant-design:question-circle-outlined" class="ml-2" /> | ||
128 | + </Tooltip> | ||
129 | + </Divider> | ||
130 | + <FlipFlop ref="flipFlopElRef" /> | ||
131 | + </template> | ||
132 | + <template #executionCondition> | ||
133 | + <Divider orientation="left"> | ||
134 | + <Tooltip> | ||
135 | + <template #title> 执行条件可为空,消息需要满足所有执行条件才会被处理。 </template> | ||
136 | + <label>执行条件</label> | ||
137 | + <Icon icon="ant-design:question-circle-outlined" class="ml-2" /> | ||
138 | + </Tooltip> | ||
139 | + </Divider> | ||
140 | + <FlipFlop | ||
141 | + ref="executionConditionElRef" | ||
142 | + addButtonName="执行条件 (AND)" | ||
143 | + default-null | ||
144 | + :panel-title="(index) => `执行条件${index}`" | ||
145 | + /> | ||
146 | + </template> | ||
147 | + <template #executionAction> | ||
148 | + <Divider orientation="left"> | ||
149 | + <Tooltip> | ||
150 | + <template #title> | ||
151 | + 触发器和执行条件都满足时,场景联动会做什么,例如:设备联动、告警通知等。 | ||
152 | + </template> | ||
153 | + <label><span class="text-red-400 mr-1">*</span>执行动作</label> | ||
154 | + <Icon icon="ant-design:question-circle-outlined" class="ml-2" /> | ||
155 | + </Tooltip> | ||
156 | + </Divider> | ||
157 | + <ExecutionAction ref="executionActionElRef" /> | ||
158 | + </template> | ||
159 | + </BasicForm> | ||
160 | + </BasicDrawer> | ||
161 | +</template> |
1 | +import type { ComputedRef, InjectionKey } from 'vue'; | ||
2 | +import { createContext, useContext } from '/@/hooks/core/useContext'; | ||
3 | + | ||
4 | +export interface SceneLinkageDrawerContextProps { | ||
5 | + organizationId: ComputedRef<string | undefined>; | ||
6 | + disabledDrawer: ComputedRef<boolean>; | ||
7 | +} | ||
8 | + | ||
9 | +const key: InjectionKey<SceneLinkageDrawerContextProps> = Symbol('scene-linkage-drawer-context'); | ||
10 | + | ||
11 | +export function createSceneLinkageDrawerContext(context: SceneLinkageDrawerContextProps) { | ||
12 | + return createContext<SceneLinkageDrawerContextProps>(context, key, { native: true }); | ||
13 | +} | ||
14 | + | ||
15 | +export function useSceneLinkageDrawerContext() { | ||
16 | + return useContext<SceneLinkageDrawerContextProps>(key); | ||
17 | +} |
1 | +import { ExecutionActionDataItemType } from '../ExecutionAction/type'; | ||
2 | +import { FlipFlopConditionType } from '../FlipFlop/types'; | ||
3 | +import { FormFieldsEnum } from './config'; | ||
4 | +export type SceneLinkageDrawerDataType = ModalParamsType<SceneLinkageDataType>; | ||
5 | + | ||
6 | +export interface SceneLinkageFormRecordType { | ||
7 | + [FormFieldsEnum.NAME]: string; | ||
8 | + [FormFieldsEnum.ORGANIZATION_ID]: string; | ||
9 | + [FormFieldsEnum.DESCRIPTION]: string; | ||
10 | +} | ||
11 | + | ||
12 | +export interface SceneLinkageDataType { | ||
13 | + id?: string; | ||
14 | + doActions: ExecutionActionDataItemType[]; | ||
15 | + triggers: FlipFlopConditionType[]; | ||
16 | + doConditions: FlipFlopConditionType[]; | ||
17 | + name: string; | ||
18 | + description?: string; | ||
19 | + organizationId: string; | ||
20 | +} |
1 | +import { Ref, unref } from 'vue'; | ||
2 | +import { ExecutionAction } from '../ExecutionAction'; | ||
3 | +import { FlipFlop } from '../FlipFlop'; | ||
4 | +import { DefineComponentsBasicExpose } from '/#/utils'; | ||
5 | +import { SceneLinkageDataType, SceneLinkageFormRecordType } from './type'; | ||
6 | +import { FormActionType } from '/@/components/Form'; | ||
7 | + | ||
8 | +interface UseSceneLinkageDataParamsType { | ||
9 | + flipFlopElRef: Ref<InstanceType<typeof FlipFlop> | undefined>; | ||
10 | + executionConditionElRef: Ref<InstanceType<typeof FlipFlop> | undefined>; | ||
11 | + executionActionElRef: Ref<InstanceType<typeof ExecutionAction> | undefined>; | ||
12 | + basicFormActionType: FormActionType; | ||
13 | +} | ||
14 | + | ||
15 | +export function useSceneLinkageData({ | ||
16 | + flipFlopElRef, | ||
17 | + executionActionElRef, | ||
18 | + executionConditionElRef, | ||
19 | + basicFormActionType, | ||
20 | +}: UseSceneLinkageDataParamsType): DefineComponentsBasicExpose<SceneLinkageDataType> { | ||
21 | + const getFieldsValue = () => { | ||
22 | + const doActions = unref(executionActionElRef)?.getFieldsValue() || []; | ||
23 | + const doConditions = unref(executionConditionElRef)?.getFieldsValue() || []; | ||
24 | + const triggers = unref(flipFlopElRef)?.getFieldsValue() || []; | ||
25 | + const { name, description, organizationId } = | ||
26 | + basicFormActionType.getFieldsValue() as SceneLinkageFormRecordType; | ||
27 | + | ||
28 | + return { | ||
29 | + name, | ||
30 | + description, | ||
31 | + organizationId, | ||
32 | + doActions, | ||
33 | + doConditions, | ||
34 | + triggers, | ||
35 | + }; | ||
36 | + }; | ||
37 | + | ||
38 | + const setFieldsValue = (record: SceneLinkageDataType) => { | ||
39 | + const { doActions, doConditions, triggers, name, description, organizationId } = record; | ||
40 | + | ||
41 | + basicFormActionType.setFieldsValue({ name, description, organizationId }); | ||
42 | + | ||
43 | + unref(flipFlopElRef)?.setFieldsValue(triggers); | ||
44 | + | ||
45 | + unref(executionConditionElRef)?.setFieldsValue(doConditions); | ||
46 | + | ||
47 | + unref(executionActionElRef)?.setFieldsValue(doActions); | ||
48 | + }; | ||
49 | + | ||
50 | + const validate = async () => { | ||
51 | + await basicFormActionType.validate?.(); | ||
52 | + await unref(flipFlopElRef)?.validate?.(); | ||
53 | + await unref(executionConditionElRef)?.validate?.(); | ||
54 | + await unref(executionActionElRef)?.validate?.(); | ||
55 | + }; | ||
56 | + | ||
57 | + const resetFieldsValue = () => { | ||
58 | + basicFormActionType.resetFields(); | ||
59 | + unref(flipFlopElRef)?.resetFieldsValue?.(); | ||
60 | + unref(executionConditionElRef)?.resetFieldsValue?.(true); | ||
61 | + unref(executionActionElRef)?.resetFieldsValue?.(); | ||
62 | + }; | ||
63 | + | ||
64 | + return { | ||
65 | + getFieldsValue, | ||
66 | + setFieldsValue, | ||
67 | + validate, | ||
68 | + resetFieldsValue, | ||
69 | + }; | ||
70 | +} |
1 | import { formatToDateTime } from '/@/utils/dateUtil'; | 1 | import { formatToDateTime } from '/@/utils/dateUtil'; |
2 | -import { Number_Operation, String_Operation, Boolean_Operation } from '/@/enums/operationEnum'; | 2 | +import { |
3 | + NumberOperationEnum, | ||
4 | + StringOperationEnum, | ||
5 | + BooleanOperationEnum, | ||
6 | +} from '../../../../enums/linkedgeEnum'; | ||
3 | 7 | ||
4 | // 生成触发器或执行条件JSON数据 | 8 | // 生成触发器或执行条件JSON数据 |
5 | export const genTriggerOrConditionData = (triggerData) => { | 9 | export const genTriggerOrConditionData = (triggerData) => { |
@@ -164,26 +168,26 @@ export const genActionData = (actionData) => { | @@ -164,26 +168,26 @@ export const genActionData = (actionData) => { | ||
164 | }; | 168 | }; |
165 | 169 | ||
166 | export const operationNumber_OR_TIME = [ | 170 | export const operationNumber_OR_TIME = [ |
167 | - { label: '等于', value: Number_Operation.EQUAL, symbol: '=' }, | ||
168 | - { label: '不等于', value: Number_Operation.NOT_EQUAL, symbol: '!=' }, | ||
169 | - { label: '小于', value: Number_Operation.LESS, symbol: '<' }, | ||
170 | - { label: '小于等于', value: Number_Operation.LESS_OR_EQUAL, symbol: '<=' }, | ||
171 | - { label: '大于', value: Number_Operation.GREATER, symbol: '>' }, | ||
172 | - { label: '大于等于', value: Number_Operation.GREATER_OR_EQUAL, symbol: '>=' }, | 171 | + { label: '等于', value: NumberOperationEnum.EQUAL, symbol: '=' }, |
172 | + { label: '不等于', value: NumberOperationEnum.NOT_EQUAL, symbol: '!=' }, | ||
173 | + { label: '小于', value: NumberOperationEnum.LESS, symbol: '<' }, | ||
174 | + { label: '小于等于', value: NumberOperationEnum.LESS_OR_EQUAL, symbol: '<=' }, | ||
175 | + { label: '大于', value: NumberOperationEnum.GREATER, symbol: '>' }, | ||
176 | + { label: '大于等于', value: NumberOperationEnum.GREATER_OR_EQUAL, symbol: '>=' }, | ||
173 | ]; | 177 | ]; |
174 | 178 | ||
175 | export const operationString = [ | 179 | export const operationString = [ |
176 | - { label: '等于', value: String_Operation.EQUAL, symbol: '=' }, | ||
177 | - { label: '不等于', value: String_Operation.NOT_EQUAL, symbol: '!=' }, | ||
178 | - { label: '开始于', value: String_Operation.BEGAN_IN, symbol: '开始于' }, | ||
179 | - { label: '结束于', value: String_Operation.END_IN, symbol: '结束于' }, | ||
180 | - { label: '包含', value: String_Operation.INCLUDE, symbol: '包含' }, | ||
181 | - { label: '不包含', value: String_Operation.NOT_INCLUDE, symbol: '不包含' }, | 180 | + { label: '等于', value: StringOperationEnum.EQUAL, symbol: '=' }, |
181 | + { label: '不等于', value: StringOperationEnum.NOT_EQUAL, symbol: '!=' }, | ||
182 | + { label: '开始于', value: StringOperationEnum.STARTS_WITH, symbol: '开始于' }, | ||
183 | + { label: '结束于', value: StringOperationEnum.ENDS_WITH, symbol: '结束于' }, | ||
184 | + { label: '包含', value: StringOperationEnum.CONTAINS, symbol: '包含' }, | ||
185 | + { label: '不包含', value: StringOperationEnum.NOT_CONTAINS, symbol: '不包含' }, | ||
182 | ]; | 186 | ]; |
183 | 187 | ||
184 | export const operationBoolean = [ | 188 | export const operationBoolean = [ |
185 | - { label: '等于', value: Boolean_Operation.EQUAL, symbol: '=' }, | ||
186 | - { label: '不等于', value: Boolean_Operation.NOT_EQUAL, symbol: '!=' }, | 189 | + { label: '等于', value: BooleanOperationEnum.EQUAL, symbol: '=' }, |
190 | + { label: '不等于', value: BooleanOperationEnum.NOT_EQUAL, symbol: '!=' }, | ||
187 | ]; | 191 | ]; |
188 | 192 | ||
189 | export const scheduleOptions = [ | 193 | export const scheduleOptions = [ |
@@ -376,7 +380,8 @@ export function isType(operationType) { | @@ -376,7 +380,8 @@ export function isType(operationType) { | ||
376 | } | 380 | } |
377 | export function conditionPreView(data, operationType) { | 381 | export function conditionPreView(data, operationType) { |
378 | if (operationType === 'NUMERIC' || operationType === 'DATE_TIME') { | 382 | if (operationType === 'NUMERIC' || operationType === 'DATE_TIME') { |
379 | - const { EQUAL, NOT_EQUAL, LESS, LESS_OR_EQUAL, GREATER, GREATER_OR_EQUAL } = Number_Operation; | 383 | + const { EQUAL, NOT_EQUAL, LESS, LESS_OR_EQUAL, GREATER, GREATER_OR_EQUAL } = |
384 | + NumberOperationEnum; | ||
380 | return data.map((item) => { | 385 | return data.map((item) => { |
381 | return { | 386 | return { |
382 | operation: | 387 | operation: |
@@ -398,7 +403,14 @@ export function conditionPreView(data, operationType) { | @@ -398,7 +403,14 @@ export function conditionPreView(data, operationType) { | ||
398 | }; | 403 | }; |
399 | }); | 404 | }); |
400 | } else if (operationType === 'STRING') { | 405 | } else if (operationType === 'STRING') { |
401 | - const { EQUAL, NOT_EQUAL, BEGAN_IN, END_IN, INCLUDE, NOT_INCLUDE } = String_Operation; | 406 | + const { |
407 | + EQUAL, | ||
408 | + NOT_EQUAL, | ||
409 | + STARTS_WITH: BEGAN_IN, | ||
410 | + ENDS_WITH: END_IN, | ||
411 | + CONTAINS: INCLUDE, | ||
412 | + NOT_CONTAINS: NOT_INCLUDE, | ||
413 | + } = StringOperationEnum; | ||
402 | return data.map((item) => { | 414 | return data.map((item) => { |
403 | return { | 415 | return { |
404 | operation: | 416 | operation: |
@@ -420,7 +432,7 @@ export function conditionPreView(data, operationType) { | @@ -420,7 +432,7 @@ export function conditionPreView(data, operationType) { | ||
420 | }; | 432 | }; |
421 | }); | 433 | }); |
422 | } else if (operationType === 'BOOLEAN') { | 434 | } else if (operationType === 'BOOLEAN') { |
423 | - const { EQUAL, NOT_EQUAL } = Boolean_Operation; | 435 | + const { EQUAL, NOT_EQUAL } = BooleanOperationEnum; |
424 | return data.map((item) => { | 436 | return data.map((item) => { |
425 | return { | 437 | return { |
426 | operation: | 438 | operation: |
src/views/rule/linkedge/cpns/AlarmSchedule.vue
deleted
100644 → 0
1 | -<template> | ||
2 | - <BasicModal | ||
3 | - v-bind="$attrs" | ||
4 | - :width="600" | ||
5 | - :title="isUpdateFlag ? '编辑' + title : '新增' + title" | ||
6 | - centered | ||
7 | - @register="registerModal" | ||
8 | - @ok="handleOk" | ||
9 | - @cancel="handleCancel" | ||
10 | - > | ||
11 | - <BasicForm @register="registerForm" ref="basicFormRef"> | ||
12 | - <template #customEnable> | ||
13 | - <template v-for="(item, optionIndex) in options" :key="item.flag"> | ||
14 | - <div :class="optionIndex >= 1 ? 'mt-4' : ''" class="flex"> | ||
15 | - <div class="ml-4 mr-4 flex items-center"> | ||
16 | - <Checkbox v-model:checked="item.enabled">星期{{ item.flag }}</Checkbox> | ||
17 | - </div> | ||
18 | - <TimePicker | ||
19 | - placeholder="开始时间" | ||
20 | - v-model:value="item.startsOn" | ||
21 | - value-format="x" | ||
22 | - format="HH:mm" | ||
23 | - :disabled="!item.enabled" | ||
24 | - /> | ||
25 | - <span class="ml-4 mr-4 flex items-center">~</span> | ||
26 | - <TimePicker | ||
27 | - @change="handleBlur(item.startsOn, item.endsOn)" | ||
28 | - placeholder="结束时间" | ||
29 | - v-model:value="item.endsOn" | ||
30 | - value-format="x" | ||
31 | - format="HH:mm" | ||
32 | - :disabled="!item.enabled" | ||
33 | - /> | ||
34 | - </div> | ||
35 | - </template> | ||
36 | - </template> | ||
37 | - <template #timing> | ||
38 | - <TimePicker | ||
39 | - placeholder="开始时间" | ||
40 | - v-model:value="timeState.startsOn" | ||
41 | - value-format="x" | ||
42 | - format="HH:mm" | ||
43 | - /> | ||
44 | - <span class="ml-4 mr-4">~</span> | ||
45 | - <TimePicker | ||
46 | - @change="handleTimeBlur(timeState.startsOn, timeState.endsOn)" | ||
47 | - placeholder="结束时间" | ||
48 | - v-model:value="timeState.endsOn" | ||
49 | - value-format="x" | ||
50 | - format="HH:mm" | ||
51 | - /> | ||
52 | - </template> | ||
53 | - </BasicForm> | ||
54 | - </BasicModal> | ||
55 | -</template> | ||
56 | - | ||
57 | -<script lang="ts" setup> | ||
58 | - import { reactive, ref, watch, nextTick } from 'vue'; | ||
59 | - import { useModalInner, BasicModal } from '/@/components/Modal'; | ||
60 | - import { BasicForm, useForm } from '/@/components/Form'; | ||
61 | - import { alarmScheduleSchemas } from '../config/config.data'; | ||
62 | - import { Checkbox, TimePicker } from 'ant-design-vue'; | ||
63 | - import { useMessage } from '/@/hooks/web/useMessage'; | ||
64 | - | ||
65 | - const emit = defineEmits(['register', 'cancel']); | ||
66 | - const title = ref(''); | ||
67 | - const isUpdateFlag = ref(false); | ||
68 | - const { createMessage } = useMessage(); | ||
69 | - const [registerForm, { setFieldsValue, getFieldsValue }] = useForm({ | ||
70 | - showActionButtonGroup: false, | ||
71 | - schemas: alarmScheduleSchemas, | ||
72 | - }); | ||
73 | - const timeState = reactive({ | ||
74 | - startsOn: null, | ||
75 | - endsOn: null, | ||
76 | - }); | ||
77 | - | ||
78 | - const options = ref([ | ||
79 | - { | ||
80 | - enabled: false, | ||
81 | - dayOfWeek: 1, | ||
82 | - flag: '一', | ||
83 | - endsOn: null, | ||
84 | - startsOn: null, | ||
85 | - }, | ||
86 | - { | ||
87 | - enabled: false, | ||
88 | - dayOfWeek: 2, | ||
89 | - flag: '二', | ||
90 | - endsOn: null, | ||
91 | - startsOn: null, | ||
92 | - }, | ||
93 | - { | ||
94 | - enabled: false, | ||
95 | - dayOfWeek: 3, | ||
96 | - flag: '三', | ||
97 | - endsOn: null, | ||
98 | - startsOn: null, | ||
99 | - }, | ||
100 | - { | ||
101 | - enabled: false, | ||
102 | - dayOfWeek: 4, | ||
103 | - flag: '四', | ||
104 | - endsOn: null, | ||
105 | - startsOn: null, | ||
106 | - }, | ||
107 | - { | ||
108 | - enabled: false, | ||
109 | - dayOfWeek: 5, | ||
110 | - flag: '五', | ||
111 | - endsOn: null, | ||
112 | - startsOn: null, | ||
113 | - }, | ||
114 | - { | ||
115 | - enabled: false, | ||
116 | - dayOfWeek: 6, | ||
117 | - flag: '六', | ||
118 | - endsOn: null, | ||
119 | - startsOn: null, | ||
120 | - }, | ||
121 | - { | ||
122 | - enabled: false, | ||
123 | - dayOfWeek: 7, | ||
124 | - flag: '日', | ||
125 | - endsOn: null, | ||
126 | - startsOn: null, | ||
127 | - }, | ||
128 | - ]); | ||
129 | - const basicFormRef = ref<InstanceType<typeof BasicForm>>(); | ||
130 | - let index = ref(null); | ||
131 | - watch( | ||
132 | - options, | ||
133 | - (newValue) => { | ||
134 | - const arr = []; | ||
135 | - for (let item of newValue) { | ||
136 | - if (item.enabled && item.startsOn && item.endsOn) { | ||
137 | - arr.push(true); | ||
138 | - } else if ((!item.enabled && item.startsOn && item.endsOn) || item.enabled) { | ||
139 | - arr.push(false); | ||
140 | - } | ||
141 | - } | ||
142 | - const flag = arr.length ? !arr.every((item) => item) : true; | ||
143 | - nextTick(() => { | ||
144 | - setModalProps({ | ||
145 | - okButtonProps: { | ||
146 | - disabled: flag, | ||
147 | - }, | ||
148 | - }); | ||
149 | - }); | ||
150 | - }, | ||
151 | - { | ||
152 | - deep: true, | ||
153 | - } | ||
154 | - ); | ||
155 | - | ||
156 | - const [registerModal, { closeModal, setModalProps }] = useModalInner((data) => { | ||
157 | - watch([timeState, basicFormRef.value.formModel], ([timeState, formModel]) => { | ||
158 | - setModalProps({ | ||
159 | - okButtonProps: { | ||
160 | - disabled: | ||
161 | - timeState.startsOn === null || | ||
162 | - timeState.endsOn === null || | ||
163 | - !formModel.daysOfWeek?.length, | ||
164 | - }, | ||
165 | - }); | ||
166 | - watch( | ||
167 | - () => formModel.schedule, | ||
168 | - () => { | ||
169 | - timeState.startsOn = null; | ||
170 | - timeState.endsOn = null; | ||
171 | - } | ||
172 | - ); | ||
173 | - }); | ||
174 | - const { value, currentIndex, isUpdate, scheduleData } = data; | ||
175 | - isUpdateFlag.value = isUpdate; | ||
176 | - if (value === 'SPECIFIC_TIME') { | ||
177 | - title.value = '定时启用'; | ||
178 | - } else { | ||
179 | - title.value = '自定义启用'; | ||
180 | - } | ||
181 | - | ||
182 | - index.value = currentIndex; | ||
183 | - const dayZenoTime = Math.round(new Date(new Date().toLocaleDateString()).getTime()); | ||
184 | - // 编辑 | ||
185 | - setFieldsValue({ | ||
186 | - schedule: value, | ||
187 | - }); | ||
188 | - if (isUpdate) { | ||
189 | - nextTick(() => { | ||
190 | - // 回显定时启用 | ||
191 | - if (scheduleData.type === 'SPECIFIC_TIME') { | ||
192 | - setFieldsValue({ | ||
193 | - daysOfWeek: scheduleData.daysOfWeek, | ||
194 | - }); | ||
195 | - timeState.startsOn = scheduleData.startsOn + dayZenoTime + ''; | ||
196 | - timeState.endsOn = scheduleData.endsOn + dayZenoTime + ''; | ||
197 | - } | ||
198 | - // 回显自定义启用 | ||
199 | - if (scheduleData.type === 'CUSTOM') { | ||
200 | - for (let [index, item] of scheduleData?.items.entries()) { | ||
201 | - if (item.enabled) { | ||
202 | - options.value[index].enabled = item.enabled; | ||
203 | - options.value[index].startsOn = item.startsOn + dayZenoTime + ''; | ||
204 | - options.value[index].endsOn = item.endsOn + dayZenoTime + ''; | ||
205 | - } | ||
206 | - } | ||
207 | - } | ||
208 | - }); | ||
209 | - } | ||
210 | - }); | ||
211 | - const scheduleData = ref({ | ||
212 | - type: 'ANY_TIME', | ||
213 | - }); | ||
214 | - const handleBlur = (eS, eE) => { | ||
215 | - if (eS > eE) { | ||
216 | - return createMessage.warn('开始时间不能大于结束时间'); | ||
217 | - } | ||
218 | - }; | ||
219 | - const handleTimeBlur = (eS, eE) => { | ||
220 | - if (eS > eE) { | ||
221 | - return createMessage.warn('开始时间不能大于结束时间'); | ||
222 | - } | ||
223 | - }; | ||
224 | - const handleOk = () => { | ||
225 | - const { schedule: type, timezone, daysOfWeek } = getFieldsValue(); | ||
226 | - // 获取当天0时时间戳 | ||
227 | - const dayZenoTime = Math.round(new Date(new Date().toLocaleDateString()).getTime()); | ||
228 | - if (type === 'CUSTOM') { | ||
229 | - const items = options.value.map((item) => { | ||
230 | - return { | ||
231 | - startsOn: item.startsOn ? item.startsOn - dayZenoTime : 0, | ||
232 | - endsOn: item.endsOn ? item.endsOn - dayZenoTime : 0, | ||
233 | - dayOfWeek: item.dayOfWeek, | ||
234 | - enabled: item.enabled, | ||
235 | - }; | ||
236 | - }); | ||
237 | - scheduleData.value = { | ||
238 | - type, | ||
239 | - timezone, | ||
240 | - items, | ||
241 | - }; | ||
242 | - } else if (type === 'SPECIFIC_TIME') { | ||
243 | - scheduleData.value = { | ||
244 | - type, | ||
245 | - timezone, | ||
246 | - daysOfWeek, | ||
247 | - startsOn: timeState.startsOn - dayZenoTime, | ||
248 | - endsOn: timeState.endsOn - dayZenoTime, | ||
249 | - }; | ||
250 | - } | ||
251 | - closeModal(); | ||
252 | - }; | ||
253 | - const handleCancel = () => { | ||
254 | - emit('cancel', index.value); | ||
255 | - }; | ||
256 | - | ||
257 | - defineExpose({ | ||
258 | - scheduleData, | ||
259 | - }); | ||
260 | -</script> | ||
261 | - | ||
262 | -<style lang="less" scoped> | ||
263 | - :deep(.ant-time-picker) { | ||
264 | - width: 12rem; | ||
265 | - } | ||
266 | -</style> |
src/views/rule/linkedge/cpns/ClearAlarm.vue
deleted
100644 → 0
1 | -<template> | ||
2 | - <div> | ||
3 | - <CollapseContainer style="background-color: #f2f2f2" title="清除告警" :canExpan="false"> | ||
4 | - <template #action> | ||
5 | - <div class="flex"> | ||
6 | - <div class="flex"> | ||
7 | - <span class="mr-2">启用规则:</span> | ||
8 | - <template v-for="(item, scheduleIndex) in scheduleOptions" :key="item.label"> | ||
9 | - <div | ||
10 | - :class="{ 'ml-4': scheduleIndex >= 1, active: scheduleIndex === currentIndex }" | ||
11 | - class="cursor-pointer" | ||
12 | - @click="handleScheduleChange(item.value)" | ||
13 | - >{{ item.label }}</div | ||
14 | - > | ||
15 | - </template> | ||
16 | - </div> | ||
17 | - <Tooltip title="移除" class="ml-4"> | ||
18 | - <Icon | ||
19 | - icon="fluent:delete-off-20-regular" | ||
20 | - size="20" | ||
21 | - class="mr-2 cursor-pointer" | ||
22 | - @click="handleDelete(index)" | ||
23 | - v-if="clearRuleList.length > 1" | ||
24 | - /> | ||
25 | - </Tooltip> | ||
26 | - </div> | ||
27 | - </template> | ||
28 | - <BasicForm @register="registerForm"> | ||
29 | - <template #operationType="{ model, field }"> | ||
30 | - <Select | ||
31 | - :options="options" | ||
32 | - :disabled="disabled" | ||
33 | - v-model:value="model[field]" | ||
34 | - @change="operationType = model[field]" | ||
35 | - placeholder="请选择比较类型" | ||
36 | - allowClear | ||
37 | - /> | ||
38 | - </template> | ||
39 | - <template #time="{ model, field }"> | ||
40 | - <Input v-model:value="model[field]" placeholder="请输入持续时间"> | ||
41 | - <template #addonAfter> | ||
42 | - <Select | ||
43 | - :disabled="disabled" | ||
44 | - v-model:value="model[`timeUnit`]" | ||
45 | - :options="timeUnitOptions" | ||
46 | - style="width: 60px" | ||
47 | - /> | ||
48 | - </template> | ||
49 | - </Input> | ||
50 | - </template> | ||
51 | - </BasicForm> | ||
52 | - <Card size="small" :bordered="false" style="border: 2px dashed #d9d9d9" v-if="operationType"> | ||
53 | - <ConditionScreening | ||
54 | - :childGetFieldsValue="childGetFieldsValue" | ||
55 | - ref="conditionScreeningRef" | ||
56 | - /> | ||
57 | - </Card> | ||
58 | - </CollapseContainer> | ||
59 | - <AlarmSchedule ref="alarmScheduleRef" @register="registerModal" @cancel="handleCancel" /> | ||
60 | - </div> | ||
61 | -</template> | ||
62 | -<script lang="ts" setup> | ||
63 | - import { ref, provide, nextTick, unref } from 'vue'; | ||
64 | - import { CollapseContainer } from '/@/components/Container/index'; | ||
65 | - import { BasicForm, useForm } from '/@/components/Form/index'; | ||
66 | - import { Card, Select, Input, Tooltip } from 'ant-design-vue'; | ||
67 | - import { trigger_condition_schema, TOption } from '../config/config.data'; | ||
68 | - import { getAttribute } from '/@/api/ruleengine/ruleengineApi'; | ||
69 | - import ConditionScreening from './ConditionScreening.vue'; | ||
70 | - import { scheduleOptions, timeUnitOptions, options } from '../config/formatData'; | ||
71 | - import { Icon } from '/@/components/Icon'; | ||
72 | - import AlarmSchedule from './AlarmSchedule.vue'; | ||
73 | - import { useModal } from '/@/components/Modal'; | ||
74 | - import { cloneDeep } from 'lodash-es'; | ||
75 | - import useCommonFun from '../hooks/useCommonFun'; | ||
76 | - | ||
77 | - const { useByProductGetAttribute } = useCommonFun(); | ||
78 | - defineProps({ | ||
79 | - index: { | ||
80 | - type: Number, | ||
81 | - required: true, | ||
82 | - }, | ||
83 | - clearRuleList: { | ||
84 | - type: Array, | ||
85 | - default: () => [], | ||
86 | - }, | ||
87 | - }); | ||
88 | - const emit = defineEmits(['delete']); | ||
89 | - const isUpdate = ref(false); | ||
90 | - const conditionScreeningRef = ref(); | ||
91 | - const [registerForm, { resetFields, getFieldsValue, updateSchema, setFieldsValue, setProps }] = | ||
92 | - useForm({ | ||
93 | - //TODO-wenwei-修复 | ||
94 | - schemas: cloneDeep(trigger_condition_schema), | ||
95 | - //TODO-wenwei-修复 | ||
96 | - showActionButtonGroup: false, | ||
97 | - }); | ||
98 | - | ||
99 | - const alarmScheduleRef = ref<InstanceType<typeof AlarmSchedule>>(); | ||
100 | - const getFieldsValueFunc = () => { | ||
101 | - const predicate = conditionScreeningRef?.value?.refItem?.conditionScreeningRefs?.value?.map( | ||
102 | - (item) => item.getFieldsValue() | ||
103 | - ); | ||
104 | - return { ...getFieldsValue(), predicate, schedule: alarmScheduleRef.value.scheduleData }; | ||
105 | - }; | ||
106 | - | ||
107 | - const updateFieldDeviceId = () => { | ||
108 | - // console.log(deviceList); | ||
109 | - // console.log(isUpdate); | ||
110 | - }; | ||
111 | - | ||
112 | - const resetFieldsValueFunc = () => resetFields(); | ||
113 | - // 回显数据函数 | ||
114 | - const setFieldsFormValueFun = (fieldsValue) => { | ||
115 | - setFieldsValue(fieldsValue); | ||
116 | - }; | ||
117 | - const updateFieldAttributeFunc = async (e) => { | ||
118 | - const res = await getAttribute(e); | ||
119 | - const options = ref<TOption[]>([]); | ||
120 | - useByProductGetAttribute(res, updateSchema, options); | ||
121 | - }; | ||
122 | - //TODO-fengtao | ||
123 | - const schedule = ref('ANY_TIME'); | ||
124 | - const operationType = ref<string>(''); | ||
125 | - provide('operationType', operationType); | ||
126 | - | ||
127 | - const handleDelete = (index: number) => { | ||
128 | - emit('delete', index); | ||
129 | - }; | ||
130 | - | ||
131 | - // 子组件获取父组件的值 | ||
132 | - const childGetFieldsValue = () => getFieldsValue(); | ||
133 | - | ||
134 | - // 获取conditionScreeningForm的组件 | ||
135 | - const getRefItemConditionScreeningRefs = async () => { | ||
136 | - await nextTick(); | ||
137 | - return conditionScreeningRef.value.refItem.conditionScreeningRefs; | ||
138 | - }; | ||
139 | - | ||
140 | - const setConditionScreeningList = (list) => { | ||
141 | - conditionScreeningRef.value.conditionScreeningList = list; | ||
142 | - }; | ||
143 | - const setRichText = (list) => { | ||
144 | - conditionScreeningRef.value.otherAttribute = list; | ||
145 | - }; | ||
146 | - | ||
147 | - const currentIndex = ref(0); | ||
148 | - const [registerModal, { openModal }] = useModal(); | ||
149 | - const handleScheduleChange = (value) => { | ||
150 | - if (unref(disabled)) return; | ||
151 | - const index = scheduleOptions.findIndex((item) => item.value === value); | ||
152 | - // 报警日程弹窗 | ||
153 | - if (index !== 0) { | ||
154 | - openModal(true, { | ||
155 | - isUpdate: isUpdate.value, | ||
156 | - value, | ||
157 | - currentIndex: currentIndex.value, | ||
158 | - scheduleData, | ||
159 | - }); | ||
160 | - } else { | ||
161 | - alarmScheduleRef.value.scheduleData = { | ||
162 | - type: value, | ||
163 | - }; | ||
164 | - } | ||
165 | - currentIndex.value = index; | ||
166 | - }; | ||
167 | - const handleCancel = (index) => { | ||
168 | - currentIndex.value = index; | ||
169 | - }; | ||
170 | - const scheduleData = ref(null); | ||
171 | - | ||
172 | - const disabled = ref<boolean>(false); | ||
173 | - const setDisabledProps = (value) => { | ||
174 | - setProps(value); | ||
175 | - disabled.value = true; | ||
176 | - }; | ||
177 | - | ||
178 | - const setCancelDisabled = () => { | ||
179 | - disabled.value = false; | ||
180 | - }; | ||
181 | - defineExpose({ | ||
182 | - getFieldsValue, | ||
183 | - updateFieldDeviceId, | ||
184 | - resetFieldsValueFunc, | ||
185 | - setFieldsFormValueFun, | ||
186 | - childGetFieldsValue, | ||
187 | - conditionScreeningRef, | ||
188 | - schedule, | ||
189 | - operationType, | ||
190 | - getFieldsValueFunc, | ||
191 | - getRefItemConditionScreeningRefs, | ||
192 | - setConditionScreeningList, | ||
193 | - setRichText, | ||
194 | - currentIndex, | ||
195 | - scheduleData, | ||
196 | - isUpdate, | ||
197 | - alarmScheduleRef, | ||
198 | - updateFieldAttributeFunc, | ||
199 | - setDisabledProps, | ||
200 | - setCancelDisabled, | ||
201 | - }); | ||
202 | -</script> | ||
203 | -<style> | ||
204 | - .active { | ||
205 | - color: #377dff; | ||
206 | - } | ||
207 | -</style> |
src/views/rule/linkedge/cpns/ConditionScreening.vue
deleted
100644 → 0
1 | -<template> | ||
2 | - <div> | ||
3 | - <CollapseContainer ref="collapseContainerRef" @expand="handleExpand"> | ||
4 | - <template #title> | ||
5 | - <div>条件筛选</div> | ||
6 | - <RichText :otherAttribute="otherAttribute" @resetFilter="resetFilter" /> | ||
7 | - </template> | ||
8 | - | ||
9 | - <template v-for="(item, index) in conditionScreeningList" :key="item"> | ||
10 | - <ConditionScreeningForm | ||
11 | - :conditionScreeningList="conditionScreeningList" | ||
12 | - :ref="refItem.conditionScreeningRefs" | ||
13 | - :index="index" | ||
14 | - @deleteConditionForm="deleteConditionForm" | ||
15 | - /> | ||
16 | - </template> | ||
17 | - </CollapseContainer> | ||
18 | - | ||
19 | - <div class="flex justify-between"> | ||
20 | - <a-button | ||
21 | - :disabled="isViewDisabledBtn == 'isView' ? true : false" | ||
22 | - type="primary" | ||
23 | - class="mt-4 ml-2" | ||
24 | - @click="addConditionForm" | ||
25 | - >新增条件筛选</a-button | ||
26 | - > | ||
27 | - <a-button | ||
28 | - :disabled="isViewDisabledBtn == 'isView' ? true : false" | ||
29 | - type="primary" | ||
30 | - class="mt-4 mr-2" | ||
31 | - @click="preView" | ||
32 | - v-if="isPreview" | ||
33 | - >保存</a-button | ||
34 | - > | ||
35 | - </div> | ||
36 | - </div> | ||
37 | -</template> | ||
38 | - | ||
39 | -<script lang="ts" setup> | ||
40 | - import { unref, ref } from 'vue'; | ||
41 | - import ConditionScreeningForm from './ConditionScreeningForm.vue'; | ||
42 | - import { conditionPreView } from '../config/formatData.ts'; | ||
43 | - import { CollapseContainer } from '/@/components/Container/index'; | ||
44 | - import RichText from './RichText.vue'; | ||
45 | - const props = defineProps({ | ||
46 | - childGetFieldsValue: { | ||
47 | - type: Function, | ||
48 | - required: true, | ||
49 | - }, | ||
50 | - }); | ||
51 | - const refItem = { | ||
52 | - conditionScreeningRefs: ref([]), | ||
53 | - }; | ||
54 | - const isViewDisabledBtn = window.localStorage.getItem('isViewDisabledBtn'); | ||
55 | - const isPreview = ref(true); | ||
56 | - const collapseContainerRef = ref(); | ||
57 | - const conditionScreeningList = ref([Date.now()]); | ||
58 | - const addConditionForm = () => { | ||
59 | - if (!unref(isPreview)) { | ||
60 | - collapseContainerRef.value.handleExpand(); | ||
61 | - } | ||
62 | - unref(conditionScreeningList).push(Date.now()); | ||
63 | - const lastIndex = refItem.conditionScreeningRefs.value.length - 1; | ||
64 | - refItem.conditionScreeningRefs.value[lastIndex]?.appendSchemaByField( | ||
65 | - { | ||
66 | - field: 'AND', | ||
67 | - label: '和', | ||
68 | - component: 'Input', | ||
69 | - slot: 'and', | ||
70 | - // labelWidth: 50, | ||
71 | - colProps: { span: 3 }, | ||
72 | - }, | ||
73 | - 'value' | ||
74 | - ); | ||
75 | - }; | ||
76 | - const handleExpand = (show) => { | ||
77 | - isPreview.value = show; | ||
78 | - }; | ||
79 | - | ||
80 | - const otherAttribute = ref([]); | ||
81 | - // 预览条件筛选结果 | ||
82 | - const preView = async () => { | ||
83 | - const attributes = []; | ||
84 | - const fieldsValue = props.childGetFieldsValue(); | ||
85 | - for (let i = 0; i < unref(refItem.conditionScreeningRefs).length; i++) { | ||
86 | - const valid = await unref(refItem.conditionScreeningRefs)[i].validate(); | ||
87 | - if (!valid) return; | ||
88 | - attributes.push({ | ||
89 | - ...unref(refItem.conditionScreeningRefs)[i].getFieldsValue(), | ||
90 | - attribute: fieldsValue.type2, | ||
91 | - }); | ||
92 | - } | ||
93 | - otherAttribute.value = conditionPreView(attributes, fieldsValue.operationType); | ||
94 | - // collapseContainerRef.value.handleExpand(); | ||
95 | - collapseContainerRef.value.show = true; | ||
96 | - }; | ||
97 | - | ||
98 | - //ft add 表单数据置空 | ||
99 | - const resetConditionScreenForm = () => { | ||
100 | - conditionScreeningList.value = []; | ||
101 | - resetFilter(); | ||
102 | - for (let i = 0; i < unref(refItem.conditionScreeningRefs).length; i++) { | ||
103 | - unref(refItem.conditionScreeningRefs)[i]?.resetConditionScreenForm(); | ||
104 | - } | ||
105 | - }; | ||
106 | - //ft add 表单数据置空 | ||
107 | - | ||
108 | - const resetFilter = () => { | ||
109 | - otherAttribute.value = []; | ||
110 | - }; | ||
111 | - const deleteConditionForm = (index) => { | ||
112 | - unref(conditionScreeningList).splice(index, 1); | ||
113 | - const lastIndex = refItem.conditionScreeningRefs.value.length - 2; | ||
114 | - refItem.conditionScreeningRefs.value[lastIndex]?.removeSchemaByFiled('AND'); | ||
115 | - }; | ||
116 | - | ||
117 | - defineExpose({ | ||
118 | - refItem, | ||
119 | - conditionScreeningList, | ||
120 | - otherAttribute, | ||
121 | - resetConditionScreenForm, | ||
122 | - }); | ||
123 | -</script> | ||
124 | - | ||
125 | -<style lang="less" scoped> | ||
126 | - :deep(.vben-collapse-container__header) { | ||
127 | - overflow: hidden !important; | ||
128 | - overflow-y: scroll !important; | ||
129 | - white-space: pre !important; | ||
130 | - height: 93px !important; | ||
131 | - } | ||
132 | -</style> |
src/views/rule/linkedge/cpns/ConditionScreeningForm.vue
deleted
100644 → 0
1 | -<template> | ||
2 | - <div class="flex" style="align-items: center"> | ||
3 | - <BasicForm @register="registerFormCondition" class="basicStyle" /> | ||
4 | - <Tooltip title="移除" class="ml-4"> | ||
5 | - <Icon | ||
6 | - icon="fluent:delete-off-20-regular" | ||
7 | - size="20" | ||
8 | - class="mr-2 cursor-pointer" | ||
9 | - @click="deleteConditionForm(index)" | ||
10 | - /> | ||
11 | - </Tooltip> | ||
12 | - </div> | ||
13 | -</template> | ||
14 | - | ||
15 | -<script lang="ts" setup> | ||
16 | - import { watch, ref, onMounted, inject } from 'vue'; | ||
17 | - import { BasicForm, useForm } from '/@/components/Form/index'; | ||
18 | - import { Icon } from '/@/components/Icon'; | ||
19 | - import { Tooltip } from 'ant-design-vue'; | ||
20 | - import { isType } from '../config/formatData'; | ||
21 | - defineProps({ | ||
22 | - index: { | ||
23 | - type: Number, | ||
24 | - required: true, | ||
25 | - }, | ||
26 | - conditionScreeningList: { | ||
27 | - type: Array, | ||
28 | - default: () => [], | ||
29 | - }, | ||
30 | - }); | ||
31 | - const emit = defineEmits(['deleteConditionForm']); | ||
32 | - const operationType = inject('operationType'); | ||
33 | - let schemas = ref([]); | ||
34 | - const isViewDisabledBtn = window.localStorage.getItem('isViewDisabledBtn'); | ||
35 | - onMounted(() => { | ||
36 | - schemas.value = isType(operationType.value); | ||
37 | - | ||
38 | - if (isViewDisabledBtn == 'isView') setProps({ disabled: true }); | ||
39 | - }); | ||
40 | - watch(operationType, (newValue) => { | ||
41 | - schemas.value = isType(newValue); | ||
42 | - resetFields(); | ||
43 | - }); | ||
44 | - const [ | ||
45 | - registerFormCondition, | ||
46 | - { | ||
47 | - resetFields, | ||
48 | - appendSchemaByField, | ||
49 | - removeSchemaByFiled, | ||
50 | - getFieldsValue, | ||
51 | - setFieldsValue, | ||
52 | - validate, | ||
53 | - setProps, | ||
54 | - }, | ||
55 | - ] = useForm({ | ||
56 | - showActionButtonGroup: false, | ||
57 | - labelWidth: 100, | ||
58 | - compact: true, | ||
59 | - schemas, | ||
60 | - }); | ||
61 | - const deleteConditionForm = (index: number) => { | ||
62 | - if (isViewDisabledBtn == 'isView') return; | ||
63 | - emit('deleteConditionForm', index); | ||
64 | - }; | ||
65 | - // ft add | ||
66 | - const resetConditionScreenForm = () => resetFields(); | ||
67 | - // ft add | ||
68 | - defineExpose({ | ||
69 | - appendSchemaByField, | ||
70 | - removeSchemaByFiled, | ||
71 | - getFieldsValue, | ||
72 | - setFieldsValue, | ||
73 | - validate, | ||
74 | - resetConditionScreenForm, | ||
75 | - }); | ||
76 | -</script> | ||
77 | - | ||
78 | -<style lang="less"> | ||
79 | - .basicStyle { | ||
80 | - margin-top: 0.5rem; | ||
81 | - width: 80%; | ||
82 | - border: 1px dashed #d9d9d9; | ||
83 | - padding: 1rem 0 0.5rem; | ||
84 | - border-radius: 0.25rem; | ||
85 | - } | ||
86 | -</style> |
src/views/rule/linkedge/cpns/RichText.vue
deleted
100644 → 0
1 | -<template> | ||
2 | - <div | ||
3 | - style="width: 100%; flex-wrap: wrap" | ||
4 | - v-if="otherAttribute.length > 0 && otherAttribute[0].value && otherAttribute[0].operation" | ||
5 | - class="flex" | ||
6 | - > | ||
7 | - <template v-for="(item, index) in otherAttribute" :key="item.value"> | ||
8 | - <div class="flex" v-if="item?.value && item?.attribute"> | ||
9 | - <div class="text mr-2" style="color: #305680">{{ item.attribute }}</div> | ||
10 | - {{ item.operation }} | ||
11 | - <div class="text ml-2" style="color: #ff8c68">{{ item.value }}</div> | ||
12 | - <span v-if="otherAttribute[index + 1]">和</span> | ||
13 | - </div> | ||
14 | - </template> | ||
15 | - </div> | ||
16 | -</template> | ||
17 | - | ||
18 | -<script lang="ts" setup> | ||
19 | - import { watch, inject } from 'vue'; | ||
20 | - defineProps({ | ||
21 | - otherAttribute: { | ||
22 | - type: Array, | ||
23 | - default: () => [], | ||
24 | - }, | ||
25 | - }); | ||
26 | - const emit = defineEmits(['resetFilter']); | ||
27 | - const operationType = inject('operationType'); | ||
28 | - watch(operationType, () => { | ||
29 | - emit('resetFilter'); | ||
30 | - }); | ||
31 | -</script> | ||
32 | - | ||
33 | -<style scoped> | ||
34 | - .text { | ||
35 | - border: 1px solid #d2d2d2; | ||
36 | - border-radius: 5px; | ||
37 | - padding: 0 5px; | ||
38 | - margin: 0 5px; | ||
39 | - } | ||
40 | -</style> |
src/views/rule/linkedge/cpns/Trigger-Condition.vue
deleted
100644 → 0
1 | -<template> | ||
2 | - <div> | ||
3 | - <CollapseContainer :title="`${title} ${index + 1}`"> | ||
4 | - <template #action> | ||
5 | - <div class="flex"> | ||
6 | - <div class="flex"> | ||
7 | - <span class="mr-2">启用规则:</span> | ||
8 | - <template v-for="(item, scheduleIndex) in scheduleOptions" :key="item.label"> | ||
9 | - <div | ||
10 | - :class="{ 'ml-4': scheduleIndex >= 1, active: scheduleIndex === currentIndex }" | ||
11 | - class="cursor-pointer !p-0" | ||
12 | - :disabled="disabled" | ||
13 | - @click="handleScheduleChange(item.value)" | ||
14 | - >{{ item.label }}</div | ||
15 | - > | ||
16 | - </template> | ||
17 | - </div> | ||
18 | - <Tooltip title="移除" class="ml-4"> | ||
19 | - <Icon | ||
20 | - icon="fluent:delete-off-20-regular" | ||
21 | - size="20" | ||
22 | - class="mr-2 cursor-pointer" | ||
23 | - @click="handleDelete({ index, title })" | ||
24 | - /> | ||
25 | - </Tooltip> | ||
26 | - </div> | ||
27 | - </template> | ||
28 | - <BasicForm @register="registerForm"> | ||
29 | - <template #operationType="{ model, field }"> | ||
30 | - <Select | ||
31 | - :options="options" | ||
32 | - :disabled="disabled" | ||
33 | - v-model:value="model[field]" | ||
34 | - :getPopupContainer="(triggerNode) => triggerNode.parentNode" | ||
35 | - @change="operationType = model[field]" | ||
36 | - placeholder="请选择比较类型" | ||
37 | - allowClear | ||
38 | - /> | ||
39 | - </template> | ||
40 | - <template #time="{ model, field }"> | ||
41 | - <Input v-model:value="model[field]" placeholder="请输入持续时间"> | ||
42 | - <template #addonAfter> | ||
43 | - <Select | ||
44 | - :disabled="disabled" | ||
45 | - v-model:value="model[`timeUnit`]" | ||
46 | - :options="timeUnitOptions" | ||
47 | - style="width: 60px" | ||
48 | - /> | ||
49 | - </template> | ||
50 | - </Input> | ||
51 | - </template> | ||
52 | - </BasicForm> | ||
53 | - <Card size="small" :bordered="false" style="border: 2px dashed #d9d9d9" v-if="operationType"> | ||
54 | - <ConditionScreening | ||
55 | - :childGetFieldsValue="childGetFieldsValue" | ||
56 | - ref="conditionScreeningRef" | ||
57 | - /> | ||
58 | - </Card> | ||
59 | - </CollapseContainer> | ||
60 | - <AlarmSchedule ref="alarmScheduleRef" @register="registerModal" @cancel="handleCancel" /> | ||
61 | - </div> | ||
62 | -</template> | ||
63 | -<script lang="ts" setup> | ||
64 | - import { Card, Input, Select, Tooltip } from 'ant-design-vue'; | ||
65 | - import { cloneDeep } from 'lodash-es'; | ||
66 | - import { nextTick, provide, ref } from 'vue'; | ||
67 | - import { TOption, trigger_condition_schema } from '../config/config.data'; | ||
68 | - import { options, scheduleOptions, timeUnitOptions } from '../config/formatData'; | ||
69 | - import useCommonFun from '../hooks/useCommonFun'; | ||
70 | - import AlarmSchedule from './AlarmSchedule.vue'; | ||
71 | - import ConditionScreening from './ConditionScreening.vue'; | ||
72 | - import { getAttribute } from '/@/api/ruleengine/ruleengineApi'; | ||
73 | - import { CollapseContainer } from '/@/components/Container/index'; | ||
74 | - import { BasicForm, useForm } from '/@/components/Form/index'; | ||
75 | - import { Icon } from '/@/components/Icon'; | ||
76 | - import { useModal } from '/@/components/Modal'; | ||
77 | - import { useMessage } from '/@/hooks/web/useMessage'; | ||
78 | - import { unref } from 'vue'; | ||
79 | - | ||
80 | - const { useByProductGetAttribute } = useCommonFun(); | ||
81 | - defineProps({ | ||
82 | - title: { | ||
83 | - type: String, | ||
84 | - required: true, | ||
85 | - }, | ||
86 | - index: { | ||
87 | - type: Number, | ||
88 | - required: true, | ||
89 | - }, | ||
90 | - provideOrgid: { | ||
91 | - type: String, | ||
92 | - default: '', | ||
93 | - }, | ||
94 | - }); | ||
95 | - const emit = defineEmits(['delete']); | ||
96 | - const { createMessage } = useMessage(); | ||
97 | - | ||
98 | - const isUpdate = ref(false); | ||
99 | - const conditionScreeningRef = ref(); | ||
100 | - const [registerForm, { resetFields, getFieldsValue, updateSchema, setFieldsValue, setProps }] = | ||
101 | - useForm({ | ||
102 | - //TODO-wenwei-修复 | ||
103 | - schemas: cloneDeep(trigger_condition_schema), | ||
104 | - //TODO-wenwei-修复 | ||
105 | - showActionButtonGroup: false, | ||
106 | - }); | ||
107 | - | ||
108 | - const alarmScheduleRef = ref<InstanceType<typeof AlarmSchedule>>(); | ||
109 | - | ||
110 | - //ft-add | ||
111 | - const resetConditionForm = () => { | ||
112 | - conditionScreeningRef?.value?.resetConditionScreenForm(); | ||
113 | - }; | ||
114 | - //ft-add | ||
115 | - | ||
116 | - const getFieldsValueFunc = () => { | ||
117 | - const predicate = conditionScreeningRef?.value?.refItem?.conditionScreeningRefs?.value?.map( | ||
118 | - (item) => item.getFieldsValue() | ||
119 | - ); | ||
120 | - //TODO-fengtao-设备、属性、条件筛选验证 | ||
121 | - const validate = getFieldsValue(); | ||
122 | - if (validate.deviceProfileId === undefined || !validate.deviceProfileId) { | ||
123 | - createMessage.error('请选择产品'); | ||
124 | - throw '请选择产品'; | ||
125 | - } | ||
126 | - if (validate.triggerType == undefined) return createMessage.error('请选择设备触发方式'); | ||
127 | - if (validate.type1 == undefined) return createMessage.error('请选择属性触发方式'); | ||
128 | - if (!validate.deviceType) return createMessage.error('请选择设备类型'); | ||
129 | - if (validate.device == undefined) return createMessage.error('请选择设备'); | ||
130 | - if (validate.device == 'PART') { | ||
131 | - if (validate.entityId == undefined) return createMessage.error('请选择设备'); | ||
132 | - } | ||
133 | - if (validate.type2 == '' || validate.type2 == null) return createMessage.error('请选择属性'); | ||
134 | - if (predicate == undefined) return createMessage.error('请填写条件筛选'); | ||
135 | - let predicateIsRequired = false; | ||
136 | - if (predicate) { | ||
137 | - predicate.some((f) => { | ||
138 | - let arr = Object.keys(f); | ||
139 | - if (arr.length == 0) { | ||
140 | - predicateIsRequired = true; | ||
141 | - } | ||
142 | - if (f.operation == undefined) { | ||
143 | - predicateIsRequired = true; | ||
144 | - } | ||
145 | - if (f.value == undefined) { | ||
146 | - predicateIsRequired = true; | ||
147 | - } | ||
148 | - }); | ||
149 | - } | ||
150 | - if (predicateIsRequired) return createMessage.error('请填写条件筛选'); | ||
151 | - //TODO-fengtao-设备、属性、条件筛选验证 | ||
152 | - return { ...getFieldsValue(), predicate, schedule: alarmScheduleRef.value?.scheduleData }; | ||
153 | - }; | ||
154 | - | ||
155 | - //TODO-fengtao | ||
156 | - const updateFieldDeviceId = (_deviceList: any[], _, _isUpdate) => { | ||
157 | - // console.log(deviceList); | ||
158 | - // console.log(isUpdate); | ||
159 | - }; | ||
160 | - //TODO-fengtao | ||
161 | - const resetFieldsValueFunc = () => { | ||
162 | - resetConditionForm(); | ||
163 | - resetFields(); | ||
164 | - }; | ||
165 | - // 回显数据函数 | ||
166 | - const setFieldsFormValueFun = (fieldsValue) => { | ||
167 | - setFieldsValue(fieldsValue); | ||
168 | - }; | ||
169 | - //TODO-fengtao | ||
170 | - const updateFieldAttributeFunc = async (e) => { | ||
171 | - const res = await getAttribute(e); | ||
172 | - const options = ref<TOption[]>([]); | ||
173 | - useByProductGetAttribute(res, updateSchema, options); | ||
174 | - }; | ||
175 | - //TODO-fengtao | ||
176 | - const handleDelete = (params: { index: number; title: string }) => { | ||
177 | - if (unref(disabled)) return; | ||
178 | - emit('delete', params); | ||
179 | - }; | ||
180 | - const operationType = ref<string>(''); | ||
181 | - provide('operationType', operationType); | ||
182 | - | ||
183 | - // 子组件获取父组件的值 | ||
184 | - const childGetFieldsValue = () => getFieldsValue(); | ||
185 | - | ||
186 | - // 获取conditionScreeningForm的组件 | ||
187 | - const getRefItemConditionScreeningRefs = async () => { | ||
188 | - await nextTick(); | ||
189 | - return conditionScreeningRef.value.refItem.conditionScreeningRefs; | ||
190 | - }; | ||
191 | - | ||
192 | - const setConditionScreeningList = (list) => { | ||
193 | - conditionScreeningRef.value.conditionScreeningList = list; | ||
194 | - }; | ||
195 | - const setRichText = (list) => { | ||
196 | - conditionScreeningRef.value.otherAttribute = list; | ||
197 | - }; | ||
198 | - | ||
199 | - const [registerModal, { openModal }] = useModal(); | ||
200 | - const currentIndex = ref(0); | ||
201 | - const handleScheduleChange = (value) => { | ||
202 | - if (unref(disabled)) return; | ||
203 | - const index = scheduleOptions.findIndex((item) => item.value === value); | ||
204 | - // 报警日程弹窗 | ||
205 | - if (index !== 0) { | ||
206 | - openModal(true, { | ||
207 | - isUpdate: isUpdate.value, | ||
208 | - value, | ||
209 | - currentIndex: currentIndex.value, | ||
210 | - scheduleData, | ||
211 | - }); | ||
212 | - } else { | ||
213 | - alarmScheduleRef.value.scheduleData = { | ||
214 | - type: value, | ||
215 | - }; | ||
216 | - } | ||
217 | - currentIndex.value = index; | ||
218 | - }; | ||
219 | - const handleCancel = (index) => { | ||
220 | - currentIndex.value = index; | ||
221 | - }; | ||
222 | - const scheduleData = ref(null); | ||
223 | - //FT add 2022-10-27 | ||
224 | - const updateFieldAlarmConfig = () => { | ||
225 | - //什么也不做 | ||
226 | - // console.log(alarmConfigList); | ||
227 | - }; | ||
228 | - //FT add 2022-10-27 | ||
229 | - const updateEditFieldAlarmConfig = () => { | ||
230 | - //什么也不做 | ||
231 | - // console.log(alarmConfigList); | ||
232 | - }; | ||
233 | - | ||
234 | - const disabled = ref<boolean>(false); | ||
235 | - const setDisabledProps = (value) => { | ||
236 | - setProps(value); | ||
237 | - disabled.value = true; | ||
238 | - }; | ||
239 | - | ||
240 | - const setCancelDisabled = () => { | ||
241 | - disabled.value = false; | ||
242 | - }; | ||
243 | - | ||
244 | - defineExpose({ | ||
245 | - getFieldsValueFunc, | ||
246 | - updateFieldDeviceId, | ||
247 | - resetFieldsValueFunc, | ||
248 | - setFieldsFormValueFun, | ||
249 | - childGetFieldsValue, | ||
250 | - operationType, | ||
251 | - getRefItemConditionScreeningRefs, | ||
252 | - setConditionScreeningList, | ||
253 | - setRichText, | ||
254 | - currentIndex, | ||
255 | - scheduleData, | ||
256 | - isUpdate, | ||
257 | - alarmScheduleRef, | ||
258 | - updateFieldAttributeFunc, | ||
259 | - updateFieldAlarmConfig, | ||
260 | - updateEditFieldAlarmConfig, | ||
261 | - setDisabledProps, | ||
262 | - setCancelDisabled, | ||
263 | - }); | ||
264 | -</script> | ||
265 | - | ||
266 | -<style lang="less" scoped> | ||
267 | - ///移除选择框默认样式(24px)否则超出默认宽度会造成页面样式错乱 | ||
268 | - :deep(.ant-select-selector) { | ||
269 | - padding-right: 0 !important; | ||
270 | - } | ||
271 | -</style> | ||
272 | - | ||
273 | -<style> | ||
274 | - .active { | ||
275 | - color: #377dff; | ||
276 | - } | ||
277 | -</style> |
src/views/rule/linkedge/cpns/action.vue
deleted
100644 → 0
1 | -<template> | ||
2 | - <CollapseContainer :title="`执行动作 ${actionIndex + 1}`"> | ||
3 | - <template #action> | ||
4 | - <Tooltip title="移除" v-if="actionData.length > 1"> | ||
5 | - <Icon | ||
6 | - icon="fluent:delete-off-20-regular" | ||
7 | - size="20" | ||
8 | - class="mr-2 cursor-pointer" | ||
9 | - @click="handleDelete(actionIndex)" | ||
10 | - /> | ||
11 | - </Tooltip> | ||
12 | - </template> | ||
13 | - <BasicForm @register="registerAction"> | ||
14 | - <template #outTarget="{ model, field }"> | ||
15 | - <Select | ||
16 | - :disabled="disabled" | ||
17 | - :options="options" | ||
18 | - v-model:value="model[field]" | ||
19 | - @change="changeOutTarget" | ||
20 | - @dropdown-visible-change="handleDropdownVisibleChange" | ||
21 | - placeholder="请选择执行动作" | ||
22 | - allowClear | ||
23 | - /> | ||
24 | - </template> | ||
25 | - <template #alarmConfigSlot="{ model, field }"> | ||
26 | - <a-select | ||
27 | - allowClear | ||
28 | - :disabled="disabled" | ||
29 | - placeholder="请选择告警配置" | ||
30 | - v-model:value="model[field]" | ||
31 | - style="width: 205px; margin-left: 10px" | ||
32 | - :options="alarmConfigOptions.map((item) => ({ value: item.value, label: item.label }))" | ||
33 | - > | ||
34 | - <template #dropdownRender="{ menuNode: menu }"> | ||
35 | - <v-nodes :vnodes="menu" /> | ||
36 | - <a-divider style="margin: 4px 0" /> | ||
37 | - <div @click="handleOpenAlarmConfig" style="padding: 4px 0; cursor: pointer"> | ||
38 | - <plus-outlined /> | ||
39 | - 新增告警配置 | ||
40 | - </div> | ||
41 | - </template> | ||
42 | - </a-select> | ||
43 | - </template> | ||
44 | - <template #doContext="{ model, field }"> | ||
45 | - <div v-if="model['transportType'] === TransportTypeEnum.TCP"> | ||
46 | - <Input v-model:value="model[field]" :disabled="disabled" placeholder="请输入自定义命令" /> | ||
47 | - </div> | ||
48 | - <div | ||
49 | - v-show="model['transportType'] !== TransportTypeEnum.TCP" | ||
50 | - class="flex" | ||
51 | - style="align-items: center" | ||
52 | - > | ||
53 | - <div v-show="!disabled" ref="jsoneditorRef" style="height: 100%; width: 100%"></div> | ||
54 | - | ||
55 | - <a-textarea | ||
56 | - v-show="disabled" | ||
57 | - :disabled="true" | ||
58 | - :value="JSON.stringify(disabledValue)" | ||
59 | - :rows="4" | ||
60 | - /> | ||
61 | - | ||
62 | - <a-button style="margin: -5px 0" type="text" @click="handlePremitter">格式化</a-button> | ||
63 | - <Tooltip | ||
64 | - :title=" | ||
65 | - JSON.stringify({ | ||
66 | - method: 'setDOValue', | ||
67 | - params: { devID: '492S211218028819', data: { DO1: 1 } }, | ||
68 | - }) | ||
69 | - " | ||
70 | - class="ml-2" | ||
71 | - > | ||
72 | - <QuestionCircleOutlined style="font-size: 1rem" /> | ||
73 | - </Tooltip> | ||
74 | - </div> | ||
75 | - </template> | ||
76 | - <template #clearAlarm> | ||
77 | - <Checkbox v-model:checked="checked" :disabled="disabled" @change="handleCheckedChange"> | ||
78 | - 清除告警 | ||
79 | - </Checkbox> | ||
80 | - <Tooltip title="清除告警与触发器一一对应"> | ||
81 | - <QuestionCircleOutlined /> | ||
82 | - </Tooltip> | ||
83 | - </template> | ||
84 | - </BasicForm> | ||
85 | - <template v-for="(item, index) in clearRuleList" :key="item"> | ||
86 | - <Card | ||
87 | - :bordered="false" | ||
88 | - style="border: 2px dashed #d9d9d9; margin-top: 1rem" | ||
89 | - :bodyStyle="{ padding: 0 }" | ||
90 | - > | ||
91 | - <ClearAlarm | ||
92 | - :clearRuleList="clearRuleList" | ||
93 | - :index="index" | ||
94 | - :ref="refItem.clearRuleRefs" | ||
95 | - @delete="deleteClearRule" | ||
96 | - /> | ||
97 | - </Card> | ||
98 | - </template> | ||
99 | - | ||
100 | - <a-button v-if="checked && isAddClearRule" class="mt-4" type="primary" @click="addClearRule" | ||
101 | - >新增清除告警</a-button | ||
102 | - > | ||
103 | - </CollapseContainer> | ||
104 | - <AlarmConfigDrawer | ||
105 | - :defaultEnable="true" | ||
106 | - @register="registerAlarmContactDrawer" | ||
107 | - @success="handleSuccess" | ||
108 | - /> | ||
109 | -</template> | ||
110 | - | ||
111 | -<script lang="ts"> | ||
112 | - import { defineComponent } from 'vue'; | ||
113 | - import { isNullOrUnDef, isNumber, isObject, isString } from '/@/utils/is'; | ||
114 | - import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const'; | ||
115 | - export default defineComponent({ | ||
116 | - components: { | ||
117 | - VNodes: (_, { attrs }) => { | ||
118 | - return attrs.vnodes; | ||
119 | - }, | ||
120 | - Input, | ||
121 | - }, | ||
122 | - inheritAttrs: false, | ||
123 | - }); | ||
124 | -</script> | ||
125 | -<script lang="ts" setup> | ||
126 | - import { ref, onMounted, nextTick, unref, computed, provide } from 'vue'; | ||
127 | - import { CollapseContainer } from '/@/components/Container/index'; | ||
128 | - import { BasicForm, useForm } from '/@/components/Form/index'; | ||
129 | - import { Tooltip, Select, Checkbox, Card, Input, message } from 'ant-design-vue'; | ||
130 | - import { Icon } from '/@/components/Icon'; | ||
131 | - import { actionSchema, CommandTypeEnum } from '../config/config.data'; | ||
132 | - import jsoneditor from 'jsoneditor'; | ||
133 | - import 'jsoneditor/dist/jsoneditor.min.css'; | ||
134 | - import { QuestionCircleOutlined, PlusOutlined } from '@ant-design/icons-vue'; | ||
135 | - import ClearAlarm from './ClearAlarm.vue'; | ||
136 | - import { useMessage } from '/@/hooks/web/useMessage'; | ||
137 | - import { useDrawer } from '/@/components/Drawer'; | ||
138 | - import AlarmConfigDrawer from '/@/views/alarm/config/ContactDrawer.vue'; | ||
139 | - | ||
140 | - const props = defineProps({ | ||
141 | - actionIndex: { | ||
142 | - type: Number, | ||
143 | - required: true, | ||
144 | - }, | ||
145 | - actionData: { | ||
146 | - type: Array, | ||
147 | - default: () => [], | ||
148 | - }, | ||
149 | - triggerData: { | ||
150 | - type: Array, | ||
151 | - default: () => [], | ||
152 | - }, | ||
153 | - deviceList: { | ||
154 | - type: Array, | ||
155 | - required: true, | ||
156 | - }, | ||
157 | - arr: { | ||
158 | - type: Array, | ||
159 | - default: () => [], | ||
160 | - }, | ||
161 | - provideOrgid: { | ||
162 | - type: String, | ||
163 | - default: '', | ||
164 | - }, | ||
165 | - }); | ||
166 | - //新增代码2022-11-23 | ||
167 | - const handlePremitter = () => { | ||
168 | - const value = unref(jsonInstance).get(); | ||
169 | - if (!value) return; | ||
170 | - return unref(jsonInstance).set(value); | ||
171 | - }; | ||
172 | - //新增代码2022-11-23 | ||
173 | - const alarmConfigOptions: any = ref([]); | ||
174 | - const [registerAlarmContactDrawer, { openDrawer }] = useDrawer(); | ||
175 | - async function handleSuccess() { | ||
176 | - emit('dynamicChangeAlarmConfig'); | ||
177 | - } | ||
178 | - // 新增或编辑 | ||
179 | - const handleOpenAlarmConfig = () => { | ||
180 | - openDrawer(true, { | ||
181 | - isUpdate: false, | ||
182 | - }); | ||
183 | - }; | ||
184 | - const isAddClearRule = computed(() => clearRuleList.value.length < props.triggerData.length); | ||
185 | - const refItem = { | ||
186 | - clearRuleRefs: ref([]), | ||
187 | - }; | ||
188 | - const { createMessage } = useMessage(); | ||
189 | - | ||
190 | - const emit = defineEmits(['deleteAction', 'getActionFormArr', 'dynamicChangeAlarmConfig']); | ||
191 | - const options = computed(() => { | ||
192 | - return [ | ||
193 | - { label: '设备输出', value: 'DEVICE_OUT' }, | ||
194 | - { | ||
195 | - label: '告警输出', | ||
196 | - value: 'MSG_NOTIFY', | ||
197 | - disabled: | ||
198 | - props.arr.filter((item) => item.outTarget === 'MSG_NOTIFY').length || | ||
199 | - !props.triggerData.length, | ||
200 | - }, | ||
201 | - ]; | ||
202 | - }); | ||
203 | - | ||
204 | - const changeOutTarget = (e) => { | ||
205 | - if (!e) validateFields(['outTarget']); | ||
206 | - else clearValidate('outTarget'); | ||
207 | - emit('getActionFormArr'); | ||
208 | - }; | ||
209 | - const [ | ||
210 | - registerAction, | ||
211 | - { | ||
212 | - getFieldsValue, | ||
213 | - resetFields, | ||
214 | - setFieldsValue, | ||
215 | - validate, | ||
216 | - clearValidate, | ||
217 | - validateFields, | ||
218 | - setProps, | ||
219 | - }, | ||
220 | - ] = useForm({ | ||
221 | - schemas: actionSchema, | ||
222 | - showActionButtonGroup: false, | ||
223 | - }); | ||
224 | - | ||
225 | - // 获取整个执行动作表单值 | ||
226 | - const getFieldsValueFunc = () => { | ||
227 | - const clearRule = refItem.clearRuleRefs.value.map((item) => { | ||
228 | - const predicate = item.conditionScreeningRef?.refItem?.conditionScreeningRefs?.value?.map( | ||
229 | - (item) => item.getFieldsValue() | ||
230 | - ); | ||
231 | - return { | ||
232 | - ...item.getFieldsValue(), | ||
233 | - predicate, | ||
234 | - schedule: item.alarmScheduleRef.scheduleData, | ||
235 | - }; | ||
236 | - }); | ||
237 | - //TODO-fengtao-清除告警验证 | ||
238 | - let productIdIsRequired = false; | ||
239 | - let deviceIdIsRequired = false; | ||
240 | - let attrIsRequired = false; | ||
241 | - let predicateIsRequired = false; | ||
242 | - let predicateDoubleIsRequired = false; | ||
243 | - let triggerTypeIsRequired = false; | ||
244 | - let type1IsRequired = false; | ||
245 | - if (clearRule) { | ||
246 | - clearRule.some((s) => { | ||
247 | - if (s.device == 'PART') { | ||
248 | - if (s.entityId == undefined) { | ||
249 | - deviceIdIsRequired = true; | ||
250 | - } | ||
251 | - } | ||
252 | - if (s.deviceProfileId === undefined) { | ||
253 | - productIdIsRequired = true; | ||
254 | - } | ||
255 | - if (s.type2 == '' || s.type2 == null) { | ||
256 | - attrIsRequired = true; | ||
257 | - } | ||
258 | - if (s.type1 == '' || s.type1 == null) { | ||
259 | - type1IsRequired = true; | ||
260 | - } | ||
261 | - if (s.triggerType == '' || s.triggerType == null || s.triggerType == undefined) { | ||
262 | - triggerTypeIsRequired = true; | ||
263 | - } | ||
264 | - if (s.predicate == undefined) { | ||
265 | - predicateIsRequired = true; | ||
266 | - } | ||
267 | - if (s?.predicate) { | ||
268 | - s?.predicate.some((f) => { | ||
269 | - let arr = Object.keys(f); | ||
270 | - if (arr.length == 0) { | ||
271 | - predicateDoubleIsRequired = true; | ||
272 | - } | ||
273 | - if (f.operation == undefined) { | ||
274 | - predicateDoubleIsRequired = true; | ||
275 | - } | ||
276 | - if (f.value == undefined) { | ||
277 | - predicateDoubleIsRequired = true; | ||
278 | - } | ||
279 | - }); | ||
280 | - } | ||
281 | - }); | ||
282 | - } | ||
283 | - if (productIdIsRequired) return createMessage.error('请选择产品'); | ||
284 | - if (deviceIdIsRequired) return createMessage.error('请选择设备'); | ||
285 | - if (attrIsRequired) return createMessage.error('请选择属性'); | ||
286 | - if (predicateIsRequired) return createMessage.error('请填写条件筛选'); | ||
287 | - if (predicateDoubleIsRequired) return createMessage.error('请填写条件筛选'); | ||
288 | - if (triggerTypeIsRequired) return createMessage.error('请选择设备触发方式'); | ||
289 | - if (type1IsRequired) return createMessage.error('请选择属性触发方式'); | ||
290 | - //TODO-fengtao-清除告警验证 | ||
291 | - //TODO-fengtao-设备验证 | ||
292 | - const validate = getFieldsValue(); | ||
293 | - //ft-add-2022-11-22 | ||
294 | - const type = validate?.outTarget; | ||
295 | - if (type === 'DEVICE_OUT') { | ||
296 | - if (!validate.deviceType) return createMessage.error('请选择类型'); | ||
297 | - if (!validate.deviceProfileId) return createMessage.error('请选择产品'); | ||
298 | - if (validate.device == 'PART' && validate.deviceId == undefined) | ||
299 | - return createMessage.error('请选择设备'); | ||
300 | - } | ||
301 | - if (type === 'MSG_NOTIFY') { | ||
302 | - if (!validate.alarm_config) return createMessage.error('请选择告警配置'); | ||
303 | - if (!validate.alarm_level) return createMessage.error('请选择告警等级'); | ||
304 | - } | ||
305 | - //ft-add-2022-11-22 | ||
306 | - //TODO-fengtao-设备验证 | ||
307 | - const value = getFieldsValue(); | ||
308 | - const isTCPTransportType = value.transportType === TransportTypeEnum.TCP; | ||
309 | - const doContext = unref(jsonInstance)?.get() || {}; | ||
310 | - const serviceInputValue = Reflect.get(value, 'serviceInputValue') || {}; | ||
311 | - | ||
312 | - const getServiceIdentifier = Reflect.get(value, 'serviceIdentifier'); | ||
313 | - | ||
314 | - const isCustomCommand = Number(value.commandType) === CommandTypeEnum.CUSTOM; | ||
315 | - | ||
316 | - const thingsModelKeys = Reflect.get(value, 'thingsModelKeys') || []; | ||
317 | - | ||
318 | - if (!isCustomCommand) { | ||
319 | - for (const key of thingsModelKeys) { | ||
320 | - if (isNullOrUnDef((serviceInputValue || {})[key]) || '') { | ||
321 | - message.warning('服务命令为必填项'); | ||
322 | - throw ''; | ||
323 | - } | ||
324 | - } | ||
325 | - } | ||
326 | - | ||
327 | - return { | ||
328 | - ...value, | ||
329 | - ...(isCustomCommand | ||
330 | - ? { doContext: isTCPTransportType ? value.doContext : doContext } | ||
331 | - : { | ||
332 | - doContext: isTCPTransportType | ||
333 | - ? value.serviceIdentifier | ||
334 | - : { [getServiceIdentifier]: serviceInputValue }, | ||
335 | - }), | ||
336 | - clearRule, | ||
337 | - }; | ||
338 | - }; | ||
339 | - | ||
340 | - const setFieldsFormValueFun = (fieldsValue) => { | ||
341 | - const doContext = Reflect.get(fieldsValue, 'doContext'); | ||
342 | - const commandType = Reflect.get(fieldsValue, 'commandType'); | ||
343 | - const getServiceIdentifier = Object.keys(doContext?.params || {})[0]; | ||
344 | - | ||
345 | - setFieldsValue({ | ||
346 | - ...fieldsValue, | ||
347 | - ...(isNumber(fieldsValue.commandType) | ||
348 | - ? { commandType: String(fieldsValue.commandType) } | ||
349 | - : {}), | ||
350 | - ...(commandType === CommandTypeEnum.SERVICE | ||
351 | - ? { | ||
352 | - serviceInputValue: isObject(doContext.params) | ||
353 | - ? doContext?.params?.[getServiceIdentifier] || {} | ||
354 | - : {}, | ||
355 | - tcpServiceCommand: isString(doContext.params) ? doContext.params : '', | ||
356 | - } | ||
357 | - : { doContext: doContext.params }), | ||
358 | - }); | ||
359 | - }; | ||
360 | - //ft-add | ||
361 | - const resetConditionForm = () => {}; | ||
362 | - | ||
363 | - //ft-add | ||
364 | - const resetFieldsValueFunc = () => { | ||
365 | - resetFields(); | ||
366 | - resetConditionForm(); | ||
367 | - }; | ||
368 | - | ||
369 | - const orgIdItem = ref(''); | ||
370 | - const isUpdateItem = ref(''); | ||
371 | - const deviceListItem = ref([]); | ||
372 | - | ||
373 | - //update 2022-11-28 | ||
374 | - const updateFieldDeviceId = (deviceList, orgId, isUpdate) => { | ||
375 | - if (isUpdate.value) { | ||
376 | - orgIdItem.value = orgId.value; | ||
377 | - } | ||
378 | - orgIdItem.value = orgId.value; | ||
379 | - isUpdateItem.value = isUpdate.value; | ||
380 | - deviceListItem.value = deviceList.value; | ||
381 | - }; | ||
382 | - //FT add 2022-10-27 | ||
383 | - const updateEditFieldAlarmConfig = (alarmConfigList) => { | ||
384 | - alarmConfigOptions.value = alarmConfigList.value.filter((item) => item.status); | ||
385 | - }; | ||
386 | - const updateFieldAlarmConfig = (alarmConfigList) => { | ||
387 | - alarmConfigOptions.value = alarmConfigList.value.filter((item) => item.status); | ||
388 | - }; | ||
389 | - //FT add 2022-10-27 | ||
390 | - | ||
391 | - const checked = ref(false); | ||
392 | - const validateForm = () => { | ||
393 | - return validate(); | ||
394 | - }; | ||
395 | - | ||
396 | - const handleDelete = (actionIndex) => { | ||
397 | - if (unref(disabled)) return; | ||
398 | - emit('deleteAction', actionIndex); | ||
399 | - }; | ||
400 | - | ||
401 | - const clearRuleList = ref([]); | ||
402 | - const handleCheckedChange = (e: Event) => { | ||
403 | - if (!e.target.checked) { | ||
404 | - clearRuleList.value = []; | ||
405 | - } else { | ||
406 | - addClearRule(); | ||
407 | - } | ||
408 | - }; | ||
409 | - const addClearRule = () => { | ||
410 | - unref(clearRuleList).push(Date.now()); | ||
411 | - nextTick(() => { | ||
412 | - setFields(refItem.clearRuleRefs); | ||
413 | - }); | ||
414 | - }; | ||
415 | - | ||
416 | - function setFields(refs) { | ||
417 | - unref(refs).map((item) => { | ||
418 | - item.updateFieldDeviceId( | ||
419 | - deviceListItem.value, | ||
420 | - props.provideOrgid || orgIdItem.value, | ||
421 | - isUpdateItem.value | ||
422 | - ); | ||
423 | - }); | ||
424 | - } | ||
425 | - const deleteClearRule = (index) => { | ||
426 | - unref(clearRuleList).splice(index, 1); | ||
427 | - }; | ||
428 | - | ||
429 | - // json 以及初始化JSON | ||
430 | - const jsoneditorRef = ref(); | ||
431 | - const jsonValue = ref({}); | ||
432 | - const jsonInstance = ref(); | ||
433 | - | ||
434 | - const disabledValue = ref(); //查看时使用表单全部禁用使用这个来获取表单数据 | ||
435 | - onMounted(() => { | ||
436 | - nextTick(() => { | ||
437 | - let options = { | ||
438 | - mode: 'code', | ||
439 | - mainMenuBar: false, | ||
440 | - statusBar: false, | ||
441 | - }; | ||
442 | - let editor = new jsoneditor(jsoneditorRef.value, options); | ||
443 | - editor.set(jsonValue.value); | ||
444 | - jsonInstance.value = editor; | ||
445 | - }); | ||
446 | - }); | ||
447 | - | ||
448 | - const getJsonValue = () => unref(jsonInstance).get(); | ||
449 | - const setJsonValue = (Json) => { | ||
450 | - nextTick(() => { | ||
451 | - disabledValue.value = Json; | ||
452 | - unref(jsonInstance).set(Json); | ||
453 | - }); | ||
454 | - }; | ||
455 | - | ||
456 | - const operationType = ref<string>(''); | ||
457 | - provide('operationType', operationType); | ||
458 | - | ||
459 | - const getRefItemConditionScreeningRefs = async () => { | ||
460 | - await nextTick(); | ||
461 | - return refItem.clearRuleRefs.value.map( | ||
462 | - (item) => item.conditionScreeningRef.refItem.conditionScreeningRefs | ||
463 | - ); | ||
464 | - }; | ||
465 | - const setConditionScreeningList = (list) => { | ||
466 | - refItem.clearRuleRefs.value.map((item) => { | ||
467 | - item.conditionScreeningRef.conditionScreeningList = list; | ||
468 | - }); | ||
469 | - }; | ||
470 | - | ||
471 | - const setRichText = (list, index) => { | ||
472 | - refItem.clearRuleRefs.value[index].conditionScreeningRef.otherAttribute = list; | ||
473 | - }; | ||
474 | - | ||
475 | - const handleDropdownVisibleChange = () => { | ||
476 | - emit('getActionFormArr'); | ||
477 | - }; | ||
478 | - | ||
479 | - const disabled = ref<boolean>(false); | ||
480 | - const setDisabledProps = async (value) => { | ||
481 | - setProps(value); | ||
482 | - disabled.value = true; | ||
483 | - await nextTick(); | ||
484 | - unref(refItem.clearRuleRefs)?.forEach((item) => { | ||
485 | - item.setDisabledProps(value); | ||
486 | - }); | ||
487 | - }; | ||
488 | - | ||
489 | - const setCancelDisabled = () => { | ||
490 | - disabled.value = false; | ||
491 | - unref(refItem.clearRuleRefs)?.forEach((item) => { | ||
492 | - item.setCancelDisabled(); | ||
493 | - }); | ||
494 | - }; | ||
495 | - | ||
496 | - defineExpose({ | ||
497 | - getFieldsValue, | ||
498 | - getFieldsValueFunc, | ||
499 | - setFieldsFormValueFun, | ||
500 | - resetFieldsValueFunc, | ||
501 | - updateFieldDeviceId, | ||
502 | - updateFieldAlarmConfig, | ||
503 | - validateForm, | ||
504 | - getJsonValue, | ||
505 | - setJsonValue, | ||
506 | - jsonInstance, | ||
507 | - updateEditFieldAlarmConfig, | ||
508 | - checked, | ||
509 | - getRefItemConditionScreeningRefs, | ||
510 | - setConditionScreeningList, | ||
511 | - setRichText, | ||
512 | - refItem, | ||
513 | - clearRuleList, | ||
514 | - resetConditionForm, | ||
515 | - handleDropdownVisibleChange, | ||
516 | - setDisabledProps, | ||
517 | - setCancelDisabled, | ||
518 | - }); | ||
519 | -</script> | ||
520 | - | ||
521 | -<style> | ||
522 | - .jsoneditor { | ||
523 | - border: none; | ||
524 | - } | ||
525 | -</style> |
@@ -67,11 +67,11 @@ | @@ -67,11 +67,11 @@ | ||
67 | </Tag> | 67 | </Tag> |
68 | </template> | 68 | </template> |
69 | </BasicTable> | 69 | </BasicTable> |
70 | - <SceneLinkAgeDrawer @register="registerDrawer" @success="handleSuccess" /> | 70 | + <SceneLinkageDrawer @register="registerDrawer" @success="handleSuccess" /> |
71 | </div> | 71 | </div> |
72 | </template> | 72 | </template> |
73 | <script lang="ts" setup> | 73 | <script lang="ts" setup> |
74 | - import { nextTick } from 'vue'; | 74 | + import { nextTick, toRaw, unref } from 'vue'; |
75 | import { BasicTable, useTable, TableAction } from '/@/components/Table'; | 75 | import { BasicTable, useTable, TableAction } from '/@/components/Table'; |
76 | import { useDrawer } from '/@/components/Drawer'; | 76 | import { useDrawer } from '/@/components/Drawer'; |
77 | import { | 77 | import { |
@@ -84,10 +84,12 @@ | @@ -84,10 +84,12 @@ | ||
84 | import { columns, searchFormSchema } from './config/config.data'; | 84 | import { columns, searchFormSchema } from './config/config.data'; |
85 | import { USER_INFO_KEY } from '/@/enums/cacheEnum'; | 85 | import { USER_INFO_KEY } from '/@/enums/cacheEnum'; |
86 | import { getAuthCache } from '/@/utils/auth'; | 86 | import { getAuthCache } from '/@/utils/auth'; |
87 | - import SceneLinkAgeDrawer from './SceneLinkAgeDrawer.vue'; | 87 | + import { SceneLinkageDrawer } from './components/SceneLinkageDrawer'; |
88 | import { useMessage } from '/@/hooks/web/useMessage'; | 88 | import { useMessage } from '/@/hooks/web/useMessage'; |
89 | import { Authority } from '/@/components/Authority'; | 89 | import { Authority } from '/@/components/Authority'; |
90 | import { usePermission } from '/@/hooks/web/usePermission'; | 90 | import { usePermission } from '/@/hooks/web/usePermission'; |
91 | + import { SceneLinkageDrawerDataType } from './components/SceneLinkageDrawer/type'; | ||
92 | + import { DataActionModeEnum } from '/@/enums/toolEnum'; | ||
91 | 93 | ||
92 | const userInfo: any = getAuthCache(USER_INFO_KEY); | 94 | const userInfo: any = getAuthCache(USER_INFO_KEY); |
93 | const userId = userInfo.userId; | 95 | const userId = userInfo.userId; |
@@ -129,25 +131,22 @@ | @@ -129,25 +131,22 @@ | ||
129 | }); | 131 | }); |
130 | 132 | ||
131 | function handleAdd() { | 133 | function handleAdd() { |
132 | - window.localStorage.setItem('isViewDisabledBtn', 'noView'); | ||
133 | openDrawer(true, { | 134 | openDrawer(true, { |
134 | - isUpdate: false, | ||
135 | - }); | 135 | + mode: DataActionModeEnum.CREATE, |
136 | + } as SceneLinkageDrawerDataType); | ||
136 | } | 137 | } |
137 | 138 | ||
138 | function handleEdit(record: Recordable) { | 139 | function handleEdit(record: Recordable) { |
139 | - window.localStorage.setItem('isViewDisabledBtn', 'noView'); | ||
140 | openDrawer(true, { | 140 | openDrawer(true, { |
141 | - record, | ||
142 | - isUpdate: true, | ||
143 | - }); | 141 | + mode: DataActionModeEnum.UPDATE, |
142 | + record: toRaw(unref(record)), | ||
143 | + } as SceneLinkageDrawerDataType); | ||
144 | } | 144 | } |
145 | function handleView(record: Recordable) { | 145 | function handleView(record: Recordable) { |
146 | - window.localStorage.setItem('isViewDisabledBtn', 'isView'); | ||
147 | openDrawer(true, { | 146 | openDrawer(true, { |
147 | + mode: DataActionModeEnum.READ, | ||
148 | record, | 148 | record, |
149 | - isUpdate: 3, | ||
150 | - }); | 149 | + } as SceneLinkageDrawerDataType); |
151 | } | 150 | } |
152 | function handleSuccess() { | 151 | function handleSuccess() { |
153 | reload(); | 152 | reload(); |
@@ -121,11 +121,11 @@ | @@ -121,11 +121,11 @@ | ||
121 | const handleNewRecord = () => { | 121 | const handleNewRecord = () => { |
122 | const { componentKey } = unref(selectWidgetKeys); | 122 | const { componentKey } = unref(selectWidgetKeys); |
123 | if (componentKey === 'HumidityComponent2' && unref(dataSource).length >= 6) { | 123 | if (componentKey === 'HumidityComponent2' && unref(dataSource).length >= 6) { |
124 | - createMessage.warning('绑定的数据源不能超过6条~'); | 124 | + createMessage.warning('绑定的数据源不能超过6条'); |
125 | return; | 125 | return; |
126 | } | 126 | } |
127 | if (unref(dataSource).length >= DATA_SOURCE_LIMIT_NUMBER) { | 127 | if (unref(dataSource).length >= DATA_SOURCE_LIMIT_NUMBER) { |
128 | - createMessage.warning('绑定的数据源不能超过10条~'); | 128 | + createMessage.warning('绑定的数据源不能超过10条'); |
129 | return; | 129 | return; |
130 | } | 130 | } |
131 | dataSource.value.push(genNewDataSourceItem()); | 131 | dataSource.value.push(genNewDataSourceItem()); |
@@ -246,7 +246,7 @@ | @@ -246,7 +246,7 @@ | ||
246 | ? await updateDataComponent(value) | 246 | ? await updateDataComponent(value) |
247 | : await addDataComponent(value); | 247 | : await addDataComponent(value); |
248 | createMessage.success( | 248 | createMessage.success( |
249 | - `${unref(currentMode) === DataActionModeEnum.UPDATE ? '编辑' : '新增'}成功~` | 249 | + `${unref(currentMode) === DataActionModeEnum.UPDATE ? '编辑' : '新增'}成功` |
250 | ); | 250 | ); |
251 | closeModal(); | 251 | closeModal(); |
252 | emit('ok'); | 252 | emit('ok'); |
@@ -9,13 +9,13 @@ import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; | @@ -9,13 +9,13 @@ import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; | ||
9 | import { getModelServices } from '/@/api/device/modelOfMatter'; | 9 | import { getModelServices } from '/@/api/device/modelOfMatter'; |
10 | import { findDictItemByCode } from '/@/api/system/dict'; | 10 | import { findDictItemByCode } from '/@/api/system/dict'; |
11 | import { FormSchema, useComponentRegister } from '/@/components/Form'; | 11 | import { FormSchema, useComponentRegister } from '/@/components/Form'; |
12 | -import { DataTypeEnum } from '/@/components/Form/src/externalCompns/components/StructForm/config'; | ||
13 | import { OrgTreeSelect } from '/@/views/common/OrgTreeSelect'; | 12 | import { OrgTreeSelect } from '/@/views/common/OrgTreeSelect'; |
14 | import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const'; | 13 | import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const'; |
15 | import { CommandTypeEnum } from '/@/views/rule/linkedge/config/config.data'; | 14 | import { CommandTypeEnum } from '/@/views/rule/linkedge/config/config.data'; |
16 | import { DataActionModeEnum } from '/@/enums/toolEnum'; | 15 | import { DataActionModeEnum } from '/@/enums/toolEnum'; |
17 | import { TaskTypeEnum } from '/@/views/task/center/config'; | 16 | import { TaskTypeEnum } from '/@/views/task/center/config'; |
18 | import { createPickerSearch } from '/@/utils/pickerSearch'; | 17 | import { createPickerSearch } from '/@/utils/pickerSearch'; |
18 | +import { DataTypeEnum } from '/@/enums/objectModelEnum'; | ||
19 | 19 | ||
20 | useComponentRegister('OrgTreeSelect', OrgTreeSelect); | 20 | useComponentRegister('OrgTreeSelect', OrgTreeSelect); |
21 | 21 | ||
@@ -362,7 +362,7 @@ export const commonDataSourceSchemas = (): FormSchema[] => { | @@ -362,7 +362,7 @@ export const commonDataSourceSchemas = (): FormSchema[] => { | ||
362 | (isControlComponent(category!) && | 362 | (isControlComponent(category!) && |
363 | unref(selectWidgetKeys).componentKey !== 'LateralNumericalControl') || | 363 | unref(selectWidgetKeys).componentKey !== 'LateralNumericalControl') || |
364 | isBooleanComponent(unref(selectWidgetKeys)) | 364 | isBooleanComponent(unref(selectWidgetKeys)) |
365 | - ? DataTypeEnum.IS_BOOL | 365 | + ? DataTypeEnum.BOOL |
366 | : undefined, | 366 | : undefined, |
367 | }); | 367 | }); |
368 | 368 |
@@ -60,7 +60,10 @@ | @@ -60,7 +60,10 @@ | ||
60 | event: DataActionModeEnum.DELETE, | 60 | event: DataActionModeEnum.DELETE, |
61 | icon: 'ant-design:delete-outlined', | 61 | icon: 'ant-design:delete-outlined', |
62 | auth: VisualComponentPermission.DELETE, | 62 | auth: VisualComponentPermission.DELETE, |
63 | - onClick: handleDelete, | 63 | + popconfirm: { |
64 | + title: '是否确认删除操作?', | ||
65 | + onConfirm: handleDelete.bind(null), | ||
66 | + }, | ||
64 | }, | 67 | }, |
65 | ]); | 68 | ]); |
66 | 69 | ||
@@ -71,7 +74,7 @@ | @@ -71,7 +74,7 @@ | ||
71 | async function handleDelete() { | 74 | async function handleDelete() { |
72 | try { | 75 | try { |
73 | await deleteDataComponent({ dataBoardId: unref(boardId), ids: [props.sourceInfo.id] }); | 76 | await deleteDataComponent({ dataBoardId: unref(boardId), ids: [props.sourceInfo.id] }); |
74 | - createMessage.success('删除成功~'); | 77 | + createMessage.success('删除成功'); |
75 | emit('ok'); | 78 | emit('ok'); |
76 | } catch (error) {} | 79 | } catch (error) {} |
77 | } | 80 | } |
@@ -132,7 +135,7 @@ | @@ -132,7 +135,7 @@ | ||
132 | layout: layout as Layout, | 135 | layout: layout as Layout, |
133 | }, | 136 | }, |
134 | }); | 137 | }); |
135 | - createMessage.success('复制成功~'); | 138 | + createMessage.success('复制成功'); |
136 | emit('ok'); | 139 | emit('ok'); |
137 | } catch (error) { | 140 | } catch (error) { |
138 | throw error; | 141 | throw error; |
@@ -10,3 +10,10 @@ export interface ModalParamsType<T = Recordable> { | @@ -10,3 +10,10 @@ export interface ModalParamsType<T = Recordable> { | ||
10 | record: T; | 10 | record: T; |
11 | [key: string]: any; | 11 | [key: string]: any; |
12 | } | 12 | } |
13 | + | ||
14 | +export interface DefineComponentsBasicExpose<T = Recordable> { | ||
15 | + getFieldsValue: () => T; | ||
16 | + setFieldsValue: (value: T) => any; | ||
17 | + validate?: () => Promise<any>; | ||
18 | + resetFieldsValue?: (...args) => any; | ||
19 | +} |