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> |