Showing
6 changed files
with
2102 additions
and
0 deletions
src/locales/lang/zh-CN/equipment/ledger.ts
0 → 100644
1 | +export default { | |
2 | + ledgerListText: '台账列表', | |
3 | + createLedgerText: '创建台账', | |
4 | + importLedgerText: '导入台账', | |
5 | + deviceName: '设备名称', | |
6 | + deviceCode: '设备编码', | |
7 | + deviceType: '设备类型', | |
8 | + status: '状态', | |
9 | + description: '负责人', | |
10 | + operationSuccessText: '操作成功', | |
11 | + editProductText: '编辑台账', | |
12 | + updateOrganization: '修改组织', | |
13 | + batchActionText: '批量操作', | |
14 | +} | ... | ... |
src/views/equipment/ledger/config/data.ts
0 → 100644
1 | +import { FormSchema, useComponentRegister } from '/@/components/Form'; | |
2 | +import { findDictItemByCode } from '/@/api/system/dict'; | |
3 | +import { getGatewayDevice, queryDeviceProfileBy } from '/@/api/device/deviceManager'; | |
4 | +import { JSONEditor } from '/@/components/CodeEditor'; | |
5 | +import { DeviceRecord, DeviceTypeEnum } from '/@/api/device/model/deviceModel'; | |
6 | +import { h } from 'vue'; | |
7 | +import { TaskTypeEnum } from '/@/views/task/center/config'; | |
8 | +import { createImgPreview } from '/@/components/Preview'; | |
9 | +import { uploadThumbnail } from '/@/api/configuration/center/configurationCenter'; | |
10 | +import LockControlGroup from '/@/components/Form/src/components/LockControlGroup.vue'; | |
11 | +import { OrgTreeSelect } from '/@/views/common/OrgTreeSelect'; | |
12 | +import { TCPProtocolTypeEnum, TransportTypeEnum } from '/@/enums/deviceEnum'; | |
13 | +import { HexInput, InputTypeEnum } from '../../profiles/components/ObjectModelForm/HexInput'; | |
14 | +import { DeviceProfileDetail } from '/@/api/device/model/deviceConfigModel'; | |
15 | +import { getDeviceProfileOtaPackages, getOtaPackageInfo } from '/@/api/ota'; | |
16 | +import { QueryDeviceProfileOtaPackagesType } from '/@/api/ota/model'; | |
17 | +import { OTAPackageType } from '/@/enums/otaEnum'; | |
18 | +import { createPickerSearch } from '/@/utils/pickerSearch'; | |
19 | +import { useI18n } from '/@/hooks/web/useI18n'; | |
20 | + | |
21 | +useComponentRegister('JSONEditor', JSONEditor); | |
22 | +useComponentRegister('LockControlGroup', LockControlGroup); | |
23 | +useComponentRegister('OrgTreeSelect', OrgTreeSelect); | |
24 | +useComponentRegister('HexInput', HexInput); | |
25 | + | |
26 | +const { t } = useI18n(); | |
27 | + | |
28 | +export enum TypeEnum { | |
29 | + IS_GATEWAY = 'GATEWAY', | |
30 | + SENSOR = 'SENSOR', | |
31 | +} | |
32 | + | |
33 | +export const isGateWay = (type: string) => { | |
34 | + return type === TypeEnum.IS_GATEWAY; | |
35 | +}; | |
36 | + | |
37 | +const updateProductHelpMessage = [ | |
38 | + t('deviceManagement.device.updateProductHelpMessage.text1'), | |
39 | + t('deviceManagement.device.updateProductHelpMessage.text2'), | |
40 | +]; | |
41 | + | |
42 | +export const updateOrgHelpMessage = [ | |
43 | + t('deviceManagement.device.updateOrgHelpMessage.text1'), | |
44 | + t('deviceManagement.device.updateOrgHelpMessage.text2'), | |
45 | + t('deviceManagement.device.updateOrgHelpMessage.text3'), | |
46 | +]; | |
47 | +// 第一步的表单 | |
48 | +export const step1Schemas: FormSchema[] = [ | |
49 | + { | |
50 | + field: 'icon', | |
51 | + label: t('business.deviceImageText'), | |
52 | + component: 'ApiUpload', | |
53 | + changeEvent: 'update:fileList', | |
54 | + valueField: 'fileList', | |
55 | + componentProps: ({ formModel }) => { | |
56 | + return { | |
57 | + listType: 'picture-card', | |
58 | + maxFileLimit: 1, | |
59 | + accept: '.png,.jpg,.jpeg,.gif', | |
60 | + api: async (file: File) => { | |
61 | + try { | |
62 | + const formData = new FormData(); | |
63 | + formData.set('file', file); | |
64 | + const { fileStaticUri, fileName } = await uploadThumbnail(formData); | |
65 | + return { | |
66 | + uid: fileStaticUri, | |
67 | + name: fileName, | |
68 | + url: fileStaticUri, | |
69 | + }; | |
70 | + } catch (error) { | |
71 | + return {}; | |
72 | + } | |
73 | + }, | |
74 | + onPreview: (fileList) => { | |
75 | + createImgPreview({ imageList: [fileList.url!] }); | |
76 | + }, | |
77 | + onDelete(url: string) { | |
78 | + formModel.deleteUrl = url!; | |
79 | + }, | |
80 | + }; | |
81 | + }, | |
82 | + }, | |
83 | + { | |
84 | + field: 'deleteUrl', | |
85 | + label: '', | |
86 | + component: 'Input', | |
87 | + show: false, | |
88 | + }, | |
89 | + { | |
90 | + field: 'alias', | |
91 | + label: t('business.aliasText'), | |
92 | + component: 'Input', | |
93 | + componentProps: { | |
94 | + maxLength: 32, | |
95 | + }, | |
96 | + }, | |
97 | + { | |
98 | + field: 'name', | |
99 | + label: t('business.deviceNameText'), | |
100 | + component: 'Input', | |
101 | + componentProps: { | |
102 | + maxLength: 32, | |
103 | + }, | |
104 | + required: true, | |
105 | + slot: 'snCode', | |
106 | + }, | |
107 | + { | |
108 | + field: 'transportType', | |
109 | + label: '类型', | |
110 | + component: 'Input', | |
111 | + show: false, | |
112 | + }, | |
113 | + { | |
114 | + field: 'deviceProfileId', | |
115 | + label: '', | |
116 | + component: 'Input', | |
117 | + show: false, | |
118 | + }, | |
119 | + { | |
120 | + field: 'isUpdate', | |
121 | + label: '编辑模式', | |
122 | + component: 'Switch', | |
123 | + show: false, | |
124 | + }, | |
125 | + { | |
126 | + field: 'tcpDeviceProtocol', | |
127 | + label: 'TCP设备协议类型', | |
128 | + component: 'Input', | |
129 | + show: false, | |
130 | + }, | |
131 | + | |
132 | + { | |
133 | + field: 'profileId', | |
134 | + label: t('business.affiliatedProductText'), | |
135 | + required: true, | |
136 | + component: 'LockControlGroup', | |
137 | + helpMessage: updateProductHelpMessage, | |
138 | + renderComponentContent: () => ({ | |
139 | + popconfirmTitle: () => | |
140 | + updateProductHelpMessage.map((text) => h('div', { style: { maxWidth: '200px' } }, text)), | |
141 | + }), | |
142 | + componentProps: ({ formActionType, formModel }) => { | |
143 | + const { setFieldsValue } = formActionType; | |
144 | + return { | |
145 | + component: 'ApiSelect', | |
146 | + defaultLockStatus: !!formModel?.isUpdate, | |
147 | + componentProps: { | |
148 | + api: async () => { | |
149 | + const options = await queryDeviceProfileBy({ | |
150 | + deviceType: formModel?.isUpdate ? formModel?.deviceType : null, | |
151 | + }); | |
152 | + | |
153 | + return options; | |
154 | + }, | |
155 | + labelField: 'name', | |
156 | + valueField: 'tbProfileId', | |
157 | + onChange(_value: string, option: DeviceProfileDetail) { | |
158 | + const { deviceType, transportType, id } = option; | |
159 | + setFieldsValue({ | |
160 | + deviceType: deviceType, | |
161 | + transportType, | |
162 | + deviceProfileId: id, | |
163 | + gatewayId: null, | |
164 | + code: null, | |
165 | + addressCode: null, | |
166 | + tcpDeviceProtocol: option?.profileData?.transportConfiguration?.protocol, | |
167 | + }); | |
168 | + }, | |
169 | + onOptionsChange(options: (DeviceProfileDetail & Record<'value', string>)[]) { | |
170 | + const { profileId } = formModel; | |
171 | + if (profileId) { | |
172 | + const selectRecord = options.find((item) => item.value === profileId); | |
173 | + selectRecord && | |
174 | + setFieldsValue({ | |
175 | + transportType: selectRecord!.transportType, | |
176 | + tcpDeviceProtocol: selectRecord?.profileData?.transportConfiguration?.protocol, | |
177 | + }); | |
178 | + } | |
179 | + }, | |
180 | + placeholder: t('deviceManagement.device.productPlaceholderText'), | |
181 | + ...createPickerSearch(), | |
182 | + }, | |
183 | + }; | |
184 | + }, | |
185 | + }, | |
186 | + { | |
187 | + field: 'deviceType', | |
188 | + label: t('business.deviceTypeText'), | |
189 | + required: true, | |
190 | + component: 'ApiSelect', | |
191 | + dynamicDisabled: true, | |
192 | + helpMessage: t('deviceManagement.device.deviceTypeHelpText'), | |
193 | + componentProps: { | |
194 | + api: findDictItemByCode, | |
195 | + params: { | |
196 | + dictCode: 'device_type', | |
197 | + }, | |
198 | + labelField: 'itemText', | |
199 | + valueField: 'itemValue', | |
200 | + }, | |
201 | + }, | |
202 | + { | |
203 | + field: 'addressType', | |
204 | + label: '', | |
205 | + component: 'Input', | |
206 | + show: false, | |
207 | + defaultValue: 'HEX', | |
208 | + }, | |
209 | + { | |
210 | + field: 'deviceState', | |
211 | + label: '', | |
212 | + component: 'Input', | |
213 | + show: false, | |
214 | + }, | |
215 | + { | |
216 | + field: 'addressCode', | |
217 | + label: t('deviceManagement.device.addressCodeText'), | |
218 | + dynamicDisabled({ values }) { | |
219 | + return ( | |
220 | + values.isUpdate && | |
221 | + values.deviceType === DeviceTypeEnum.SENSOR && | |
222 | + values.deviceState === 'ONLINE' && | |
223 | + values.transportType === TransportTypeEnum.TCP | |
224 | + ); | |
225 | + }, | |
226 | + dynamicRules({ values }) { | |
227 | + return [ | |
228 | + { | |
229 | + required: | |
230 | + values?.transportType === TransportTypeEnum.TCP && | |
231 | + values?.tcpDeviceProtocol === TCPProtocolTypeEnum.MODBUS_RTU, | |
232 | + message: t('deviceManagement.device.addressCodeHelpText'), | |
233 | + pattern: values?.addressType === 'HEX' ? /^[0-9A-Fa-f]{2}$/ : /^[0-9A-Fa-f]{1,2}$/, | |
234 | + }, | |
235 | + ]; | |
236 | + }, | |
237 | + helpMessage({ values }) { | |
238 | + return [ | |
239 | + t('deviceManagement.device.addressCodeHelpText'), | |
240 | + values.transportType === TransportTypeEnum.TCP && | |
241 | + values.deviceType === DeviceTypeEnum.SENSOR | |
242 | + ? t('deviceManagement.device.tcpAddressCodeHelpText') | |
243 | + : '', | |
244 | + ]; | |
245 | + }, | |
246 | + component: 'HexInput', | |
247 | + changeEvent: 'update:value', | |
248 | + valueField: 'value', | |
249 | + componentProps: ({ formModel }) => { | |
250 | + return { | |
251 | + type: InputTypeEnum.HEX, | |
252 | + maxValue: parseInt('FF', 16), | |
253 | + onHexChange: (e) => { | |
254 | + formModel.addressType = e; | |
255 | + }, | |
256 | + }; | |
257 | + }, | |
258 | + ifShow: ({ values }) => { | |
259 | + return ( | |
260 | + values?.transportType === TransportTypeEnum.TCP && | |
261 | + values?.tcpDeviceProtocol === TCPProtocolTypeEnum.MODBUS_RTU | |
262 | + ); | |
263 | + }, | |
264 | + }, | |
265 | + { | |
266 | + field: 'code', | |
267 | + label: t('deviceManagement.device.codeText'), | |
268 | + dynamicRules({ values }) { | |
269 | + return [ | |
270 | + { | |
271 | + required: | |
272 | + values?.transportType === TransportTypeEnum.TCP && | |
273 | + values.deviceType === DeviceTypeEnum.SENSOR, | |
274 | + }, | |
275 | + ]; | |
276 | + }, | |
277 | + dynamicDisabled({ values }) { | |
278 | + return ( | |
279 | + values.isUpdate && | |
280 | + values.deviceType === DeviceTypeEnum.SENSOR && | |
281 | + values.deviceState === 'ONLINE' && | |
282 | + values.transportType === TransportTypeEnum.TCP | |
283 | + ); | |
284 | + }, | |
285 | + helpMessage({ values }) { | |
286 | + return ( | |
287 | + values.transportType === TransportTypeEnum.TCP && | |
288 | + values.deviceType === DeviceTypeEnum.SENSOR | |
289 | + ? ['tcp网关子设备在线时,不能修改设备标识或地址码'] | |
290 | + : false | |
291 | + ) as any; | |
292 | + }, | |
293 | + component: 'Input', | |
294 | + componentProps: () => { | |
295 | + return { | |
296 | + maxLength: 255, | |
297 | + placeholder: t('deviceManagement.device.codePlaceholderText'), | |
298 | + }; | |
299 | + }, | |
300 | + ifShow: ({ values }) => { | |
301 | + return ( | |
302 | + values?.transportType === TransportTypeEnum.TCP && | |
303 | + values?.tcpDeviceProtocol === TaskTypeEnum.CUSTOM | |
304 | + ); | |
305 | + }, | |
306 | + }, | |
307 | + | |
308 | + { | |
309 | + field: 'brand', | |
310 | + component: 'ApiRadioGroup', | |
311 | + label: t('deviceManagement.device.brandText'), | |
312 | + required: true, | |
313 | + defaultValue: 'DIY_', | |
314 | + componentProps: { | |
315 | + api: findDictItemByCode, | |
316 | + params: { | |
317 | + dictCode: 'device_brand_gateway', | |
318 | + }, | |
319 | + labelField: 'itemText', | |
320 | + valueField: 'itemValue', | |
321 | + }, | |
322 | + ifShow: ({ values }) => isGateWay(values.deviceType), | |
323 | + }, | |
324 | + { | |
325 | + field: 'gatewayId', | |
326 | + label: t('enum.deviceType.GATEWAY'), | |
327 | + required: true, | |
328 | + component: 'ApiSelect', | |
329 | + ifShow: ({ values }) => values.deviceType === 'SENSOR', | |
330 | + componentProps: ({ formModel, formActionType }) => { | |
331 | + const { transportType, deviceType, gatewayId } = formModel; | |
332 | + const { setFieldsValue } = formActionType; | |
333 | + if (!transportType) return {}; | |
334 | + return { | |
335 | + api: async (params: Recordable) => { | |
336 | + try { | |
337 | + const result = await getGatewayDevice(params as any); | |
338 | + return result.map((item) => ({ ...item, alias: item.alias || item.name })); | |
339 | + } catch (e) { | |
340 | + return []; | |
341 | + } | |
342 | + }, | |
343 | + params: { | |
344 | + transportType, | |
345 | + gatewayId: deviceType === DeviceTypeEnum.SENSOR && gatewayId ? gatewayId : null, | |
346 | + }, | |
347 | + valueField: 'tbDeviceId', | |
348 | + labelField: 'alias', | |
349 | + onChange: async (_value: string, option: DeviceRecord) => { | |
350 | + setFieldsValue({ sensorOrganizationId: option?.organizationId, organizationId: null }); | |
351 | + }, | |
352 | + onOptionsChange(options: (DeviceRecord & Record<'value', string>)[]) { | |
353 | + if (formModel?.deviceType === DeviceTypeEnum.SENSOR && formModel?.gatewayId) { | |
354 | + const result = options.find((item) => item.value === formModel?.gatewayId); | |
355 | + result && setFieldsValue({ sensorOrganizationId: result?.organizationId }); | |
356 | + } | |
357 | + }, | |
358 | + ...createPickerSearch(), | |
359 | + }; | |
360 | + }, | |
361 | + }, | |
362 | + { | |
363 | + field: 'sensorOrganizationId', | |
364 | + label: '依据网关设备请求的组织数组', | |
365 | + component: 'Input', | |
366 | + ifShow: false, | |
367 | + }, | |
368 | + { | |
369 | + field: 'customerId', | |
370 | + label: '用来判断编辑时禁用组织修改', | |
371 | + component: 'Input', | |
372 | + ifShow: false, | |
373 | + }, | |
374 | + { | |
375 | + field: 'organizationId', | |
376 | + label: t('business.affiliatedOrganizationText'), | |
377 | + component: 'LockControlGroup', | |
378 | + required: true, | |
379 | + helpMessage: updateOrgHelpMessage, | |
380 | + renderComponentContent: () => ({ | |
381 | + popconfirmTitle: () => | |
382 | + updateOrgHelpMessage.map((text) => h('div', { style: { maxWidth: '240px' } }, text)), | |
383 | + }), | |
384 | + componentProps: ({ formModel, formActionType }) => { | |
385 | + return { | |
386 | + component: 'OrgTreeSelect', | |
387 | + defaultLockStatus: !!formModel?.isUpdate, | |
388 | + disabled: !!formModel?.customerId, | |
389 | + componentProps: { | |
390 | + apiTreeSelectProps: { | |
391 | + params: { | |
392 | + organizationId: formModel?.sensorOrganizationId, | |
393 | + }, | |
394 | + }, | |
395 | + onOptionsChange: (options: Recordable[]) => { | |
396 | + if (!formModel?.organizationId && formModel?.deviceType === DeviceTypeEnum.SENSOR) { | |
397 | + const firstItem = options?.[0]; | |
398 | + | |
399 | + if (firstItem && firstItem?.id) { | |
400 | + const { setFieldsValue, clearValidate } = formActionType; | |
401 | + setFieldsValue({ organizationId: firstItem.id }); | |
402 | + clearValidate('organizationId'); | |
403 | + } | |
404 | + } | |
405 | + }, | |
406 | + placeholder: t('deviceManagement.device.organizationPlaceholderText'), | |
407 | + }, | |
408 | + }; | |
409 | + }, | |
410 | + }, | |
411 | + { | |
412 | + field: 'directorId', | |
413 | + label: t('deviceManagement.device.directorName'), | |
414 | + component: 'Input', | |
415 | + componentProps: { | |
416 | + maxLength: 255, | |
417 | + }, | |
418 | + }, | |
419 | + { | |
420 | + field: 'label', | |
421 | + label: t('deviceManagement.device.deviceLabelText'), | |
422 | + component: 'Input', | |
423 | + componentProps: { | |
424 | + maxLength: 255, | |
425 | + }, | |
426 | + }, | |
427 | + { | |
428 | + field: 'deviceAddress', | |
429 | + label: t('business.deviceLocationText'), | |
430 | + component: 'Input', | |
431 | + slot: 'deviceAddress', | |
432 | + }, | |
433 | + | |
434 | + { | |
435 | + field: 'firmwareId', | |
436 | + label: t('deviceManagement.product.assignHardwareText'), | |
437 | + component: 'ApiSearchSelect', | |
438 | + ifShow: ({ model }) => model?.isUpdate, | |
439 | + componentProps: ({ formModel }) => { | |
440 | + return { | |
441 | + api: async (params: QueryDeviceProfileOtaPackagesType) => { | |
442 | + if (!params.deviceProfileId) return []; | |
443 | + const result = await getDeviceProfileOtaPackages(params); | |
444 | + return result.data.map((item) => ({ | |
445 | + label: `${item.title}(${item.version})`, | |
446 | + value: item.id.id, | |
447 | + })); | |
448 | + }, | |
449 | + params: (textSearch: string) => { | |
450 | + return { | |
451 | + textSearch, | |
452 | + page: 0, | |
453 | + type: OTAPackageType.FIRMWARE, | |
454 | + pageSize: 10, | |
455 | + deviceProfileId: formModel?.profileId, | |
456 | + }; | |
457 | + }, | |
458 | + queryApi: async (id: string) => { | |
459 | + const result = await getOtaPackageInfo(id); | |
460 | + return { label: `${result.title}(${result.version})`, value: result.id.id }; | |
461 | + }, | |
462 | + }; | |
463 | + }, | |
464 | + }, | |
465 | + { | |
466 | + field: 'softwareId', | |
467 | + label: t('deviceManagement.product.assignSoftwareText'), | |
468 | + component: 'ApiSearchSelect', | |
469 | + ifShow: ({ model }) => model?.isUpdate, | |
470 | + componentProps: ({ formModel }) => { | |
471 | + return { | |
472 | + api: async (params: QueryDeviceProfileOtaPackagesType) => { | |
473 | + if (!params.deviceProfileId) return []; | |
474 | + const result = await getDeviceProfileOtaPackages(params); | |
475 | + return result.data.map((item) => ({ | |
476 | + label: `${item.title}(${item.version})`, | |
477 | + value: item.id.id, | |
478 | + })); | |
479 | + }, | |
480 | + params: (textSearch: string) => { | |
481 | + return { | |
482 | + textSearch, | |
483 | + page: 0, | |
484 | + type: OTAPackageType.SOFTWARE, | |
485 | + pageSize: 10, | |
486 | + deviceProfileId: formModel?.profileId, | |
487 | + }; | |
488 | + }, | |
489 | + queryApi: async (id: string) => { | |
490 | + const result = await getOtaPackageInfo(id); | |
491 | + return { label: `${result.title}(${result.version})`, value: result.id.id }; | |
492 | + }, | |
493 | + }; | |
494 | + }, | |
495 | + }, | |
496 | + { | |
497 | + field: 'description', | |
498 | + label: t('common.remarkText'), | |
499 | + component: 'InputTextArea', | |
500 | + componentProps: { | |
501 | + maxLength: 500, | |
502 | + }, | |
503 | + }, | |
504 | + { | |
505 | + field: 'id', | |
506 | + label: 'id', | |
507 | + component: 'Input', | |
508 | + show: false, | |
509 | + componentProps: { | |
510 | + maxLength: 36, | |
511 | + placeholder: '请输入id', | |
512 | + }, | |
513 | + }, | |
514 | + { | |
515 | + field: 'tenantId', | |
516 | + label: '租户Code', | |
517 | + component: 'Input', | |
518 | + show: false, | |
519 | + componentProps: { | |
520 | + maxLength: 36, | |
521 | + placeholder: '请输入租户Code', | |
522 | + }, | |
523 | + }, | |
524 | + { | |
525 | + field: 'tbDeviceId', | |
526 | + label: 'tbDeviceId', | |
527 | + component: 'Input', | |
528 | + show: false, | |
529 | + componentProps: { | |
530 | + maxLength: 36, | |
531 | + placeholder: '请输入tbDeviceId', | |
532 | + }, | |
533 | + }, | |
534 | +]; | |
535 | + | |
536 | +export enum credentialTypeEnum { | |
537 | + ACCESS_TOKEN = 'ACCESS_TOKEN', | |
538 | + X_509 = 'X509_CERTIFICATE', | |
539 | + MQTT_BASIC = 'MQTT_BASIC', | |
540 | +} | |
541 | +// 第二步的表单 | |
542 | +export const step2Schemas: FormSchema[] = [ | |
543 | + { | |
544 | + label: '', | |
545 | + component: 'Checkbox', | |
546 | + field: 'addAgree', | |
547 | + slot: 'addAgree', | |
548 | + }, | |
549 | + { | |
550 | + label: t('deviceManagement.device.certificateTypeText'), | |
551 | + component: 'Select', | |
552 | + field: 'credentialType', | |
553 | + required: true, | |
554 | + componentProps({ formActionType }) { | |
555 | + const { updateSchema, setFieldsValue } = formActionType; | |
556 | + return { | |
557 | + options: [ | |
558 | + { | |
559 | + value: credentialTypeEnum.ACCESS_TOKEN, | |
560 | + label: 'Access Token', | |
561 | + }, | |
562 | + { | |
563 | + value: credentialTypeEnum.X_509, | |
564 | + label: 'X.509', | |
565 | + }, | |
566 | + { | |
567 | + value: credentialTypeEnum.MQTT_BASIC, | |
568 | + label: 'MQTT Basic', | |
569 | + }, | |
570 | + ], | |
571 | + onChange(value) { | |
572 | + setFieldsValue({ | |
573 | + publicKey: '', | |
574 | + credentialsId: '', | |
575 | + clientId: '', | |
576 | + username: '', | |
577 | + password: '', | |
578 | + }); | |
579 | + if (value === credentialTypeEnum.ACCESS_TOKEN) { | |
580 | + updateSchema([ | |
581 | + { | |
582 | + field: 'credentialsId', | |
583 | + ifShow: true, | |
584 | + }, | |
585 | + { | |
586 | + field: 'clientId', | |
587 | + ifShow: false, | |
588 | + }, | |
589 | + { | |
590 | + field: 'username', | |
591 | + ifShow: false, | |
592 | + }, | |
593 | + { | |
594 | + field: 'password', | |
595 | + ifShow: false, | |
596 | + }, | |
597 | + { | |
598 | + field: 'publicKey', | |
599 | + ifShow: false, | |
600 | + }, | |
601 | + ]); | |
602 | + } else if (value === credentialTypeEnum.X_509) { | |
603 | + updateSchema([ | |
604 | + { | |
605 | + field: 'publicKey', | |
606 | + ifShow: true, | |
607 | + }, | |
608 | + { | |
609 | + field: 'credentialsId', | |
610 | + ifShow: false, | |
611 | + }, | |
612 | + { | |
613 | + field: 'clientId', | |
614 | + ifShow: false, | |
615 | + }, | |
616 | + { | |
617 | + field: 'username', | |
618 | + ifShow: false, | |
619 | + }, | |
620 | + { | |
621 | + field: 'password', | |
622 | + ifShow: false, | |
623 | + }, | |
624 | + ]); | |
625 | + } else if (value === credentialTypeEnum.MQTT_BASIC) { | |
626 | + updateSchema([ | |
627 | + { | |
628 | + field: 'clientId', | |
629 | + ifShow: true, | |
630 | + }, | |
631 | + { | |
632 | + field: 'username', | |
633 | + ifShow: true, | |
634 | + }, | |
635 | + { | |
636 | + field: 'password', | |
637 | + ifShow: true, | |
638 | + }, | |
639 | + { | |
640 | + field: 'publicKey', | |
641 | + ifShow: false, | |
642 | + }, | |
643 | + { | |
644 | + field: 'credentialsId', | |
645 | + ifShow: false, | |
646 | + }, | |
647 | + ]); | |
648 | + } else { | |
649 | + updateSchema([ | |
650 | + { | |
651 | + field: 'clientId', | |
652 | + ifShow: false, | |
653 | + }, | |
654 | + { | |
655 | + field: 'username', | |
656 | + ifShow: false, | |
657 | + }, | |
658 | + { | |
659 | + field: 'password', | |
660 | + ifShow: false, | |
661 | + }, | |
662 | + { | |
663 | + field: 'publicKey', | |
664 | + ifShow: false, | |
665 | + }, | |
666 | + { | |
667 | + field: 'credentialsId', | |
668 | + ifShow: false, | |
669 | + }, | |
670 | + ]); | |
671 | + } | |
672 | + }, | |
673 | + }; | |
674 | + }, | |
675 | + ifShow: ({ values }) => values.addAgree, | |
676 | + }, | |
677 | + { | |
678 | + label: t('deviceManagement.device.accessTokenText'), | |
679 | + component: 'Input', | |
680 | + field: 'credentialsId', | |
681 | + required: true, | |
682 | + ifShow: false, | |
683 | + slot: 'credentialsId', | |
684 | + componentProps: { | |
685 | + maxLength: 36, | |
686 | + }, | |
687 | + }, | |
688 | + { | |
689 | + label: t('deviceManagement.device.rsaPublicKeyText'), | |
690 | + component: 'InputTextArea', | |
691 | + field: 'publicKey', | |
692 | + required: true, | |
693 | + ifShow: false, | |
694 | + componentProps: { | |
695 | + rows: 8, | |
696 | + }, | |
697 | + }, | |
698 | + { | |
699 | + label: t('deviceManagement.device.clientIdText'), | |
700 | + component: 'Input', | |
701 | + field: 'clientId', | |
702 | + ifShow: false, | |
703 | + slot: 'clientId', | |
704 | + componentProps: { | |
705 | + maxLength: 36, | |
706 | + }, | |
707 | + }, | |
708 | + { | |
709 | + label: t('deviceManagement.device.usernameText'), | |
710 | + component: 'Input', | |
711 | + field: 'username', | |
712 | + required: true, | |
713 | + ifShow: false, | |
714 | + componentProps: { | |
715 | + maxLength: 255, | |
716 | + }, | |
717 | + }, | |
718 | + { | |
719 | + label: t('deviceManagement.device.passwordText'), | |
720 | + component: 'InputPassword', | |
721 | + field: 'password', | |
722 | + componentProps: { | |
723 | + maxLength: 36, | |
724 | + }, | |
725 | + ifShow: false, | |
726 | + }, | |
727 | +]; | |
728 | + | |
729 | +// 管理凭证的表单配置项 | |
730 | +export const TokenSchemas: FormSchema[] = [ | |
731 | + { | |
732 | + label: t('deviceManagement.device.certificateTypeText'), | |
733 | + component: 'Select', | |
734 | + field: 'credentialType', | |
735 | + required: true, | |
736 | + componentProps({ formActionType }) { | |
737 | + const { updateSchema, setFieldsValue } = formActionType; | |
738 | + return { | |
739 | + options: [ | |
740 | + { | |
741 | + value: credentialTypeEnum.ACCESS_TOKEN, | |
742 | + label: 'Access Token', | |
743 | + }, | |
744 | + { | |
745 | + value: credentialTypeEnum.X_509, | |
746 | + label: 'X.509', | |
747 | + }, | |
748 | + { | |
749 | + value: credentialTypeEnum.MQTT_BASIC, | |
750 | + label: 'MQTT Basic', | |
751 | + }, | |
752 | + ], | |
753 | + onChange(value) { | |
754 | + setFieldsValue({ | |
755 | + publicKey: '', | |
756 | + credentialsId: '', | |
757 | + clientId: '', | |
758 | + username: '', | |
759 | + password: '', | |
760 | + }); | |
761 | + if (value === credentialTypeEnum.ACCESS_TOKEN) { | |
762 | + updateSchema([ | |
763 | + { | |
764 | + field: 'credentialsId', | |
765 | + ifShow: true, | |
766 | + }, | |
767 | + { | |
768 | + field: 'clientId', | |
769 | + ifShow: false, | |
770 | + }, | |
771 | + { | |
772 | + field: 'username', | |
773 | + ifShow: false, | |
774 | + }, | |
775 | + { | |
776 | + field: 'password', | |
777 | + ifShow: false, | |
778 | + }, | |
779 | + { | |
780 | + field: 'publicKey', | |
781 | + ifShow: false, | |
782 | + }, | |
783 | + ]); | |
784 | + } else if (value === credentialTypeEnum.X_509) { | |
785 | + updateSchema([ | |
786 | + { | |
787 | + field: 'publicKey', | |
788 | + ifShow: true, | |
789 | + }, | |
790 | + { | |
791 | + field: 'credentialsId', | |
792 | + ifShow: false, | |
793 | + }, | |
794 | + { | |
795 | + field: 'clientId', | |
796 | + ifShow: false, | |
797 | + }, | |
798 | + { | |
799 | + field: 'username', | |
800 | + ifShow: false, | |
801 | + }, | |
802 | + { | |
803 | + field: 'password', | |
804 | + ifShow: false, | |
805 | + }, | |
806 | + ]); | |
807 | + } else if (value === credentialTypeEnum.MQTT_BASIC) { | |
808 | + updateSchema([ | |
809 | + { | |
810 | + field: 'clientId', | |
811 | + ifShow: true, | |
812 | + }, | |
813 | + { | |
814 | + field: 'username', | |
815 | + ifShow: true, | |
816 | + }, | |
817 | + { | |
818 | + field: 'password', | |
819 | + ifShow: true, | |
820 | + }, | |
821 | + { | |
822 | + field: 'publicKey', | |
823 | + ifShow: false, | |
824 | + }, | |
825 | + { | |
826 | + field: 'credentialsId', | |
827 | + ifShow: false, | |
828 | + }, | |
829 | + ]); | |
830 | + } else { | |
831 | + updateSchema([ | |
832 | + { | |
833 | + field: 'clientId', | |
834 | + ifShow: false, | |
835 | + }, | |
836 | + { | |
837 | + field: 'username', | |
838 | + ifShow: false, | |
839 | + }, | |
840 | + { | |
841 | + field: 'password', | |
842 | + ifShow: false, | |
843 | + }, | |
844 | + { | |
845 | + field: 'publicKey', | |
846 | + ifShow: false, | |
847 | + }, | |
848 | + { | |
849 | + field: 'credentialsId', | |
850 | + ifShow: false, | |
851 | + }, | |
852 | + ]); | |
853 | + } | |
854 | + }, | |
855 | + }; | |
856 | + }, | |
857 | + }, | |
858 | + { | |
859 | + label: t('deviceManagement.device.accessTokenText'), | |
860 | + component: 'Input', | |
861 | + field: 'credentialsId', | |
862 | + required: true, | |
863 | + ifShow: false, | |
864 | + slot: 'credentialsId', | |
865 | + componentProps: { | |
866 | + maxLength: 36, | |
867 | + }, | |
868 | + }, | |
869 | + { | |
870 | + label: t('deviceManagement.device.rsaPublicKeyText'), | |
871 | + component: 'InputTextArea', | |
872 | + field: 'publicKey', | |
873 | + required: true, | |
874 | + ifShow: false, | |
875 | + componentProps: { | |
876 | + rows: 8, | |
877 | + }, | |
878 | + }, | |
879 | + { | |
880 | + label: t('deviceManagement.device.clientIdText'), | |
881 | + component: 'Input', | |
882 | + field: 'clientId', | |
883 | + ifShow: false, | |
884 | + slot: 'clientId', | |
885 | + componentProps: { | |
886 | + maxLength: 36, | |
887 | + }, | |
888 | + }, | |
889 | + { | |
890 | + label: t('deviceManagement.device.usernameText'), | |
891 | + component: 'Input', | |
892 | + field: 'username', | |
893 | + required: true, | |
894 | + ifShow: false, | |
895 | + componentProps: { | |
896 | + maxLength: 255, | |
897 | + }, | |
898 | + }, | |
899 | + { | |
900 | + label: t('deviceManagement.device.passwordText'), | |
901 | + component: 'InputPassword', | |
902 | + field: 'password', | |
903 | + ifShow: false, | |
904 | + componentProps: { | |
905 | + maxLength: 36, | |
906 | + }, | |
907 | + }, | |
908 | + { | |
909 | + label: 'id', | |
910 | + component: 'Input', | |
911 | + field: 'id', | |
912 | + show: false, | |
913 | + componentProps: { | |
914 | + maxLength: 36, | |
915 | + placeholder: '请输入id', | |
916 | + }, | |
917 | + }, | |
918 | + { | |
919 | + label: 'tbDeviceId', | |
920 | + component: 'Input', | |
921 | + field: 'tbDeviceId', | |
922 | + show: false, | |
923 | + componentProps: { | |
924 | + maxLength: 36, | |
925 | + placeholder: '请输入tbDeviceId', | |
926 | + }, | |
927 | + dynamicRules: () => { | |
928 | + return [ | |
929 | + { | |
930 | + required: false, | |
931 | + validator: (_, value) => { | |
932 | + if (String(value).length > 36) { | |
933 | + return Promise.reject('字数不超过36个字'); | |
934 | + } | |
935 | + return Promise.resolve(); | |
936 | + }, | |
937 | + }, | |
938 | + ]; | |
939 | + }, | |
940 | + }, | |
941 | +]; | ... | ... |
1 | +import { formatToDateTime } from '/@/utils/dateUtil'; | |
2 | +import { FormSchema, useComponentRegister } from '/@/components/Form'; | |
3 | +import { BasicColumn } from '/@/components/Table'; | |
4 | +import { getCustomerList } from '/@/api/device/deviceManager'; | |
5 | +import { DescItem } from '/@/components/Description/index'; | |
6 | +import moment from 'moment'; | |
7 | +import { CSSProperties, h } from 'vue'; | |
8 | +import { Button, Tag, Tooltip } from 'ant-design-vue'; | |
9 | +import { TypeEnum, updateOrgHelpMessage } from './data'; | |
10 | +import { PageEnum } from '/@/enums/pageEnum'; | |
11 | +import { useGo } from '/@/hooks/web/usePage'; | |
12 | +import { useAuthDeviceDetail } from '../hook/useAuthDeviceDetail'; | |
13 | +import { findDictItemByCode } from '/@/api/system/dict'; | |
14 | +import { isNullOrUnDef } from '/@/utils/is'; | |
15 | +import { useI18n } from '/@/hooks/web/useI18n'; | |
16 | +import { DeviceStatusEnum } from '/@/enums/deviceEnum'; | |
17 | +import { AlarmStatus } from '/@/enums/alarmEnum'; | |
18 | +const { t } = useI18n(); | |
19 | +import { OrgTreeSelect } from '/@/views/common/OrgTreeSelect'; | |
20 | + | |
21 | +useComponentRegister('OrgTreeSelect', OrgTreeSelect); | |
22 | + | |
23 | +// 设备详情的描述 | |
24 | +export const descSchema = (emit: EmitType): DescItem[] => { | |
25 | + return [ | |
26 | + { | |
27 | + field: 'createTime', | |
28 | + label: t('common.createTimeText'), | |
29 | + }, | |
30 | + { | |
31 | + field: 'name', | |
32 | + label: t('business.deviceNameText'), | |
33 | + render(val, data: Record<'alias' | 'name', string>) { | |
34 | + return h(Tooltip, { title: data.alias || val }, () => | |
35 | + h('span', { style: { cursor: 'pointer' } as CSSProperties }, data.alias || val) | |
36 | + ); | |
37 | + }, | |
38 | + }, | |
39 | + { | |
40 | + field: 'label', | |
41 | + label: t('deviceManagement.device.deviceLabelText'), | |
42 | + render(val) { | |
43 | + return h(Tooltip, { title: val }, () => | |
44 | + h('span', { style: { cursor: 'pointer' } as CSSProperties }, val) | |
45 | + ); | |
46 | + }, | |
47 | + }, | |
48 | + { | |
49 | + field: 'deviceProfile.name', | |
50 | + label: t('business.productText'), | |
51 | + render(val) { | |
52 | + const go = useGo(); | |
53 | + const { isCustomer } = useAuthDeviceDetail(); | |
54 | + return h( | |
55 | + Button, | |
56 | + { | |
57 | + type: 'link', | |
58 | + style: { padding: 0 }, | |
59 | + onClick: () => | |
60 | + !isCustomer | |
61 | + ? go(PageEnum.DEVICE_PROFILE + '?name=' + encodeURIComponent(String(val))) | |
62 | + : '', | |
63 | + }, | |
64 | + { default: () => val } | |
65 | + ); | |
66 | + }, | |
67 | + }, | |
68 | + { | |
69 | + field: 'gatewayName', | |
70 | + label: t('business.affiliatedGatewayText'), | |
71 | + show: (data) => !!data.gatewayName, | |
72 | + render(val, data) { | |
73 | + if (TypeEnum.SENSOR !== data.deviceType) return val; | |
74 | + return h( | |
75 | + Button, | |
76 | + { type: 'link', style: { padding: 0 }, onClick: () => emit('open-gateway-device', data) }, | |
77 | + { default: () => data.gatewayAlias || val } | |
78 | + ); | |
79 | + }, | |
80 | + }, | |
81 | + { | |
82 | + field: 'deviceType', | |
83 | + label: t('business.deviceTypeText'), | |
84 | + render: (text) => { | |
85 | + return text && t(`enum.deviceType.${text}`); | |
86 | + }, | |
87 | + }, | |
88 | + { | |
89 | + field: 'deviceInfo.sip.cameraCode', | |
90 | + label: t('deviceManagement.device.deviceCameraCodeText'), | |
91 | + }, | |
92 | + { | |
93 | + field: 'deviceInfo.sip.hostAddress', | |
94 | + label: t('deviceManagement.device.hostAddressText'), | |
95 | + }, | |
96 | + { | |
97 | + field: 'deviceInfo.sip.manufacturer', | |
98 | + label: t('deviceManagement.device.brandText'), | |
99 | + }, | |
100 | + { | |
101 | + field: 'deviceInfo.sip.streamMode', | |
102 | + label: t('deviceManagement.device.streamModeText'), | |
103 | + }, | |
104 | + { | |
105 | + field: 'description', | |
106 | + label: t('common.descText'), | |
107 | + // span: 2, | |
108 | + render(val) { | |
109 | + return h(Tooltip, { title: val }, () => | |
110 | + h('span', { style: { cursor: 'pointer' } as CSSProperties }, val) | |
111 | + ); | |
112 | + }, | |
113 | + }, | |
114 | + ]; | |
115 | +}; | |
116 | + | |
117 | +// 实时数据表格 | |
118 | +export const realTimeDataColumns: BasicColumn[] = [ | |
119 | + { | |
120 | + title: t('deviceManagement.device.keyText'), | |
121 | + dataIndex: 'name', | |
122 | + width: 100, | |
123 | + }, | |
124 | + { | |
125 | + title: t('deviceManagement.device.valueText'), | |
126 | + dataIndex: 'rawValue', | |
127 | + width: 160, | |
128 | + format(text) { | |
129 | + return isNullOrUnDef(text) ? '--' : text; | |
130 | + }, | |
131 | + }, | |
132 | + { | |
133 | + title: t('deviceManagement.device.latestUpdateTimeText'), | |
134 | + dataIndex: 'time', | |
135 | + width: 120, | |
136 | + format: (text) => formatToDateTime(text, 'YYYY-MM-DD HH:mm:ss'), | |
137 | + }, | |
138 | +]; | |
139 | + | |
140 | +// 告警 | |
141 | +export const alarmSearchSchemas: FormSchema[] = [ | |
142 | + { | |
143 | + field: 'status', | |
144 | + label: t('business.deviceStatusText'), | |
145 | + component: 'Select', | |
146 | + colProps: { span: 6 }, | |
147 | + componentProps: { | |
148 | + options: Object.values(AlarmStatus).map((value) => ({ | |
149 | + label: t(`enum.alarmStaus.${value}`), | |
150 | + value, | |
151 | + })), | |
152 | + }, | |
153 | + }, | |
154 | + { | |
155 | + field: 'alarmType', | |
156 | + label: t('deviceManagement.device.alarmSceneText'), | |
157 | + component: 'Input', | |
158 | + colProps: { span: 6 }, | |
159 | + componentProps: { | |
160 | + maxLength: 36, | |
161 | + }, | |
162 | + }, | |
163 | + { | |
164 | + field: 'alarmTime', | |
165 | + label: t('deviceManagement.device.alarmTimeText'), | |
166 | + component: 'RangePicker', | |
167 | + componentProps: { | |
168 | + showTime: { defaultValue: [moment('00:00:00', 'HH:mm:ss'), moment('23:59:59', 'HH:mm:ss')] }, | |
169 | + }, | |
170 | + colProps: { span: 7 }, | |
171 | + }, | |
172 | + { | |
173 | + field: 'severity', | |
174 | + label: t('deviceManagement.device.alarmSeverityText'), | |
175 | + component: 'ApiSelect', | |
176 | + colProps: { span: 6 }, | |
177 | + componentProps: { | |
178 | + api: findDictItemByCode, | |
179 | + params: { | |
180 | + dictCode: 'severity_type', | |
181 | + }, | |
182 | + labelField: 'itemText', | |
183 | + valueField: 'itemValue', | |
184 | + }, | |
185 | + }, | |
186 | +]; | |
187 | +export const alarmColumns: BasicColumn[] = [ | |
188 | + { | |
189 | + title: t('deviceManagement.device.alarmTimeText'), | |
190 | + dataIndex: 'createdTime', | |
191 | + width: 180, | |
192 | + }, | |
193 | + { | |
194 | + title: t('deviceManagement.device.alarmDeviceText'), | |
195 | + dataIndex: 'deviceName', | |
196 | + width: 120, | |
197 | + customRender: ({ record }) => { | |
198 | + const { deviceAlias, deviceName } = record || {}; | |
199 | + return deviceAlias || deviceName; | |
200 | + }, | |
201 | + }, | |
202 | + { | |
203 | + title: t('deviceManagement.device.alarmSceneText'), | |
204 | + dataIndex: 'type', | |
205 | + width: 160, | |
206 | + }, | |
207 | + { | |
208 | + title: t('deviceManagement.device.alarmSeverityText'), | |
209 | + dataIndex: 'severity', | |
210 | + width: 90, | |
211 | + format: (text) => alarmLevel(text), | |
212 | + }, | |
213 | + { | |
214 | + title: t('deviceManagement.device.alarmDetailsText'), | |
215 | + dataIndex: 'details', | |
216 | + slots: { customRender: 'details' }, | |
217 | + width: 160, | |
218 | + }, | |
219 | + { | |
220 | + title: t('deviceManagement.device.statusText'), | |
221 | + dataIndex: 'status', | |
222 | + // format: (text) => statusType(text), | |
223 | + width: 100, | |
224 | + }, | |
225 | +]; | |
226 | + | |
227 | +export const alarmSchemasForm: FormSchema[] = [ | |
228 | + { | |
229 | + field: 'deviceName', | |
230 | + label: '告警设备', | |
231 | + component: 'Input', | |
232 | + componentProps: { | |
233 | + disabled: true, | |
234 | + }, | |
235 | + }, | |
236 | + | |
237 | + { | |
238 | + field: 'startTs', | |
239 | + label: '开始时间', | |
240 | + component: 'Input', | |
241 | + componentProps: { | |
242 | + disabled: true, | |
243 | + }, | |
244 | + }, | |
245 | + { | |
246 | + field: 'endTs', | |
247 | + label: '结束时间', | |
248 | + component: 'Input', | |
249 | + componentProps: { | |
250 | + disabled: true, | |
251 | + }, | |
252 | + }, | |
253 | + { | |
254 | + field: 'ackTs', | |
255 | + label: '处理时间', | |
256 | + component: 'Input', | |
257 | + componentProps: { | |
258 | + disabled: true, | |
259 | + }, | |
260 | + ifShow: ({ values }) => values.status === '激活已确认' || values.status === '清除已确认', | |
261 | + }, | |
262 | + { | |
263 | + field: 'clearTs', | |
264 | + label: '清除时间', | |
265 | + component: 'Input', | |
266 | + componentProps: { | |
267 | + disabled: true, | |
268 | + }, | |
269 | + ifShow: ({ values }) => values.status === '清除已确认' || values.status === '清除未确认', | |
270 | + }, | |
271 | + { | |
272 | + field: 'type', | |
273 | + label: '告警场景', | |
274 | + component: 'Input', | |
275 | + componentProps: { | |
276 | + disabled: true, | |
277 | + }, | |
278 | + }, | |
279 | + { | |
280 | + field: 'severity', | |
281 | + label: '严重程度', | |
282 | + component: 'Input', | |
283 | + componentProps: { | |
284 | + disabled: true, | |
285 | + }, | |
286 | + }, | |
287 | + { | |
288 | + field: 'status', | |
289 | + label: '状态', | |
290 | + component: 'Input', | |
291 | + componentProps: { | |
292 | + disabled: true, | |
293 | + }, | |
294 | + }, | |
295 | +]; | |
296 | +// 子设备 | |
297 | +export const childDeviceSchemas: FormSchema[] = [ | |
298 | + { | |
299 | + field: 'deviceState', | |
300 | + label: t('business.deviceStatusText'), | |
301 | + colProps: { span: 6 }, | |
302 | + component: 'Select', | |
303 | + componentProps: { | |
304 | + maxLength: 255, | |
305 | + options: Object.values(DeviceStatusEnum).map((value) => ({ | |
306 | + label: t(`enum.deviceStatus.${value}`), | |
307 | + value, | |
308 | + })), | |
309 | + }, | |
310 | + }, | |
311 | + { | |
312 | + field: 'name', | |
313 | + label: t('business.deviceNameText'), | |
314 | + component: 'Input', | |
315 | + colProps: { span: 6 }, | |
316 | + componentProps: { | |
317 | + maxLength: 255, | |
318 | + }, | |
319 | + }, | |
320 | +]; | |
321 | + | |
322 | +const deviceStatusColorMapping = { | |
323 | + [DeviceStatusEnum.INACTIVE]: 'warning', | |
324 | + [DeviceStatusEnum.OFFLINE]: 'error', | |
325 | + [DeviceStatusEnum.ONLINE]: 'success', | |
326 | +}; | |
327 | +export const childDeviceColumns: BasicColumn[] = [ | |
328 | + { | |
329 | + title: t('deviceManagement.device.nameText'), | |
330 | + dataIndex: 'tbDeviceName', | |
331 | + width: 120, | |
332 | + slots: { | |
333 | + customRender: 'tbDeviceName', | |
334 | + }, | |
335 | + }, | |
336 | + { | |
337 | + title: t('deviceManagement.device.labelText'), | |
338 | + dataIndex: 'label', | |
339 | + width: 160, | |
340 | + }, | |
341 | + { | |
342 | + title: t('deviceManagement.device.statusText'), | |
343 | + dataIndex: 'deviceState', | |
344 | + customRender: ({ text }) => { | |
345 | + return h(Tag, { color: deviceStatusColorMapping[text] }, () => | |
346 | + t(`enum.deviceStatus.${text}`) | |
347 | + ); | |
348 | + }, | |
349 | + width: 160, | |
350 | + }, | |
351 | + { | |
352 | + title: t('deviceManagement.device.latestConnectionTimeText'), | |
353 | + dataIndex: 'lastOnlineTime', | |
354 | + format: (text) => formatToDateTime(text, 'YYYY-MM-DD HH:mm:ss'), | |
355 | + width: 160, | |
356 | + }, | |
357 | + { | |
358 | + title: t('common.updateTimeText'), | |
359 | + dataIndex: 'createdTime', | |
360 | + format: (text) => formatToDateTime(text, 'YYYY-MM-DD HH:mm:ss'), | |
361 | + width: 160, | |
362 | + }, | |
363 | +]; | |
364 | + | |
365 | +export const alarmLevel = (type: string): string => { | |
366 | + const { t } = useI18n(); | |
367 | + | |
368 | + return t(`enum.alarmLevel.${type}`); | |
369 | +}; | |
370 | + | |
371 | +export const customerForm: FormSchema[] = [ | |
372 | + { | |
373 | + field: 'customerId', | |
374 | + label: t('deviceManagement.device.assignCustomerText'), | |
375 | + component: 'ApiSelect', | |
376 | + componentProps: { | |
377 | + api: getCustomerList, | |
378 | + immediate: false, | |
379 | + labelField: 'realName', | |
380 | + valueField: 'customerId', | |
381 | + }, | |
382 | + required: true, | |
383 | + colProps: { span: 12 }, | |
384 | + }, | |
385 | +]; | |
386 | + | |
387 | +export const orgForm: FormSchema[] = [ | |
388 | + { | |
389 | + field: 'sensorOrganizationId', | |
390 | + label: t('deviceManagement.device.organizeArrayBased'), | |
391 | + component: 'Input', | |
392 | + ifShow: false, | |
393 | + }, | |
394 | + { | |
395 | + field: 'organizationId', | |
396 | + component: 'OrgTreeSelect', | |
397 | + helpMessage: updateOrgHelpMessage, | |
398 | + label: t('deviceManagement.device.chooseOrganization'), | |
399 | + required: true, | |
400 | + componentProps: ({ formModel }) => { | |
401 | + return { | |
402 | + apiTreeSelectProps: { | |
403 | + params: { | |
404 | + organizationId: formModel?.sensorOrganizationId, | |
405 | + }, | |
406 | + }, | |
407 | + }; | |
408 | + }, | |
409 | + }, | |
410 | +]; | ... | ... |
1 | +import { formatToDate } from '/@/utils/dateUtil'; | |
2 | +import { BasicColumn } from '/@/components/Table'; | |
3 | +import { FormSchema } from '/@/components/Table'; | |
4 | +import { DeviceRecord } from '/@/api/device/model/deviceModel'; | |
5 | +// import { deviceProfile } from '/@/api/device/deviceManager'; | |
6 | +import { h } from 'vue'; | |
7 | +import { Tag, Tooltip } from 'ant-design-vue'; | |
8 | +import { handeleCopy } from '../../../device/profiles/step/topic'; | |
9 | +import { useI18n } from '/@/hooks/web/useI18n'; | |
10 | +import { DeviceStatusEnum, DeviceTypeEnum } from '/@/enums/deviceEnum'; | |
11 | + | |
12 | +const { t } = useI18n(); | |
13 | +import edgefornt from '/@/assets/icons/edgefornt.svg'; | |
14 | + | |
15 | +export enum DeviceListAuthEnum { | |
16 | + /** | |
17 | + * @description 新增 | |
18 | + */ | |
19 | + CREATE = 'api:yt:device:post', | |
20 | + | |
21 | + /** | |
22 | + * @description 删除 | |
23 | + */ | |
24 | + DELETE = 'api:yt:device:delete', | |
25 | + | |
26 | + /** | |
27 | + * @description 编辑 | |
28 | + */ | |
29 | + UPDATE = 'api:yt:device:update', | |
30 | + | |
31 | + /** | |
32 | + * @description 详情 | |
33 | + */ | |
34 | + DETAIL = 'api:yt:device:get', | |
35 | + | |
36 | + /** | |
37 | + * @description 导入 | |
38 | + */ | |
39 | + IMPORT = 'api:yt:device:import', | |
40 | + | |
41 | + /** | |
42 | + * @description 公开 | |
43 | + */ | |
44 | + PUBLIC = 'api:yt:device:public', | |
45 | + | |
46 | + /** | |
47 | + * @description 上下线 | |
48 | + */ | |
49 | + ONLINE = 'api:yt:device:online:record', | |
50 | + | |
51 | + /** | |
52 | + * @description 管理设备凭证 | |
53 | + */ | |
54 | + EQUIPMENT = 'api:yt:device:equipment', | |
55 | + | |
56 | + /** | |
57 | + * @description 分配客户 | |
58 | + */ | |
59 | + ASSIGN = 'api:yt:device:assign', | |
60 | + | |
61 | + /** | |
62 | + * @description 命令下发 | |
63 | + */ | |
64 | + RPC = 'api:yt:device:rpc', | |
65 | + | |
66 | + /** | |
67 | + * @description 更新产品 | |
68 | + */ | |
69 | + UPDATE_PRODUCT = 'api:yt:device:update:product', | |
70 | +} | |
71 | + | |
72 | +// 表格列数据 | |
73 | +export const columns: BasicColumn[] = [ | |
74 | + { | |
75 | + title: t('business.deviceStatusText'), | |
76 | + dataIndex: 'deviceState', | |
77 | + width: 110, | |
78 | + className: 'device-status', | |
79 | + slots: { customRender: 'deviceState' }, | |
80 | + }, | |
81 | + { | |
82 | + title: t('business.deviceImageText'), | |
83 | + dataIndex: 'deviceInfo.avatar', | |
84 | + width: 70, | |
85 | + slots: { customRender: 'img' }, | |
86 | + }, | |
87 | + { | |
88 | + dataIndex: 'name', | |
89 | + title: t('deviceManagement.device.aliasNameText'), | |
90 | + width: 210, | |
91 | + slots: { customRender: 'name', title: 'deviceTitle' }, | |
92 | + className: 'device-name-edge', | |
93 | + customRender: ({ record }) => { | |
94 | + return h('div', { class: 'py-3 px-3.5' }, [ | |
95 | + record.alias && | |
96 | + h( | |
97 | + 'div', | |
98 | + { | |
99 | + class: 'cursor-pointer truncate', | |
100 | + }, | |
101 | + h( | |
102 | + Tooltip, | |
103 | + { | |
104 | + placement: 'topLeft', | |
105 | + title: `${record.alias}`, | |
106 | + }, | |
107 | + () => `${record.alias}` | |
108 | + ) | |
109 | + ), | |
110 | + h( | |
111 | + 'div', | |
112 | + { | |
113 | + class: 'cursor-pointer text-blue-500 truncate', | |
114 | + onClick: () => { | |
115 | + handeleCopy(`${record.name}`); | |
116 | + }, | |
117 | + }, | |
118 | + h( | |
119 | + Tooltip, | |
120 | + { | |
121 | + placement: 'topLeft', | |
122 | + title: `${record.name}`, | |
123 | + }, | |
124 | + () => `${record.name}` | |
125 | + ) | |
126 | + ), | |
127 | + record.isEdge | |
128 | + ? h('div', { class: 'absolute top-0 left-0', fill: '#1890ff' }, [ | |
129 | + h('img', { src: edgefornt, class: 'w-12.5 h-12.5' }), | |
130 | + h( | |
131 | + 'span', | |
132 | + { | |
133 | + class: | |
134 | + 'absolute top-0.5 left-0.5 text-light-50 transform -rotate-45 translate-y-0 !text-10px', | |
135 | + }, | |
136 | + t('common.edgeFlag') | |
137 | + ), | |
138 | + ]) | |
139 | + : '', | |
140 | + ]); | |
141 | + }, | |
142 | + }, | |
143 | + { | |
144 | + title: t('business.deviceTypeText'), | |
145 | + dataIndex: 'deviceType', | |
146 | + width: 130, | |
147 | + customRender({ text }) { | |
148 | + return h(Tag, { color: 'success' }, () => t(`enum.deviceType.${text}`)); | |
149 | + }, | |
150 | + }, | |
151 | + { | |
152 | + title: t('business.affiliatedProductText'), | |
153 | + dataIndex: 'deviceProfile.name', | |
154 | + width: 180, | |
155 | + slots: { customRender: 'deviceProfile' }, | |
156 | + ellipsis: true, | |
157 | + }, | |
158 | + { | |
159 | + title: t('business.affiliatedOrganizationText'), | |
160 | + dataIndex: 'organizationDTO.name', | |
161 | + width: 100, | |
162 | + }, | |
163 | + // { | |
164 | + // title: '客户', | |
165 | + // dataIndex: 'customerName', | |
166 | + // width: 100, | |
167 | + // }, | |
168 | + { | |
169 | + title: t('business.publicText'), | |
170 | + dataIndex: 'public', | |
171 | + width: 100, | |
172 | + customRender({ record }: { record: DeviceRecord }) { | |
173 | + const flag = record?.customerAdditionalInfo?.isPublic; | |
174 | + return h(Tag, { color: flag ? 'blue' : 'orange' }, () => | |
175 | + flag ? t('business.publicText') : t('business.privateText') | |
176 | + ); | |
177 | + }, | |
178 | + }, | |
179 | + { | |
180 | + title: t('deviceManagement.device.lastOnlineTimeText'), | |
181 | + dataIndex: 'lastOnlineTime', | |
182 | + format: (text) => text && formatToDate(text, 'YYYY-MM-DD HH:mm:ss'), | |
183 | + width: 160, | |
184 | + }, | |
185 | + { | |
186 | + title: t('deviceManagement.device.lastOfflineTimeText'), | |
187 | + dataIndex: 'lastOfflineTime', | |
188 | + format: (text) => { | |
189 | + return text ? formatToDate(text, 'YYYY-MM-DD HH:mm:ss') : ''; | |
190 | + }, | |
191 | + width: 160, | |
192 | + }, | |
193 | +]; | |
194 | + | |
195 | +// 查询字段 | |
196 | +export const searchFormSchema: FormSchema[] = [ | |
197 | + { | |
198 | + field: 'code', | |
199 | + label: t('equipment.ledger.deviceCode'), | |
200 | + component: 'Input', | |
201 | + colProps: { span: 6 }, | |
202 | + componentProps: { | |
203 | + maxLength: 255, | |
204 | + }, | |
205 | + }, | |
206 | + { | |
207 | + field: 'deviceType', | |
208 | + label: t('equipment.ledger.deviceType'), | |
209 | + component: 'Select', | |
210 | + componentProps: { | |
211 | + options: Object.values(DeviceTypeEnum).map((value) => ({ | |
212 | + label: t(`enum.deviceType.${value}`), | |
213 | + value, | |
214 | + })), | |
215 | + }, | |
216 | + colProps: { span: 6 }, | |
217 | + }, | |
218 | + { | |
219 | + field: 'deviceState', | |
220 | + label: t('business.deviceStatusText'), | |
221 | + component: 'Select', | |
222 | + componentProps: { | |
223 | + options: Object.values(DeviceStatusEnum).map((value) => ({ | |
224 | + label: t(`enum.deviceStatus.${value}`), | |
225 | + value, | |
226 | + })), | |
227 | + }, | |
228 | + colProps: { span: 6 }, | |
229 | + } | |
230 | +]; | ... | ... |
src/views/equipment/ledger/index.ts
0 → 100644
src/views/equipment/ledger/index.vue
0 → 100644
1 | +<template> | |
2 | + <div> | |
3 | + <PageWrapper dense contentFullHeight contentClass="flex"> | |
4 | + <OrganizationIdTree @select="handleSelect" ref="organizationIdTreeRef" /> | |
5 | + <BasicTable style="flex: auto" @register="registerTable" class="w-5/6 xl:w-4/5 device-table"> | |
6 | + <template #toolbar> | |
7 | + <Authority :value="DeviceListAuthEnum.CREATE"> | |
8 | + <a-button type="primary" @click="handleCreate" v-if="authBtn(role)"> | |
9 | + {{ t('equipment.ledger.createLedgerText') }} | |
10 | + </a-button> | |
11 | + </Authority> | |
12 | + | |
13 | + <Authority :value="DeviceListAuthEnum.IMPORT"> | |
14 | + <Button type="primary" > | |
15 | + {{ t('equipment.ledger.importLedgerText') }} | |
16 | + </Button> | |
17 | + </Authority> | |
18 | + | |
19 | + <Authority | |
20 | + :value="[ | |
21 | + DeviceListAuthEnum.DELETE, | |
22 | + DeviceListAuthEnum.ASSIGN, | |
23 | + DeviceListAuthEnum.UPDATE_PRODUCT, | |
24 | + ]" | |
25 | + > | |
26 | + <AuthDropDown | |
27 | + v-if="authBtn(role)" | |
28 | + :disabled="isPublicAndPrivateFlag || !isExistOption" | |
29 | + :dropMenuList="[ | |
30 | + { | |
31 | + text: t('common.delText'), | |
32 | + auth: DeviceListAuthEnum.DELETE, | |
33 | + icon: 'ant-design:delete-outlined', | |
34 | + event: '', | |
35 | + disabled: !batchPrivateFlag, | |
36 | + popconfirm: { | |
37 | + title: t('common.deleteConfirmText'), | |
38 | + onConfirm: () => handleDelete(), | |
39 | + }, | |
40 | + }, | |
41 | + { | |
42 | + text: t('equipment.ledger.editProductText'), | |
43 | + auth: DeviceListAuthEnum.UPDATE_PRODUCT, | |
44 | + icon: 'clarity:note-edit-line', | |
45 | + event: '', | |
46 | + disabled: !batchPrivateFlag || batchUpdateProductFlag, | |
47 | + onClick: handelOpenBatchUpdateProductModal, | |
48 | + }, | |
49 | + { | |
50 | + text: t('business.publicText'), | |
51 | + icon: 'ant-design:wallet-outlined', | |
52 | + event: '', | |
53 | + disabled: !batchPrivateFlag, | |
54 | + onClick: handleBatchPublic.bind(null), | |
55 | + }, | |
56 | + { | |
57 | + text: t('business.privateText'), | |
58 | + icon: 'ant-design:wallet-outlined', | |
59 | + event: '', | |
60 | + disabled: batchPrivateFlag, | |
61 | + onClick: handleBatchPrivate.bind(null), | |
62 | + }, | |
63 | + { | |
64 | + text: t('equipment.ledger.updateOrganization'), | |
65 | + icon: 'ant-design:wallet-outlined', | |
66 | + event: '', | |
67 | + disabled: !batchPrivateFlag || batchSensorFlag || diffBatchSensorFlag, | |
68 | + onClick: handleBatchOrg.bind(null), | |
69 | + }, | |
70 | + ]" | |
71 | + > | |
72 | + <Button type="primary" :disabled="isPublicAndPrivateFlag || !isExistOption"> | |
73 | + {{ t('equipment.ledger.batchActionText') }} | |
74 | + </Button> | |
75 | + </AuthDropDown> | |
76 | + </Authority> | |
77 | + </template> | |
78 | + <template #img="{ record }"> | |
79 | + <TableImg | |
80 | + :size="30" | |
81 | + :showBadge="false" | |
82 | + :simpleShow="true" | |
83 | + :imgList=" | |
84 | + typeof record?.deviceInfo?.avatar !== 'undefined' && | |
85 | + record?.deviceInfo?.avatar !== '' && | |
86 | + record?.deviceInfo?.avatar != null | |
87 | + ? [record.deviceInfo.avatar] | |
88 | + : null | |
89 | + " | |
90 | + /> | |
91 | + </template> | |
92 | + <template #deviceProfile="{ record }"> | |
93 | + <Button | |
94 | + @click="!isCustomer ? goDeviceProfile(record.deviceProfile.name) : null" | |
95 | + type="link" | |
96 | + > | |
97 | + {{ record.deviceProfile.name }} | |
98 | + </Button> | |
99 | + </template> | |
100 | + <template #deviceState="{ record }"> | |
101 | + <div v-if="record.isCollect"> | |
102 | + <div class="absolute top-0 left-0 device-collect"> </div> | |
103 | + <Icon | |
104 | + icon="ph:star-fill" | |
105 | + class="fill-light-50 absolute top-0.5 left-0.5" | |
106 | + color="#fff" | |
107 | + :size="12" | |
108 | + /> | |
109 | + </div> | |
110 | + | |
111 | + <Tag | |
112 | + :color=" | |
113 | + record.deviceState == DeviceState.INACTIVE | |
114 | + ? 'warning' | |
115 | + : record.deviceState == DeviceState.ONLINE | |
116 | + ? 'success' | |
117 | + : record.deviceState == DeviceState.ACTIVE | |
118 | + ? 'success' | |
119 | + : 'error' | |
120 | + " | |
121 | + class="ml-2" | |
122 | + > | |
123 | + {{ t(`enum.deviceStatus.${record.deviceState}`) }} | |
124 | + </Tag> | |
125 | + </template> | |
126 | + <template #action="{ record }"> | |
127 | + <TableAction | |
128 | + :actions="[ | |
129 | + { | |
130 | + label: AlarmDetailActionButton({ hasAlarm: !!record.alarmStatus }), | |
131 | + icon: 'ant-design:eye-outlined', | |
132 | + auth: DeviceListAuthEnum.DETAIL, | |
133 | + // onClick: handleDetail.bind(null, record), | |
134 | + }, | |
135 | + { | |
136 | + label: t('common.editText'), | |
137 | + auth: DeviceListAuthEnum.UPDATE, | |
138 | + icon: 'clarity:note-edit-line', | |
139 | + ifShow: authBtn(role), | |
140 | + // onClick: handleEdit.bind(null, record), | |
141 | + }, | |
142 | + ]" | |
143 | + :dropDownActions="[ | |
144 | + // record.customerId | |
145 | + // ? { | |
146 | + // label: t('deviceManagement.device.cancelAssignText'), | |
147 | + // icon: 'mdi:account-arrow-left', | |
148 | + // ifShow: authBtn(role) && !record?.customerAdditionalInfo?.isPublic, | |
149 | + // auth: DeviceListAuthEnum.ASSIGN, | |
150 | + // popConfirm: { | |
151 | + // title: t('deviceManagement.device.cancelAssignConfirmText'), | |
152 | + // confirm: handleCancelDispatchCustomer.bind(null, record), | |
153 | + // }, | |
154 | + // } | |
155 | + // : { | |
156 | + // label: t('deviceManagement.device.assignCustomerText'), | |
157 | + // icon: 'mdi:account-arrow-right', | |
158 | + // ifShow: authBtn(role), | |
159 | + // auth: DeviceListAuthEnum.ASSIGN, | |
160 | + // onClick: handleDispatchCustomer.bind(null, record), | |
161 | + // }, | |
162 | + { | |
163 | + label: record?.customerAdditionalInfo?.isPublic | |
164 | + ? t('business.privateText') | |
165 | + : t('business.publicText'), | |
166 | + auth: DeviceListAuthEnum.PUBLIC, | |
167 | + icon: record?.customerAdditionalInfo?.isPublic | |
168 | + ? 'ant-design:lock-outlined' | |
169 | + : 'ant-design:unlock-outlined', | |
170 | + // onClick: handlePublicDevice.bind(null, record), | |
171 | + }, | |
172 | + { | |
173 | + label: t('deviceManagement.device.onlineRecordText'), | |
174 | + auth: DeviceListAuthEnum.ONLINE, | |
175 | + icon: 'ant-design:rise-outlined', | |
176 | + // onClick: handleUpAndDownRecord.bind(null, record), | |
177 | + }, | |
178 | + !record.isCollect | |
179 | + ? { | |
180 | + label: t('deviceManagement.device.collectText'), | |
181 | + icon: 'ant-design:heart-outlined', | |
182 | + // onClick: handelCollect.bind(null, record), | |
183 | + } | |
184 | + : { | |
185 | + label: t('deviceManagement.device.cancelCollectText'), | |
186 | + icon: 'ant-design:heart-outlined', | |
187 | + popConfirm: { | |
188 | + title: t('deviceManagement.device.cancelCollectConfirmText'), | |
189 | + // confirm: handelCollect.bind(null, record), | |
190 | + }, | |
191 | + }, | |
192 | + { | |
193 | + label: t('common.delText'), | |
194 | + auth: DeviceListAuthEnum.DELETE, | |
195 | + icon: 'ant-design:delete-outlined', | |
196 | + ifShow: authBtn(role) && record.customerId === undefined, | |
197 | + color: 'error', | |
198 | + popConfirm: { | |
199 | + title: !!record.isEdge | |
200 | + ? t('common.edgeDeviceOperationConfirm') | |
201 | + : t('common.deleteConfirmText'), | |
202 | + confirm: handleDelete.bind(null, record), | |
203 | + }, | |
204 | + }, | |
205 | + ]" | |
206 | + /> | |
207 | + </template> | |
208 | + </BasicTable> | |
209 | +<!-- <LedgerDetailDrawer--> | |
210 | +<!-- @register="registerDetailDrawer"--> | |
211 | +<!-- @open-tb-device-detail="handleOpenTbDeviceDetail"--> | |
212 | +<!-- @open-gateway-device-detail="handleOpenGatewayDetail"--> | |
213 | +<!-- />--> | |
214 | + </PageWrapper> | |
215 | + </div> | |
216 | +</template> | |
217 | +<script setup lang="ts"> | |
218 | +import {CSSProperties, h, reactive, ref} from "vue"; | |
219 | +import { PageWrapper } from '/@/components/Page'; | |
220 | +const searchInfo = reactive<Recordable>({}); | |
221 | +const { organizationIdTreeRef, resetFn } = useResetOrganizationTree(searchInfo); | |
222 | +import {BasicTable, TableAction, TableImg, useTable} from "/@/components/Table"; | |
223 | +import {OrganizationIdTree, useResetOrganizationTree} from "/@/views/common/organizationIdTree"; | |
224 | +import { | |
225 | + deleteDevice, | |
226 | + devicePage, | |
227 | + doBatchPrivateDevice, | |
228 | + doBatchPublicDevice | |
229 | +} from "/@/api/device/deviceManager"; | |
230 | +import { | |
231 | + columns, | |
232 | + DeviceListAuthEnum, | |
233 | + searchFormSchema | |
234 | +} from "/@/views/equipment/ledger/config/ledger.data"; | |
235 | +import {DeviceModel, DeviceRecord, DeviceState} from "/@/api/device/model/deviceModel"; | |
236 | +import {useI18n} from "/@/hooks/web/useI18n"; | |
237 | +import {authBtn} from "/@/enums/roleEnum"; | |
238 | +import {Badge, Button, Tag} from "ant-design-vue"; | |
239 | +import {Authority} from "/@/components/Authority"; | |
240 | +import {AuthDropDown} from "/@/components/Widget"; | |
241 | +import {getAuthCache} from "/@/utils/auth"; | |
242 | +import {USER_INFO_KEY} from "/@/enums/cacheEnum"; | |
243 | +import {useBatchOperation} from "/@/utils/useBatchOperation"; | |
244 | +import {useMessage} from "/@/hooks/web/useMessage"; | |
245 | +import {DataActionModeEnum} from "/@/enums/toolEnum"; | |
246 | +import { | |
247 | + BatchUpdateProductModalParamsType | |
248 | +} from "/@/views/device/list/cpns/modal/BatchUpdateProductModal/index"; | |
249 | +import {useModal} from "/@/components/Modal"; | |
250 | +import Icon from "/@/components/Icon"; | |
251 | +// import LedgerDetailDrawer from "./cpns/modal/LedgerDetailDrawer.vue"; | |
252 | +const { t } = useI18n(); | |
253 | +const userInfo: any = getAuthCache(USER_INFO_KEY); | |
254 | +const role: string = userInfo.roles[0]; | |
255 | +const isPublicAndPrivateFlag = ref(false); | |
256 | +const batchPrivateFlag = ref(true); | |
257 | +const batchUpdateProductFlag = ref(true); | |
258 | +const batchSensorFlag = ref(false); | |
259 | +const { createMessage } = useMessage(); | |
260 | +const diffBatchSensorFlag = ref(false); | |
261 | +const [registerOrgModal, { openModal: openOrgodal }] = useModal(); | |
262 | +const [registerBatchUpdateProductModal, { openModal: openBatchUpdateProductModal }] = useModal(); | |
263 | + | |
264 | +const AlarmDetailActionButton = ({ hasAlarm }: { hasAlarm?: boolean }) => | |
265 | + h( | |
266 | + Badge, | |
267 | + { offset: [0, -5] }, | |
268 | + { | |
269 | + default: () => h('span', { style: { color: '#377dff' } }, t('common.detailText')), | |
270 | + count: () => | |
271 | + h( | |
272 | + 'div', | |
273 | + { | |
274 | + style: { | |
275 | + visibility: hasAlarm ? 'visible' : 'hidden', | |
276 | + width: '14px', | |
277 | + height: '14px', | |
278 | + display: 'flex', | |
279 | + justifyContent: 'center', | |
280 | + alignItems: 'center', | |
281 | + border: '1px solid #f46161', | |
282 | + borderRadius: '50%', | |
283 | + } as CSSProperties, | |
284 | + }, | |
285 | + h(Icon, { icon: 'mdi:bell-warning', color: '#f46161', size: 12 }) | |
286 | + ), | |
287 | + } | |
288 | + ); | |
289 | + | |
290 | +const [ | |
291 | + registerTable, | |
292 | + { | |
293 | + reload, | |
294 | + setLoading, | |
295 | + setSelectedRowKeys, | |
296 | + getForm, | |
297 | + getSelectRowKeys, | |
298 | + getSelectRows, | |
299 | + getRowSelection, | |
300 | + clearSelectedRowKeys, | |
301 | + }, | |
302 | +] = useTable({ | |
303 | + title: t('equipment.ledger.ledgerListText'), | |
304 | + api: devicePage, | |
305 | + columns, | |
306 | + beforeFetch: (params) => { | |
307 | + const { deviceProfileId } = params; | |
308 | + if (!deviceProfileId) return; | |
309 | + const obj = { | |
310 | + ...params, | |
311 | + ...{ | |
312 | + deviceProfileIds: deviceProfileId ? [deviceProfileId] : null, | |
313 | + }, | |
314 | + }; | |
315 | + delete obj.deviceProfileId; | |
316 | + return obj; | |
317 | + }, | |
318 | + formConfig: { | |
319 | + labelWidth: 140, | |
320 | + schemas: searchFormSchema, | |
321 | + resetFunc: resetFn, | |
322 | + }, | |
323 | + useSearchForm: true, | |
324 | + showTableSetting: true, | |
325 | + bordered: true, | |
326 | + showIndexColumn: false, | |
327 | + rowKey: 'id', | |
328 | + searchInfo: searchInfo, | |
329 | + clickToRowSelect: false, | |
330 | + rowClassName: (record) => ((record as DeviceRecord).alarmStatus ? 'device-alarm-badge' : ''), | |
331 | + actionColumn: { | |
332 | + width: 200, | |
333 | + title: t('common.actionText'), | |
334 | + slots: { customRender: 'action' }, | |
335 | + fixed: 'right', | |
336 | + }, | |
337 | + rowSelection: { | |
338 | + type: 'checkbox', | |
339 | + getCheckboxProps: (record: DeviceModel) => { | |
340 | + return { disabled: !!record.customerId && record.customerName !== 'Public' }; | |
341 | + }, | |
342 | + onSelect(_record, _selected, selectedRows) { | |
343 | + const [firstItem] = selectedRows as DeviceRecord[]; | |
344 | + const { deviceType } = firstItem || {}; | |
345 | + batchUpdateProductFlag.value = | |
346 | + !selectedRows.length || | |
347 | + !selectedRows.every((item) => (item as DeviceRecord).deviceType === deviceType); | |
348 | + | |
349 | + batchPrivateFlag.value = selectedRows.some((item: DeviceRecord) => !item?.customerId); | |
350 | + const filterSensor = selectedRows.map((filterItem: DeviceRecord) => filterItem.deviceType); | |
351 | + batchSensorFlag.value = | |
352 | + filterSensor.includes('SENSOR') && | |
353 | + (filterSensor.includes('DIRECT_CONNECTION') || filterSensor.includes('GATEWAY')); // 网关子和直连设备或者网关设备,则禁用组织修改 | |
354 | + const filterGatewayId = selectedRows | |
355 | + .filter((filterItem: DeviceRecord) => filterItem.deviceType === 'SENSOR') | |
356 | + .map((mapItem: DeviceRecord) => mapItem.gatewayId); | |
357 | + diffBatchSensorFlag.value = [...new Set(filterGatewayId)].length > 1; // 数组长度大于1,说明选择的网关子设备所属网关为多个,则禁用组织修改 | |
358 | + isPublicAndPrivateFlag.value = | |
359 | + selectedRows.some((item: DeviceRecord) => !item?.customerId) && | |
360 | + selectedRows.some((item: DeviceRecord) => item?.customerAdditionalInfo?.isPublic); | |
361 | + }, | |
362 | + onSelectAll(_selected, selectedRows) { | |
363 | + const [firstItem] = selectedRows as DeviceRecord[]; | |
364 | + const { deviceType } = firstItem || {}; | |
365 | + batchUpdateProductFlag.value = | |
366 | + !selectedRows.length || | |
367 | + !selectedRows.every((item) => (item as DeviceRecord).deviceType === deviceType); | |
368 | + | |
369 | + batchPrivateFlag.value = selectedRows.some((item) => !item?.customerId); | |
370 | + const filterSensor = selectedRows.map((filterItem) => filterItem.deviceType); | |
371 | + batchSensorFlag.value = | |
372 | + filterSensor.includes('SENSOR') && | |
373 | + (filterSensor.includes('DIRECT_CONNECTION') || filterSensor.includes('GATEWAY')); // 网关子和直连设备或者网关设备,则禁用组织修改 | |
374 | + const filterGatewayId = selectedRows | |
375 | + .filter((filterItem: DeviceRecord) => filterItem.deviceType === 'SENSOR') | |
376 | + .map((mapItem: DeviceRecord) => mapItem.gatewayId); | |
377 | + diffBatchSensorFlag.value = [...new Set(filterGatewayId)].length > 1; // 数组长度大于1,说明选择的网关子设备所属网关为多个,则禁用组织修改 | |
378 | + isPublicAndPrivateFlag.value = | |
379 | + selectedRows.some((item) => !item?.customerId) && | |
380 | + selectedRows.some((item) => item?.customerAdditionalInfo?.isPublic); | |
381 | + }, | |
382 | + }, | |
383 | +}); | |
384 | + | |
385 | +const { isExistOption } = useBatchOperation(getRowSelection, setSelectedRowKeys); | |
386 | +function handleCreate() { | |
387 | + // openModal(true, { | |
388 | + // isUpdate: false, | |
389 | + // }); | |
390 | +} | |
391 | + | |
392 | +// 批量公开设备 | |
393 | +const handleBatchPublic = async () => { | |
394 | + setLoading(true); | |
395 | + try { | |
396 | + const options = getSelectRows(); | |
397 | + const tbDeviceIdJoinStr = options.map((item) => item.tbDeviceId).join(','); | |
398 | + const res = await doBatchPublicDevice(tbDeviceIdJoinStr); | |
399 | + createMessage.success( | |
400 | + res === '公开成功' ? t('common.publicSuccess') : t('common.publicError') | |
401 | + ); | |
402 | + } finally { | |
403 | + handleReload(); | |
404 | + setLoading(false); | |
405 | + } | |
406 | +}; | |
407 | + | |
408 | +function handleReload() { | |
409 | + setSelectedRowKeys([]); | |
410 | + handleSuccess(); | |
411 | +} | |
412 | + | |
413 | +// 批量私有设备 | |
414 | +const handleBatchPrivate = async () => { | |
415 | + setLoading(true); | |
416 | + try { | |
417 | + const options = getSelectRows(); | |
418 | + const tbDeviceIdJoinStr = options.map((item) => item.tbDeviceId).join(','); | |
419 | + const res = await doBatchPrivateDevice(tbDeviceIdJoinStr); | |
420 | + createMessage.success( | |
421 | + res === '私有成功' ? t('common.privateSuccess') : t('common.privateError') | |
422 | + ); | |
423 | + } finally { | |
424 | + handleReload(); | |
425 | + setLoading(false); | |
426 | + } | |
427 | +}; | |
428 | + | |
429 | +const handleBatchOrg = () => { | |
430 | + const options = getSelectRows(); | |
431 | + openOrgodal(true, options); | |
432 | +}; | |
433 | + | |
434 | +const handelOpenBatchUpdateProductModal = () => { | |
435 | + const rows: DeviceRecord[] = getSelectRows(); | |
436 | + const [firstItem] = rows; | |
437 | + | |
438 | + openBatchUpdateProductModal(true, { | |
439 | + mode: DataActionModeEnum.UPDATE, | |
440 | + record: { | |
441 | + sourceDeviceProfileName: firstItem.deviceProfile.name, | |
442 | + deviceType: firstItem.deviceType, | |
443 | + deviceIds: rows.map((item) => item.tbDeviceId), | |
444 | + deviceProfile: firstItem.deviceProfile, | |
445 | + }, | |
446 | + } as BatchUpdateProductModalParamsType); | |
447 | +}; | |
448 | + | |
449 | +const handleDelete = async (record?: DeviceRecord) => { | |
450 | + let ids: string[] = []; | |
451 | + if (record) { | |
452 | + ids.push(record.id); | |
453 | + } else { | |
454 | + ids = getSelectRowKeys(); | |
455 | + } | |
456 | + try { | |
457 | + setLoading(true); | |
458 | + await deleteDevice(ids); | |
459 | + createMessage.success(t('common.deleteSuccessText')); | |
460 | + handleReload(); | |
461 | + } catch (error) { | |
462 | + throw error; | |
463 | + } finally { | |
464 | + setLoading(false); | |
465 | + } | |
466 | +}; | |
467 | + | |
468 | +function handleSuccess() { | |
469 | + reload(); | |
470 | +} | |
471 | + | |
472 | +function handleSelect(organization) { | |
473 | + searchInfo.organizationId = organization; | |
474 | + handleSuccess(); | |
475 | +} | |
476 | + | |
477 | +</script> | |
478 | + | |
479 | +<style scoped lang="less"> | |
480 | +.device-table { | |
481 | + :deep(.ant-form-item-control-input-content) { | |
482 | + & > div > div { | |
483 | + width: 100%; | |
484 | + } | |
485 | + } | |
486 | +} | |
487 | +</style> | |
488 | + | |
489 | +<style lang="less"> | |
490 | +.device-status { | |
491 | + position: relative; | |
492 | + | |
493 | + .device-collect { | |
494 | + width: 0; | |
495 | + height: 0; | |
496 | + border-top: 30px solid #377dff; | |
497 | + border-right: 30px solid transparent; | |
498 | + } | |
499 | +} | |
500 | + | |
501 | +.device-name-edge { | |
502 | + width: 100%; | |
503 | + height: 100%; | |
504 | + padding: 0 !important; | |
505 | + position: relative; | |
506 | +} | |
507 | +</style> | ... | ... |