Commit bc828e45ac858e4173103f9ee2115119beac6887
Merge branch 'main_dev'
# Conflicts: # src/views/rule/designer/packages/Filter/CheckExistenceFields/create.config.ts # src/views/rule/linkedge/cpns/AlarmSchedule.vue # src/views/rule/linkedge/cpns/ClearAlarm.vue # src/views/rule/linkedge/cpns/ConditionScreening.vue # src/views/rule/linkedge/cpns/Trigger-Condition.vue
Showing
82 changed files
with
3249 additions
and
850 deletions
Too many changes to show.
To preserve performance only 82 of 376 files are displayed.
... | ... | @@ -25,7 +25,7 @@ module.exports = defineConfig({ |
25 | 25 | 'plugin:jest/recommended', |
26 | 26 | ], |
27 | 27 | rules: { |
28 | - 'no-console': 'off', | |
28 | + 'no-console': 'error', | |
29 | 29 | 'vue/script-setup-uses-vars': 'error', |
30 | 30 | '@typescript-eslint/ban-ts-ignore': 'off', |
31 | 31 | '@typescript-eslint/explicit-function-return-type': 'off', | ... | ... |
1 | 1 | import { BasicPageParams } from '/@/api/model/baseModel'; |
2 | +import { ViewType } from '/@/views/visual/board/config/panelDetail'; | |
2 | 3 | |
3 | 4 | export interface BigScreenCenterItemsModel { |
4 | 5 | id: string; |
6 | + thumbnail?: string; | |
5 | 7 | name: string; |
6 | 8 | createTime: string; |
7 | 9 | creator: string; |
... | ... | @@ -9,6 +11,8 @@ export interface BigScreenCenterItemsModel { |
9 | 11 | state: number; |
10 | 12 | publicId: string; |
11 | 13 | organizationId?: string; |
14 | + viewType?: ViewType; | |
15 | + organizationDTO: { name: string }; | |
12 | 16 | } |
13 | 17 | export type queryPageParams = BasicPageParams & { |
14 | 18 | name?: Nullable<string>; | ... | ... |
... | ... | @@ -2,15 +2,46 @@ import { BasicPageParams } from '/@/api/model/baseModel'; |
2 | 2 | |
3 | 3 | export interface ConfigurationCenterItemsModal { |
4 | 4 | id: string; |
5 | - name: string; | |
6 | - createTime: string; | |
7 | 5 | creator: string; |
8 | - remark: string; | |
9 | - publicId?: string; | |
10 | - organizationId?: string; | |
11 | - platform?: string; | |
12 | - productIds?: string; | |
6 | + createTime: string; | |
7 | + updater: string; | |
8 | + updateTime: string; | |
9 | + name: string; | |
10 | + enabled: boolean; | |
11 | + tenantId: string; | |
12 | + publicId: string; | |
13 | + viewType: string; | |
14 | + accessCredentials: string; | |
15 | + organizationId: string; | |
16 | + platform: string; | |
17 | + thumbnail: string; | |
18 | + organizationDTO: OrganizationDto; | |
19 | + templateId: string; | |
20 | + productAndDevice: ProductAndDevice[]; | |
21 | + remark?: string; | |
22 | +} | |
23 | + | |
24 | +export interface OrganizationDto { | |
25 | + name: string; | |
26 | + enabled: boolean; | |
27 | + sort: number; | |
28 | + children: any[]; | |
13 | 29 | } |
30 | + | |
31 | +export interface ProductAndDevice { | |
32 | + profileId: string; | |
33 | + name: string; | |
34 | + transportType: string; | |
35 | + deviceType: string; | |
36 | + deviceList: DeviceList[]; | |
37 | +} | |
38 | + | |
39 | +export interface DeviceList { | |
40 | + deviceId: string; | |
41 | + name: string; | |
42 | + codeType: any; | |
43 | +} | |
44 | + | |
14 | 45 | export type queryPageParams = BasicPageParams & { |
15 | 46 | name?: Nullable<string>; |
16 | 47 | organizationId?: Nullable<number>; | ... | ... |
1 | 1 | import { DataType } from '../../device/model/modelOfMatterModel'; |
2 | -import { DataTypeEnum } from '/@/components/Form/src/externalCompns/components/StructForm/config'; | |
2 | +import { DataTypeEnum } from '/@/enums/objectModelEnum'; | |
3 | 3 | import { DataSource } from '/@/views/visual/palette/types'; |
4 | 4 | |
5 | 5 | export interface AddDataBoardParams { |
... | ... | @@ -15,6 +15,8 @@ export interface UpdateDataBoardParams extends AddDataBoardParams { |
15 | 15 | export interface GetDataBoardParams { |
16 | 16 | page?: number; |
17 | 17 | pageSize?: number; |
18 | + name?: string; | |
19 | + organizationId?: string; | |
18 | 20 | orderFiled?: string; |
19 | 21 | orderType?: string; |
20 | 22 | } |
... | ... | @@ -47,7 +49,9 @@ export interface DataBoardRecord { |
47 | 49 | layout: Layout[]; |
48 | 50 | defaultConfig: string; |
49 | 51 | tenantStatus: string; |
52 | + componentNum?: number; | |
50 | 53 | publicId: string; |
54 | + organizationId?: string; | |
51 | 55 | accessCredentials?: string; |
52 | 56 | } |
53 | 57 | ... | ... |
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 | +// export enum CATEGORY_POST { | |
8 | + | |
9 | +// } | |
10 | + | |
11 | +/** | |
12 | + * 新增产品分类 | |
13 | + */ | |
14 | +export const deviceProfileCategory = (params) => { | |
15 | + return defHttp.post<any>({ | |
16 | + url: `${Api.DEVICE_PROFILE_CATEGORY}`, | |
17 | + params, | |
18 | + }); | |
19 | +}; | |
20 | + | |
21 | +/** | |
22 | + * 获取列表 | |
23 | + */ | |
24 | +export const getDeviceClassList = (params) => { | |
25 | + return defHttp.get<any>({ | |
26 | + url: Api.DEVICE_PROFILE_CATEGORY, | |
27 | + params, | |
28 | + }); | |
29 | +}; | |
30 | + | |
31 | +/** | |
32 | + * 删除产品分类 | |
33 | + */ | |
34 | +export const deleteDeviceClass = (params) => { | |
35 | + return defHttp.delete<any>({ | |
36 | + url: Api.DEVICE_PROFILE_CATEGORY, | |
37 | + params, | |
38 | + }); | |
39 | +}; | ... | ... |
... | ... | @@ -43,120 +43,7 @@ export interface IDeviceConfigAddOrEditModel { |
43 | 43 | icon?: string; |
44 | 44 | id?: string; |
45 | 45 | name?: string; |
46 | - profileData?: { | |
47 | - configuration: {}; | |
48 | - transportConfiguration: {}; | |
49 | - provisionConfiguration: { | |
50 | - provisionDeviceSecret: string; | |
51 | - }; | |
52 | - //报警类型字段 | |
53 | - alarms: [ | |
54 | - { | |
55 | - id: 'highTemperatureAlarmID'; | |
56 | - alarmType: 'High Temperature Alarm'; | |
57 | - createRules: { | |
58 | - additionalProp1: { | |
59 | - condition: { | |
60 | - condition: [ | |
61 | - { | |
62 | - key: { | |
63 | - type: 'TIME_SERIES'; | |
64 | - key: 'temp'; | |
65 | - }; | |
66 | - valueType: 'NUMERIC'; | |
67 | - value: {}; | |
68 | - predicate: {}; | |
69 | - } | |
70 | - ]; | |
71 | - spec: {}; | |
72 | - }; | |
73 | - schedule: { | |
74 | - type: 'ANY_TIME'; | |
75 | - }; | |
76 | - alarmDetails: string; | |
77 | - dashboardId: { | |
78 | - id: '784f394c-42b6-435a-983c-b7beff2784f9'; | |
79 | - entityType: 'DASHBOARD'; | |
80 | - }; | |
81 | - }; | |
82 | - additionalProp2: { | |
83 | - condition: { | |
84 | - condition: [ | |
85 | - { | |
86 | - key: { | |
87 | - type: 'TIME_SERIES'; | |
88 | - key: 'temp'; | |
89 | - }; | |
90 | - valueType: 'NUMERIC'; | |
91 | - value: {}; | |
92 | - predicate: {}; | |
93 | - } | |
94 | - ]; | |
95 | - spec: {}; | |
96 | - }; | |
97 | - schedule: { | |
98 | - type: 'ANY_TIME'; | |
99 | - }; | |
100 | - alarmDetails: string; | |
101 | - dashboardId: { | |
102 | - id: '784f394c-42b6-435a-983c-b7beff2784f9'; | |
103 | - entityType: 'DASHBOARD'; | |
104 | - }; | |
105 | - }; | |
106 | - additionalProp3: { | |
107 | - condition: { | |
108 | - condition: [ | |
109 | - { | |
110 | - key: { | |
111 | - type: 'TIME_SERIES'; | |
112 | - key: 'temp'; | |
113 | - }; | |
114 | - valueType: 'NUMERIC'; | |
115 | - value: {}; | |
116 | - predicate: {}; | |
117 | - } | |
118 | - ]; | |
119 | - spec: {}; | |
120 | - }; | |
121 | - schedule: { | |
122 | - type: 'ANY_TIME'; | |
123 | - }; | |
124 | - alarmDetails: string; | |
125 | - dashboardId: { | |
126 | - id: '784f394c-42b6-435a-983c-b7beff2784f9'; | |
127 | - entityType: 'DASHBOARD'; | |
128 | - }; | |
129 | - }; | |
130 | - }; | |
131 | - clearRule: { | |
132 | - condition: { | |
133 | - condition: [ | |
134 | - { | |
135 | - key: { | |
136 | - type: 'TIME_SERIES'; | |
137 | - key: 'temp'; | |
138 | - }; | |
139 | - valueType: 'NUMERIC'; | |
140 | - value: {}; | |
141 | - predicate: {}; | |
142 | - } | |
143 | - ]; | |
144 | - spec: {}; | |
145 | - }; | |
146 | - schedule: { | |
147 | - type: 'ANY_TIME'; | |
148 | - }; | |
149 | - alarmDetails: string; | |
150 | - dashboardId: { | |
151 | - id: '784f394c-42b6-435a-983c-b7beff2784f9'; | |
152 | - entityType: 'DASHBOARD'; | |
153 | - }; | |
154 | - }; | |
155 | - propagate: true; | |
156 | - propagateRelationTypes: [string]; | |
157 | - } | |
158 | - ]; | |
159 | - }; | |
46 | + profileData?: ProfileData; | |
160 | 47 | roleIds?: [string]; |
161 | 48 | tbProfileId?: string; |
162 | 49 | tenantExpireTime?: '2021-12-15T02:17:26.645Z'; |
... | ... | @@ -219,7 +106,7 @@ export interface ProfileData { |
219 | 106 | configuration: Configuration; |
220 | 107 | transportConfiguration: TransportConfiguration; |
221 | 108 | provisionConfiguration: ProvisionConfiguration; |
222 | - alarms?: any; | |
109 | + alarms: any; | |
223 | 110 | } |
224 | 111 | |
225 | 112 | export interface ProfileRecord { |
... | ... | @@ -297,8 +184,3 @@ export interface Configuration { |
297 | 184 | export interface TransportConfiguration { |
298 | 185 | type: string; |
299 | 186 | } |
300 | - | |
301 | -export interface ProvisionConfiguration { | |
302 | - type: string; | |
303 | - provisionDeviceSecret: any; | |
304 | -} | ... | ... |
1 | -import { | |
2 | - DataTypeEnum, | |
3 | - FunctionType, | |
4 | -} from '/@/views/device/profiles/step/cpns/physical/cpns/config'; | |
1 | +import { DataTypeEnum } from '/@/enums/objectModelEnum'; | |
2 | +import { FunctionType } from '/@/views/device/profiles/step/cpns/physical/cpns/config'; | |
5 | 3 | |
6 | 4 | export interface Specs { |
7 | 5 | min: string; |
... | ... | @@ -26,7 +24,7 @@ export interface DataType { |
26 | 24 | |
27 | 25 | export interface StructJSON { |
28 | 26 | functionName?: string; |
29 | - identifier?: string; | |
27 | + identifier: string; | |
30 | 28 | remark?: string; |
31 | 29 | dataType?: DataType; |
32 | 30 | serviceCommand?: string; |
... | ... | @@ -41,13 +39,14 @@ export interface FunctionJson { |
41 | 39 | } |
42 | 40 | |
43 | 41 | export interface ModelOfMatterParams { |
44 | - deviceProfileId: string; | |
42 | + deviceProfileId?: string; | |
45 | 43 | functionJson: FunctionJson; |
46 | 44 | functionName: string; |
47 | 45 | functionType: FunctionType; |
48 | 46 | identifier: string; |
49 | 47 | remark: string; |
50 | 48 | id?: string; |
49 | + categoryId?: string; | |
51 | 50 | callType?: string; |
52 | 51 | eventType?: string; |
53 | 52 | accessMode?: string; |
... | ... | @@ -57,10 +56,12 @@ export interface ModelOfMatterParams { |
57 | 56 | export interface GetModelTslParams { |
58 | 57 | functionType: FunctionType; |
59 | 58 | deviceProfileId: string; |
59 | + ifShowClass?: string | Boolean; | |
60 | 60 | } |
61 | 61 | |
62 | 62 | export interface ImportModelOfMatterType { |
63 | 63 | data: Recordable; |
64 | 64 | functionType: string; |
65 | - tkDeviceProfileId: string; | |
65 | + tkDeviceProfileId?: string; | |
66 | + categoryId?: string; | |
66 | 67 | } | ... | ... |
... | ... | @@ -18,13 +18,22 @@ enum ModelOfMatter { |
18 | 18 | IMPORT = '/things_model/import', |
19 | 19 | |
20 | 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', | |
27 | + EXCEL_EXPORT = '/things_model/downloadTemplate', | |
21 | 28 | } |
22 | 29 | |
23 | 30 | export const getModelList = ( |
24 | 31 | params: BasicPageParams & { |
25 | - deviceProfileId: string; | |
32 | + deviceProfileId?: string; | |
26 | 33 | functionTyp?: FunctionType; |
27 | 34 | nameOrIdentifier?: string; |
35 | + selectType?: string | undefined; | |
36 | + id?: string; | |
28 | 37 | } |
29 | 38 | ) => { |
30 | 39 | return defHttp.get({ |
... | ... | @@ -82,3 +91,92 @@ export const importModelOfMatter = (data: ImportModelOfMatterType) => { |
82 | 91 | data, |
83 | 92 | }); |
84 | 93 | }; |
94 | + | |
95 | +// 产品品类接口 | |
96 | + | |
97 | +// 新增 | |
98 | +export const createModelCategory = (params: Partial<ModelOfMatterParams>) => { | |
99 | + return defHttp.post({ | |
100 | + url: ModelOfMatter.CATEGORY, | |
101 | + params, | |
102 | + }); | |
103 | +}; | |
104 | + | |
105 | +// 修改 | |
106 | +export const updateModelCategory = (params: Partial<ModelOfMatterParams>) => { | |
107 | + return defHttp.put({ | |
108 | + url: ModelOfMatter.CATEGORY, | |
109 | + params, | |
110 | + }); | |
111 | +}; | |
112 | + | |
113 | +// 删除 | |
114 | +export const deleteModelCategory = (params: string[]) => { | |
115 | + return defHttp.delete({ | |
116 | + url: ModelOfMatter.CATEGORY, | |
117 | + params: { | |
118 | + ids: params, | |
119 | + }, | |
120 | + }); | |
121 | +}; | |
122 | + | |
123 | +// 导入 | |
124 | +export const importModelCategory = (data: ImportModelOfMatterType) => { | |
125 | + return defHttp.post({ | |
126 | + url: ModelOfMatter.CATEGORY_IMPORT, | |
127 | + data, | |
128 | + }); | |
129 | +}; | |
130 | + | |
131 | +// 导出 | |
132 | +export const ExportModelCategory = (params: any) => { | |
133 | + const { functionType, deviceProfileId } = params; | |
134 | + return defHttp.get({ | |
135 | + url: `${ModelOfMatter.CATEGORY_EXPORT}/${functionType}/${deviceProfileId}`, | |
136 | + params, | |
137 | + }); | |
138 | +}; | |
139 | + | |
140 | +/** | |
141 | + * 物模型产品品类界面excel导入 | |
142 | + */ | |
143 | + | |
144 | +export const importCsvCategory = (params: { | |
145 | + categoryId?: string; | |
146 | + deviceProfileId?: string; | |
147 | + file: FormData; | |
148 | +}) => { | |
149 | + const { categoryId } = params || {}; | |
150 | + | |
151 | + return defHttp.post<any>({ | |
152 | + url: `${ModelOfMatter.IMPORT_CSV}?categoryId=${categoryId}`, | |
153 | + params: params.file, | |
154 | + }); | |
155 | +}; | |
156 | + | |
157 | +/** | |
158 | + * 物模型产品物模型界面excel导入 | |
159 | + */ | |
160 | + | |
161 | +export const importCsvDeviceProfileId = (params: { | |
162 | + categoryId?: string; | |
163 | + deviceProfileId?: string; | |
164 | + file: FormData; | |
165 | +}) => { | |
166 | + const { deviceProfileId } = params || {}; | |
167 | + | |
168 | + return defHttp.post<any>({ | |
169 | + url: `${ModelOfMatter.IMPORT_CSV}?deviceProfileId=${deviceProfileId}`, | |
170 | + params: params.file, | |
171 | + }); | |
172 | +}; | |
173 | + | |
174 | +/** | |
175 | + * 物模型excel导出模板 | |
176 | + */ | |
177 | +export const excelExport = () => { | |
178 | + return defHttp.get({ | |
179 | + url: `${ModelOfMatter.EXCEL_EXPORT}`, | |
180 | + responseType: 'blob', | |
181 | + }); | |
182 | +}; | ... | ... |
... | ... | @@ -8,6 +8,7 @@ import { |
8 | 8 | enum MessageRecordsApi { |
9 | 9 | SMS_RECORDS = '/sms_log', |
10 | 10 | EMAIL_RECORDS = '/mail_log', |
11 | + MESSAGE_RECORDS = '/message_log', | |
11 | 12 | } |
12 | 13 | |
13 | 14 | /** |
... | ... | @@ -16,7 +17,7 @@ enum MessageRecordsApi { |
16 | 17 | */ |
17 | 18 | export const smsLogPage = (params: MessageRecordsParams) => { |
18 | 19 | return defHttp.get<SmsLogModelResult>({ |
19 | - url: MessageRecordsApi.SMS_RECORDS, | |
20 | + url: MessageRecordsApi.MESSAGE_RECORDS, | |
20 | 21 | params, |
21 | 22 | }); |
22 | 23 | }; |
... | ... | @@ -49,7 +50,7 @@ export const deleteMailLog = (ids: string[]) => |
49 | 50 | */ |
50 | 51 | export const deleteSmsLog = (ids: string[]) => |
51 | 52 | defHttp.delete({ |
52 | - url: MessageRecordsApi.SMS_RECORDS, | |
53 | + url: MessageRecordsApi.MESSAGE_RECORDS, | |
53 | 54 | data: { |
54 | 55 | ids: ids, |
55 | 56 | }, | ... | ... |
1 | 1 | import { BasicPageParams } from '/@/api/model/baseModel'; |
2 | +import { SceneLinkageDataType } from '/@/views/rule/linkedge/components/SceneLinkageDrawer/type'; | |
2 | 3 | |
3 | 4 | export type ScreenLinkPageQueryParam = BasicPageParams & ScreenParams; |
4 | 5 | |
... | ... | @@ -14,97 +15,7 @@ export type ScreenByDeptIdParams = { |
14 | 15 | // organizationId: '2f5c8f2a-196c-4941-8771-290f9da76219'; |
15 | 16 | }; |
16 | 17 | |
17 | -export interface ScreenAddModel { | |
18 | - createTime?: string; | |
19 | - creator?: string; | |
20 | - defaultConfig?: string; | |
21 | - description?: string; | |
22 | - doAction?: [ | |
23 | - { | |
24 | - command: string; | |
25 | - createTime: string; | |
26 | - creator: string; | |
27 | - defaultConfig: string; | |
28 | - description: string; | |
29 | - deviceId: string; | |
30 | - enabled: true; | |
31 | - icon: string; | |
32 | - id: string; | |
33 | - name: string; | |
34 | - outPut: string; | |
35 | - outTarget: string; | |
36 | - roleIds: []; | |
37 | - tenantCode: string; | |
38 | - tenantExpireTime: string; | |
39 | - tenantId: string; | |
40 | - tenantStatus: 'DISABLED'; | |
41 | - updateTime: string; | |
42 | - updater: string; | |
43 | - } | |
44 | - ]; | |
45 | - doCondition?: [ | |
46 | - { | |
47 | - compare: 0; | |
48 | - createTime: string; | |
49 | - creator: string; | |
50 | - defaultConfig: string; | |
51 | - description: string; | |
52 | - deviceId: string; | |
53 | - enabled: true; | |
54 | - icon: string; | |
55 | - id: string; | |
56 | - name: string; | |
57 | - property: string; | |
58 | - roleIds: []; | |
59 | - status: string; | |
60 | - tenantCode: string; | |
61 | - tenantExpireTime: string; | |
62 | - tenantId: string; | |
63 | - tenantStatus: 'DISABLED'; | |
64 | - updateTime: string; | |
65 | - updater: string; | |
66 | - value: string; | |
67 | - } | |
68 | - ]; | |
69 | - enabled?: true; | |
70 | - icon?: string; | |
71 | - id?: string; | |
72 | - name?: string; | |
73 | - organizationId?: string; | |
74 | - roleIds?: [string]; | |
75 | - status?: string; | |
76 | - tenantCode?: string; | |
77 | - tenantExpireTime?: string; | |
78 | - tenantId?: string; | |
79 | - tenantStatus?: 'DISABLED'; | |
80 | - triggers?: [ | |
81 | - { | |
82 | - attributeChoose?: string; | |
83 | - compare?: 0; | |
84 | - createTime?: string; | |
85 | - creator?: string; | |
86 | - defaultConfig?: string; | |
87 | - description?: string; | |
88 | - deviceId?: string; | |
89 | - enabled?: true; | |
90 | - icon?: string; | |
91 | - id?: string; | |
92 | - name?: string; | |
93 | - roleIds?: []; | |
94 | - tenantCode?: string; | |
95 | - tenantExpireTime?: string; | |
96 | - tenantId?: string; | |
97 | - tenantStatus?: 'DISABLED'; | |
98 | - tiggerEvent?: string; | |
99 | - touchWay?: string; | |
100 | - updateTime?: string; | |
101 | - updater?: string; | |
102 | - value?: string; | |
103 | - } | |
104 | - ]; | |
105 | - updateTime?: string; | |
106 | - updater?: string; | |
107 | -} | |
18 | +export type ScreenAddModel = SceneLinkageDataType; | |
108 | 19 | |
109 | 20 | export interface IChangeStatus { |
110 | 21 | status?: number; | ... | ... |
... | ... | @@ -6,6 +6,7 @@ import { |
6 | 6 | ScreenByDeptIdParams, |
7 | 7 | IChangeStatus, |
8 | 8 | } from '/@/api/ruleengine/model/ruleengineModel'; |
9 | +import { DeviceModel } from '../device/model/deviceModel'; | |
9 | 10 | |
10 | 11 | enum ScreenManagerApi { |
11 | 12 | /** |
... | ... | @@ -126,7 +127,7 @@ export const byOrganizationIdGetMasterDevice = (params: { |
126 | 127 | deviceProfileId?: string; |
127 | 128 | }) => { |
128 | 129 | const { organizationId, deviceProfileId } = params; |
129 | - return defHttp.get({ | |
130 | + return defHttp.get<DeviceModel[]>({ | |
130 | 131 | url: `${ScreenManagerApi.MASTER_GET_DEVICE}`, |
131 | 132 | params: { deviceProfileId, organizationId }, |
132 | 133 | }); | ... | ... |
... | ... | @@ -37,10 +37,11 @@ export interface AccountListItem { |
37 | 37 | } |
38 | 38 | |
39 | 39 | export interface OrganizationListItem { |
40 | - id: string; | |
41 | - name: string; | |
40 | + id?: string; | |
41 | + name?: string; | |
42 | 42 | parentId?: string; |
43 | - remark: string; | |
43 | + remark?: string; | |
44 | + organizationId?: string; | |
44 | 45 | } |
45 | 46 | |
46 | 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> | |
\ No newline at end of file | ... | ... |
src/assets/svg/down-svg.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="1701135308389" class="icon" viewBox="0 0 1079 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1473" xmlns:xlink="http://www.w3.org/1999/xlink" width="210.7421875" height="200"><path d="M602.973867 717.627733l342.024533-342.075733a48.759467 48.759467 0 0 0 14.344533-34.577067 48.896 48.896 0 0 0-83.456-34.5856l-307.5072 307.541334-307.464533-307.549867a48.913067 48.913067 0 0 0-69.128533 0 48.955733 48.955733 0 0 0 0 69.154133L533.845333 717.610667a48.930133 48.930133 0 0 0 69.128534 0.017066z" fill="#AAAAAA" p-id="1474"></path></svg> | |
\ No newline at end of file | ... | ... |
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> | |
\ No newline at end of file | ... | ... |
... | ... | @@ -12,8 +12,8 @@ |
12 | 12 | overlayClassName="app-locale-picker-overlay" |
13 | 13 | > |
14 | 14 | <span class="cursor-pointer flex items-center" :style="{ color: color }"> |
15 | - <!-- <Icon icon="ion:language" /> --> | |
16 | - <!-- <span v-if="showText" class="ml-1">{{ getLocaleText }}</span> --> | |
15 | + <!-- <Icon icon="ion:language" /> | |
16 | + <span v-if="showText" class="ml-1">{{ getLocaleText }}</span> --> | |
17 | 17 | </span> |
18 | 18 | </Dropdown> |
19 | 19 | </template> | ... | ... |
1 | 1 | <template> |
2 | - <div class="anticon" :class="getAppLogoClass" @click="goHome"> | |
2 | + <div class="anticon" :class="getAppLogoClass"> | |
3 | 3 | <img v-if="getLogo" :src="getLogo" /> |
4 | 4 | <img v-else src="/src/assets/images/logo.png" /> |
5 | - <div class="ml-2 truncate md:opacity-100" :class="getTitleClass" v-show="showTitle"> | |
5 | + <div | |
6 | + class="ml-2 truncate md:opacity-100" | |
7 | + :class="getTitleClass" | |
8 | + v-show="showTitle" | |
9 | + :style="{ color: getTitleColor }" | |
10 | + > | |
6 | 11 | {{ getTitle }} |
7 | 12 | </div> |
8 | 13 | </div> |
... | ... | @@ -51,6 +56,8 @@ |
51 | 56 | const getTitle = computed(() => { |
52 | 57 | return userStore.platInfo?.name ?? title; |
53 | 58 | }); |
59 | + | |
60 | + const getTitleColor = computed(() => userStore.platInfo?.backgroundColor || '#377dff'); | |
54 | 61 | </script> |
55 | 62 | <style lang="less" scoped> |
56 | 63 | @prefix-cls: ~'@{namespace}-app-logo'; | ... | ... |
src/components/CardList/index.ts
0 → 100644
1 | +<script setup lang="ts"> | |
2 | + import { BasicForm, FormActionType, useForm } from '/@/components/Form'; | |
3 | + import { List, ListProps, Button, Tooltip, DropdownButton, Menu } from 'ant-design-vue'; | |
4 | + import { computed, Ref, ref, toRaw, unref, useSlots, watch } from 'vue'; | |
5 | + import { useCardListScroll } from './hooks/useCardListScroll'; | |
6 | + import { BasicCardListPropsType, CardListActionType } from './types'; | |
7 | + import CardListLayout from './components/CardListLayout.vue'; | |
8 | + import { usePagination } from './hooks/usePagination'; | |
9 | + import { useLoading } from './hooks/useLoading'; | |
10 | + import { useCardListData } from './hooks/useCardListData'; | |
11 | + import { useListGrid } from './hooks/useListGrid'; | |
12 | + import { FETCH_SETTING } from '../../Table/src/const'; | |
13 | + import { useCardForm } from './hooks/useCardForm'; | |
14 | + import { Icon } from '/@/components/Icon'; | |
15 | + import { useCardListSelected } from './hooks/useCardListSelected'; | |
16 | + | |
17 | + const emit = defineEmits<{ | |
18 | + (eventName: 'fetchSuccess', result: { items: Recordable[]; total: number }): void; | |
19 | + (eventName: 'fetchError', error: Error): void; | |
20 | + ( | |
21 | + eventName: 'register', | |
22 | + cardListActionType: CardListActionType, | |
23 | + formActions: FormActionType | |
24 | + ): void; | |
25 | + }>(); | |
26 | + | |
27 | + const slots = useSlots(); | |
28 | + | |
29 | + const props = withDefaults(defineProps<BasicCardListPropsType>(), { | |
30 | + showCardListHeader: true, | |
31 | + showCardListSetting: true, | |
32 | + useSearchForm: false, | |
33 | + gutter: 16, | |
34 | + immediate: true, | |
35 | + fetchSetting: () => FETCH_SETTING, | |
36 | + autoCreateKey: true, | |
37 | + offsetHeight: 0, | |
38 | + }); | |
39 | + | |
40 | + const tableData = ref<Recordable[]>([]); | |
41 | + | |
42 | + const listElRef = ref<Nullable<ComponentElRef>>(null); | |
43 | + | |
44 | + const innerPropsRef = ref<BasicCardListPropsType>(); | |
45 | + | |
46 | + const [registerForm, formActions] = useForm(); | |
47 | + | |
48 | + const getProps = computed<BasicCardListPropsType>(() => { | |
49 | + return { ...props, ...(unref(innerPropsRef) || {}) } as BasicCardListPropsType; | |
50 | + }); | |
51 | + | |
52 | + const { getListGrid, cardListLayout } = useListGrid(getProps); | |
53 | + | |
54 | + const { getPaginationInfo, getPagination, setPagination } = usePagination( | |
55 | + getProps, | |
56 | + cardListLayout | |
57 | + ); | |
58 | + | |
59 | + const { loading, setLoading, getLoading } = useLoading(); | |
60 | + | |
61 | + const { | |
62 | + getDataSourceRef, | |
63 | + getRowKey, | |
64 | + fetch, | |
65 | + reload, | |
66 | + updateTableDataRecord, | |
67 | + findCardDataRecord, | |
68 | + setCardListData, | |
69 | + } = useCardListData(getProps, formActions, { | |
70 | + setLoading, | |
71 | + getPaginationInfo, | |
72 | + emit, | |
73 | + tableData, | |
74 | + setPagination, | |
75 | + }); | |
76 | + | |
77 | + const { redoHeight, containerHeight } = useCardListScroll( | |
78 | + listElRef as Ref<Nullable<ComponentElRef>>, | |
79 | + getProps, | |
80 | + getDataSourceRef | |
81 | + ); | |
82 | + | |
83 | + const { replaceFormSlotKey, getFormProps, getFormSlotKeys } = useCardForm( | |
84 | + getProps, | |
85 | + slots, | |
86 | + fetch, | |
87 | + getLoading | |
88 | + ); | |
89 | + | |
90 | + const { | |
91 | + handlerSelected, | |
92 | + selectedClass, | |
93 | + selectedKeys, | |
94 | + selectAllToggle, | |
95 | + selectedAll, | |
96 | + clearSelectedKeys, | |
97 | + getSelectedKeys, | |
98 | + getSelectedRecords, | |
99 | + getSelections, | |
100 | + } = useCardListSelected(getDataSourceRef, getProps, { | |
101 | + updateTableDataRecord, | |
102 | + findCardDataRecord, | |
103 | + getRowKey, | |
104 | + setCardListData, | |
105 | + }); | |
106 | + | |
107 | + function handleCardListChange() { | |
108 | + reload(); | |
109 | + } | |
110 | + | |
111 | + const getBindValues = computed<ListProps>(() => { | |
112 | + const dataSource = unref(getDataSourceRef); | |
113 | + | |
114 | + const props = { | |
115 | + dataSource, | |
116 | + grid: unref(getListGrid), | |
117 | + loading: unref(loading), | |
118 | + pagination: { | |
119 | + ...toRaw(unref(getPaginationInfo)), | |
120 | + onChange: (current: number, pageSize: number) => { | |
121 | + setPagination({ current, pageSize }); | |
122 | + handleCardListChange(); | |
123 | + }, | |
124 | + }, | |
125 | + rowKey: (record: Recordable) => record[unref(getRowKey)], | |
126 | + } as ListProps; | |
127 | + return props; | |
128 | + }); | |
129 | + | |
130 | + watch(cardListLayout, () => { | |
131 | + const pageSize = cardListLayout.row * cardListLayout.col; | |
132 | + setPagination({ pageSize }); | |
133 | + handleCardListChange(); | |
134 | + }); | |
135 | + | |
136 | + const CartListActionType: CardListActionType = { | |
137 | + setProps, | |
138 | + setLoading, | |
139 | + setPagination, | |
140 | + getPagination, | |
141 | + reload, | |
142 | + selectedAll, | |
143 | + clearSelectedKeys, | |
144 | + getSelectedKeys, | |
145 | + getSelectedRecords, | |
146 | + getSelections, | |
147 | + }; | |
148 | + | |
149 | + function setProps(props: Partial<BasicCardListPropsType> = {}) { | |
150 | + innerPropsRef.value = { ...(unref(innerPropsRef) || {}), ...props } as BasicCardListPropsType; | |
151 | + } | |
152 | + | |
153 | + emit('register', CartListActionType, formActions); | |
154 | +</script> | |
155 | + | |
156 | +<template> | |
157 | + <main class="basic-card-list p-4 flex flex-col w-full h-full"> | |
158 | + <section v-if="getProps.useSearchForm" class="w-full bg-light-50 dark:bg-dark-900 p-4 mb-4"> | |
159 | + <BasicForm | |
160 | + @register="registerForm" | |
161 | + v-bind="getFormProps" | |
162 | + @advanced-change="redoHeight" | |
163 | + @reset="reload()" | |
164 | + @submit="reload()" | |
165 | + > | |
166 | + <template #[replaceFormSlotKey(item)]="data" v-for="item in getFormSlotKeys"> | |
167 | + <slot :name="item" v-bind="data"></slot> | |
168 | + </template> | |
169 | + </BasicForm> | |
170 | + </section> | |
171 | + | |
172 | + <section class="w-full bg-light-50 dark:bg-dark-900 p-4 flex-auto h-full"> | |
173 | + <List ref="listElRef" v-bind="getBindValues"> | |
174 | + <template #header> | |
175 | + <section class="flex justify-between items-center"> | |
176 | + <div> | |
177 | + <span v-if="getProps.title" class="font-bold text-base px-4"> | |
178 | + {{ getProps.title }} | |
179 | + </span> | |
180 | + <slot v-else name="title"></slot> | |
181 | + </div> | |
182 | + <div class="flex flex-auto justify-end mr-4 gap-4"> | |
183 | + <slot name="toolbar"></slot> | |
184 | + </div> | |
185 | + <div v-if="showCardListSetting" class="flex gap-4"> | |
186 | + <DropdownButton v-if="getProps.selections" @click="selectAllToggle" type="primary"> | |
187 | + <template #overlay> | |
188 | + <Menu> | |
189 | + <Menu.Item @click="selectedAll">全选</Menu.Item> | |
190 | + <Menu.Item @click="clearSelectedKeys">反选</Menu.Item> | |
191 | + </Menu> | |
192 | + </template> | |
193 | + <span> | |
194 | + {{ selectedKeys.length === getDataSourceRef.length ? '反选' : '全选' }} | |
195 | + </span> | |
196 | + </DropdownButton> | |
197 | + <CardListLayout v-model:row="cardListLayout.row" v-model:col="cardListLayout.col" /> | |
198 | + <Tooltip title="刷新"> | |
199 | + <Button type="primary" @click="reload()" :loading="loading"> | |
200 | + <Icon icon="ant-design:reload-outlined" :size="20" /> | |
201 | + </Button> | |
202 | + </Tooltip> | |
203 | + </div> | |
204 | + </section> | |
205 | + </template> | |
206 | + <template #renderItem="{ item }: BasicCardListRenderItem<any>"> | |
207 | + <List.Item | |
208 | + :class="selectedKeys.includes(item[getRowKey]) ? selectedClass : ''" | |
209 | + @click="handlerSelected($event, item)" | |
210 | + > | |
211 | + <slot | |
212 | + name="renderItem" | |
213 | + :item="item" | |
214 | + :totalHeight="containerHeight" | |
215 | + :checked="item?.checked" | |
216 | + > | |
217 | + </slot> | |
218 | + </List.Item> | |
219 | + </template> | |
220 | + </List> | |
221 | + </section> | |
222 | + </main> | |
223 | +</template> | |
224 | + | |
225 | +<style lang="less" scoped> | |
226 | + .basic-card-list { | |
227 | + :deep(.ant-list) { | |
228 | + .ant-spin-container { | |
229 | + overflow-x: hidden; | |
230 | + overflow-y: auto; | |
231 | + | |
232 | + .ant-list-item { | |
233 | + border: 2px transparent solid; | |
234 | + position: relative; | |
235 | + | |
236 | + &.basic-card-list-item-checked { | |
237 | + border: 2px solid #377dff; | |
238 | + | |
239 | + &::after { | |
240 | + position: absolute; | |
241 | + inset-block-start: 2px; | |
242 | + inset-inline-end: 2px; | |
243 | + width: 0; | |
244 | + height: 0; | |
245 | + border: 10px solid #1677ff; | |
246 | + border-block-end: 10px solid transparent; | |
247 | + border-inline-start: 10px solid transparent; | |
248 | + // border-start-end-radius: 6px; | |
249 | + content: ''; | |
250 | + } | |
251 | + | |
252 | + &::before { | |
253 | + content: ''; | |
254 | + position: absolute; | |
255 | + background-color: #53a2fd; | |
256 | + width: 100%; | |
257 | + height: 100%; | |
258 | + opacity: 0.2; | |
259 | + z-index: 99; | |
260 | + } | |
261 | + } | |
262 | + } | |
263 | + } | |
264 | + | |
265 | + .ant-list-header { | |
266 | + border-bottom: transparent; | |
267 | + } | |
268 | + | |
269 | + .ant-list-pagination { | |
270 | + margin-top: 16px; | |
271 | + height: 25px; | |
272 | + } | |
273 | + | |
274 | + .ant-list-empty-text { | |
275 | + height: 100%; | |
276 | + display: flex; | |
277 | + align-items: center; | |
278 | + justify-content: center; | |
279 | + } | |
280 | + } | |
281 | + } | |
282 | +</style> | ... | ... |
1 | +<script setup lang="ts"> | |
2 | + import { Button, Popover } from 'ant-design-vue'; | |
3 | + import { reactive, ref, watch } from 'vue'; | |
4 | + import { Icon } from '/@/components/Icon'; | |
5 | + | |
6 | + const emit = defineEmits(['update:row', 'update:col']); | |
7 | + | |
8 | + const props = withDefaults( | |
9 | + defineProps<{ | |
10 | + row?: number; | |
11 | + col?: number; | |
12 | + }>(), | |
13 | + { | |
14 | + row: 2, | |
15 | + col: 5, | |
16 | + } | |
17 | + ); | |
18 | + | |
19 | + const MAX_ROW = 4; | |
20 | + const MAX_COL = 9; | |
21 | + | |
22 | + const visible = ref(false); | |
23 | + | |
24 | + const selectedLayout = reactive({ row: props.row, col: props.col }); | |
25 | + | |
26 | + const handleOver = (row: number, col: number) => { | |
27 | + Object.assign(selectedLayout, { row, col }); | |
28 | + }; | |
29 | + | |
30 | + const handleSelectConfirm = () => { | |
31 | + const { row, col } = selectedLayout; | |
32 | + emit('update:row', row); | |
33 | + emit('update:col', col); | |
34 | + visible.value = false; | |
35 | + }; | |
36 | + | |
37 | + watch( | |
38 | + () => [props.row, props.col, visible.value], | |
39 | + () => { | |
40 | + selectedLayout.row = props.row; | |
41 | + selectedLayout.col = props.col; | |
42 | + } | |
43 | + ); | |
44 | +</script> | |
45 | + | |
46 | +<template> | |
47 | + <Popover v-model:visible="visible"> | |
48 | + <template #content> | |
49 | + <section class="flex flex-wrap w-36"> | |
50 | + <table class="border-collapse" @click="handleSelectConfirm"> | |
51 | + <tr v-for="row in MAX_ROW" :key="row" class="border border-gray-300 border-solid"> | |
52 | + <td | |
53 | + v-for="col in MAX_COL" | |
54 | + :class="selectedLayout.col >= col && selectedLayout.row >= row ? 'bg-blue-500' : ''" | |
55 | + :key="col" | |
56 | + class="w-4 h-4 border border-gray-300 border-solid cursor-pointer" | |
57 | + @mouseover="handleOver(row, col)" | |
58 | + > | |
59 | + </td> | |
60 | + </tr> | |
61 | + </table> | |
62 | + <div class="text-center w-full"> | |
63 | + {{ selectedLayout.col }} x {{ selectedLayout.row }} 布局 | |
64 | + </div> | |
65 | + </section> | |
66 | + </template> | |
67 | + <Button type="primary"> | |
68 | + <Icon icon="ant-design:layout-filled" :size="20" /> | |
69 | + </Button> | |
70 | + </Popover> | |
71 | +</template> | ... | ... |
1 | +import type { ComputedRef, ExtractPropTypes, Slots } from 'vue'; | |
2 | +import { unref, computed } from 'vue'; | |
3 | +import { isFunction } from '/@/utils/is'; | |
4 | +import { BasicCardListPropsType } from '../types'; | |
5 | +import { FetchParams } from './useCardListData'; | |
6 | +import { basicProps } from '/@/components/Form/src/props'; | |
7 | + | |
8 | +export function useCardForm( | |
9 | + propsRef: ComputedRef<BasicCardListPropsType>, | |
10 | + slots: Slots, | |
11 | + fetch: (opt?: FetchParams | undefined) => Promise<void>, | |
12 | + getLoading: ComputedRef<boolean | undefined> | |
13 | +) { | |
14 | + const getFormProps = computed((): ExtractPropTypes<typeof basicProps> => { | |
15 | + const { formConfig } = unref(propsRef); | |
16 | + const { submitButtonOptions } = formConfig || {}; | |
17 | + return { | |
18 | + showAdvancedButton: true, | |
19 | + ...formConfig, | |
20 | + submitButtonOptions: { loading: unref(getLoading), ...submitButtonOptions }, | |
21 | + compact: true, | |
22 | + } as any; | |
23 | + }); | |
24 | + | |
25 | + const getFormSlotKeys: ComputedRef<string[]> = computed(() => { | |
26 | + const keys = Object.keys(slots); | |
27 | + return keys | |
28 | + .map((item) => (item.startsWith('form-') ? item : null)) | |
29 | + .filter((item) => !!item) as string[]; | |
30 | + }); | |
31 | + | |
32 | + function replaceFormSlotKey(key: string) { | |
33 | + if (!key) return ''; | |
34 | + return key?.replace?.(/form\-/, '') ?? ''; | |
35 | + } | |
36 | + | |
37 | + function handleSearchInfoChange(info: Recordable) { | |
38 | + const { handleSearchInfoFn } = unref(propsRef); | |
39 | + if (handleSearchInfoFn && isFunction(handleSearchInfoFn)) { | |
40 | + info = handleSearchInfoFn(info) || info; | |
41 | + } | |
42 | + fetch({ searchInfo: info, page: 1 }); | |
43 | + } | |
44 | + | |
45 | + return { | |
46 | + getFormProps, | |
47 | + replaceFormSlotKey, | |
48 | + getFormSlotKeys, | |
49 | + handleSearchInfoChange, | |
50 | + }; | |
51 | +} | ... | ... |
1 | +import { WatchStopHandle, ref, unref, watch } from 'vue'; | |
2 | +import { BasicCardListPropsType, CardListActionType } from '../types'; | |
3 | +import { isProdMode } from '/@/utils/env'; | |
4 | +import { error } from '/@/utils/log'; | |
5 | +import { FormActionType } from '/@/components/Form'; | |
6 | +import { getDynamicProps } from '/@/utils'; | |
7 | +import { PaginationProps } from 'ant-design-vue'; | |
8 | +import { FetchParams } from './useCardListData'; | |
9 | +import { tryOnUnmounted } from '@vueuse/core'; | |
10 | + | |
11 | +type UseCardListMethod = CardListActionType & { | |
12 | + getForm: () => FormActionType; | |
13 | +}; | |
14 | + | |
15 | +export function useCardList( | |
16 | + cardListProps?: BasicCardListPropsType | |
17 | +): [(instance: CardListActionType, formInstance: FormActionType) => void, CardListActionType] { | |
18 | + const cardListRef = ref<Nullable<CardListActionType>>(null); | |
19 | + const loadedRef = ref<Nullable<boolean>>(false); | |
20 | + const formRef = ref<Nullable<FormActionType>>(null); | |
21 | + | |
22 | + let stopWatch: WatchStopHandle; | |
23 | + | |
24 | + function register(instance: CardListActionType, formInstance: FormActionType) { | |
25 | + isProdMode() && | |
26 | + tryOnUnmounted(() => { | |
27 | + cardListRef.value = null; | |
28 | + loadedRef.value = null; | |
29 | + }); | |
30 | + | |
31 | + if (unref(loadedRef) && isProdMode() && instance === unref(cardListRef)) return; | |
32 | + | |
33 | + cardListRef.value = instance; | |
34 | + formRef.value = formInstance; | |
35 | + cardListProps && instance.setProps(getDynamicProps(cardListProps)); | |
36 | + loadedRef.value = true; | |
37 | + | |
38 | + stopWatch?.(); | |
39 | + | |
40 | + stopWatch = watch( | |
41 | + () => cardListProps, | |
42 | + () => { | |
43 | + cardListProps && instance.setProps(getDynamicProps(cardListProps)); | |
44 | + }, | |
45 | + { | |
46 | + immediate: true, | |
47 | + deep: true, | |
48 | + } | |
49 | + ); | |
50 | + } | |
51 | + | |
52 | + function getCardListInstance(): CardListActionType { | |
53 | + const cardList = unref(cardListRef); | |
54 | + if (!cardList) { | |
55 | + error( | |
56 | + 'The CardList instance has not been obtained yet, please make sure the card list is presented when performing the card list operation!' | |
57 | + ); | |
58 | + } | |
59 | + return cardList as CardListActionType; | |
60 | + } | |
61 | + | |
62 | + const cardListActionType: UseCardListMethod = { | |
63 | + getForm: () => { | |
64 | + return unref(formRef) as unknown as FormActionType; | |
65 | + }, | |
66 | + setLoading: (loading: boolean) => { | |
67 | + return getCardListInstance().setLoading(loading); | |
68 | + }, | |
69 | + setProps: (props: Partial<BasicCardListPropsType>) => { | |
70 | + getCardListInstance().setProps(props); | |
71 | + }, | |
72 | + getPagination: () => { | |
73 | + return getCardListInstance().getPagination(); | |
74 | + }, | |
75 | + setPagination: (pagination: Partial<PaginationProps>) => { | |
76 | + return getCardListInstance().setPagination(pagination); | |
77 | + }, | |
78 | + reload: (opt?: FetchParams) => { | |
79 | + return getCardListInstance().reload(opt); | |
80 | + }, | |
81 | + selectedAll: () => { | |
82 | + return getCardListInstance().selectedAll(); | |
83 | + }, | |
84 | + clearSelectedKeys: () => { | |
85 | + return getCardListInstance().clearSelectedKeys(); | |
86 | + }, | |
87 | + getSelectedKeys: () => { | |
88 | + return getCardListInstance().getSelectedKeys(); | |
89 | + }, | |
90 | + getSelectedRecords: () => { | |
91 | + return getCardListInstance().getSelectedRecords(); | |
92 | + }, | |
93 | + getSelections: () => { | |
94 | + return getCardListInstance().getSelections(); | |
95 | + }, | |
96 | + }; | |
97 | + | |
98 | + return [register, cardListActionType]; | |
99 | +} | ... | ... |
1 | +import { Ref, computed, onMounted, ref, unref, watch, watchEffect } from 'vue'; | |
2 | +import { BasicCardListPropsType, CardListEmitType, UseLoading, UsePaginationType } from '../types'; | |
3 | +import { FormActionType } from '/@/components/Form'; | |
4 | +import { isBoolean, isFunction } from '/@/utils/is'; | |
5 | +import { FETCH_SETTING, ROW_KEY } from '/@/components/Table/src/const'; | |
6 | +import { PaginationProps } from 'ant-design-vue'; | |
7 | +import { cloneDeep, get } from 'lodash-es'; | |
8 | +import { buildUUID } from '/@/utils/uuid'; | |
9 | +import { useTimeoutFn } from '/@/hooks/core/useTimeout'; | |
10 | + | |
11 | +interface ActionType { | |
12 | + setLoading: UseLoading['setLoading']; | |
13 | + getPaginationInfo: UsePaginationType['getPaginationInfo']; | |
14 | + setPagination: UsePaginationType['setPagination']; | |
15 | + tableData: Ref<Recordable[]>; | |
16 | + emit: CardListEmitType; | |
17 | +} | |
18 | + | |
19 | +export interface FetchParams { | |
20 | + searchInfo?: Recordable; | |
21 | + page?: number; | |
22 | + sortInfo?: Recordable; | |
23 | + filterInfo?: Recordable; | |
24 | +} | |
25 | + | |
26 | +export function useCardListData( | |
27 | + propsRef: Ref<BasicCardListPropsType>, | |
28 | + formActionType: FormActionType, | |
29 | + actionType: ActionType | |
30 | +) { | |
31 | + const { getFieldsValue } = unref(formActionType)! || {}; | |
32 | + const { getPaginationInfo, setLoading, setPagination, tableData, emit } = actionType; | |
33 | + | |
34 | + const dataSourceRef = ref<Recordable[]>([]); | |
35 | + const rawDataSourceRef = ref<Recordable>({}); | |
36 | + | |
37 | + watchEffect(() => { | |
38 | + tableData.value = unref(dataSourceRef); | |
39 | + }); | |
40 | + | |
41 | + watch( | |
42 | + () => unref(propsRef).dataSource, | |
43 | + () => { | |
44 | + const { dataSource, api } = unref(propsRef); | |
45 | + !api && dataSource && (dataSourceRef.value = dataSource); | |
46 | + }, | |
47 | + { | |
48 | + immediate: true, | |
49 | + } | |
50 | + ); | |
51 | + | |
52 | + function setCardKey(items: any[]) { | |
53 | + if (!items || !Array.isArray(items)) return; | |
54 | + items.forEach((item) => { | |
55 | + if (!item[ROW_KEY]) { | |
56 | + item[ROW_KEY] = buildUUID(); | |
57 | + } | |
58 | + }); | |
59 | + } | |
60 | + | |
61 | + const getAutoCreateKey = computed<boolean>(() => { | |
62 | + return !!(unref(propsRef).autoCreateKey && !unref(propsRef).rowKey); | |
63 | + }); | |
64 | + | |
65 | + const getRowKey = computed<string | number>(() => { | |
66 | + const { rowKey } = unref(propsRef); | |
67 | + return unref(getAutoCreateKey) ? ROW_KEY : rowKey!; | |
68 | + }); | |
69 | + | |
70 | + const getDataSourceRef = computed(() => { | |
71 | + const dataSource = unref(dataSourceRef); | |
72 | + if (!dataSource || dataSource.length === 0) { | |
73 | + return unref(dataSourceRef); | |
74 | + } | |
75 | + if (unref(getAutoCreateKey)) { | |
76 | + const firstItem = dataSource[0]; | |
77 | + const lastItem = dataSource[dataSource.length - 1]; | |
78 | + | |
79 | + if (firstItem && lastItem) { | |
80 | + if (!firstItem[ROW_KEY] || !lastItem[ROW_KEY]) { | |
81 | + const data = cloneDeep(unref(dataSourceRef)); | |
82 | + data.forEach((item) => { | |
83 | + if (!item[ROW_KEY]) { | |
84 | + item[ROW_KEY] = buildUUID(); | |
85 | + } | |
86 | + if (item.children && item.children.length) { | |
87 | + setCardKey(item.children); | |
88 | + } | |
89 | + }); | |
90 | + dataSourceRef.value = data; | |
91 | + } | |
92 | + } | |
93 | + } | |
94 | + | |
95 | + return unref(dataSourceRef); | |
96 | + }); | |
97 | + | |
98 | + async function fetch(opt?: FetchParams) { | |
99 | + const { api, searchInfo, fetchSetting, beforeFetch, afterFetch, useSearchForm, pagination } = | |
100 | + unref(propsRef); | |
101 | + | |
102 | + if (!api || !isFunction(api)) return; | |
103 | + | |
104 | + try { | |
105 | + setLoading(true); | |
106 | + const { pageField, sizeField, listField, totalField } = Object.assign( | |
107 | + {}, | |
108 | + FETCH_SETTING, | |
109 | + fetchSetting | |
110 | + ); | |
111 | + let pageParams: Recordable = {}; | |
112 | + | |
113 | + const { current = 1, pageSize = 10 } = unref(getPaginationInfo) as PaginationProps; | |
114 | + | |
115 | + if ((isBoolean(pagination) && !pagination) || isBoolean(getPaginationInfo)) { | |
116 | + pageParams = {}; | |
117 | + } else { | |
118 | + pageParams[pageField] = current; | |
119 | + pageParams[sizeField] = pageSize; | |
120 | + } | |
121 | + | |
122 | + let params: Recordable = { | |
123 | + ...pageParams, | |
124 | + ...(useSearchForm ? getFieldsValue() : {}), | |
125 | + ...searchInfo, | |
126 | + }; | |
127 | + if (beforeFetch && isFunction(beforeFetch)) { | |
128 | + params = (await beforeFetch(params)) || params; | |
129 | + } | |
130 | + | |
131 | + const res = await api(params); | |
132 | + rawDataSourceRef.value = res; | |
133 | + | |
134 | + const isArrayResult = Array.isArray(res); | |
135 | + | |
136 | + let resultItems: Recordable[] = isArrayResult ? res : get(res, listField); | |
137 | + const resultTotal: number = isArrayResult ? 0 : get(res, totalField); | |
138 | + | |
139 | + // 假如数据变少,导致总页数变少并小于当前选中页码,通过getPaginationRef获取到的页码是不正确的,需获取正确的页码再次执行 | |
140 | + if (resultTotal) { | |
141 | + const currentTotalPage = Math.ceil(resultTotal / pageSize); | |
142 | + if (current > currentTotalPage) { | |
143 | + setPagination({ | |
144 | + current: currentTotalPage, | |
145 | + }); | |
146 | + fetch(opt); | |
147 | + } | |
148 | + } | |
149 | + | |
150 | + if (afterFetch && isFunction(afterFetch)) { | |
151 | + resultItems = (await afterFetch(resultItems, res)) || resultItems; | |
152 | + } | |
153 | + dataSourceRef.value = resultItems; | |
154 | + setPagination({ | |
155 | + total: resultTotal || 0, | |
156 | + }); | |
157 | + if (opt && opt.page) { | |
158 | + setPagination({ | |
159 | + current: opt.page || 1, | |
160 | + }); | |
161 | + } | |
162 | + emit('fetchSuccess', { | |
163 | + items: unref(resultItems), | |
164 | + total: resultTotal, | |
165 | + }); | |
166 | + } catch (error) { | |
167 | + emit('fetchError', error as Error); | |
168 | + dataSourceRef.value = []; | |
169 | + setPagination({ | |
170 | + total: 0, | |
171 | + }); | |
172 | + } finally { | |
173 | + setLoading(false); | |
174 | + } | |
175 | + } | |
176 | + | |
177 | + function findCardDataRecord(rowKey: string | number) { | |
178 | + if (!dataSourceRef.value || dataSourceRef.value.length == 0) return; | |
179 | + | |
180 | + const rowKeyName = unref(getRowKey); | |
181 | + if (!rowKeyName) return; | |
182 | + | |
183 | + const findRow = (array: any[]) => { | |
184 | + let ret; | |
185 | + array.some(function iter(r) { | |
186 | + if (Reflect.has(r, rowKeyName) && r[rowKeyName] === rowKey) { | |
187 | + ret = r; | |
188 | + return true; | |
189 | + } | |
190 | + }); | |
191 | + return ret; | |
192 | + }; | |
193 | + | |
194 | + return findRow(dataSourceRef.value); | |
195 | + } | |
196 | + | |
197 | + function updateTableDataRecord( | |
198 | + rowKey: string | number, | |
199 | + record: Recordable | |
200 | + ): Recordable | undefined { | |
201 | + const row = findCardDataRecord(rowKey); | |
202 | + if (row) { | |
203 | + for (const field in row) { | |
204 | + if (Reflect.has(record, field)) row[field] = record[field]; | |
205 | + } | |
206 | + return row; | |
207 | + } | |
208 | + } | |
209 | + | |
210 | + async function reload(opt?: FetchParams) { | |
211 | + await fetch(opt); | |
212 | + } | |
213 | + | |
214 | + function setCardListData<T = Recordable>(values: T[]) { | |
215 | + dataSourceRef.value = values as Recordable[]; | |
216 | + } | |
217 | + | |
218 | + onMounted(() => { | |
219 | + useTimeoutFn(() => { | |
220 | + unref(propsRef).immediate && fetch(); | |
221 | + }, 16); | |
222 | + }); | |
223 | + | |
224 | + return { | |
225 | + reload, | |
226 | + fetch, | |
227 | + getRowKey, | |
228 | + getDataSourceRef, | |
229 | + setCardListData, | |
230 | + findCardDataRecord, | |
231 | + updateTableDataRecord, | |
232 | + }; | |
233 | +} | ... | ... |
1 | +import { Ref, ref, unref, watch } from 'vue'; | |
2 | +import { getBoundingClientRect } from '/@/utils/domUtils'; | |
3 | +import { BasicCardListPropsType } from '../types'; | |
4 | +import { useDebounceFn } from '@vueuse/core'; | |
5 | + | |
6 | +export function useCardListScroll( | |
7 | + cardListElRef: Ref<Nullable<ComponentElRef>>, | |
8 | + propsRef: Ref<BasicCardListPropsType>, | |
9 | + getDataSourceRef: Ref<Recordable[]> | |
10 | +) { | |
11 | + const containerHeight = ref(0); | |
12 | + | |
13 | + const redoHeight = () => { | |
14 | + const listEl = unref(cardListElRef)?.$el; | |
15 | + if (!listEl) return; | |
16 | + | |
17 | + const listContainerEl = listEl.querySelector('.ant-spin-container') as HTMLDivElement; | |
18 | + | |
19 | + if (!listContainerEl) return; | |
20 | + | |
21 | + const rect = getBoundingClientRect(listContainerEl); | |
22 | + | |
23 | + if (!rect) return; | |
24 | + | |
25 | + const { top } = rect as DOMRect; | |
26 | + | |
27 | + const { offsetHeight: otherOffsetHeight = 0 } = unref(propsRef); | |
28 | + | |
29 | + const totalHeight = document.documentElement.clientHeight; | |
30 | + | |
31 | + const offsetHeight = 32; | |
32 | + | |
33 | + const paginationHeight = 25 + 16; | |
34 | + | |
35 | + const residualHeight = totalHeight - top - offsetHeight - paginationHeight - otherOffsetHeight; | |
36 | + | |
37 | + listContainerEl.style.maxHeight = `${residualHeight}px`; | |
38 | + listContainerEl.style.height = `${residualHeight}px`; | |
39 | + | |
40 | + containerHeight.value = residualHeight; | |
41 | + }; | |
42 | + | |
43 | + const debounceRedoHeight = useDebounceFn(redoHeight, 100); | |
44 | + | |
45 | + watch( | |
46 | + () => [unref(getDataSourceRef)?.length], | |
47 | + () => { | |
48 | + debounceRedoHeight(); | |
49 | + }, | |
50 | + { | |
51 | + flush: 'post', | |
52 | + } | |
53 | + ); | |
54 | + | |
55 | + return { redoHeight, containerHeight }; | |
56 | +} | ... | ... |
1 | +import { ComputedRef, computed, ref, toRaw, unref, watch } from 'vue'; | |
2 | +import { BasicCardListPropsType, CardListSelectionsType, UseCardListDataType } from '../types'; | |
3 | +import { isBoolean, isFunction } from '/@/utils/is'; | |
4 | +import { cloneDeep } from 'lodash-es'; | |
5 | + | |
6 | +export const CHECKED_FIELD = 'CARD_LIST_CHECKED_STATUS'; | |
7 | + | |
8 | +export const DEFAULT_SELECTED_CLASS = 'basic-card-list-item-checked'; | |
9 | + | |
10 | +interface CardListSelectActionType { | |
11 | + updateTableDataRecord: UseCardListDataType['updateTableDataRecord']; | |
12 | + findCardDataRecord: UseCardListDataType['findCardDataRecord']; | |
13 | + getRowKey: UseCardListDataType['getRowKey']; | |
14 | + setCardListData: UseCardListDataType['setCardListData']; | |
15 | +} | |
16 | + | |
17 | +export function useCardListSelected( | |
18 | + getDataSourceRef: ComputedRef<Recordable[]>, | |
19 | + propsRef: ComputedRef<BasicCardListPropsType>, | |
20 | + { getRowKey }: CardListSelectActionType | |
21 | +) { | |
22 | + const selectedKeys = ref<string[]>([]); | |
23 | + | |
24 | + const selectedClass = ref(''); | |
25 | + | |
26 | + const selectionRef = ref<CardListSelectionsType>({}); | |
27 | + | |
28 | + const getSelectionsRef = computed<CardListSelectionsType & { enabled: boolean }>(() => { | |
29 | + const { selections } = unref(propsRef); | |
30 | + return { | |
31 | + enabled: !!selections, | |
32 | + ...unref(selectionRef), | |
33 | + ...(isBoolean(selections) ? {} : selections), | |
34 | + }; | |
35 | + }); | |
36 | + | |
37 | + const getHasSelectedRecordStatus = computed(() => | |
38 | + unref(getDataSourceRef).find((item) => item.checked) | |
39 | + ); | |
40 | + | |
41 | + const getShouldUseDefaultStyle = computed( | |
42 | + () => unref(getSelectionsRef).enabled && !unref(getSelectionsRef).customCheckedStyle | |
43 | + ); | |
44 | + | |
45 | + watch( | |
46 | + getSelectionsRef, | |
47 | + () => { | |
48 | + selectedClass.value = | |
49 | + isBoolean(unref(propsRef).selections) || | |
50 | + !(unref(propsRef).selections as CardListSelectionsType).customCheckedStyle | |
51 | + ? DEFAULT_SELECTED_CLASS | |
52 | + : ''; | |
53 | + }, | |
54 | + { | |
55 | + immediate: true, | |
56 | + } | |
57 | + ); | |
58 | + | |
59 | + function handlerSelected(_event: MouseEvent, record: Recordable) { | |
60 | + if (!unref(getSelectionsRef).enabled) return; | |
61 | + | |
62 | + if (unref(getSelectionsRef).beforeSelectValidate) { | |
63 | + if (!unref(getSelectionsRef).beforeSelectValidate?.(record)) return; | |
64 | + } | |
65 | + | |
66 | + const rowKey = record[unref(getRowKey)]; | |
67 | + const index = unref(selectedKeys).findIndex((key) => key === rowKey); | |
68 | + ~index ? selectedKeys.value.splice(index, 1) : unref(selectedKeys).push(rowKey); | |
69 | + | |
70 | + if (!unref(getSelectionsRef).onSelect) return; | |
71 | + | |
72 | + unref(getSelectionsRef)?.onSelect?.(record, !!~index, getSelectedRecords()); | |
73 | + } | |
74 | + | |
75 | + function selectedAll() { | |
76 | + selectedKeys.value = unref(getDataSourceRef) | |
77 | + .filter( | |
78 | + (item) => | |
79 | + isFunction(unref(getSelectionsRef).beforeSelectValidate) && | |
80 | + unref(getSelectionsRef).beforeSelectValidate?.(item) | |
81 | + ) | |
82 | + .map((item) => item[unref(getRowKey)]); | |
83 | + | |
84 | + if (!unref(getSelectionsRef).onSelect) return; | |
85 | + | |
86 | + unref(getSelectionsRef)?.onSelectAll?.(toRaw(unref(getDataSourceRef))); | |
87 | + } | |
88 | + | |
89 | + function clearSelectedKeys() { | |
90 | + selectedKeys.value = []; | |
91 | + } | |
92 | + | |
93 | + function getSelectedKeys() { | |
94 | + return toRaw(unref(selectedKeys)); | |
95 | + } | |
96 | + | |
97 | + function getSelectedRecords() { | |
98 | + const data = cloneDeep(unref(getDataSourceRef)); | |
99 | + return data.filter((item) => unref(selectedKeys).includes(item[unref(getRowKey)])); | |
100 | + } | |
101 | + | |
102 | + function selectAllToggle() { | |
103 | + unref(getDataSourceRef).length === unref(selectedKeys).length | |
104 | + ? (selectedKeys.value = []) | |
105 | + : selectedAll(); | |
106 | + } | |
107 | + | |
108 | + function getSelections() { | |
109 | + return unref(getSelectionsRef); | |
110 | + } | |
111 | + | |
112 | + return { | |
113 | + selectedKeys, | |
114 | + selectedClass, | |
115 | + getShouldUseDefaultStyle, | |
116 | + getHasSelectedRecordStatus, | |
117 | + getSelections, | |
118 | + handlerSelected, | |
119 | + selectedAll, | |
120 | + selectAllToggle, | |
121 | + clearSelectedKeys, | |
122 | + getSelectedKeys, | |
123 | + getSelectedRecords, | |
124 | + }; | |
125 | +} | ... | ... |
1 | +import { Ref, computed, reactive, unref, watch } from 'vue'; | |
2 | +import { BasicCardListPropsType, ListGridType } from '../types'; | |
3 | +import { getListGridByColumn } from '../utils'; | |
4 | +import { screenMap, sizeEnum } from '/@/enums/breakpointEnum'; | |
5 | + | |
6 | +export function useListGrid(getProps: Ref<BasicCardListPropsType>) { | |
7 | + const cardListLayout = reactive({ row: 2, col: 5 }); | |
8 | + | |
9 | + const getListGrid = computed<ListGridType>(() => { | |
10 | + const { col } = cardListLayout; | |
11 | + const { gutter = 16 } = unref(getProps); | |
12 | + return { | |
13 | + column: col, | |
14 | + gutter, | |
15 | + ...getListGridByColumn(col), | |
16 | + } as ListGridType; | |
17 | + }); | |
18 | + | |
19 | + watch( | |
20 | + () => unref(getProps).baseLayout, | |
21 | + () => { | |
22 | + Object.assign(cardListLayout, unref(getProps).baseLayout); | |
23 | + } | |
24 | + ); | |
25 | + | |
26 | + function getScreenSize() { | |
27 | + const width = document.body.clientWidth; | |
28 | + const xs = screenMap.get(sizeEnum.XS)!; | |
29 | + const sm = screenMap.get(sizeEnum.SM)!; | |
30 | + const md = screenMap.get(sizeEnum.MD)!; | |
31 | + const lg = screenMap.get(sizeEnum.LG)!; | |
32 | + const xl = screenMap.get(sizeEnum.XL)!; | |
33 | + if (width < xs) { | |
34 | + return sizeEnum.XS; | |
35 | + } else if (width < sm) { | |
36 | + return sizeEnum.SM; | |
37 | + } else if (width < md) { | |
38 | + return sizeEnum.MD; | |
39 | + } else if (width < lg) { | |
40 | + return sizeEnum.LG; | |
41 | + } else if (width < xl) { | |
42 | + return sizeEnum.XL; | |
43 | + } else { | |
44 | + return sizeEnum.XXL; | |
45 | + } | |
46 | + } | |
47 | + | |
48 | + return { | |
49 | + getListGrid, | |
50 | + cardListLayout, | |
51 | + getScreenSize, | |
52 | + }; | |
53 | +} | ... | ... |
1 | +import { computed, ref, unref } from 'vue'; | |
2 | + | |
3 | +export function useLoading() { | |
4 | + const loading = ref(false); | |
5 | + | |
6 | + const setLoading = (status: boolean) => (loading.value = status); | |
7 | + | |
8 | + const getLoading = computed(() => unref(loading)); | |
9 | + | |
10 | + return { | |
11 | + loading, | |
12 | + getLoading, | |
13 | + setLoading, | |
14 | + }; | |
15 | +} | ... | ... |
1 | +import { PaginationProps } from 'ant-design-vue'; | |
2 | +import { Ref, computed, ref, unref } from 'vue'; | |
3 | +import { LeftOutlined, RightOutlined } from '@ant-design/icons-vue'; | |
4 | +import { BasicCardListPropsType } from '../types'; | |
5 | +import { isBoolean } from '/@/utils/is'; | |
6 | + | |
7 | +interface ItemRender { | |
8 | + page: number; | |
9 | + type: 'page' | 'prev' | 'next'; | |
10 | + originalElement: any; | |
11 | +} | |
12 | + | |
13 | +function itemRender({ page, type, originalElement }: ItemRender) { | |
14 | + if (type === 'prev') { | |
15 | + return page === 0 ? null : <LeftOutlined />; | |
16 | + } else if (type === 'next') { | |
17 | + return page === 1 ? null : <RightOutlined />; | |
18 | + } | |
19 | + return originalElement; | |
20 | +} | |
21 | + | |
22 | +export function usePagination( | |
23 | + propsRef: Ref<BasicCardListPropsType>, | |
24 | + cardLayoutRef: Record<'row' | 'col', number> | |
25 | +) { | |
26 | + const configRef = ref<PaginationProps>({ | |
27 | + hideOnSinglePage: false, | |
28 | + }); | |
29 | + | |
30 | + const getPaginationInfo = computed(() => { | |
31 | + const { pagination } = unref(propsRef); | |
32 | + const { col, row } = cardLayoutRef; | |
33 | + | |
34 | + return { | |
35 | + current: 1, | |
36 | + pageSize: col * row, | |
37 | + size: 'small', | |
38 | + defaultPageSize: col * row, | |
39 | + showTotal: (total: number) => `共 ${total} 条数据`, | |
40 | + showSizeChanger: false, | |
41 | + itemRender: itemRender, | |
42 | + showQuickJumper: true, | |
43 | + ...(isBoolean(pagination) ? {} : pagination), | |
44 | + ...unref(configRef), | |
45 | + } as Partial<PaginationProps>; | |
46 | + }); | |
47 | + | |
48 | + const setPagination = (info: Partial<PaginationProps>) => { | |
49 | + const paginationInfo = unref(getPaginationInfo); | |
50 | + configRef.value = { | |
51 | + ...(!isBoolean(paginationInfo) ? paginationInfo : {}), | |
52 | + ...info, | |
53 | + }; | |
54 | + }; | |
55 | + | |
56 | + function getPagination() { | |
57 | + return unref(getPaginationInfo); | |
58 | + } | |
59 | + | |
60 | + return { getPaginationInfo, setPagination, getPagination }; | |
61 | +} | ... | ... |
src/components/CardList/src/types.ts
0 → 100644
1 | +import { PaginationProps } from 'ant-design-vue'; | |
2 | +import { FormProps } from '../../Form'; | |
3 | +import { FetchSetting } from '../../Table'; | |
4 | +import { useLoading } from './hooks/useLoading'; | |
5 | +import { usePagination } from './hooks/usePagination'; | |
6 | +import { FetchParams, useCardListData } from './hooks/useCardListData'; | |
7 | +import { useCardListSelected } from './hooks/useCardListSelected'; | |
8 | + | |
9 | +// export interface CardList | |
10 | + | |
11 | +export interface BasicCardListPropsType<T = Recordable> { | |
12 | + title?: string; | |
13 | + formConfig?: FormProps; | |
14 | + showCardListSetting?: boolean; | |
15 | + useSearchForm?: boolean; | |
16 | + api?: (params?: any) => Promise<any>; | |
17 | + dataSource?: any[]; | |
18 | + beforeFetch?: (params: FetchParams) => Promise<Recordable>; | |
19 | + pagination?: PaginationProps; | |
20 | + offsetHeight?: number; | |
21 | + gutter?: number; | |
22 | + searchInfo?: Recordable; | |
23 | + fetchSetting?: FetchSetting; | |
24 | + afterFetch?: (items: T[], result: any) => Promise<any[]>; | |
25 | + autoCreateKey?: boolean; | |
26 | + rowKey?: string | number; | |
27 | + immediate?: boolean; | |
28 | + handleSearchInfoFn?: Fn; | |
29 | + baseLayout?: Record<'row' | 'col', number>; | |
30 | + selections?: boolean | CardListSelectionsType; | |
31 | +} | |
32 | + | |
33 | +export type ListGridType = Record<'column' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl', number> & { | |
34 | + gutter: number | [number, number]; | |
35 | +}; | |
36 | + | |
37 | +export type UseLoading = ReturnType<typeof useLoading>; | |
38 | + | |
39 | +export type UsePaginationType = ReturnType<typeof usePagination>; | |
40 | + | |
41 | +export type UseCardListDataType = ReturnType<typeof useCardListData>; | |
42 | + | |
43 | +export type UseCardListSelected = ReturnType<typeof useCardListSelected>; | |
44 | + | |
45 | +export interface CardListActionType { | |
46 | + setProps: (props: Partial<BasicCardListPropsType>) => void; | |
47 | + setLoading: UseLoading['setLoading']; | |
48 | + setPagination: UsePaginationType['setPagination']; | |
49 | + getPagination: UsePaginationType['getPagination']; | |
50 | + reload: UseCardListDataType['reload']; | |
51 | + selectedAll: UseCardListSelected['selectedAll']; | |
52 | + clearSelectedKeys: UseCardListSelected['clearSelectedKeys']; | |
53 | + getSelectedKeys: UseCardListSelected['getSelectedKeys']; | |
54 | + getSelectedRecords: UseCardListSelected['getSelectedRecords']; | |
55 | + getSelections: UseCardListSelected['getSelections']; | |
56 | +} | |
57 | + | |
58 | +export interface CardListEmitType { | |
59 | + (eventName: 'fetchSuccess', result: { items: Recordable[]; total: number }): void; | |
60 | + (eventName: 'fetchError', error: Error): void; | |
61 | +} | |
62 | + | |
63 | +export interface CardListSelectionsType { | |
64 | + customCheckedStyle?: boolean; | |
65 | + beforeSelectValidate?: (record: Recordable) => boolean; | |
66 | + onSelect?: (record: Recordable, selected: boolean, allSelectedRecord: Recordable[]) => any; | |
67 | + onSelectAll?: (selectedRecords: Recordable[]) => any; | |
68 | +} | ... | ... |
src/components/CardList/src/utils/index.ts
0 → 100644
1 | +import { ListGridType } from '../types'; | |
2 | + | |
3 | +export const getListGridByColumn = (col: number): Omit<ListGridType, 'gutter' | 'column'> => { | |
4 | + return { | |
5 | + xxl: col, | |
6 | + xl: col, | |
7 | + lg: col, | |
8 | + md: col, | |
9 | + sm: col, | |
10 | + xs: col, | |
11 | + }; | |
12 | + // return { | |
13 | + // xxl: col, | |
14 | + // xl: Math.max(1, col - 1), | |
15 | + // lg: Math.max(1, col - 2), | |
16 | + // md: Math.max(1, col - 3), | |
17 | + // sm: Math.max(1, col - 4), | |
18 | + // xs: Math.max(1, col - 5), | |
19 | + // }; | |
20 | +}; | ... | ... |
... | ... | @@ -191,6 +191,10 @@ |
191 | 191 | <slot name="afterFullScreen"></slot> |
192 | 192 | </div> |
193 | 193 | </div> |
194 | - <div ref="jsonEditorElRef" class="flex-auto"></div> | |
194 | + <div | |
195 | + ref="jsonEditorElRef" | |
196 | + class="flex-auto" | |
197 | + :style="{ backgroundColor: disabled ? '#f0f0f0' : '' }" | |
198 | + ></div> | |
195 | 199 | </div> |
196 | 200 | </template> | ... | ... |
... | ... | @@ -47,6 +47,7 @@ |
47 | 47 | isClose: { type: Boolean, default: true }, |
48 | 48 | title: { type: String, default: '' }, |
49 | 49 | loading: { type: Boolean }, |
50 | + defaultExpand: { type: Boolean, default: true }, | |
50 | 51 | /** |
51 | 52 | * Can it be expanded |
52 | 53 | */ |
... | ... | @@ -75,7 +76,7 @@ |
75 | 76 | |
76 | 77 | const emit = defineEmits(['expand', 'change', 'hchange']); |
77 | 78 | |
78 | - const show = ref(true); | |
79 | + const show = ref(props.defaultExpand); | |
79 | 80 | |
80 | 81 | const { prefixCls } = useDesign('collapse-container'); |
81 | 82 | ... | ... |
... | ... | @@ -15,6 +15,8 @@ export { default as ApiUpload } from './src/components/ApiUpload.vue'; |
15 | 15 | export { default as StructForm } from './src/externalCompns/components/StructForm/StructForm.vue'; |
16 | 16 | export { default as JavaScriptFunctionEditor } from './src/components/JavaScriptFunctionEditor.vue'; |
17 | 17 | |
18 | +export { ThingsModelForm } from './src/externalCompns/components/ThingsModelForm'; | |
19 | + | |
18 | 20 | //注册自定义组件 |
19 | 21 | export { |
20 | 22 | JEasyCron, | ... | ... |
... | ... | @@ -69,7 +69,7 @@ |
69 | 69 | name: 'BasicForm', |
70 | 70 | components: { FormItem, Form, Row, FormAction }, |
71 | 71 | props: basicProps, |
72 | - emits: ['advanced-change', 'reset', 'submit', 'register'], | |
72 | + emits: ['advanced-change', 'reset', 'submit', 'register', 'fieldValueChange'], | |
73 | 73 | setup(props, { emit, attrs }) { |
74 | 74 | const formModel = reactive<Recordable>({}); |
75 | 75 | const modalFn = useModalContext(); |
... | ... | @@ -230,6 +230,7 @@ |
230 | 230 | |
231 | 231 | function setFormModel(key: string, value: any) { |
232 | 232 | formModel[key] = value; |
233 | + emit('fieldValueChange', key, value); | |
233 | 234 | const { validateTrigger } = unref(getBindValue); |
234 | 235 | if (!validateTrigger || validateTrigger === 'change') { |
235 | 236 | validateFields([key]).catch((_) => {}); | ... | ... |
... | ... | @@ -4,77 +4,102 @@ |
4 | 4 | <!-- 待完善封装InputGroup --> |
5 | 5 | <InputGroup compact> |
6 | 6 | <Select |
7 | - v-if="type !== '2'" | |
7 | + v-if="type !== RequestMethodTypeEnum.WEBSOCKET" | |
8 | 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 | 11 | :options="selectOptions" |
12 | 12 | allowClear |
13 | 13 | @change="emitChange" |
14 | 14 | /> |
15 | 15 | <Input |
16 | - @change="emitChange" | |
17 | 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 | 21 | </InputGroup> |
22 | 22 | </div> |
23 | 23 | </template> |
24 | 24 | <script lang="ts" setup> |
25 | - import { reactive, ref, watchEffect } from 'vue'; | |
25 | + import { reactive, ref, PropType, watch } from 'vue'; | |
26 | 26 | import { InputGroup, Select, Input } from 'ant-design-vue'; |
27 | - import type { SelectValue } from 'ant-design-vue/lib/select'; | |
28 | 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 | 32 | requestUrl?: string; |
34 | 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 | 41 | const props = defineProps({ |
40 | 42 | type: { |
41 | 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 | 49 | const emits = defineEmits(['change', 'update:value']); |
47 | 50 | |
48 | 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 | 60 | } else { |
57 | 61 | selectOptions.value = []; |
58 | 62 | } |
59 | 63 | }; |
60 | 64 | |
61 | - const valueObj = reactive<TypeInputGroup>({ | |
65 | + const requestTypeUrlValue = reactive<requestTypeUrlConfig>({ | |
62 | 66 | requestHttpType: undefined, |
63 | 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 | 101 | function emitChange() { |
76 | - emits('change', valueObj); | |
77 | - emits('update:value', valueObj); | |
102 | + emits('change', requestTypeUrlValue); | |
103 | + emits('update:value', requestTypeUrlValue); | |
78 | 104 | } |
79 | 105 | </script> |
80 | -<style scoped></style> | ... | ... |
1 | 1 | <script lang="ts" setup> |
2 | - import { Button, Transfer, Tag } from 'ant-design-vue'; | |
2 | + import { Transfer, Select } from 'ant-design-vue'; | |
3 | 3 | import { cloneDeep, get } from 'lodash-es'; |
4 | 4 | import { computed, CSSProperties, ExtractPropTypes, onMounted, ref, unref, watch } from 'vue'; |
5 | 5 | import { BasicModal, useModal } from '/@/components/Modal'; |
... | ... | @@ -29,7 +29,7 @@ |
29 | 29 | buttonName?: string; |
30 | 30 | modalProps?: ExtractPropTypes<InstanceType<typeof BasicModal>['$props']>; |
31 | 31 | transferProps?: ExtractPropTypes<TransferType['$props']>; |
32 | - buttonProps?: ExtractPropTypes<InstanceType<typeof Button>['$props']>; | |
32 | + selectProps?: ExtractPropTypes<InstanceType<typeof Select>['$props']>; | |
33 | 33 | disabled?: any; |
34 | 34 | }>(), |
35 | 35 | { |
... | ... | @@ -52,24 +52,15 @@ |
52 | 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 | 61 | const targetKeys = ref<string[]>(props.value || []); |
71 | 62 | |
72 | - const getOptions = computed<Recordable[]>(() => { | |
63 | + const getOptions = computed<Record<'key' | 'title', string>[]>(() => { | |
73 | 64 | const { labelField, valueField } = props; |
74 | 65 | return unref(options).map((item) => ({ |
75 | 66 | ...item, |
... | ... | @@ -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 | 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 | 103 | const handleChange = (_targetKeys: string[], direction: 'left' | 'right', moveKeys: string[]) => { |
... | ... | @@ -165,22 +157,11 @@ |
165 | 157 | <Transfer v-bind="getBindProps" /> |
166 | 158 | </section> |
167 | 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 | 166 | </div> |
186 | 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 | 5 | import { BasicModal } from '/@/components/Modal'; |
6 | 6 | import { PlusCircleOutlined } from '@ant-design/icons-vue'; |
7 | 7 | import { FormFieldsEnum, formSchemas } from './config'; |
8 | - import { DataTypeEnum } from '../StructForm/config'; | |
8 | + import { DataTypeEnum } from '/@/enums/objectModelEnum'; | |
9 | + | |
9 | 10 | const show = ref(false); |
10 | 11 | |
11 | 12 | const props = withDefaults( |
... | ... | @@ -50,7 +51,7 @@ |
50 | 51 | updateSchema([ |
51 | 52 | { |
52 | 53 | field: FormFieldsEnum.ZOOM_FACTOR, |
53 | - ifShow: props.dataType == DataTypeEnum.IS_BOOL ? false : true, | |
54 | + ifShow: props.dataType == DataTypeEnum.BOOL ? false : true, | |
54 | 55 | }, |
55 | 56 | ]); |
56 | 57 | } | ... | ... |
1 | 1 | <script lang="ts" setup> |
2 | 2 | import { Card } from 'ant-design-vue'; |
3 | 3 | import { computed, nextTick, onMounted, onUpdated, unref, watch } from 'vue'; |
4 | - import { DataTypeEnum } from '../StructForm/config'; | |
5 | 4 | import { BasicCreateFormParams } from './type'; |
6 | 5 | import { DynamicProps } from '/#/utils'; |
7 | 6 | import { Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel'; |
8 | 7 | import { BasicForm, FormProps, FormSchema, useForm } from '/@/components/Form'; |
8 | + import { DataTypeEnum } from '/@/enums/objectModelEnum'; | |
9 | 9 | import { ReadAndWriteEnum } from '/@/enums/toolEnum'; |
10 | 10 | |
11 | 11 | const props = withDefaults( |
... | ... | @@ -86,7 +86,7 @@ |
86 | 86 | // step: step, |
87 | 87 | // formatter: (value: string) => value, |
88 | 88 | // parser: (string: string) => { |
89 | - // if (dataType === DataTypeEnum.IS_NUMBER_INT) { | |
89 | + // if (dataType === DataTypeEnum.NUMBER_INT) { | |
90 | 90 | // return Number(Number(string).toFixed()); |
91 | 91 | // } |
92 | 92 | // return Number(string); |
... | ... | @@ -188,19 +188,19 @@ |
188 | 188 | dataType: dataType! as unknown as DataTypeEnum, |
189 | 189 | specs: specs as Partial<Specs>, |
190 | 190 | }; |
191 | - if (type === DataTypeEnum.IS_NUMBER_INT || type === DataTypeEnum.IS_NUMBER_DOUBLE) { | |
191 | + if (type === DataTypeEnum.NUMBER_INT || type === DataTypeEnum.NUMBER_DOUBLE) { | |
192 | 192 | schemas.push(createInputNumber(params)); |
193 | 193 | } |
194 | 194 | |
195 | - if (type === DataTypeEnum.IS_BOOL) { | |
195 | + if (type === DataTypeEnum.BOOL) { | |
196 | 196 | schemas.push(createSelect(params)); |
197 | 197 | } |
198 | 198 | |
199 | - if (type === DataTypeEnum.IS_STRING) { | |
199 | + if (type === DataTypeEnum.STRING) { | |
200 | 200 | schemas.push(createInput(params)); |
201 | 201 | } |
202 | 202 | |
203 | - if (type === DataTypeEnum.IS_STRUCT) { | |
203 | + if (type === DataTypeEnum.STRUCT) { | |
204 | 204 | schemas.push(createInputJson(params)); |
205 | 205 | } |
206 | 206 | } | ... | ... |
... | ... | @@ -14,6 +14,8 @@ |
14 | 14 | import { OpenModalMode, OpenModalParams, StructRecord } from './type'; |
15 | 15 | import { cloneDeep } from 'lodash-es'; |
16 | 16 | import { isArray } from '/@/utils/is'; |
17 | + import { FormField } from '/@/views/device/profiles/step/cpns/physical/cpns/config'; | |
18 | + import { DataTypeEnum } from '/@/enums/objectModelEnum'; | |
17 | 19 | |
18 | 20 | const emit = defineEmits(['update:value']); |
19 | 21 | |
... | ... | @@ -53,7 +55,10 @@ |
53 | 55 | const handleUpdate = (value: StructRecord) => { |
54 | 56 | openModal(true, { |
55 | 57 | mode: OpenModalMode.UPDATE, |
56 | - record: value, | |
58 | + record: { | |
59 | + ...value, | |
60 | + [FormField.HAS_STRUCT_FROM]: value?.dataType?.type === DataTypeEnum.STRUCT, | |
61 | + }, | |
57 | 62 | } as OpenModalParams); |
58 | 63 | }; |
59 | 64 | |
... | ... | @@ -90,15 +95,10 @@ |
90 | 95 | <div>参数名称: {{ item.functionName }}</div> |
91 | 96 | <div class="flex"> |
92 | 97 | <Button class="!p-0" type="link" @click="handleUpdate(item)"> |
93 | - <span>{{ $props.disabled ? '查看' : '编辑' }}</span> | |
98 | + <span>{{ disabled ? '查看' : '编辑' }}</span> | |
94 | 99 | </Button> |
95 | 100 | <Divider type="vertical" /> |
96 | - <Button | |
97 | - :disabled="$props.disabled" | |
98 | - class="!p-0" | |
99 | - type="link" | |
100 | - @click="handleDelete(item)" | |
101 | - > | |
101 | + <Button :disabled="disabled" class="!p-0" type="link" @click="handleDelete(item)"> | |
102 | 102 | <span>删除</span> |
103 | 103 | </Button> |
104 | 104 | </div> |
... | ... | @@ -114,7 +114,7 @@ |
114 | 114 | <StructFormModel |
115 | 115 | :has-struct-form="hasStructForm!" |
116 | 116 | :hidden-access-mode="hiddenAccessMode" |
117 | - :disabled="$props.disabled" | |
117 | + :disabled="disabled" | |
118 | 118 | :value-list="getValue" |
119 | 119 | @register="registerModal" |
120 | 120 | @submit="handleSaveStruct" | ... | ... |
... | ... | @@ -8,7 +8,7 @@ |
8 | 8 | import { formSchemas } from './config'; |
9 | 9 | import { BasicModal, useModalInner } from '/@/components/Modal'; |
10 | 10 | import { OpenModalMode, OpenModalParams, StructRecord } from './type'; |
11 | - import { ref, unref } from 'vue'; | |
11 | + import { computed, ref, unref } from 'vue'; | |
12 | 12 | import { transfromToStructJSON } from './util'; |
13 | 13 | import { cloneDeep } from 'lodash-es'; |
14 | 14 | import { DataType, StructJSON } from '/@/api/device/model/modelOfMatterModel'; |
... | ... | @@ -32,7 +32,6 @@ |
32 | 32 | |
33 | 33 | const [register, { validate, setFieldsValue, setProps }] = useForm({ |
34 | 34 | labelWidth: 100, |
35 | - schemas: formSchemas(props.hasStructForm, props.hiddenAccessMode), | |
36 | 35 | actionColOptions: { |
37 | 36 | span: 14, |
38 | 37 | }, |
... | ... | @@ -42,6 +41,14 @@ |
42 | 41 | showActionButtonGroup: false, |
43 | 42 | }); |
44 | 43 | |
44 | + const getFormSchemas = computed(() => { | |
45 | + const { hasStructForm, hiddenAccessMode } = props; | |
46 | + return formSchemas({ | |
47 | + hasStructForm, | |
48 | + hiddenAccessMode, | |
49 | + }); | |
50 | + }); | |
51 | + | |
45 | 52 | const [registerModal, { closeModal }] = useModalInner((record: OpenModalParams) => { |
46 | 53 | modalReceiveRecord.value = record; |
47 | 54 | const data = record.record || {}; |
... | ... | @@ -97,7 +104,7 @@ |
97 | 104 | destroy-on-close |
98 | 105 | :show-ok-btn="!$props.disabled" |
99 | 106 | > |
100 | - <BasicForm @register="register" /> | |
107 | + <BasicForm @register="register" :schemas="getFormSchemas" /> | |
101 | 108 | </BasicModal> |
102 | 109 | </template> |
103 | 110 | ... | ... |
1 | 1 | import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; |
2 | 2 | import { findDictItemByCode } from '/@/api/system/dict'; |
3 | +import { Rule } from '/@/components/Form'; | |
3 | 4 | import { FormSchema } from '/@/components/Table'; |
5 | +import { DataTypeEnum } from '/@/enums/objectModelEnum'; | |
4 | 6 | import { isNullOrUnDef } from '/@/utils/is'; |
5 | 7 | import { FormField } from '/@/views/device/profiles/step/cpns/physical/cpns/config'; |
6 | 8 | |
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 | 9 | export const validateValueRange = (_rule, value: Record<'min' | 'max', number>, _callback) => { |
16 | 10 | value = value || {}; |
17 | 11 | const { min, max } = value; |
... | ... | @@ -28,11 +22,34 @@ export const validateJSON = (_rule, value = [] as ModelOfMatterParams[], _callba |
28 | 22 | return Promise.reject('JSON对象不能为空'); |
29 | 23 | }; |
30 | 24 | |
31 | -export const formSchemas = ( | |
32 | - hasStructForm: boolean, | |
33 | - hiddenAccessMode: boolean, | |
34 | - isTcp = false | |
35 | -): FormSchema[] => { | |
25 | +interface StructFormSchemasParmasType { | |
26 | + hasStructForm: boolean; | |
27 | + hiddenAccessMode: boolean; | |
28 | + isTcp?: boolean; | |
29 | +} | |
30 | + | |
31 | +//功能名称和标识符 输入框不能包含逗号 | |
32 | +const validateExcludeComma = (field: string, errorName: string): Rule[] => { | |
33 | + return [ | |
34 | + { | |
35 | + required: true, | |
36 | + trigger: 'blur', | |
37 | + validator: () => { | |
38 | + const reg = /[,,]+/; | |
39 | + if (reg.test(field)) { | |
40 | + return Promise.reject(errorName); | |
41 | + } | |
42 | + return Promise.resolve(); | |
43 | + }, | |
44 | + }, | |
45 | + ]; | |
46 | +}; | |
47 | + | |
48 | +export const formSchemas = ({ | |
49 | + hasStructForm, | |
50 | + hiddenAccessMode, | |
51 | + isTcp = false, | |
52 | +}: StructFormSchemasParmasType): FormSchema[] => { | |
36 | 53 | return [ |
37 | 54 | { |
38 | 55 | field: FormField.FUNCTION_NAME, |
... | ... | @@ -46,6 +63,9 @@ export const formSchemas = ( |
46 | 63 | maxLength: 32, |
47 | 64 | placeholder: '请输入功能名称', |
48 | 65 | }, |
66 | + dynamicRules: ({ values }) => { | |
67 | + return validateExcludeComma(values[FormField.FUNCTION_NAME], '功能名称不能包含逗号'); | |
68 | + }, | |
49 | 69 | }, |
50 | 70 | { |
51 | 71 | field: FormField.IDENTIFIER, |
... | ... | @@ -59,6 +79,15 @@ export const formSchemas = ( |
59 | 79 | maxLength: 128, |
60 | 80 | placeholder: '请输入标识符', |
61 | 81 | }, |
82 | + dynamicRules: ({ values }) => { | |
83 | + return validateExcludeComma(values[FormField.IDENTIFIER], '标识符不能包含逗号'); | |
84 | + }, | |
85 | + }, | |
86 | + { | |
87 | + field: FormField.HAS_STRUCT_FROM, | |
88 | + label: '是否已存在结构体', | |
89 | + component: 'Input', | |
90 | + ifShow: false, | |
62 | 91 | }, |
63 | 92 | { |
64 | 93 | field: FormField.TYPE, |
... | ... | @@ -70,17 +99,16 @@ export const formSchemas = ( |
70 | 99 | }, |
71 | 100 | defaultValue: 'INT', |
72 | 101 | componentProps: ({ formActionType }) => { |
73 | - const { updateSchema, setFieldsValue } = formActionType; | |
102 | + const { setFieldsValue } = formActionType; | |
74 | 103 | return { |
75 | 104 | placeholder: '请选择数据类型', |
76 | 105 | api: async (params: Recordable) => { |
77 | 106 | try { |
78 | 107 | const record = await findDictItemByCode(params); |
79 | 108 | return hasStructForm |
80 | - ? record.filter((item) => item.itemValue !== DataTypeEnum.IS_STRUCT) | |
109 | + ? record.filter((item) => item.itemValue !== DataTypeEnum.STRUCT) | |
81 | 110 | : record; |
82 | 111 | } catch (error) { |
83 | - console.log(error); | |
84 | 112 | return []; |
85 | 113 | } |
86 | 114 | }, |
... | ... | @@ -91,14 +119,8 @@ export const formSchemas = ( |
91 | 119 | valueField: 'itemValue', |
92 | 120 | getPopupContainer: () => document.body, |
93 | 121 | onChange: (value: string) => { |
94 | - if (value == DataTypeEnum.IS_STRUCT) { | |
95 | - updateSchema({ | |
96 | - field: FormField.SPECS_LIST, | |
97 | - componentProps: { | |
98 | - hasStructForm: true, | |
99 | - }, | |
100 | - }); | |
101 | - setFieldsValue({ [FormField.SPECS_LIST]: [] }); | |
122 | + if (value == DataTypeEnum.STRUCT) { | |
123 | + setFieldsValue({ [FormField.SPECS_LIST]: [], [FormField.HAS_STRUCT_FROM]: true }); | |
102 | 124 | } |
103 | 125 | }, |
104 | 126 | }; |
... | ... | @@ -114,8 +136,8 @@ export const formSchemas = ( |
114 | 136 | span: 18, |
115 | 137 | }, |
116 | 138 | ifShow: ({ values }) => |
117 | - values[FormField.TYPE] === DataTypeEnum.IS_NUMBER_INT || | |
118 | - values[FormField.TYPE] === DataTypeEnum.IS_NUMBER_DOUBLE, | |
139 | + values[FormField.TYPE] === DataTypeEnum.NUMBER_INT || | |
140 | + values[FormField.TYPE] === DataTypeEnum.NUMBER_DOUBLE, | |
119 | 141 | rules: [{ validator: validateValueRange }], |
120 | 142 | }, |
121 | 143 | { |
... | ... | @@ -134,8 +156,8 @@ export const formSchemas = ( |
134 | 156 | }, |
135 | 157 | }, |
136 | 158 | ifShow: ({ values }) => |
137 | - values[FormField.TYPE] === DataTypeEnum.IS_NUMBER_INT || | |
138 | - values[FormField.TYPE] === DataTypeEnum.IS_NUMBER_DOUBLE, | |
159 | + values[FormField.TYPE] === DataTypeEnum.NUMBER_INT || | |
160 | + values[FormField.TYPE] === DataTypeEnum.NUMBER_DOUBLE, | |
139 | 161 | dynamicRules: ({ model }) => { |
140 | 162 | const valueRange = model[FormField.VALUE_RANGE] || {}; |
141 | 163 | const { min, max } = valueRange; |
... | ... | @@ -199,8 +221,8 @@ export const formSchemas = ( |
199 | 221 | }; |
200 | 222 | }, |
201 | 223 | ifShow: ({ values }) => |
202 | - values[FormField.TYPE] === DataTypeEnum.IS_NUMBER_INT || | |
203 | - values[FormField.TYPE] === DataTypeEnum.IS_NUMBER_DOUBLE, | |
224 | + values[FormField.TYPE] === DataTypeEnum.NUMBER_INT || | |
225 | + values[FormField.TYPE] === DataTypeEnum.NUMBER_DOUBLE, | |
204 | 226 | }, |
205 | 227 | { |
206 | 228 | field: FormField.BOOL_CLOSE, |
... | ... | @@ -214,7 +236,7 @@ export const formSchemas = ( |
214 | 236 | placeholder: '如:关', |
215 | 237 | }, |
216 | 238 | defaultValue: '关', |
217 | - ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.IS_BOOL, | |
239 | + ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.BOOL, | |
218 | 240 | dynamicRules: ({ model }) => { |
219 | 241 | const close = model[FormField.BOOL_CLOSE]; |
220 | 242 | const open = model[FormField.BOOL_OPEN]; |
... | ... | @@ -243,7 +265,7 @@ export const formSchemas = ( |
243 | 265 | placeholder: '如:开', |
244 | 266 | }, |
245 | 267 | defaultValue: '开', |
246 | - ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.IS_BOOL, | |
268 | + ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.BOOL, | |
247 | 269 | dynamicRules: ({ model }) => { |
248 | 270 | const close = model[FormField.BOOL_CLOSE]; |
249 | 271 | const open = model[FormField.BOOL_OPEN]; |
... | ... | @@ -277,7 +299,7 @@ export const formSchemas = ( |
277 | 299 | suffix: () => '字节', |
278 | 300 | }; |
279 | 301 | }, |
280 | - ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.IS_STRING, | |
302 | + ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.STRING, | |
281 | 303 | }, |
282 | 304 | { |
283 | 305 | field: FormField.EXTENSION_DESC, |
... | ... | @@ -322,8 +344,13 @@ export const formSchemas = ( |
322 | 344 | valueField: 'value', |
323 | 345 | changeEvent: 'update:value', |
324 | 346 | colProps: { span: 24 }, |
325 | - ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.IS_STRUCT, | |
347 | + ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.STRUCT, | |
326 | 348 | rules: [{ required: true, validator: validateJSON }], |
349 | + componentProps: ({ formModel }) => { | |
350 | + return { | |
351 | + hasStructForm: formModel[FormField.HAS_STRUCT_FROM], | |
352 | + }; | |
353 | + }, | |
327 | 354 | }, |
328 | 355 | { |
329 | 356 | field: FormField.REFARK, | ... | ... |
1 | -import { DataTypeEnum } from './config'; | |
2 | 1 | import { StructJSON } from '/@/api/device/model/modelOfMatterModel'; |
2 | +import { DataTypeEnum } from '/@/enums/objectModelEnum'; | |
3 | 3 | import { FormField } from '/@/views/device/profiles/step/cpns/physical/cpns/config'; |
4 | 4 | |
5 | 5 | export enum OpenModalMode { | ... | ... |
1 | 1 | import { cloneDeep } from 'lodash-es'; |
2 | -import { DataTypeEnum } from './config'; | |
3 | 2 | import { StructFormValue } from './type'; |
4 | 3 | import { DataType, ModelOfMatterParams, StructJSON } from '/@/api/device/model/modelOfMatterModel'; |
5 | 4 | import { isArray } from '/@/utils/is'; |
5 | +import { DataTypeEnum } from '/@/enums/objectModelEnum'; | |
6 | 6 | |
7 | 7 | export function transfromToStructJSON(value: StructFormValue): StructJSON { |
8 | 8 | const { |
... | ... | @@ -24,21 +24,21 @@ export function transfromToStructJSON(value: StructFormValue): StructJSON { |
24 | 24 | let dataType = {} as unknown as DataType; |
25 | 25 | |
26 | 26 | switch (type) { |
27 | - case DataTypeEnum.IS_NUMBER_INT: | |
27 | + case DataTypeEnum.NUMBER_INT: | |
28 | 28 | dataType = { |
29 | 29 | type, |
30 | 30 | specs: { valueRange, step, unit, unitName }, |
31 | 31 | }; |
32 | 32 | break; |
33 | 33 | |
34 | - case DataTypeEnum.IS_NUMBER_DOUBLE: | |
34 | + case DataTypeEnum.NUMBER_DOUBLE: | |
35 | 35 | dataType = { |
36 | 36 | type, |
37 | 37 | specs: { valueRange, step, unit, unitName }, |
38 | 38 | }; |
39 | 39 | break; |
40 | 40 | |
41 | - case DataTypeEnum.IS_BOOL: | |
41 | + case DataTypeEnum.BOOL: | |
42 | 42 | dataType = { |
43 | 43 | type, |
44 | 44 | specs: { |
... | ... | @@ -48,14 +48,14 @@ export function transfromToStructJSON(value: StructFormValue): StructJSON { |
48 | 48 | }; |
49 | 49 | break; |
50 | 50 | |
51 | - case DataTypeEnum.IS_STRING: | |
51 | + case DataTypeEnum.STRING: | |
52 | 52 | dataType = { |
53 | 53 | type, |
54 | 54 | specs: { length }, |
55 | 55 | }; |
56 | 56 | break; |
57 | 57 | |
58 | - case DataTypeEnum.IS_STRUCT: | |
58 | + case DataTypeEnum.STRUCT: | |
59 | 59 | dataType = { |
60 | 60 | type, |
61 | 61 | specs: specs! as unknown as ModelOfMatterParams[], | ... | ... |
1 | +<script setup lang="ts"> | |
2 | + import { StructJSON } from '/@/api/device/model/modelOfMatterModel'; | |
3 | + import { JsonPreview } from '/@/components/CodeEditor'; | |
4 | + import { BasicModal, useModalInner } from '/@/components/Modal'; | |
5 | + | |
6 | + defineProps<{ | |
7 | + inputData?: StructJSON[]; | |
8 | + }>(); | |
9 | + | |
10 | + defineEmits(['register']); | |
11 | + | |
12 | + const data = {}; | |
13 | + | |
14 | + const [register] = useModalInner(); | |
15 | +</script> | |
16 | + | |
17 | +<template> | |
18 | + <BasicModal @register="register" title=""> | |
19 | + <!-- --> | |
20 | + <JsonPreview :data="data" /> | |
21 | + </BasicModal> | |
22 | +</template> | ... | ... |
1 | +import { FormSchema } from '../../../types/form'; | |
2 | +import { Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel'; | |
3 | +import { DataTypeEnum } from '/@/enums/objectModelEnum'; | |
4 | +import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const'; | |
5 | + | |
6 | +export const getFormSchemas = ({ | |
7 | + structJSON: structJson, | |
8 | + required, | |
9 | + transportType, | |
10 | +}: { | |
11 | + structJSON: StructJSON[]; | |
12 | + required?: boolean; | |
13 | + transportType?: string; | |
14 | +}): FormSchema[] => { | |
15 | + const createInputNumber = ({ identifier, functionName, dataType }: StructJSON): FormSchema => { | |
16 | + const { specs } = dataType || {}; | |
17 | + const { valueRange } = specs! as Specs; | |
18 | + const { max = 2147483647, min = -2147483648 } = valueRange || {}; | |
19 | + return { | |
20 | + field: identifier, | |
21 | + label: functionName, | |
22 | + component: 'InputNumber', | |
23 | + rules: [ | |
24 | + { | |
25 | + required, | |
26 | + message: `${functionName}是必填项`, | |
27 | + }, | |
28 | + { | |
29 | + type: 'number', | |
30 | + trigger: 'change', | |
31 | + validator: (_rule, value) => { | |
32 | + const reg = /^[0-9]*$/; | |
33 | + if (!reg.test(value)) | |
34 | + return Promise.reject(new Error(`${functionName}不是一个有效的数字`)); | |
35 | + if (value < min || value > max) | |
36 | + return Promise.reject(new Error(`${functionName}取值范围在${min}~${max}之间`)); | |
37 | + | |
38 | + return Promise.resolve(value); | |
39 | + }, | |
40 | + }, | |
41 | + ], | |
42 | + componentProps: { | |
43 | + max, | |
44 | + min, | |
45 | + placeholder: `请输入${functionName}`, | |
46 | + }, | |
47 | + } as FormSchema; | |
48 | + }; | |
49 | + | |
50 | + const createInput = ({ identifier, functionName, dataType }: StructJSON): FormSchema => { | |
51 | + const { specs } = dataType || {}; | |
52 | + const { length = 10240 } = specs! as Partial<Specs>; | |
53 | + return { | |
54 | + field: identifier, | |
55 | + label: functionName, | |
56 | + component: 'Input', | |
57 | + rules: [ | |
58 | + { | |
59 | + required, | |
60 | + message: `${functionName}是必填项`, | |
61 | + }, | |
62 | + { | |
63 | + type: 'string', | |
64 | + trigger: 'change', | |
65 | + validator: (_rule, value) => { | |
66 | + if ((value?.length || 0) > length) | |
67 | + return Promise.reject(new Error(`${functionName}数据长度应该小于${length}`)); | |
68 | + | |
69 | + return Promise.resolve(value); | |
70 | + }, | |
71 | + }, | |
72 | + ], | |
73 | + componentProps: { | |
74 | + maxLength: length, | |
75 | + placeholder: `请输入${functionName}`, | |
76 | + }, | |
77 | + } as FormSchema; | |
78 | + }; | |
79 | + | |
80 | + const createSelect = ({ identifier, functionName, dataType }: StructJSON): FormSchema => { | |
81 | + const { specs } = dataType || {}; | |
82 | + const { boolClose, boolOpen } = specs! as Partial<Specs>; | |
83 | + return { | |
84 | + field: identifier, | |
85 | + label: functionName!, | |
86 | + component: 'Select', | |
87 | + rules: [ | |
88 | + { | |
89 | + required, | |
90 | + message: `${functionName}是必填项`, | |
91 | + type: 'number', | |
92 | + }, | |
93 | + ], | |
94 | + componentProps: { | |
95 | + options: [ | |
96 | + { label: `${boolClose}-0`, value: 0 }, | |
97 | + { label: `${boolOpen}-1`, value: 1 }, | |
98 | + ], | |
99 | + placeholder: `请选择${functionName}`, | |
100 | + }, | |
101 | + }; | |
102 | + }; | |
103 | + | |
104 | + const createStructJson = ({ identifier, functionName, dataType }: StructJSON): FormSchema => { | |
105 | + return { | |
106 | + field: identifier, | |
107 | + label: functionName!, | |
108 | + component: 'Input', | |
109 | + changeEvent: 'update:value', | |
110 | + componentProps: () => { | |
111 | + return { | |
112 | + inputData: dataType?.specs || [], | |
113 | + }; | |
114 | + }, | |
115 | + colSlot: identifier, | |
116 | + }; | |
117 | + }; | |
118 | + | |
119 | + const createTCPServiceCommandInput = ({ 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 | 123 | | 'TransferModal' |
124 | 124 | | 'TransferTableModal' |
125 | 125 | | 'ObjectModelValidateForm' |
126 | + | 'ThingsModelForm' | |
126 | 127 | | 'DevicePicker' |
127 | 128 | | 'ProductPicker' |
128 | 129 | | 'PollCommandInput' |
... | ... | @@ -138,4 +139,8 @@ export type ComponentType = |
138 | 139 | | 'RelationsQuery' |
139 | 140 | | 'CredentialsCard' |
140 | 141 | | 'ApiComplete' |
141 | - | 'DeviceProfileForm'; | |
142 | + | 'DeviceProfileForm' | |
143 | + | 'ConditionFilter' | |
144 | + | 'TimeRangePicker' | |
145 | + | 'TriggerDurationInput' | |
146 | + | 'AlarmProfileSelect'; | ... | ... |
... | ... | @@ -25,19 +25,18 @@ export function usePagination(refProps: ComputedRef<BasicTableProps>) { |
25 | 25 | const { t } = useI18n(); |
26 | 26 | |
27 | 27 | const configRef = ref<PaginationProps>({ |
28 | - hideOnSinglePage:true | |
28 | + hideOnSinglePage: true, | |
29 | 29 | }); |
30 | 30 | const show = ref(true); |
31 | 31 | |
32 | 32 | watchEffect(() => { |
33 | 33 | const { pagination } = unref(refProps); |
34 | - | |
34 | + | |
35 | 35 | if (!isBoolean(pagination) && pagination) { |
36 | 36 | configRef.value = { |
37 | 37 | ...unref(configRef), |
38 | 38 | ...(pagination ?? {}), |
39 | - }; | |
40 | - | |
39 | + }; | |
41 | 40 | } |
42 | 41 | }); |
43 | 42 | ... | ... |
... | ... | @@ -88,7 +88,7 @@ export function useTableScroll( |
88 | 88 | |
89 | 89 | bodyEl!.style.height = 'unset'; |
90 | 90 | |
91 | - if (!unref(getCanResize) || tableData.length === 0) return; | |
91 | + if (!unref(getCanResize)) return; | |
92 | 92 | |
93 | 93 | await nextTick(); |
94 | 94 | //Add a delay to get the correct bottomIncludeBody paginationHeight footerHeight headerHeight |
... | ... | @@ -143,7 +143,12 @@ export function useTableScroll( |
143 | 143 | height = (height > maxHeight! ? (maxHeight as number) : height) ?? height; |
144 | 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 | 153 | useWindowSizeFn(calcTableHeight, 280); |
149 | 154 | onMountedOrActivated(() => { | ... | ... |
1 | 1 | export { default as ModeSwitchButton } from './ModeSwitchButton.vue'; |
2 | 2 | export { default as CardLayoutButton } from './CardLayoutButton.vue'; |
3 | 3 | export { default as AuthIcon } from './AuthIcon.vue'; |
4 | +export { default as AuthDropDown } from './AuthDropDown.vue'; | |
4 | 5 | export { |
5 | 6 | EnumTableCardMode, |
6 | 7 | EnumTableChartMode, | ... | ... |
... | ... | @@ -11,3 +11,19 @@ export enum AlarmStatusMean { |
11 | 11 | CLEARED_ACK = '清除已确认', |
12 | 12 | ACTIVE_ACK = '激活已确认', |
13 | 13 | } |
14 | + | |
15 | +export enum AlarmLevelEnum { | |
16 | + CRITICAL = 'CRITICAL', | |
17 | + MAJOR = 'MAJOR', | |
18 | + MINOR = 'MINOR', | |
19 | + WARNING = 'WARNING', | |
20 | + INDETERMINATE = 'INDETERMINATE', | |
21 | +} | |
22 | + | |
23 | +export enum AlarmLevelNameEnum { | |
24 | + CRITICAL = '紧急', | |
25 | + MAJOR = '重要', | |
26 | + MINOR = '次要', | |
27 | + WARNING = '警告', | |
28 | + INDETERMINATE = '不确定', | |
29 | +} | ... | ... |
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 | + DEVICE_EVENT = 'DEVICE_EVENT', | |
103 | +} | |
104 | + | |
105 | +export enum DeviceTriggerTypeNameEum { | |
106 | + TIME_SERIES = '属性触发', | |
107 | + DEVICE_EVENT = '事件触发', | |
108 | +} | |
109 | + | |
110 | +export enum TriggerEntityTypeEnum { | |
111 | + ALL = 'ALL', | |
112 | + PART = 'PART', | |
113 | +} | |
114 | + | |
115 | +export enum TriggerEntityTypeNameEnum { | |
116 | + ALL = '全部', | |
117 | + PART = '部分', | |
118 | +} | |
119 | + | |
120 | +export enum TriggerValueTypeEnum { | |
121 | + NUMERIC = 'NUMERIC', | |
122 | + BOOLEAN = 'BOOLEAN', | |
123 | + STRING = 'STRING', | |
124 | + DATE_TIME = 'DATE_TIME', | |
125 | +} | |
126 | + | |
127 | +export enum TriggerValueTypeNameEnum { | |
128 | + NUMERIC = '数字', | |
129 | + BOOLEAN = '布尔值', | |
130 | + STRING = '字符串', | |
131 | + DATE_TIME = '时间', | |
132 | +} | |
133 | + | |
134 | +export enum TriggerUnitEnum { | |
135 | + SECONDS = 'SECONDS', | |
136 | + MINUTES = 'MINUTES', | |
137 | + HOURS = 'HOURS', | |
138 | + DAYS = 'DAYS', | |
139 | +} | |
140 | + | |
141 | +export enum TriggerUnitNameEnum { | |
142 | + SECONDS = '秒', | |
143 | + MINUTES = '分', | |
144 | + HOURS = '时', | |
145 | + DAYS = '天', | |
146 | +} | |
147 | + | |
148 | +export enum ExecutionActionEnum { | |
149 | + DEVICE_OUT = 'DEVICE_OUT', | |
150 | + MSG_NOTIFY = 'MSG_NOTIFY', | |
151 | +} | |
152 | + | |
153 | +export enum ExecutionActionNameEnum { | |
154 | + DEVICE_OUT = '设备输出', | |
155 | + MSG_NOTIFY = '告警输出', | |
156 | +} | |
157 | + | |
158 | +export enum CommandTypeEnum { | |
159 | + CUSTOM = 0, | |
160 | + SERVICE = 1, | |
161 | + ATTRIBUTE = 2, | |
162 | + API = 'api', | |
163 | +} | |
164 | + | |
165 | +export enum CommandTypeNameEnum { | |
166 | + CUSTOM = '自定义', | |
167 | + SERVICE = '服务', | |
168 | + ATTRIBUTE = '属性', | |
169 | +} | ... | ... |
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 | } |
8 | 8 | |
9 | 9 | export enum DataActionModeNameEnum { |
10 | - CREATE = '创建', | |
10 | + CREATE = '新增', | |
11 | 11 | READ = '查看', |
12 | 12 | UPDATE = '编辑', |
13 | 13 | DELETE = '删除', |
... | ... | @@ -40,6 +40,11 @@ export enum ServiceCallTypeEnum { |
40 | 40 | SYNC = 'SYNC', |
41 | 41 | } |
42 | 42 | |
43 | +export enum ServiceCallTypeNameEnum { | |
44 | + ASYNC = '异步', | |
45 | + SYNC = '同步', | |
46 | +} | |
47 | + | |
43 | 48 | export enum CommandDeliveryWayEnum { |
44 | 49 | ONE_WAY = 'oneway', |
45 | 50 | TWO_WAY = 'twoway', | ... | ... |
1 | +/** | |
2 | + *规则链 动作 英文配置文件 | |
3 | + 目前共有22个,分类按名称进行配置 | |
4 | + */ | |
5 | +export default { | |
6 | + title: 'Action', | |
7 | + description: 'Perform special actions', | |
8 | + assignToCustomer: { | |
9 | + name: 'assign to customer', | |
10 | + details: `Finds target Customer by customer name pattern and then assign Originator Entity to this customer. Will create new Customer if it doesn't exists and 'Create new Customer if not exists' is set to true.`, | |
11 | + description: 'Assign Message Originator Entity to Customer', | |
12 | + }, | |
13 | + clearAlarm: { | |
14 | + name: 'clear alarm', | |
15 | + details: `Details - JS function that creates JSON object based on incoming message. This object will be added into Alarm.details field.\nNode output:\nIf alarm was not cleared, original message is returned. Otherwise new Message returned with type 'ALARM', Alarm object in 'msg' property and 'metadata' will contains 'isClearedAlarm' property. Message payload can be accessed via <code>msg</code> property. For example <code>'temperature = ' + msg.temperature ;</code>. Message metadata can be accessed via <code>metadata</code> property. For example <code>'name = ' + metadata.customerName;</code>.`, | |
16 | + description: 'Clear Alarm', | |
17 | + }, | |
18 | + copyToView: { | |
19 | + name: 'copy to view', | |
20 | + details: `Copy attributes from asset/device to related entity view according to entity view configuration. \n Copy will be done only for attributes that are between start and end dates and according to attribute keys configuration. \nChanges message originator to related entity view and produces new messages according to count of updated entity views`, | |
21 | + description: | |
22 | + 'Copy attributes from asset/device to entity view and changes message originator to related entity view', | |
23 | + }, | |
24 | + createAlarm: { | |
25 | + name: 'create alarm', | |
26 | + details: `Details - JS function that creates JSON object based on incoming message. This object will be added into Alarm.details field.\nNode output:\nIf alarm was not created, original message is returned. Otherwise new Message returned with type 'ALARM', Alarm object in 'msg' property and 'metadata' will contains one of those properties 'isNewAlarm/isExistingAlarm'. Message payload can be accessed via <code>msg</code> property. For example <code>'temperature = ' + msg.temperature ;</code>. Message metadata can be accessed via <code>metadata</code> property. For example <code>'name = ' + metadata.customerName;</code>.`, | |
27 | + description: 'Create or Update Alarm', | |
28 | + }, | |
29 | + createRelation: { | |
30 | + name: 'create relation', | |
31 | + details: `If the relation already exists or successfully created - Message send via <b>Success</b> chain, otherwise <b>Failure</b> chain will be used.`, | |
32 | + description: `Finds target Entity by entity name pattern and (entity type pattern for Asset, Device) and then create a relation to Originator Entity by type and direction. If Selected entity type: Asset, Device or Customer will create new Entity if it doesn't exist and selected checkbox 'Create new entity if not exists'.<br> In case that relation from the message originator to the selected entity not exist and If selected checkbox 'Remove current relations', before creating the new relation all existed relations to message originator by type and direction will be removed.<br> If relation from the message originator to the selected entity created and If selected checkbox 'Change originator to related entity', outbound message will be processed as a message from this entity.`, | |
33 | + }, | |
34 | + deleteRelation: { | |
35 | + name: 'delete relation', | |
36 | + details: `If the relation(s) successfully deleted - Message send via <b>Success</b> chain, otherwise <b>Failure</b> chain will be used.`, | |
37 | + description: `Finds target Entity by entity name pattern and then delete a relation to Originator Entity by type and direction if 'Delete single entity' is set to true, otherwise rule node will delete all relations to the originator of the message by type and direction.`, | |
38 | + }, | |
39 | + delay: { | |
40 | + name: 'delay (deprecated)', | |
41 | + details: `Delays messages for a configurable period. Please note, this node acknowledges the message from the current queue (message will be removed from queue). Deprecated because the acknowledged message still stays in memory (to be delayed) and this does not guarantee that message will be processed even if the "retry failures and timeouts" processing strategy will be chosen.`, | |
42 | + description: `Delays incoming message (deprecated)`, | |
43 | + }, | |
44 | + deviceProfile: { | |
45 | + name: 'device profile', | |
46 | + details: `Create and clear alarms based on alarm rules defined in device profile. The output relation type is either 'Alarm Created', 'Alarm Updated', 'Alarm Severity Updated' and 'Alarm Cleared' or simply 'Success' if no alarms were affected.`, | |
47 | + description: `Process device messages based on device profile settings`, | |
48 | + }, | |
49 | + generator: { | |
50 | + name: 'generator', | |
51 | + details: `Generates messages with configurable period. Javascript function used for message generation.`, | |
52 | + description: `Periodically generates messages`, | |
53 | + }, | |
54 | + gpsGeofencingEvents: { | |
55 | + name: 'gps geofencing events', | |
56 | + details: `Extracts latitude and longitude parameters from incoming message and returns different events based on configuration parameters`, | |
57 | + description: `Produces incoming messages using GPS based geofencing`, | |
58 | + }, | |
59 | + log: { | |
60 | + name: 'log', | |
61 | + details: `Transform incoming Message with configured JS function to String and log final value into Thingsboard log file. Message payload can be accessed via <code>msg</code> property. For example <code>'temperature = ' + msg.temperature ;</code>. Message metadata can be accessed via <code>metadata</code> property. For example <code>'name = ' + metadata.customerName;</code>.`, | |
62 | + description: `Log incoming messages using JS script for transformation Message into String`, | |
63 | + }, | |
64 | + messageCount: { | |
65 | + name: 'message count', | |
66 | + details: `Count incoming messages for specified interval and produces POST_TELEMETRY_REQUEST msg with messages count`, | |
67 | + description: `Count incoming messages`, | |
68 | + }, | |
69 | + pushToEdge: { | |
70 | + name: 'push to edge', | |
71 | + details: `Push messages from cloud to edge. Message originator must be assigned to particular edge or message originator is <b>EDGE</b> entity itself. This node used only on cloud instances to push messages from cloud to edge. Once message arrived into this node it’s going to be converted into edge event and saved to the database. Node doesn't push messages directly to edge, but stores event(s) in the edge queue. <br>Supports next originator types:<br><code>DEVICE</code><br><code>ASSET</code><br><code>ENTITY_VIEW</code><br><code>DASHBOARD</code><br><code>TENANT</code><br><code>CUSTOMER</code><br><code>EDGE</code><br><br>As well node supports next message types:<br><code>POST_TELEMETRY_REQUEST</code><br><code>POST_ATTRIBUTES_REQUEST</code><br><code>ATTRIBUTES_UPDATED</code><br><code>ATTRIBUTES_DELETED</code><br><code>ALARM</code><br><br>Message will be routed via <b>Failure</b> route if node was not able to save edge event to database or unsupported originator type/message type arrived. In case successful storage edge event to database message will be routed via <b>Success</b> route.`, | |
72 | + description: `Push messages from cloud to edge`, | |
73 | + }, | |
74 | + rpcCallReply: { | |
75 | + name: 'rpc call reply', | |
76 | + details: `Expects messages with any message type. Will forward message body to the device.`, | |
77 | + description: `Sends reply to RPC call from device`, | |
78 | + }, | |
79 | + rpcCallRequest: { | |
80 | + name: 'rpc call request', | |
81 | + details: `Expects messages with "method" and "params". Will forward response from device to next nodes.If the RPC call request is originated by REST API call from user, will forward the response to user immediately.`, | |
82 | + description: `Sends RPC call to device`, | |
83 | + }, | |
84 | + saveAttributes: { | |
85 | + name: 'save attributes', | |
86 | + details: `Saves entity attributes based on configurable scope parameter. Expects messages with 'POST_ATTRIBUTES_REQUEST' message type`, | |
87 | + description: `Saves attributes data`, | |
88 | + }, | |
89 | + saveEvent: { | |
90 | + name: 'save event', | |
91 | + details: `Saves entity event . Expects messages with 'POST_EVENT_REQUEST' message type`, | |
92 | + description: `Saves device event data`, | |
93 | + }, | |
94 | + saveTimeseries: { | |
95 | + name: 'save timeseries', | |
96 | + details: `Saves timeseries telemetry data based on configurable TTL parameter. Expects messages with 'POST_TELEMETRY_REQUEST' message type. Timestamp in milliseconds will be taken from metadata.ts, otherwise 'now' message timestamp will be applied. Allows stopping updating values for incoming keys in the latest ts_kv table if 'skipLatestPersistence' is set to true.\n <br/>Enable 'useServerTs' param to use the timestamp of the message processing instead of the timestamp from the message. Useful for all sorts of sequential processing if you merge messages from multiple sources (devices, assets, etc).\n<br/>In the case of sequential processing, the platform guarantees that the messages are processed in the order of their submission to the queue. However, the timestamp of the messages originated by multiple devices/servers may be unsynchronized long before they are pushed to the queue. The DB layer has certain optimizations to ignore the updates of the \"attributes\" and \"latest values\" tables if the new record has a timestamp that is older than the previous record. So, to make sure that all the messages will be processed correctly, one should enable this parameter for sequential message processing scenarios.`, | |
97 | + description: `Saves timeseries data`, | |
98 | + }, | |
99 | + saveToCustomTable: { | |
100 | + name: 'save to custom table', | |
101 | + details: `Administrator should set the custom table name without prefix: <b>cs_tb_</b>. <br>Administrator can configure the mapping between the Message field names and Table columns name.<br><b>Note:</b>If the mapping key is <b>$entity_id</b>, that is identified by the Message Originator, then to the appropriate column name(mapping value) will be write the message originator id.<br><br>If specified message field does not exist or is not a JSON Primitive, the outbound message will be routed via <b>failure</b> chain, otherwise, the message will be routed via <b>success</b> chain.`, | |
102 | + description: `Node stores data from incoming Message payload to the Cassandra database into the predefined custom table that should have <b>cs_tb_</b> prefix, to avoid the data insertion to the common TB tables.<br><b>Note:</b> rule node can be used only for Cassandra DB.`, | |
103 | + }, | |
104 | + synchronizationEnd: { | |
105 | + name: 'synchronization end', | |
106 | + details: ``, | |
107 | + description: `This Node is now deprecated. Use "Checkpoint" instead.`, | |
108 | + }, | |
109 | + synchronizationStart: { | |
110 | + name: 'synchronization start', | |
111 | + details: `This node should be used together with "synchronization end" node. \n This node will put messages into queue based on message originator id. \nSubsequent messages will not be processed until the previous message processing is completed or timeout event occurs.\nSize of the queue per originator and timeout values are configurable on a system level`, | |
112 | + description: `This Node is now deprecated. Use "Checkpoint" instead.`, | |
113 | + }, | |
114 | + unassigFromCustomer: { | |
115 | + name: 'unassign from customer', | |
116 | + details: `Finds target Entity Customer by Customer name pattern and then unassign Originator Entity from this customer.`, | |
117 | + description: `Unassign Message Originator Entity from Customer`, | |
118 | + }, | |
119 | +}; | ... | ... |
1 | +/** | |
2 | + *规则链 属性集 英文配置文件 | |
3 | + 目前共有10个,分类按名称进行配置 | |
4 | + */ | |
5 | +export default { | |
6 | + title: 'Enrichment', | |
7 | + description: 'Add additional information to message metadata', | |
8 | + calculateDelta: { | |
9 | + name: 'calculate delta', | |
10 | + details: `Calculates delta and period based on the previous time-series reading and current data. Delta calculation is done in scope of the message originator, e.g. device, asset or customer. If there is input key, the output relation will be 'Success' unless delta is negative and corresponding configuration parameter is set. If there is no input value key in the incoming message, the output relation will be 'Other'.`, | |
11 | + description: `Calculates and adds 'delta' value into message based on the incoming and previous value`, | |
12 | + }, | |
13 | + customerAttributes: { | |
14 | + name: 'customer attributes', | |
15 | + details: `If Attributes enrichment configured, server scope attributes are added into Message metadata. If Latest Telemetry enrichment configured, latest telemetry added into metadata. To access those attributes in other nodes this template can be used <code>metadata.temperature</code>.`, | |
16 | + description: 'Add Originators Customer Attributes or Latest Telemetry into Message Metadata', | |
17 | + }, | |
18 | + customerDetails: { | |
19 | + name: 'customer details', | |
20 | + details: `If checkbox: <b>Add selected details to the message metadata</b> is selected, existing fields will be added to the message metadata instead of message data.<br><br><b>Note:</b> only Device, Asset, and Entity View type are allowed.<br><br>If the originator of the message is not assigned to Customer, or originator type is not supported - Message will be forwarded to <b>Failure</b> chain, otherwise, <b>Success</b> chain will be used.`, | |
21 | + description: 'Adds fields from Customer details to the message body or metadata', | |
22 | + }, | |
23 | + originatorAttributes: { | |
24 | + name: 'originator attributes', | |
25 | + details: `If Attributes enrichment configured, <b>CLIENT/SHARED/SERVER</b> attributes are added into Message metadata with specific prefix: <i>cs/shared/ss</i>. Latest telemetry value added into metadata without prefix. To access those attributes in other nodes this template can be used <code>metadata.cs_temperature</code> or <code>metadata.shared_limit</code> `, | |
26 | + description: 'Add Message Originator Attributes or Latest Telemetry into Message Metadata', | |
27 | + }, | |
28 | + originatorFields: { | |
29 | + name: 'originator fields', | |
30 | + details: `Will fetch fields values specified in mapping. If specified field is not part of originator fields it will be ignored.`, | |
31 | + description: 'Add Message Originator fields values into Message Metadata', | |
32 | + }, | |
33 | + originatorTelemetry: { | |
34 | + name: 'originator telemetry', | |
35 | + details: `The node allows you to select fetch mode: <b>FIRST/LAST/ALL</b> to fetch telemetry of certain time range that are added into Message metadata without any prefix. If selected fetch mode <b>ALL</b> Telemetry will be added like array into Message Metadata where <b>key</b> is Timestamp and <b>value</b> is value of Telemetry.</br>If selected fetch mode <b>FIRST</b> or <b>LAST</b> Telemetry will be added like string without Timestamp.</br>Also, the rule node allows you to select telemetry sampling order: <b>ASC</b> or <b>DESC</b>. </br>Aggregation feature allows you to fetch aggregated telemetry as a single value by <b>AVG, COUNT, SUM, MIN, MAX, NONE</b>. </br><b>Note</b>: The maximum size of the fetched array is 1000 records.\n `, | |
36 | + description: 'Add Message Originator Telemetry for selected time range into Message Metadata\n', | |
37 | + }, | |
38 | + relatedAttributes: { | |
39 | + name: 'related attributes', | |
40 | + details: `Related Entity found using configured relation direction and Relation Type. If multiple Related Entities are found, only first Entity is used for attributes enrichment, other entities are discarded. If Attributes enrichment configured, server scope attributes are added into Message metadata. If Latest Telemetry enrichment configured, latest telemetry added into metadata. To access those attributes in other nodes this template can be used <code>metadata.temperature</code>.`, | |
41 | + description: | |
42 | + 'Add Originators Related Entity Attributes or Latest Telemetry into Message Metadata', | |
43 | + }, | |
44 | + relatedDeviceAttributes: { | |
45 | + name: 'related device attributes', | |
46 | + details: `If Attributes enrichment configured, <b>CLIENT/SHARED/SERVER</b> attributes are added into Message metadata with specific prefix: <i>cs/shared/ss</i>. Latest telemetry value added into metadata without prefix. To access those attributes in other nodes this template can be used <code>metadata.cs_temperature</code> or <code>metadata.shared_limit</code>`, | |
47 | + description: | |
48 | + 'Add Originators Related Device Attributes and Latest Telemetry value into Message Metadata', | |
49 | + }, | |
50 | + tenantAttributes: { | |
51 | + name: 'tenant attributes', | |
52 | + details: `If Attributes enrichment configured, server scope attributes are added into Message metadata. If Latest Telemetry enrichment configured, latest telemetry added into metadata. To access those attributes in other nodes this template can be used <code>metadata.temperature</code>.`, | |
53 | + description: 'Add Originators Tenant Attributes or Latest Telemetry into Message Metadata', | |
54 | + }, | |
55 | + tenantDetails: { | |
56 | + name: 'tenant details', | |
57 | + details: `If checkbox: <b>Add selected details to the message metadata</b> is selected, existing fields will be added to the message metadata instead of message data.<br><br><b>Note:</b> only Device, Asset, and Entity View type are allowed.<br><br>If the originator of the message is not assigned to Tenant, or originator type is not supported - Message will be forwarded to <b>Failure</b> chain, otherwise, <b>Success</b> chain will be used.`, | |
58 | + description: 'Adds fields from Tenant details to the message body or metadata', | |
59 | + }, | |
60 | +}; | ... | ... |
1 | +/** | |
2 | + *规则链 外部的 英文配置文件 | |
3 | + 目前共有11个,分类按名称进行配置 | |
4 | + */ | |
5 | +export default { | |
6 | + title: 'External', | |
7 | + description: 'Interacting with external systems', | |
8 | + alarmNotice: { | |
9 | + name: 'alarm notice', | |
10 | + details: `notice method include sms,email and so on.`, | |
11 | + description: 'while device alarm, notice some people.', | |
12 | + }, | |
13 | + awsSns: { | |
14 | + name: 'aws sns', | |
15 | + details: `Will publish message payload to the AWS SNS topic. Outbound message will contain response fields (<code>messageId</code>, <code>requestId</code>) in the Message Metadata from the AWS SNS. For example <b>requestId</b> field can be accessed with <code>metadata.requestId</code>.`, | |
16 | + description: 'Publish message to the AWS SNS', | |
17 | + }, | |
18 | + awsSqs: { | |
19 | + name: 'aws sqs', | |
20 | + details: `Will publish message payload and metadata attributes to the AWS SQS queue. Outbound message will contain response fields (<code>messageId</code>, <code>requestId</code>, <code>messageBodyMd5</code>, <code>messageAttributesMd5</code>, <code>sequenceNumber</code>) in the Message Metadata from the AWS SQS. For example <b>requestId</b> field can be accessed with <code>metadata.requestId</code>.`, | |
21 | + description: 'Publish messages to the AWS SQS', | |
22 | + }, | |
23 | + azureIotHub: { | |
24 | + name: 'azure iot hub', | |
25 | + details: `Will publish message payload to the Azure IoT Hub with QoS <b>AT_LEAST_ONCE</b>.`, | |
26 | + description: 'Publish messages to the Azure IoT Hub', | |
27 | + }, | |
28 | + gcpPubsub: { | |
29 | + name: 'gcp pubsub', | |
30 | + details: `Will publish message payload to the Google Cloud Platform PubSub topic. Outbound message will contain response fields (<code>messageId</code> in the Message Metadata from the GCP PubSub. <b>messageId</b> field can be accessed with <code>metadata.messageId</code>.`, | |
31 | + description: 'Publish message to the Google Cloud PubSub', | |
32 | + }, | |
33 | + kafka: { | |
34 | + name: 'kafka', | |
35 | + details: `Will send record via Kafka producer to Kafka server. Outbound message will contain response fields (<code>offset</code>, <code>partition</code>, <code>topic</code>) from the Kafka in the Message Metadata. For example <b>partition</b> field can be accessed with <code>metadata.partition</code>.`, | |
36 | + description: 'Publish messages to Kafka server', | |
37 | + }, | |
38 | + mqtt: { | |
39 | + name: 'mqtt', | |
40 | + details: `Will publish message payload to the MQTT broker with QoS <b>AT_LEAST_ONCE</b>.`, | |
41 | + description: 'Publish messages to the MQTT broker', | |
42 | + }, | |
43 | + rabbitmq: { | |
44 | + name: 'rabbitmq', | |
45 | + details: `Will publish message payload to RabbitMQ queue.`, | |
46 | + description: 'Publish messages to the RabbitMQ', | |
47 | + }, | |
48 | + restApiCall: { | |
49 | + name: 'rest api call', | |
50 | + details: `Will invoke REST API call <code>GET | POST | PUT | DELETE</code> to external REST server. Message payload added into Request body. Configured attributes can be added into Headers from Message Metadata. Outbound message will contain response fields (<code>status</code>, <code>statusCode</code>, <code>statusReason</code> and response <code>headers</code>) in the Message Metadata. Response body saved in outbound Message payload. For example <b>statusCode</b> field can be accessed with <code>metadata.statusCode</code>.<br/><b>Note-</b> if you use system proxy properties, the next system proxy properties should be added: "http.proxyHost" and "http.proxyPort" or "https.proxyHost" and "https.proxyPort" or "socksProxyHost" and "socksProxyPort",and if your proxy with auth, the next ones should be added: "tb.proxy.user" and "tb.proxy.password" to the thingsboard.conf file.`, | |
51 | + description: 'Invoke REST API calls to external REST server', | |
52 | + }, | |
53 | + sendEmail: { | |
54 | + name: 'send email', | |
55 | + details: `Expects messages with <b>SEND_EMAIL</b> type. Node works only with messages that where created using <code>to Email</code> transformation Node, please connect this Node with <code>to Email</code> Node using <code>Successful</code> chain.`, | |
56 | + description: 'Sends email message via SMTP server.', | |
57 | + }, | |
58 | + sendSms: { | |
59 | + name: 'send sms', | |
60 | + details: `Will send SMS message by populating target phone numbers and sms message fields using values derived from message metadata.`, | |
61 | + description: 'Sends SMS message via SMS provider.', | |
62 | + }, | |
63 | +}; | ... | ... |
1 | +/** | |
2 | + *规则链 筛选器 英文配置文件 | |
3 | + 目前共有11个,分类按名称进行配置 | |
4 | + */ | |
5 | +export default { | |
6 | + title: 'Filter', | |
7 | + description: 'Filter incoming messages using configuration criteria', | |
8 | + checkAlarmStatus: { | |
9 | + name: 'check alarm status', | |
10 | + details: | |
11 | + 'If the alarm status matches the specified one - msg is success if does not match - msg is failure.', | |
12 | + description: 'Checks alarm status.', | |
13 | + }, | |
14 | + checkExistenceFields: { | |
15 | + name: 'check existence fields', | |
16 | + details: `If selected checkbox 'Check that all selected keys are present'\" and all keys in message data and metadata are exist - send Message via <b>True</b> chain, otherwise <b>False</b> chain is used.\nElse if the checkbox is not selected, and at least one of the keys from data or metadata of the message exists - send Message via <b>True</b> chain, otherwise, <b>False</b> chain is used. `, | |
17 | + description: 'Checks the existence of the selected keys from message data and metadata.', | |
18 | + }, | |
19 | + checkRelation: { | |
20 | + name: 'check relation', | |
21 | + details: | |
22 | + 'If at least one relation exists - send Message via <b>True</b> chain, otherwise <b>False</b> chain is used.', | |
23 | + description: `Checks the relation from the selected entity to the originator of the message by type and direction if 'Check for single entity' is set to true, otherwise rule node will check if exist any relation to the originator of the message by type and direction.`, | |
24 | + }, | |
25 | + gpsGeofencingFilter: { | |
26 | + name: 'gps geofencing filter', | |
27 | + details: `Extracts latitude and longitude parameters from incoming message and returns 'True' if they are inside configured perimeters, 'False' otherwise.`, | |
28 | + description: 'Filter incoming messages by GPS based geofencing', | |
29 | + }, | |
30 | + messageType: { | |
31 | + name: 'message type', | |
32 | + details: `If incoming MessageType is expected - send Message via <b>True</b> chain, otherwise <b>False</b> chain is used.`, | |
33 | + description: 'Filter incoming messages by Message Type', | |
34 | + }, | |
35 | + messageTypeSwitch: { | |
36 | + name: 'message type switch', | |
37 | + details: `Sends messages with message types <b>"Post attributes", "Post telemetry", "RPC Request"</b> etc. via corresponding chain, otherwise <b>Other</b> chain is used.`, | |
38 | + description: 'Route incoming messages by Message Type', | |
39 | + }, | |
40 | + originatorType: { | |
41 | + name: 'originator type', | |
42 | + details: `If Originator Type of incoming message is expected - send Message via <b>True</b> chain, otherwise <b>False</b> chain is used.`, | |
43 | + description: 'Filter incoming messages by message Originator Type', | |
44 | + }, | |
45 | + originatorTypeSwitch: { | |
46 | + name: 'originator type switch', | |
47 | + details: `Routes messages to chain according to the originator type ('Device', 'Asset', etc.).`, | |
48 | + description: 'Route incoming messages by Message Originator Type', | |
49 | + }, | |
50 | + sceneReact: { | |
51 | + name: 'originator type switch', | |
52 | + details: `Implement interactive control of devices based on business scenarios`, | |
53 | + description: 'Implement interactive control of devices based on business scenarios', | |
54 | + }, | |
55 | + script: { | |
56 | + name: 'script', | |
57 | + details: `Evaluate incoming Message with configured JS condition. If <b>True</b> - send Message via <b>True</b> chain, otherwise <b>False</b> chain is used.Message payload can be accessed via <code>msg</code> property. For example <code>msg.temperature < 10;</code><br/>Message metadata can be accessed via <code>metadata</code> property. For example <code>metadata.customerName === 'John';</code><br/>Message type can be accessed via <code>msgType</code> property.`, | |
58 | + description: 'Filter incoming messages using JS script', | |
59 | + }, | |
60 | + switch: { | |
61 | + name: 'switch', | |
62 | + details: `Node executes configured JS script. Script should return array of next Chain names where Message should be routed. If Array is empty - message not routed to next Node. Message payload can be accessed via <code>msg</code> property. For example <code>msg.temperature < 10;</code><br/>Message metadata can be accessed via <code>metadata</code> property. For example <code>metadata.customerName === 'John';</code><br/>Message type can be accessed via <code>msgType</code> property.`, | |
63 | + description: 'Route incoming Message to one or multiple output chains', | |
64 | + }, | |
65 | +}; | ... | ... |
1 | +/** | |
2 | + *规则链 Flow 英文配置文件 | |
3 | + 目前共有4个,分类按名称进行配置 | |
4 | + */ | |
5 | +export default { | |
6 | + title: 'Flow', | |
7 | + description: 'Organizes message flow', | |
8 | + acknowledge: { | |
9 | + name: 'acknowledge', | |
10 | + details: `After acknowledgement, the message is pushed to related rule nodes. Useful if you don't care what happens to this message next.`, | |
11 | + description: 'Acknowledges the incoming message', | |
12 | + }, | |
13 | + checkpoint: { | |
14 | + name: 'checkpoint', | |
15 | + details: `After successful transfer incoming message is automatically acknowledged. Queue name is configurable.`, | |
16 | + description: 'transfers the message to another queue', | |
17 | + }, | |
18 | + output: { | |
19 | + name: 'output', | |
20 | + details: `Produces output of the rule chain processing. The output is forwarded to the caller rule chain, as an output of the corresponding "input" rule node. The output rule node name corresponds to the relation type of the output message, and it is used to forward messages to other rule nodes in the caller rule chain. `, | |
21 | + description: 'transfers the message to the caller rule chain', | |
22 | + }, | |
23 | + ruleChain: { | |
24 | + name: 'rule chain', | |
25 | + details: `Allows to nest the rule chain similar to single rule node. The incoming message is forwarded to the input node of the specified target rule chain. The target rule chain may produce multiple labeled outputs. You may use the outputs to forward the results of processing to other rule nodes.`, | |
26 | + description: 'transfers the message to another rule chain', | |
27 | + }, | |
28 | +}; | ... | ... |
1 | +/** | |
2 | + *规则链 变换 英文配置文件 | |
3 | + 目前共有3个,分类按名称进行配置 | |
4 | + */ | |
5 | +export default { | |
6 | + title: 'Transform', | |
7 | + description: 'Change message Payload and metadata', | |
8 | + changeOriginator: { | |
9 | + name: 'change originator', | |
10 | + details: `Related Entity found using configured relation direction and Relation Type. If multiple Related Entities are found, only first Entity is used as new Originator, other entities are discarded.<br/>Alarm Originator found only in case original Originator is <code>Alarm</code> entity.`, | |
11 | + description: 'Change Message Originator To Tenant/Customer/Related Entity/Alarm Originator', | |
12 | + }, | |
13 | + script: { | |
14 | + name: 'script', | |
15 | + details: `JavaScript function receive 3 input parameters <br/> <code>metadata</code> - is a Message metadata.<br/><code>msg</code> - is a Message payload.<br/><code>msgType</code> - is a Message type.<br/>Should return the following structure:<br/><code>{ msg: <i style="color: #666;">new payload</i>,<br/>   metadata: <i style="color: #666;">new metadata</i>,<br/>   msgType: <i style="color: #666;">new msgType</i> }</code><br/>All fields in resulting object are optional and will be taken from original message if not specified.`, | |
16 | + description: 'Change Message payload, Metadata or Message type using JavaScript', | |
17 | + }, | |
18 | + toEmail: { | |
19 | + name: 'to email', | |
20 | + details: `Transforms message to email message by populating email fields using values derived from message metadata. Set 'SEND_EMAIL' output message type.`, | |
21 | + description: 'Transforms message to email message', | |
22 | + }, | |
23 | +}; | ... | ... |
1 | +/** | |
2 | + *规则链 动作 英文配置文件 | |
3 | + 目前共有22个,分类按名称进行配置 | |
4 | + */ | |
5 | +export default { | |
6 | + AssignToCustomerFieldsNameEnum: { | |
7 | + CUSTOMER_NAME_PATTERN: 'Customer name pattern', | |
8 | + CREATE_CUSTOMER_IF_NOT_EXISTS: 'Create new customer if not exists', | |
9 | + CUSTOMER_CACHE_EXPIRATION: 'Customers cache expiration time(sec)', | |
10 | + }, | |
11 | + ClearAlarmFieldsNameEnum: { | |
12 | + ALARM_TYPE: 'Alarm type', | |
13 | + ALARM_DETAILS_BUILD_JS: 'Alarm details builder', | |
14 | + }, | |
15 | + CreateAlarmFieldsNameEnum: { | |
16 | + ALARM_DETAILS_BUILD_JS: 'Alarm details builder', | |
17 | + USE_MESSAGE_ALARM_DATA: 'Use message alarm data', | |
18 | + OVERWRITE_ALARM_DETAILS: 'Overwrite alarm details', | |
19 | + ALARM_TYPE: 'Alarm type', | |
20 | + SEVERITY: 'Alarm severity pattern', | |
21 | + PROPAGATE: 'Propagate alarm to related entities', | |
22 | + RELATION_TYPES: 'Relation types to propagate', | |
23 | + PROPAGATE_TO_OWNER: 'Propagate alarm to entity owner (Customer or Tenant)', | |
24 | + PROPAGATE_TO_TENANT: 'Propagate alarm to Tenant', | |
25 | + DYNAMIC_SEVERITY: 'Use alarm severity pattern', | |
26 | + }, | |
27 | + CreateRelationFieldsNameEnum: { | |
28 | + DIRECTION: 'Direction', | |
29 | + ENTITY_TYPE: 'Type', | |
30 | + ENTITY_NAME_PATTERN: 'Name pattern', | |
31 | + ENTITY_TYPE_PATTERN: 'Type pattern', | |
32 | + RELATION_TYPE: 'Relation type pattern', | |
33 | + CREATE_ENTITY_IF_NOT_EXISTS: 'Create new entity if not exists', | |
34 | + REMOVE_CURRENT_RELATIONS: 'Remove current relations', | |
35 | + CHANGE_ORIGINATOR_TO_RELATED_ENTITY: 'Change originator to related entity', | |
36 | + ENTITY_CACHE_EXPIRATION: 'Entities cache expiration time(sec)', | |
37 | + }, | |
38 | + DelayDeprecatedFieldsNameEnum: { | |
39 | + USE_METADATA_PERIOD_IN_SECONDS_PATTERNS: 'Use period in seconds pattern', | |
40 | + PERIOD_IN_SECONDS: 'Period in seconds', | |
41 | + PERIOD_IN_SECONDS_PATTERN: 'Period in seconds pattern', | |
42 | + MAX_PENDING_MSGS: 'Maximum pending messages', | |
43 | + }, | |
44 | + DeleteRelationFieldsNameEnum: { | |
45 | + DELETE_FOR_SINGLE_ENTITY: 'Delete relation to specific entity', | |
46 | + DIRECTION: 'Direction', | |
47 | + ENTITY_TYPE: 'Type', | |
48 | + ENTITY_NAME_PATTERN: 'Name pattern', | |
49 | + RELATION_TYPE: 'Relation type pattern', | |
50 | + ENTITY_CACHE_EXPIRATION: 'Entities cache expiration time(sec)', | |
51 | + }, | |
52 | + DeviceProfileFieldsNameEnum: { | |
53 | + PERSIST_ALARM_RULES_STATE: 'Persist state of alarm rules', | |
54 | + FETCH_ALARM_RULES_STATE_ON_START: 'Fetch state of alarm rules', | |
55 | + }, | |
56 | + GeneratorFieldsNameEnum: { | |
57 | + MSG_COUNT: 'Message count(0 - unlimited)', | |
58 | + PERIOD_IN_SECONDS: 'Period in seconds', | |
59 | + JS_SCRIPT: 'Generate', | |
60 | + // ORIGINATOR_ID = '资产', | |
61 | + ORIGINATOR_TYPE: 'Type', | |
62 | + }, | |
63 | + GpsGeofencingEventsFieldsNameEnum: { | |
64 | + LATITUDE_KEY_NAME: 'Latitude key name', | |
65 | + LONGITUDE_KEY_NAME: 'longitude key name', | |
66 | + PERIMETER_TYPE: 'Perimeter type', | |
67 | + FETCH_PERIMETER_INFO_FROM_MESSAGE_METADATA: 'Fetch perimeter information from message metadata', | |
68 | + PERIMETER_KEY_NAME: 'Perimeter key name', | |
69 | + CENTER_LATITUDE: 'Center Latitude', | |
70 | + CENTER_LONGITUDE: 'Center Longitude', | |
71 | + RANGE: 'Range', | |
72 | + RANGE_UNIT: 'Range units', | |
73 | + POLYGONS_DEFINITION: 'Polygon definition', | |
74 | + MIN_INSIDE_DURATION: 'Minimal inside duration', | |
75 | + MIN_INSIDE_DURATION_TIME_UNIT: 'Minimal inside duration time unit', | |
76 | + MIN_OUTSIDE_DURATION: 'Minimal outside duration', | |
77 | + MIN_OUTSIDE_DURATION_TIME_UNIT: 'Minimal outside duration time unit', | |
78 | + }, | |
79 | + LogFieldsNameEnum: { | |
80 | + JS_SCRIPT: 'To string', | |
81 | + }, | |
82 | + MessageCountFieldsNameEnum: { | |
83 | + INTERVAL: 'Interval in seconds', | |
84 | + TELEMETRY_PREFIX: 'Output timeseries key prefix', | |
85 | + }, | |
86 | + PushToEdgeFieldsNameEnum: { | |
87 | + SCOPE: 'Device Attribute Range', | |
88 | + }, | |
89 | + RpcCallReplyFieldsNameEnum: { | |
90 | + REQUEST_ID_META_DATA_ATTRIBUTE: 'Request Id Metadata attribute name', | |
91 | + }, | |
92 | + RpcCallRequestFieldsNameEnum: { | |
93 | + TIMEOUT_IN_SECONDS: 'Timeout in seconds', | |
94 | + }, | |
95 | + SaveAttributesFieldsNameEnum: { | |
96 | + NOTIFY_DEVICE: 'Notify Device', | |
97 | + SCOPE: 'Device Attribute Range', | |
98 | + }, | |
99 | + SaveEventFieldsNameEnum: { | |
100 | + CONFIGURATION: 'configuration', | |
101 | + }, | |
102 | + SaveTimeseriesFieldsNameEnum: { | |
103 | + DEFAULT_TTL: 'Default TTL in seconds', | |
104 | + SKIP_LATEST_PERSISTENCE: 'Skit latest persistence', | |
105 | + USE_SERVER_TS: 'Use server ts', | |
106 | + }, | |
107 | + SaveToCustomTableFieldsNameEnum: { | |
108 | + FIELDS_MAPPING: 'Fields mapping', | |
109 | + TABLE_NAME: 'Custom table name', | |
110 | + }, | |
111 | + UnassignFromCustomerFieldsNameEnum: { | |
112 | + CUSTOMER_NAME_PATTERN: 'Customer name pattern', | |
113 | + CUSTOMER_CACHE_EXPIRATION: 'Customer cache expiration time(sec)', | |
114 | + }, | |
115 | +}; | ... | ... |
1 | +/** | |
2 | + *规则链 属性集表单 英文配置文件 | |
3 | + 目前共有10个,分类按名称进行配置 | |
4 | + */ | |
5 | +export default { | |
6 | + CalculateDeltaFieldsNameEnum: { | |
7 | + INPUT_VALUE_KEY: 'Input value key', | |
8 | + OUTPUT_VALUE_KEY: 'Output value key', | |
9 | + ROUND: 'Decimals', | |
10 | + USE_CACHE: 'Use cache for latest value', | |
11 | + TELL_FAILURE_IF_DELTA_IS_NEGATIVE: 'Tell Failure if delta is negative', | |
12 | + ADD_PERIOD_BETWEEN_MSGS: 'Add period between messages', | |
13 | + PERIOD_VALUE_KEY: 'Period value key', | |
14 | + }, | |
15 | + CustomerAttributesFieldsNameEnum: { | |
16 | + ATTR_MAPING: 'Attributes mapping', | |
17 | + TELEMETRY: 'Latest telemetry', | |
18 | + }, | |
19 | + CustomerDetailsFieldsNameEnum: { | |
20 | + DETAILS_LIST: 'Select entity details', | |
21 | + ADD_TO_METADATA: 'Add selected details to message metadata', | |
22 | + }, | |
23 | + OriginatorAttributesNameEnum: { | |
24 | + TELL_FAILURE_IF_ABSENT: 'Tell Failure', | |
25 | + CLIENT_ATTRIBUTE_NAMES: 'Client attributes', | |
26 | + SHARED_ATTRIBUTE_NAMES: 'Shared attributes', | |
27 | + SERVER_ATTRIBUTE_NAMES: 'Server attributes', | |
28 | + LATEST_TS_KEY_NAMES: 'Latest timeseries', | |
29 | + GET_LATEST_VALUE_WITH_TS: 'Fetch Latest telemetry with Timestamp', | |
30 | + }, | |
31 | + OriginatorFieldsNameEnum: { | |
32 | + FIELDS_MAPPING: 'Fields mapping', | |
33 | + }, | |
34 | + OriginatorTelemetryFieldsNameEnum: { | |
35 | + LATEST_TS_KEY_NAMES: 'Timeseries key', | |
36 | + AGGREGATION: 'Data aggregation function', | |
37 | + FETCH_MODE: 'Fetch Mode', | |
38 | + ORDER_BY: 'Order by', | |
39 | + LIMIT: 'Limit', | |
40 | + USE_METADATA_INTERVAL_PATTERNS: 'Use interval patterns', | |
41 | + START_INTERVAL: 'Start Interval', | |
42 | + START_INTERVAL_TIME_UNIT: 'Start Interval Time Unit', | |
43 | + END_INTERVAL: 'End Interval', | |
44 | + END_INTERVAL_TIME_UNIT: 'End Interval Time Unit', | |
45 | + START_INTERVAL_PATTERN: 'startIntervalPattern', | |
46 | + END_INTERVAL_PATTERN: 'endIntervalPattern', | |
47 | + }, | |
48 | + RelatedAttributesFieldsNameEnum: { | |
49 | + RELATIONS_QUERY: 'Relations query', | |
50 | + ATTR_MAPPING: 'Attributes mapping', | |
51 | + TELEMETRY: 'Latest telemetry', | |
52 | + }, | |
53 | + RelatedDeviceAttributeFieldsNameEnum: { | |
54 | + DEVICE_RELATIONS_QUERY: 'DeviceRelationsQuery', | |
55 | + TELL_FAILURE_IF_ABSENT: 'Tell Failure', | |
56 | + CLIENT_ATTRIBUTE_NAMES: 'Client attributes', | |
57 | + SHARED_ATTRIBUTE_NAMES: 'Shared attributes', | |
58 | + SERVER_ATTRIBUTE_NAMES: 'Server attributes', | |
59 | + LATEST_TS_KEY_NAMES: 'Latest timeseries', | |
60 | + GET_LATEST_VALUE_WITH_TS: 'Fetch Latest telemetry with Timestamp', | |
61 | + FETCH_LAST_LEVEL_ONLY: 'Obtain only the last level of association', | |
62 | + // DEVICE_RELATIONS_QUERY | |
63 | + DIRECTION: 'Direction', | |
64 | + MAX_LEVEL: 'Max relation level', | |
65 | + RELATION_TYPE: 'Association type', | |
66 | + DEVICE_TYPES: 'Device Type', | |
67 | + }, | |
68 | + TenantAttributesFieldsNameEnum: { | |
69 | + ATTR_MAPING: 'attrMapping', | |
70 | + TELEMETRY: 'Latest telemetry', | |
71 | + }, | |
72 | + TenantDetailsFieldsNameEnum: { | |
73 | + DETAILS_LIST: 'Add selected details to message metadata', | |
74 | + ADD_TO_METADATA: 'Select entity details', | |
75 | + }, | |
76 | +}; | ... | ... |
1 | +/** | |
2 | + *规则链 外部的 英文配置文件 | |
3 | + 目前共有11个,分类按名称进行配置 | |
4 | + */ | |
5 | +export default { | |
6 | + AlarmNoticeFieldsNameEnum: { | |
7 | + CONFIGURATION: 'configuration', | |
8 | + }, | |
9 | + AwsSnsFieldsNameEnum: { | |
10 | + TOPIC_ARN_PATTERN: 'Topic ARN pattern', | |
11 | + ACCESS_KEY_ID: 'AWS Access Key ID', | |
12 | + SECRET_ACCESS_KEY: 'AWS Secret Access Key', | |
13 | + REGION: 'AWS Region', | |
14 | + }, | |
15 | + AwsSqsFieldsNameEnum: { | |
16 | + QUEUE_TYPE: 'Queue type', | |
17 | + QUEUE_URL_PATTERN: 'Queue URL pattern', | |
18 | + DELAY_SECONDS: 'Delay(seconds)', | |
19 | + MESSAGE_ATTRIBUTES: 'Message attributes', | |
20 | + ACCESS_KEY_ID: 'AWS Access Key ID', | |
21 | + SECRET_ACCESS_KEY: 'AWS Secret Access Key', | |
22 | + REGION: 'AWS Region', | |
23 | + }, | |
24 | + AzureIotHubFieldsNameEnum: { | |
25 | + TOPIC_PATTERN: 'Topic', | |
26 | + HOST: 'Hostname', | |
27 | + PORT: 'port', | |
28 | + CONNECT_TIMEOUT_SEC: 'connectTimeoutSec', | |
29 | + CLIENT_ID: 'Device ID', | |
30 | + CLEAN_SESSION: 'cleanSession', | |
31 | + SSL: 'ssl', | |
32 | + CREDENTIALS: 'credentials', | |
33 | + TYPE: 'Credentials type', | |
34 | + SAS_KEY: 'sasKey', | |
35 | + CA_CERT: 'CA certificate file', | |
36 | + CA_CERT_FILE_NAME: 'caCertFileName', | |
37 | + PRIVATE_KEY: 'Client private key file', | |
38 | + PRIVATE_KEY_FILE_NAME: 'privateKeyFileName', | |
39 | + CERT: 'Client certificate file', | |
40 | + CERT_FILE_NAME: 'certFileName', | |
41 | + PASSWORD: 'Private key password', | |
42 | + }, | |
43 | + GcpPubsubFieldsNameEnum: { | |
44 | + PROJECT_ID: 'GCP project ID', | |
45 | + TOPIC_NAME: 'Topic name', | |
46 | + SERVICE_ACCOUNT_KEY: 'GCP service account key file', | |
47 | + SERVICE_ACCOUNT_KEY_FILE_NAME: 'serviceAccountKeyFileName', | |
48 | + MESSAGE_ATTRIBUTES: 'Message attributes', | |
49 | + }, | |
50 | + KafkaFieldsNameEnum: { | |
51 | + TOPIC_PATTERN: 'Topic pattern', | |
52 | + BOOTSTRAP_SERVERS: 'Bootstrap servers', | |
53 | + RETRIES: 'Automatically retry times if fails', | |
54 | + BATCH_SIZE: 'Produces batch size in bytes', | |
55 | + LINGER: 'Time to buffer locally(ms)', | |
56 | + BUFFER_MEMORY: 'Client buffer max size in bytes', | |
57 | + ACKS: 'Number of acknowledgments', | |
58 | + KEY_SERIALIZER: 'Key serializer', | |
59 | + VALUE_SERIALIZER: 'Value serializer', | |
60 | + OTHER_PROPERTIES: 'Other properties', | |
61 | + ADD_METADATA_KEY_VALUES_AS_KAFKA_HEADERS: | |
62 | + 'Add Message metadata key-value pairs to Kafka record headers', | |
63 | + KAFKA_HEADERS_CHARSET: 'Charset encoding', | |
64 | + }, | |
65 | + MqttFieldsNameEnum: { | |
66 | + TOPIC_PATTERN: 'Topic pattern', | |
67 | + HOST: 'Host', | |
68 | + PORT: 'Port', | |
69 | + CONNECT_TIMEOUT_SEC: 'Connection timeout(sec)', | |
70 | + CLIENT_ID: 'Client ID', | |
71 | + APPEND_CLIENT_ID_SUFFIX: 'Add Service ID as suffix to Client ID', | |
72 | + CLEAN_SESSION: 'Clean session', | |
73 | + SSL: 'Enable SSL', | |
74 | + CREDENTIALS: 'credentials', | |
75 | + | |
76 | + TYPE: 'Credentials type', | |
77 | + PASSWORD: 'Password', | |
78 | + CA_CERT: 'Server CA certificate file', | |
79 | + CA_CERT_FILE_NAME: 'caCertFileName', | |
80 | + PRIVATE_KEY: 'Client private key file', | |
81 | + PRIVATE_KEY_FILE_NAME: 'privateKeyFileName', | |
82 | + CERT: 'Client certificate file', | |
83 | + CERT_FILE_NAME: 'certFileName', | |
84 | + USERNAME: 'Username', | |
85 | + }, | |
86 | + RabbitmqFieldsNameEnum: { | |
87 | + EXCHANGE_NAME_PATTERN: 'Exchange name pattern', | |
88 | + ROUTING_KEY_PATTERN: 'Routing key pattern', | |
89 | + MESSAGE_PROPERTIES: 'Message properties', | |
90 | + HOST: 'Host', | |
91 | + PORT: 'Port', | |
92 | + VIRTUAL_HOST: 'Virtual host', | |
93 | + USERNAME: 'Username', | |
94 | + PASSWORD: 'Password', | |
95 | + AUTOMATIC_RECOVERY_ENABLED: 'Automatic recovery', | |
96 | + CONNECTION_TIMEOUT: 'Connection timeout(ms)', | |
97 | + HANDSHAKE_TIMEOUT: 'Handshake timeout(ms)', | |
98 | + CLIENT_PROPERTIES: 'Client properties', | |
99 | + }, | |
100 | + RestApiCallFieldsNameEnum: { | |
101 | + REST_ENDPOINT_URL_PATTERN: 'Endpoint URL pattern', | |
102 | + REQUEST_METHOD: 'Request method', | |
103 | + USE_SIMPLE_CLIENT_HTTP_FACTORY: 'Use simple client HTTP factory', | |
104 | + IGNORE_REQUEST_BODY: 'Without request body', | |
105 | + ENABLE_PROXY: 'Enable proxy', | |
106 | + USE_SYSTEM_PROXY_PROPERTIES: 'Use system proxy properties', | |
107 | + PROXY_SCHEME: 'Proxy scheme', | |
108 | + PROXY_HOST: 'Proxy host', | |
109 | + PROXY_PORT: 'Proxy port', | |
110 | + PROXY_USER: 'Proxy user', | |
111 | + PROXY_PASSWORD: 'Proxy password', | |
112 | + READ_TIMEOUT_MS: 'Read timeout in millis', | |
113 | + MAX_PARALLEL_REQUESTS_COUNT: 'Max number of parallel request', | |
114 | + HEADERS: 'Header', | |
115 | + USE_REDIS_QUEUE_FOR_MSG_PERSISTENCE: 'Use redis queue for message persistence', | |
116 | + TRIM_QUEUE: 'Trim redis queue', | |
117 | + MAX_QUEUE_SIZE: 'Redis queue max size', | |
118 | + CREDENTIALS: 'Credentials', | |
119 | + | |
120 | + TYPE: 'Credentials type', | |
121 | + PASSWORD: 'Password', | |
122 | + CA_CERT: 'Server CA certificate file', | |
123 | + CA_CERT_FILE_NAME: 'caCertFileName', | |
124 | + PRIVATE_KEY: 'Client private key file', | |
125 | + PRIVATE_KEY_FILE_NAME: 'privateKeyFileName', | |
126 | + CERT: 'Client certificate file', | |
127 | + CERT_FILE_NAME: 'certFileName', | |
128 | + USERNAME: 'Username', | |
129 | + }, | |
130 | + SendEmailFieldsNameEnum: { | |
131 | + USE_SYSTEM_SMTP_SETTINGS: 'Use system SMTP settings', | |
132 | + SMTP_PROTOCOL: 'Protocol', | |
133 | + SMTP_HOST: 'SMTP host', | |
134 | + SMTP_PORT: 'SMTP port', | |
135 | + TIMEOUT: 'Timeout ms', | |
136 | + ENABLE_TLS: 'Enable TLS', | |
137 | + TLS_VERSION: 'TLS version', | |
138 | + ENABLE_PROXY: 'Enable proxy', | |
139 | + PROXY_HOST: 'Proxy host', | |
140 | + PROXY_PORT: 'Proxy port', | |
141 | + PROXY_USER: 'Proxy user', | |
142 | + PROXY_PASSWORD: 'Proxy password', | |
143 | + USERNAME: 'Username', | |
144 | + PASSWORD: 'Password', | |
145 | + }, | |
146 | + SendSMSFieldsNameEnum: { | |
147 | + NUMBERS_TO_TEMPLATE: 'Phone Numbers To Template', | |
148 | + SMS_MESSAGE_TEMPLATE: 'SMS message Template', | |
149 | + USE_SYSTEM_SMS_SETTINGS: 'User system SMS provider settings', | |
150 | + SMS_PROVIDER_CONFIGURATION: 'smsProviderConfiguration', | |
151 | + | |
152 | + ACCESS_KEY_ID: 'AWS Access Key ID', | |
153 | + SECRET_ACCESS_KEY: 'AWS Access Key', | |
154 | + REGION: 'AWS Area', | |
155 | + TYPE: 'SMS Service provider type', | |
156 | + NUMBER_FROM: "Sender's phone number", | |
157 | + ACCOUNT_SID: 'Twilio Account SID', | |
158 | + ACCOUNT_TOKEN: 'Twilio Account Token', | |
159 | + }, | |
160 | +}; | ... | ... |
1 | +/** | |
2 | + *规则链 筛选器表单 英文配置文件 | |
3 | + 目前共有11个,分类按名称进行配置 | |
4 | + */ | |
5 | +export default { | |
6 | + CheckAlarmStatusFieldNameEnum: { | |
7 | + ALARM_STATUS_LIST: 'Alarm status filter', | |
8 | + }, | |
9 | + CheckExistenceFieldsNameEnum: { | |
10 | + MESSAGE_NAMES: 'Message data', | |
11 | + METADATA_NAMES: 'Message metadata', | |
12 | + CHECK_ALL_KEYS: 'Check that all selected keys are present', | |
13 | + }, | |
14 | + CheckRelationFieldsNameEnum: { | |
15 | + DIRECTION: 'Direction', | |
16 | + CHECK_FOR_SINGLE_ENTITY: 'Check relation to specific entity', | |
17 | + ENTITY_TYPE: 'Type', | |
18 | + RELEATION_TYPE: 'Association type', | |
19 | + }, | |
20 | + GpsGeofencingFilterFieldsNameEnum: { | |
21 | + LATITUDE_KEY_NAME: 'Latitude Key Name', | |
22 | + LONGITUDE_KEY_NAME: 'Longitude Key Name', | |
23 | + PERIMETER_TYPE: 'Perimeter Type', | |
24 | + FETCH_PERIMETER_INFO_FROM_MESSAGE_METADATA: 'Fetch perimeter information from message metadata', | |
25 | + CENTER_LATITUDE: 'Center latitude', | |
26 | + CENTER_LONGITUDE: 'Center longitude', | |
27 | + RANGE: 'Range', | |
28 | + RANGE_UNIT: 'Range unit', | |
29 | + PERIMETER_KEY_NAME: 'Perimeter key name', | |
30 | + POLYGONS_DEFINITION: 'Polygons definition', | |
31 | + }, | |
32 | + MessageTypeFieldsNameEnum: { | |
33 | + MESSAGE_TYPES: 'Message Types Filter', | |
34 | + }, | |
35 | + OriginatorTypeFieldsNameEnum: { | |
36 | + ORIGINATOR_TYPES: 'Originator types filter', | |
37 | + }, | |
38 | + ScriptFieldsNameEnum: { | |
39 | + JS_SCRIPT: 'Filter', | |
40 | + }, | |
41 | + SwitchFieldsNameEnum: { | |
42 | + JS_SCRIPT: 'Filter', | |
43 | + }, | |
44 | +}; | ... | ... |
1 | +/** | |
2 | + *规则链 变换 英文配置文件 | |
3 | + 目前共有3个,分类按名称进行配置 | |
4 | + */ | |
5 | +export default { | |
6 | + ChangeOriginatorFieldsNameEnum: { | |
7 | + ORIGINATOR_SOURCE: 'Originator source', | |
8 | + RELATIONS_QUERY: 'Relations Query', | |
9 | + }, | |
10 | + ScriptFieldsNameEnum: { | |
11 | + JS_SCRIPT: 'Transform', | |
12 | + }, | |
13 | + ToEmailFieldsNameEnum: { | |
14 | + FROM_TEMPLATE: 'From Template', | |
15 | + TO_TEMPLATE: 'To Template', | |
16 | + CC_TEMPLATE: 'Cc Template', | |
17 | + BCC_TEMPLATE: 'Bcc Template', | |
18 | + SUBJECT_TEMPLATE: 'Subject Template', | |
19 | + MAIL_BODY_TYPE: 'Mail body type', | |
20 | + IS_HTML_TEMPLATE: 'Dynamic mail body type', | |
21 | + BODY_TEMPLATE: 'Body Template', | |
22 | + }, | |
23 | +}; | ... | ... |
1 | +/** | |
2 | + *规则链 动作 中文配置文件 | |
3 | + 目前共有22个,分类按名称进行配置 | |
4 | + */ | |
5 | +export default { | |
6 | + title: '动作', | |
7 | + description: '执行特别动作', | |
8 | + assignToCustomer: { | |
9 | + name: '分配给客户', | |
10 | + details: `按客户名称模式查找目标客户,然后将发起者实体分配给该客户。如果不存在将创建新客户,并且【如果不存在则创建新客户]设置为true。`, | |
11 | + description: '将消息发起人实体分配给客户', | |
12 | + }, | |
13 | + clearAlarm: { | |
14 | + name: '清除告警', | |
15 | + details: `详情-基于传入消息创建JSON对象的JS函数。该对象将被添加到Alarm.details字段中。节点输出:如果告警未被清除,则返回原始消息。否则,返回类型为【告警】新消息【msg】属性和【metadata】中的告警对象将包含【isClearedAlarm】属性。可以通过【msg】属性访问消息负载。例如'temperature='+msg.temperature。也可以通过【metadata】属性访问消息元数据。例如'name='+metadata.customerName。`, | |
16 | + description: '清除告警', | |
17 | + }, | |
18 | + copyToView: { | |
19 | + name: '复制到视图', | |
20 | + details: `根据实体视图配置,将属性从资产/设备复制到相关的实体视图。复制将仅针对开始日期和结束日期之间的属性,并根据属性键进行配置。将消息发起者更改为相关的实体视图,并根据更新的实体视图的计数生成新消息。`, | |
21 | + description: '将属性从资产/设备复制到实体视图,并将消息发起者更改为相关实体视图。', | |
22 | + }, | |
23 | + createAlarm: { | |
24 | + name: '创建告警', | |
25 | + details: `详情-基于传入消息创建JSON对象的JS函数。该对象将被添加到Alarm.details字段中。节点输出:如果告警未被创建,则返回原始消息。否则,返回类型为【告警】的新消息,【msg】属性和【metadata】中的告警对象将包含其中一个属性【isNewAlarm/isExistingAlarm】。可以通过【msg】属性访问消息负载。例如'temperature='+msg.temperature。可以通过【metadata】属性访问消息元数据。例如'name='+metadata.customerName。`, | |
26 | + description: '创建或更新告警。', | |
27 | + }, | |
28 | + createRelation: { | |
29 | + name: '创建关联关系', | |
30 | + details: `如果关系已存在或已成功创建;通过Success链发送消息,否则将使用Failure链路。`, | |
31 | + description: `按实体名称模式和(资产、设备的实体类型模式)查找目标实体,然后按类型和方向创建与发起人实体的关系。如果所选实体类型:资产、设备或客户将创建新实体(如果不存在),并选中【创建新实体】;如果从邮件发起人到所选实体的关系不存在,并且选中【删除当前关系】,则在创建新关系之前,将删除按类型和方向与邮件发起人的所有现有关系;如果创建了从邮件发件人到所选实体的关系,并且选中了【将发件人更改为相关实体】,则出站邮件将作为来自该实体的邮件进行处理。`, | |
32 | + }, | |
33 | + deleteRelation: { | |
34 | + name: '删除关联关系', | |
35 | + details: `如果成功删除关系;通过则走Success链发送消息,否则将使用Failure链。`, | |
36 | + description: `如果“删除单个实体”设置为true,则按实体名称模式查找目标实体,然后按类型和方向删除与发起者实体的关系,否则规则节点将按类型和顺序,删除与消息发起人的所有关系。`, | |
37 | + }, | |
38 | + delay: { | |
39 | + name: '延迟(已弃用)', | |
40 | + details: `为消息传输配置一段时间的延迟。请注意,此节点确认当前队列中的消息(消息将从队列中删除)。不推荐,因为已确认的消息仍保留在内存中(待延迟),并且即使选择了【重试失败和超时】处理策略,也不能保证消息得到处理。`, | |
41 | + description: `延迟传入消息(已弃用)`, | |
42 | + }, | |
43 | + deviceProfile: { | |
44 | + name: '设备配置', | |
45 | + details: `根据设备配置文件中定义的告警规则,创建和清除报警。输出关联类型为【告警创建】、【告警更新】、【告警严重性更新】和【告警已清除】,如果没有告警受到影响,则仅为【成功】。`, | |
46 | + description: `根据设备配置文件处理设备消息。`, | |
47 | + }, | |
48 | + generator: { | |
49 | + name: '生成器', | |
50 | + details: `生成具有可配置周期的消息。用于生成消息的Javascript函数。`, | |
51 | + description: `定期生成消息。`, | |
52 | + }, | |
53 | + gpsGeofencingEvents: { | |
54 | + name: 'GPS地理围栏事件', | |
55 | + details: `从传入消息中提取纬度和经度参数,并根据配置参数返回不同的事件。`, | |
56 | + description: `使用基于GPS的地理围栏生成传入消息。`, | |
57 | + }, | |
58 | + log: { | |
59 | + name: '日志', | |
60 | + details: `将配置JS函数的传入Message转换为String,并将最终值记录到平台日志文件中。可以通过msg属性访问消息负载。例如temperature= +msg.temperature。可以通过metadata属性访问消息元数据。例如name= +metadata.customerName。`, | |
61 | + description: `使用JS脚本记录传入消息,将Message转换为String。`, | |
62 | + }, | |
63 | + messageCount: { | |
64 | + name: '消息计数', | |
65 | + details: `对指定间隔内的传入消息进行计数,并生成具有消息计数的POST_ELEMETRY_REQUEST消息。`, | |
66 | + description: `统计传入消息。`, | |
67 | + }, | |
68 | + pushToEdge: { | |
69 | + name: '推送消息到边缘端', | |
70 | + details: `将消息从云端推送到边缘。消息发起者必须分配给指定边缘,或者消息发起方是边缘实体本身。此节点仅在云实例上用于将消息从云推送到边缘。一旦消息到达该节点,它将被转换为边缘事件并保存到数据库中。节点不直接将消息推送到边缘,而是将事件存储在边缘队列中;支持下一个发起方类型:DEVICE/ASSET/ENTITY_VIEW/DASHBOARD/TENANT/CUSTOMER/EDGE;节点也支持下一条消息类型:POST_ELEMETRY_REQUEST/POST_ATTRIBUTES_REQUEST/ATTRIBUTES_UPDATED/ATTRIBUTES_DELETED/ALARM;如果节点无法将边缘事件保存到数据库或到达了不受支持的发起者类型/消息类型,则消息将通过Failure进行路由。在成功存储边缘事件到数据库的情况下,消息将通过Success进行路由。`, | |
71 | + description: `将消息从云端推送到边缘端。`, | |
72 | + }, | |
73 | + rpcCallReply: { | |
74 | + name: 'rpc请求回复', | |
75 | + details: `应为任何消息类型的消息。将消息正文转发到设备。`, | |
76 | + description: `从设备发送对RPC调用的答复。`, | |
77 | + }, | |
78 | + rpcCallRequest: { | |
79 | + name: 'RPC调用请求', | |
80 | + details: `带有method和params的消息,将响应从设备转发到下一个节点。如果RPC调用请求是由用户的REST API调用发起的,则会立即将响应转发给用户。`, | |
81 | + description: `将RPC调用发送到设备。`, | |
82 | + }, | |
83 | + saveAttributes: { | |
84 | + name: '保存属性', | |
85 | + details: `根据可配置的作用域参数保存实体属性。消息类型为POST_ATTRIBUTES_REQUEST的消息`, | |
86 | + description: `保存属性数据。`, | |
87 | + }, | |
88 | + saveEvent: { | |
89 | + name: '保存事件', | |
90 | + details: `保存实体事件,消息类型为POST_EVENT_REQUEST的消息。`, | |
91 | + description: `保存设备事件数据。`, | |
92 | + }, | |
93 | + saveTimeseries: { | |
94 | + name: '保存时序数据', | |
95 | + details: `根据可配置的TTL参数保存时序遥测数据,消息类型为POST_TELEMETRY_REQUEST的消息。时间戳(以毫秒为单位)将取自元数据时间戳,否则将应用现在消息时间戳,如果skipLatestPersistence设置为true,则允许停止更新最新ts_kv表中传入键的值。启用【useServerTs】参数以使用消息处理的时间戳,而不是消息中的时间戳。如果合并的消息来自多种类型(设备、资产等)的消息,则对各种顺序处理都很有用。在顺序处理的情况下,平台保证消息按照提交到队列的顺序进行处理。然而,由多个设备/服务器发起的消息的时间戳可能早在它们被推送到队列之前就不同步了。如果新记录的时间戳比以前的记录旧,则DB层具有某些优化,可以忽略“属性”和“最新值”表的更新。因此,为了确保所有消息都得到正确处理,应该为顺序消息处理场景启用此参数。`, | |
96 | + description: `保存时序数据。`, | |
97 | + }, | |
98 | + saveToCustomTable: { | |
99 | + name: '保存到自定义表', | |
100 | + details: `管理员应设置不带前缀的自定义表名:【cs_tb_】,管理员可以配置消息字段名称和Table列名称之间的映射;注意:如果映射键为【$entity_id】;由消息发起人标识,然后到适当的列名(映射值)将写入消息发起人id。如果指定的消息字段不存在或不是标准JSON,出站消息将通过failure链路;否则,消息将通过success链进行。`, | |
101 | + description: `节点将来自Cassandra数据库的传入Message有效负载的数据,存储到预定义的自定义表中,该表应以cs_tb_为前缀,以避免数据插入到公共tb表中。注意:规则节点只能用于Cassandra DB。`, | |
102 | + }, | |
103 | + synchronizationEnd: { | |
104 | + name: '同步结束', | |
105 | + details: ``, | |
106 | + description: `此节点现在已弃用。请改用“检查数据点”。`, | |
107 | + }, | |
108 | + synchronizationStart: { | |
109 | + name: '同步启动', | |
110 | + details: `此节点应与“同步结束”节点一起使用。此节点将根据消息发起方id将消息放入队列。在上一条消息处理完成或发生超时事件之前,不会处理后续消息。每个发起方的队列大小和超时值可在系统级别进行配置。`, | |
111 | + description: `此节点现在已弃用。请改用【检查数据点】。`, | |
112 | + }, | |
113 | + unassigFromCustomer: { | |
114 | + name: '从客户取消分配', | |
115 | + details: `按客户端名称模式查找目标实体客户,然后取消分配该客户的发起者实体。.`, | |
116 | + description: `从客户取消分配消息发起者实体。`, | |
117 | + }, | |
118 | +}; | ... | ... |
1 | +/** | |
2 | + *规则链 属性集 中文配置文件 | |
3 | + 目前共有10个,分类按名称进行配置 | |
4 | + */ | |
5 | +export default { | |
6 | + title: '属性', | |
7 | + description: '向消息元数据中添加附加信息', | |
8 | + calculateDelta: { | |
9 | + name: '计算增量', | |
10 | + details: `该节点可以在规则中获取上一次遥测的值,以此可以实现二次遥测的差。比如一个设备,一天上传一次数据,如果你要对比今天和昨天的数据,并将两者数据差保存到数据库,就能够使用该节点。增量计算是在消息发起者的范围内完成的,例如设备、资产或客户。如果有输入键,则输出关系将为“成功”,除非增量为负并且设置了相应的配置参数。如果传入消息中没有输入值键,则输出关系将为“其他”。`, | |
11 | + description: `根据传入值和上一个值计算增量值并将其添加到消息中。`, | |
12 | + }, | |
13 | + customerAttributes: { | |
14 | + name: '客户属性', | |
15 | + details: `如果已配置属性,则会将服务器范围属性添加到消息元数据中。如果配置了'最新遥测',则将最新遥测添加到元数据中。要访问其他节点中的这些属性,可以使用metadata.temperature访问。`, | |
16 | + description: `将发起者客户属性或最新遥测添加到消息元数据中。`, | |
17 | + }, | |
18 | + customerDetails: { | |
19 | + name: '客户详情', | |
20 | + details: `如果选中:【将选定的详细信息添加到消息元数据】,则现有字段将添加到消息元数据,而不是消息数据。注意:只允许设备、资产和实体视图类型;如果消息的原始发件人未分配给客户或者不支持原始发件人类型的消息将转发到失败链路,否则将使用成功链路。`, | |
21 | + description: `将“客户详细信息”中的字段添加到消息正文或元数据中。`, | |
22 | + }, | |
23 | + originatorAttributes: { | |
24 | + name: '发起者属性', | |
25 | + details: `如果配置了单个/多个属性,客户端属性/共享属性/服务器属性将添加到具有特定前缀的消息元数据中:cs/SHARED/ss。添加到元数据中的最新遥测值,不带前缀。要访问其他节点中的这些属性,可以使用metadata.cs_temperature或metadata.shared_limit。`, | |
26 | + description: `将消息发起者的属性或最新遥测添加到消息元数据中。`, | |
27 | + }, | |
28 | + originatorFields: { | |
29 | + name: '发起者字段', | |
30 | + details: `将获取映射中指定的字段值。如果指定的字段不是原始发件人字段的一部分,它将被忽略。`, | |
31 | + description: '将消息发起人字段值添加到消息元数据中。', | |
32 | + }, | |
33 | + originatorTelemetry: { | |
34 | + name: '发起者遥测', | |
35 | + details: `该节点允许您选择提取模式:FIRST/LAST/ALL 以提取添加到消息元数据中的特定时间范围的遥测数据,而不带任何前缀。如果选择提取模式,ALL遥测将像数组一样添加到消息元数据中,其中Key为时间戳,Value是遥测的值;如果选择提取模式FIRST或LAST遥测将像不带时间戳的字符串一样添加;此外,规则节点允许您选择遥测采样顺序:ASC或DESC;聚合功能允许您通过AVG、COUNT、SUM、MIN、MAX、NONE将聚合遥测作为单个值获取;注意:提取数据放入数组中最大记录为1000条。`, | |
36 | + description: '将选定时间范围的消息发起人遥测添加到消息元数据中。', | |
37 | + }, | |
38 | + relatedAttributes: { | |
39 | + name: '关联属性', | |
40 | + details: `使用配置的关系方向和关系类型找到相关实体;若找到多个相关实体,则只有第一个实体用于单个/多个属性,其他实体将被丢弃;如果配置了属性,则会将服务器范围属性添加到消息元数据中;如果配置了最新遥测,则将最新遥测添加到元数据中。要访问其他节点中的这些属性,可以使用metadata.temperature访问。`, | |
41 | + description: '将发起人相关实体属性或最新遥测添加到消息元数据中。', | |
42 | + }, | |
43 | + relatedDeviceAttributes: { | |
44 | + name: '关联设备属性', | |
45 | + details: `如果配置了单个/多个属性,CLIENT/SHARED/SERVER 属性将添加到具有特定前缀的消息元数据中:cs/SHARED/ss。添加到元数据中的最新遥测值,不带前缀。要访问其他节点中的这些属性,可以使用 metadata.cs_temperature或metadata.shared_limit。`, | |
46 | + description: '关联设备属性将发起者关联设备属性和最新遥测值添加到消息元数据中。', | |
47 | + }, | |
48 | + tenantAttributes: { | |
49 | + name: '租户属性', | |
50 | + details: `如果配置了单个/多个属性,则会将服务器范围属性添加到消息元数据中。如果配置了单个/多个“最新遥测”,则将最新遥测添加到元数据中。要访问其他节点中的这些属性,可以使用metadata.temperature。`, | |
51 | + description: '将发起者租户属性或最新遥测添加到消息元数据中。', | |
52 | + }, | |
53 | + tenantDetails: { | |
54 | + name: '租户详情', | |
55 | + details: `如果选中:【将选定的详细信息添加到消息元数据】,则现有字段将添加到消息元数据而不是消息数据;注意:只允许设备、资产和实体视图类型;如果原始的消息发件人未分配给Tenant或者不支持原始发件人类型,消息将转发到失败链,否则将使用成功链路。`, | |
56 | + description: '将租户详情中的字段添加到消息正文或元数据中。', | |
57 | + }, | |
58 | +}; | ... | ... |
1 | +/** | |
2 | + *规则链 外部的 中文配置文件 | |
3 | + 目前共有11个,分类按名称进行配置 | |
4 | + */ | |
5 | +export default { | |
6 | + title: '外部', | |
7 | + description: '与外部系统交互', | |
8 | + alarmNotice: { | |
9 | + name: '告警通知', | |
10 | + details: `通知方式包括短信、电子邮件等。`, | |
11 | + description: '当设备产生告警时,通知联系人。', | |
12 | + }, | |
13 | + awsSns: { | |
14 | + name: 'AWS SNS', | |
15 | + details: `将向AWS SNS主题发布消息有效载荷;出站消息将在AWS SNS的消息元数据中包含响应字段(messageId、requestId),例如requestId字段可以使用Metadata.requestId访问。`, | |
16 | + description: '将消息发布到AWS SNS。', | |
17 | + }, | |
18 | + awsSqs: { | |
19 | + name: 'AWS SQS', | |
20 | + details: `将向AWS SQS队列发布消息有效负载和元数据属性。出站消息将在AWS SQS的消息元数据中包含响应字段(messageId、requestId、messageBodyMd5、messageAttributesMd5、sequenceNumber)。例如requestId字段可以使用Metadata.requestId访问。`, | |
21 | + description: '向AWS SQS发布消息。', | |
22 | + }, | |
23 | + azureIotHub: { | |
24 | + name: 'Azure物联网中心', | |
25 | + details: `将消息有效负载发布到Azure物联网中心,使用QoS的条件至少一次。`, | |
26 | + description: '将消息发布到Azure物联网中心。', | |
27 | + }, | |
28 | + gcpPubsub: { | |
29 | + name: 'GCP pubsub', | |
30 | + details: `将向Google Cloud Platform PubSub主题发布消息负载。出站消息将包含响应字段(GCP PubSub的消息元数据中的messageId字段可以通过Metadata.messageId访问)。`, | |
31 | + description: '将消息发布到Google Cloud PubSub。', | |
32 | + }, | |
33 | + kafka: { | |
34 | + name: 'Kafka', | |
35 | + details: `将通过Kafka生产记录发送到Kafka服务器。出站消息将包含来自消息元数据中Kafka的响应字段(offset、partition和topic)。例如,partition字段可以通过metadata.partition访问。`, | |
36 | + description: '将消息发布到Kafka服务器。', | |
37 | + }, | |
38 | + mqtt: { | |
39 | + name: 'MQTT', | |
40 | + details: `将消息有效负载发布到MQTT代理,使用QoS的条件至少一次。`, | |
41 | + description: '将消息发布到MQTT代理。', | |
42 | + }, | |
43 | + rabbitmq: { | |
44 | + name: 'RabbitMQ', | |
45 | + details: `将向RabbitMQ队列发布消息负载。`, | |
46 | + description: '将消息发布到RabbitMQ。', | |
47 | + }, | |
48 | + restApiCall: { | |
49 | + name: 'rest_api调用', | |
50 | + details: `将调用REST-API调用GET|POST|PUT|DELETE到外部REST服务器。已将消息有效负载添加到请求正文中。配置的属性可以从消息元数据添加到标头中。出站消息将在消息元数据中包含响应字段(status、statusCode和响应headers)。响应正文保存在出站消息负载中。例如,可以使用metadata.statusCode访问statusCode字段。注意:如果使用系统代理属性,则应添加下一个系统代理属性:“http.proxyHost”和“http.prroxyPort”或“https.proxyHost”和“https.prroxyPort”,接下来的两个应该添加到thingsbard.conf文件中:“tb.proxy.user”和“tb.prroxy.password”。`, | |
51 | + description: '调用对外部REST服务器的REST_API', | |
52 | + }, | |
53 | + sendEmail: { | |
54 | + name: '发送邮件', | |
55 | + details: `应为SEND_EMAIL类型的邮件。节点仅适用于使用to Email转换节点创建的消息,请使用Successful链将与to Email节点一起使用。`, | |
56 | + description: '通过SMTP服务器发送电子邮件。', | |
57 | + }, | |
58 | + sendSms: { | |
59 | + name: '发送短信', | |
60 | + details: `将通过使用从消息元数据派生的值填充目标电话号码和短信字段来发送短信。`, | |
61 | + description: '通过短信提供商发送短信。', | |
62 | + }, | |
63 | +}; | ... | ... |
1 | +/** | |
2 | + *规则链 筛选器 中文配置文件 | |
3 | + 目前共有11个,分类按名称进行配置 | |
4 | + */ | |
5 | +export default { | |
6 | + title: '过滤', | |
7 | + description: '使用配置条件筛选传入消息', | |
8 | + checkAlarmStatus: { | |
9 | + name: '检查告警状态', | |
10 | + details: '如果警报状态与指定的状态匹配,则消息成功;如果不匹配,则消息失败。', | |
11 | + description: '检查告警状态', | |
12 | + }, | |
13 | + checkExistenceFields: { | |
14 | + name: '检查字段是否存在', | |
15 | + details: `如果选中【检查所有选择的键是否存在】 ,就必选满足Message data中配置的字段和Message metadata中配置的字段,都必须存在才会返回true发送消息,否则返回false。 | |
16 | + 如果未选中,Message data中配置的字段或者Message metadata中配置的字段至少有一个键存在返回true,否则返回false。`, | |
17 | + description: '检查消息中的字段数据和消息元数据中所选择的键是否都存在。', | |
18 | + }, | |
19 | + checkRelation: { | |
20 | + name: '检查实体关系是否存在', | |
21 | + details: '如果至少存在一个关系,通过True链路发送消息,否则使用False链路。', | |
22 | + description: `检查关系是按类型和方向将所选实体发送给消息的发起者;如果“检查单个实体”设置为true,否则规则节点将按类型和方向检查是否存在与消息发起者的任何关系。`, | |
23 | + }, | |
24 | + gpsGeofencingFilter: { | |
25 | + name: 'GPS地理围栏过滤', | |
26 | + details: `从传入消息中提取纬度和经度参数,如果它们位于配置的周界内,则返回'True',否则返回'False'。`, | |
27 | + description: '通过基于GPS的地理围栏过滤传入消息。', | |
28 | + }, | |
29 | + messageType: { | |
30 | + name: '消息类型过滤', | |
31 | + details: `如果需要传入消息类型通过,则'True'链发送消息,否则使用'False'链路。`, | |
32 | + description: '按消息类型传入消息。', | |
33 | + }, | |
34 | + messageTypeSwitch: { | |
35 | + name: '消息类型切换', | |
36 | + details: `通过相应链发送消息类型为'属性上报','遥测上报','RPC请求'等的消息,否则使用其他链。`, | |
37 | + description: '按消息类型路由传入消息。', | |
38 | + }, | |
39 | + originatorType: { | |
40 | + name: '实体类型过滤', | |
41 | + details: `如果预期传入消息的发起者类型,通过则True链发送消息,否则使用False链路。`, | |
42 | + description: '按消息发起者类型过滤传入消息。', | |
43 | + }, | |
44 | + originatorTypeSwitch: { | |
45 | + name: '实体类型转换', | |
46 | + details: `根据发起方类型(设备、资产等)将消息路由到链。`, | |
47 | + description: '按消息发起者类型路由传入消息。', | |
48 | + }, | |
49 | + sceneReact: { | |
50 | + name: '场景交互', | |
51 | + details: `基于业务场景,实现设备的交互控制。`, | |
52 | + description: '基于业务场景,实现设备的交互控制。', | |
53 | + }, | |
54 | + script: { | |
55 | + name: '脚本', | |
56 | + details: `使用配置的JS匹配条件传入消息。如果为True,则通过True链发送消息,否则使用False链。可以通过msg属性访问消息负载。 例如 msg.temperature < 10 ;可以通过metadata属性访问消息元数据。例如metadata.customerName === 'John';可以通过msgType属性访问消息类型。`, | |
57 | + description: '使用脚本过滤传入消息。', | |
58 | + }, | |
59 | + switch: { | |
60 | + name: '转换器', | |
61 | + details: `节点执行配置的JS脚本。脚本返回的消息应该路由到下一个链名称的数组。如果数组为空,则消息不会路由到下一个节点。可以通过 msg 属性访问消息负载。例如 msg.temperature < 10; 可以通过 metadata 属性访问消息元数据。例如 metadata.customerName==='John' ;可以通过 msgType属性访问消息类型。`, | |
62 | + description: '将传入消息路由到一个或多个输出链。', | |
63 | + }, | |
64 | +}; | ... | ... |
1 | +/** | |
2 | + *规则链 Flow 中文配置文件 | |
3 | + 目前共有4个,分类按名称进行配置 | |
4 | + */ | |
5 | +export default { | |
6 | + title: '流程', | |
7 | + description: '组织消息流', | |
8 | + acknowledge: { | |
9 | + name: '确认消息', | |
10 | + details: `在确认之后,消息被推送到相关的规则节点。如果你不需要这条消息,接下来会发生什么,这很有用。`, | |
11 | + description: '确认传入消息。', | |
12 | + }, | |
13 | + checkpoint: { | |
14 | + name: '检查数据点', | |
15 | + details: `成功传输后,自动确认收到消息。队列名称是可配置的。`, | |
16 | + description: '将消息转移到另一个队列。', | |
17 | + }, | |
18 | + output: { | |
19 | + name: '输出', | |
20 | + details: `产生规则链处理的输出。输出作为相应的“输入”规则节点的输出转发给调用者规则链。输出规则节点名称对应于输出消息的关系类型,用于将消息转发给调用者规则链中的其他规则节点。`, | |
21 | + description: '将消息传递给呼叫者规则链。', | |
22 | + }, | |
23 | + ruleChain: { | |
24 | + name: '规则链', | |
25 | + details: `允许嵌套规则链,类似于单个规则节点。传入消息被转发到指定目标规则链的输入节点。目标规则链可能会产生多个标记的输出。您可以使用输出将处理结果转发给其他规则节点。`, | |
26 | + description: '将消息传递到另一个规则链。', | |
27 | + }, | |
28 | +}; | ... | ... |