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