|
|
@@ -1,337 +1,308 @@
|
|
|
<template>
|
|
|
- <ContentWrap>
|
|
|
- <div style="display: flex; flex-direction: row; height: 12em; margin-top: 2px">
|
|
|
- <div style="flex: 1; height: 12em; margin-left: 20px">
|
|
|
- <el-image
|
|
|
- :key="index"
|
|
|
- :src="defaultPicUrl"
|
|
|
- style="width: 35em; height: 12em"
|
|
|
- @click="imagePreview(defaultPicUrl)"
|
|
|
- fit="contain" />
|
|
|
+ <ContentWrap v-loading="formLoading" class="device-main-wrap" :body-style="{ padding: '0' }">
|
|
|
+ <div class="device-main">
|
|
|
+ <div class="device-media">
|
|
|
+ <button class="device-image-frame" type="button" @click="imagePreview(defaultPicUrl)">
|
|
|
+ <el-image :src="defaultPicUrl" class="device-image" fit="cover">
|
|
|
+ <template #error>
|
|
|
+ <div class="device-image__empty">
|
|
|
+ <Icon icon="ep:picture" />
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-image>
|
|
|
+ <span class="image-preview-mask">
|
|
|
+ <Icon icon="ep:zoom-in" />
|
|
|
+ <span>{{ t('action.preview') }}</span>
|
|
|
+ </span>
|
|
|
+ </button>
|
|
|
</div>
|
|
|
- <div style="flex: 2; height: 12em; margin-top: 23px">
|
|
|
- <el-form ref="formRef" :disabled="false" :model="formData" label-width="120px">
|
|
|
- <el-row>
|
|
|
- <el-col :span="8">
|
|
|
- <el-form-item :label="t('iotDevice.yfCode')" prop="yfDeviceCode">
|
|
|
- {{ formData.yfDeviceCode }}
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="8">
|
|
|
- <el-form-item :label="t('iotDevice.code')" prop="deviceCode">
|
|
|
- {{ formData.deviceCode }}
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="8">
|
|
|
- <el-form-item :label="t('chooseMaintain.deviceName')" prop="deviceName">
|
|
|
- {{ formData.deviceName }}
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="8">
|
|
|
- <el-form-item :label="t('iotDevice.brand')" prop="brand">
|
|
|
- {{ formData.brandName }}
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="8">
|
|
|
- <el-form-item :label="t('iotDevice.dept')" prop="deptId">
|
|
|
- {{ formData.deptName }}
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="8">
|
|
|
- <el-form-item :label="t('info.deviceClass')" prop="assetClass">
|
|
|
- {{ formData.assetClassName }}
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="8">
|
|
|
- <el-form-item :label="t('monitor.status')" prop="deviceStatus">
|
|
|
- {{ getDictLabel(DICT_TYPE.PMS_DEVICE_STATUS, formData.deviceStatus) }}
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="8">
|
|
|
- <el-form-item :label="t('devicePerson.assets')" prop="assetProperty">
|
|
|
- {{ getDictLabel(DICT_TYPE.PMS_ASSET_PROPERTY, formData.assetProperty) }}
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="8">
|
|
|
- <el-form-item :label="t('deviceForm.model')" prop="model">
|
|
|
- {{ formData.model ? formData.model : '-' }}
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="8">
|
|
|
- <el-form-item :label="t('devicePerson.rp')" prop="responsibleNames">
|
|
|
- {{ formData.responsibleNames ? formData.responsibleNames : '-' }}
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
- </el-form>
|
|
|
+
|
|
|
+ <div class="device-content">
|
|
|
+ <div class="device-head">
|
|
|
+ <div class="device-head__main">
|
|
|
+ <div class="device-title-row">
|
|
|
+ <h1 class="device-title">{{ formatValue(formData.deviceName) }}</h1>
|
|
|
+ <el-tag class="device-status" effect="light" round>
|
|
|
+ {{ statusLabel }}
|
|
|
+ </el-tag>
|
|
|
+ </div>
|
|
|
+ <div class="device-subtitle">
|
|
|
+ <Icon icon="ep:collection-tag" />
|
|
|
+ <span>{{ assetPropertyLabel }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="device-info-grid">
|
|
|
+ <div v-for="field in mainInfoFields" :key="field.prop" class="device-info-item">
|
|
|
+ <span class="device-info-item__label">
|
|
|
+ <Icon :icon="field.icon" />
|
|
|
+ {{ field.label }}
|
|
|
+ </span>
|
|
|
+ <strong>{{ field.value }}</strong>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</ContentWrap>
|
|
|
- <ContentWrap v-loading="formLoading">
|
|
|
- <el-tabs v-model="activeName" @tab-click="handleTabClick">
|
|
|
+
|
|
|
+ <ContentWrap
|
|
|
+ v-loading="formLoading"
|
|
|
+ class="device-detail-wrap"
|
|
|
+ :body-style="{ padding: '0 18px 18px' }">
|
|
|
+ <el-tabs v-model="activeName" class="device-tabs">
|
|
|
<el-tab-pane :label="t('deviceInfo.basicInformation')" name="info">
|
|
|
- <el-form style="margin-top: 5px; margin-left: 35px; margin-right: 35px">
|
|
|
- <el-row style="border-bottom: 1px solid #dcdfe6">
|
|
|
- <el-col :span="6">
|
|
|
- <el-form-item :label="t('deviceInfo.mfg')" prop="manufacturerId">
|
|
|
- {{ formData.zzName }}
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="6">
|
|
|
- <el-form-item :label="t('deviceInfo.pd')" prop="manDate">
|
|
|
- {{ formatDate(formData.manDate, 'YYYY-MM-DD') }}
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="6">
|
|
|
- <el-form-item :label="t('deviceInfo.supplier')" prop="supplierId">
|
|
|
- {{ formData.supplierName ? formData.supplierName : '-' }}
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="6">
|
|
|
- <el-form-item :label="t('deviceInfo.ni')" prop="nameplate">
|
|
|
- {{ formData.nameplate ? formData.nameplate : '-' }}
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="6">
|
|
|
- <el-form-item :label="t('deviceInfo.warranty')" prop="expires">
|
|
|
- {{ formData.expires ? formatDate(formData.expires, 'YYYY-MM-DD') : '-' }}
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
- <el-row style="margin-top: 20px; border-bottom: 1px solid #dcdfe6">
|
|
|
- <el-col :span="6">
|
|
|
- <el-form-item :label="t('deviceInfo.pp')" prop="plPrice">
|
|
|
- {{ formData.plPrice ? formData.plPrice : '-' }}
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="6">
|
|
|
- <el-form-item :label="t('deviceInfo.pdate')" prop="plDate">
|
|
|
- {{ formData.plDate ? formatDate(formData.plDate, 'YYYY-MM-DD') : '-' }}
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="6">
|
|
|
- <el-form-item :label="t('deviceInfo.dp')" prop="plYear">
|
|
|
- {{ formData.plYear ? formData.plYear : '-' }}
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="6">
|
|
|
- <el-form-item :label="t('deviceInfo.ds')" prop="plStartDate">
|
|
|
- {{ formData.plStartDate ? formatDate(formData.plStartDate, 'YYYY-MM-DD') : '-' }}
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="6">
|
|
|
- <el-form-item :label="t('deviceInfo.yd')" prop="plMonthed">
|
|
|
- {{ formData.plMonthed ? formData.plMonthed : '-' }}
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="6">
|
|
|
- <el-form-item :label="t('deviceInfo.yy')" prop="plAmounted">
|
|
|
- {{ formData.plAmounted ? formData.plAmounted : '-' }}
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="6">
|
|
|
- <el-form-item :label="t('deviceInfo.sy')" prop="remainAmount">
|
|
|
- {{ formData.remainAmount ? formData.remainAmount : '-' }}
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
-
|
|
|
- <el-col :span="6">
|
|
|
- <el-form-item :label="t('deviceInfo.my')" prop="monthAmount">
|
|
|
- {{ formData.monthAmount ? formData.monthAmount : '-' }}
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
-
|
|
|
- <el-col :span="6">
|
|
|
- <el-form-item :label="t('deviceInfo.mmy')" prop="totalMonth">
|
|
|
- {{ formData.totalMonth ? formData.totalMonth : '-' }}
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
-
|
|
|
- <el-col :span="6">
|
|
|
- <el-form-item :label="t('deviceInfo.currency')" prop="currency">
|
|
|
- {{ formData.currency ? formData.currency : '-' }}
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
- <el-row style="margin-top: 20px">
|
|
|
- <el-col v-for="field in list" :key="field.sort" :span="6">
|
|
|
- <el-form-item :label="field.name" :prop="field.identifier">
|
|
|
- {{ field.value }}
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
- </el-form>
|
|
|
+ <DeviceBasicInfo :device="formData" :template-fields="templateFields" />
|
|
|
</el-tab-pane>
|
|
|
<el-tab-pane :label="t('deviceInfo.fileLibrary')" name="file">
|
|
|
- <!-- <DeviceUpload ref="fileRef" v-if="loadedTabs.includes('1')" />-->
|
|
|
- <DeviceFile
|
|
|
- ref="fileRef"
|
|
|
- :deviceId="id"
|
|
|
- :deviceName="formData.deviceName"
|
|
|
- v-if="loadedTabs.includes('1')" />
|
|
|
+ <DeviceFileLibrary
|
|
|
+ v-if="activeName === 'file'"
|
|
|
+ :device-id="deviceId"
|
|
|
+ :device-name="formData.deviceName" />
|
|
|
</el-tab-pane>
|
|
|
<el-tab-pane :label="t('deviceInfo.deviceBOM')" name="bom">
|
|
|
- <BomList
|
|
|
- ref="bomRef"
|
|
|
- v-model:activeName="activeName"
|
|
|
- :deviceId="id"
|
|
|
- :deviceCategoryName="formData.assetClassName"
|
|
|
- v-if="loadedTabs.includes('2')" />
|
|
|
+ <DeviceBomLibrary
|
|
|
+ v-if="activeName === 'bom'"
|
|
|
+ :device-id="deviceId"
|
|
|
+ :device-category-name="formData.assetClassName" />
|
|
|
</el-tab-pane>
|
|
|
<el-tab-pane :label="t('deviceInfo.operationRecords')" name="record">
|
|
|
- <RecordList
|
|
|
- ref="recordRef"
|
|
|
- v-model:activeName="activeName"
|
|
|
- :deviceId="id"
|
|
|
- v-if="loadedTabs.includes('3')" />
|
|
|
+ <DeviceOperationRecord v-if="activeName === 'record'" :device-id="deviceId" />
|
|
|
</el-tab-pane>
|
|
|
<el-tab-pane :label="t('deviceInfo.faultRecords')" name="failure">
|
|
|
- <FailureList
|
|
|
- ref="failureRef"
|
|
|
- v-model:activeName="activeName"
|
|
|
- :deviceId="id"
|
|
|
- v-if="loadedTabs.includes('4')" />
|
|
|
+ <DeviceFailureRecord v-if="activeName === 'failure'" :device-id="deviceId" />
|
|
|
</el-tab-pane>
|
|
|
<el-tab-pane :label="t('deviceInfo.repairRecords')" name="maintain">
|
|
|
- <MaintainList
|
|
|
- ref="maintainRef"
|
|
|
- v-model:activeName="activeName"
|
|
|
- :deviceId="id"
|
|
|
- v-if="loadedTabs.includes('5')" />
|
|
|
+ <DeviceMaintainRecord v-if="activeName === 'maintain'" :device-id="deviceId" />
|
|
|
</el-tab-pane>
|
|
|
<el-tab-pane :label="t('deviceInfo.maintenanceRecords')" name="maintenance">
|
|
|
- <MaintenanceList
|
|
|
- ref="maintenanceRef"
|
|
|
- v-model:activeName="activeName"
|
|
|
- :deviceId="id"
|
|
|
- v-if="loadedTabs.includes('6')" />
|
|
|
+ <DeviceMaintenanceRecord v-if="activeName === 'maintenance'" :device-id="deviceId" />
|
|
|
</el-tab-pane>
|
|
|
<el-tab-pane :label="t('deviceInfo.inspectionRecords')" name="inspect">
|
|
|
- <InspectList
|
|
|
- ref="inspectRef"
|
|
|
- v-model:activeName="activeName"
|
|
|
- :deviceId="id"
|
|
|
- v-if="loadedTabs.includes('7')" />
|
|
|
+ <DeviceInspectRecord v-if="activeName === 'inspect'" :device-id="deviceId" />
|
|
|
</el-tab-pane>
|
|
|
<el-tab-pane :label="t('deviceInfo.transferRecords')" name="allot">
|
|
|
- <AllotLogList
|
|
|
- ref="allotRef"
|
|
|
- v-model:activeName="activeName"
|
|
|
- :deviceId="id"
|
|
|
- v-if="loadedTabs.includes('8')" />
|
|
|
+ <DeviceAllotRecord v-if="activeName === 'allot'" :device-id="deviceId" />
|
|
|
</el-tab-pane>
|
|
|
<el-tab-pane :label="t('deviceInfo.statusChangeRecords')" name="status">
|
|
|
- <DeviceStatusLogList
|
|
|
- ref="statusRef"
|
|
|
- v-model:activeName="activeName"
|
|
|
- :deviceId="id"
|
|
|
- v-if="loadedTabs.includes('9')" />
|
|
|
+ <DeviceStatusRecord v-if="activeName === 'status'" :device-id="deviceId" />
|
|
|
</el-tab-pane>
|
|
|
<el-tab-pane :label="t('deviceInfo.RPAdjustmentRecords')" name="person">
|
|
|
- <PersonList
|
|
|
- ref="personRef"
|
|
|
- v-model:activeName="activeName"
|
|
|
- :deviceId="id"
|
|
|
- v-if="loadedTabs.includes('10')" />
|
|
|
+ <DevicePersonRecord v-if="activeName === 'person'" :device-id="deviceId" />
|
|
|
</el-tab-pane>
|
|
|
-
|
|
|
- <!-- 关联设备 -->
|
|
|
<el-tab-pane :label="t('deviceInfo.associationDevice')" name="association">
|
|
|
- <AssociationDevices
|
|
|
- ref="personRef"
|
|
|
- v-model:activeName="activeName"
|
|
|
- :deviceId="id"
|
|
|
- v-if="loadedTabs.includes('11')" />
|
|
|
+ <DeviceAssociationRecord v-if="activeName === 'association'" :device-id="deviceId" />
|
|
|
</el-tab-pane>
|
|
|
</el-tabs>
|
|
|
</ContentWrap>
|
|
|
</template>
|
|
|
-<script lang="ts" setup>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import { computed, onMounted, ref } from 'vue'
|
|
|
import { IotDeviceApi, IotDeviceVO } from '@/api/pms/device'
|
|
|
-import { DICT_TYPE, getDictLabel } from '@/utils/dict'
|
|
|
-import { formatDate } from '../../../utils/formatTime'
|
|
|
-import DeviceUpload from '@/views/pms/device/DeviceUpload.vue'
|
|
|
-import BomInfo from '@/views/pms/device/bom/BomInfo.vue'
|
|
|
-import DeviceFile from '@/views/pms/device/DeviceFile.vue'
|
|
|
-import BomList from '@/views/pms/device/bom/BomList.vue'
|
|
|
-import FailureList from '@/views/pms/device/FailureList.vue'
|
|
|
-import MaintainList from '@/views/pms/device/MaintainList.vue'
|
|
|
-import InspectList from '@/views/pms/device/InspectList.vue'
|
|
|
-import MaintenanceList from '@/views/pms/device/maintenance/MaintenanceList.vue'
|
|
|
-import AllotLogList from '@/views/pms/device/allotlog/AllotLogList.vue'
|
|
|
-import DeviceStatusLogList from '@/views/pms/device/statuslog/DeviceStatusLogList.vue'
|
|
|
-import PersonList from '@/views/pms/device/personlog/PersonList.vue'
|
|
|
-import RecordList from '@/views/pms/device/record/RecordList.vue'
|
|
|
-import AssociationDevices from '@/views/pms/device/completeSet/AssociationDevices.vue'
|
|
|
import { createImageViewer } from '@/components/ImageViewer'
|
|
|
-import { ref, onMounted } from 'vue'
|
|
|
import { getAccessToken } from '@/utils/auth'
|
|
|
+import { DICT_TYPE, getDictLabel } from '@/utils/dict'
|
|
|
+import DeviceBasicInfo from './DeviceBasicInfo.vue'
|
|
|
+import DeviceAssociationRecord from './DeviceAssociationRecord.vue'
|
|
|
+import DeviceAllotRecord from './DeviceAllotRecord.vue'
|
|
|
+import DeviceBomLibrary from './DeviceBomLibrary.vue'
|
|
|
+import DeviceFailureRecord from './DeviceFailureRecord.vue'
|
|
|
+import DeviceFileLibrary from './DeviceFileLibrary.vue'
|
|
|
+import DeviceInspectRecord from './DeviceInspectRecord.vue'
|
|
|
+import DeviceMaintenanceRecord from './DeviceMaintenanceRecord.vue'
|
|
|
+import DeviceMaintainRecord from './DeviceMaintainRecord.vue'
|
|
|
+import DeviceOperationRecord from './DeviceOperationRecord.vue'
|
|
|
+import DevicePersonRecord from './DevicePersonRecord.vue'
|
|
|
+import DeviceStatusRecord from './DeviceStatusRecord.vue'
|
|
|
+
|
|
|
+defineOptions({ name: 'DeviceInfo' })
|
|
|
+
|
|
|
+type DeviceDetail = Partial<
|
|
|
+ Omit<
|
|
|
+ IotDeviceVO,
|
|
|
+ | 'model'
|
|
|
+ | 'manDate'
|
|
|
+ | 'expires'
|
|
|
+ | 'plPrice'
|
|
|
+ | 'plDate'
|
|
|
+ | 'plYear'
|
|
|
+ | 'plStartDate'
|
|
|
+ | 'plMonthed'
|
|
|
+ | 'plAmounted'
|
|
|
+ | 'remainAmount'
|
|
|
+ | 'monthAmount'
|
|
|
+ | 'totalMonth'
|
|
|
+ >
|
|
|
+> & {
|
|
|
+ yfDeviceCode?: string
|
|
|
+ brandName?: string
|
|
|
+ assetClass?: number
|
|
|
+ assetClassName?: string
|
|
|
+ model?: string | number
|
|
|
+ responsibleNames?: string
|
|
|
+ devicePersons?: string
|
|
|
+ zzName?: string
|
|
|
+ supplierName?: string
|
|
|
+ manufacturerName?: string
|
|
|
+ manDate?: number | string | Date
|
|
|
+ expires?: number | string | Date
|
|
|
+ plDate?: number | string | Date
|
|
|
+ plStartDate?: number | string | Date
|
|
|
+ enableDate?: number | string | Date
|
|
|
+ nameplate?: string
|
|
|
+ plPrice?: number | string
|
|
|
+ plYear?: number | string
|
|
|
+ plMonthed?: number | string
|
|
|
+ plAmounted?: number | string
|
|
|
+ remainAmount?: number | string
|
|
|
+ monthAmount?: number | string
|
|
|
+ totalMonth?: number | string
|
|
|
+ currency?: string
|
|
|
+ templateJson?: string
|
|
|
+}
|
|
|
+
|
|
|
+interface TemplateField {
|
|
|
+ sort?: number
|
|
|
+ name?: string
|
|
|
+ value?: string | number | null
|
|
|
+ identifier?: string
|
|
|
+}
|
|
|
+
|
|
|
+interface MainInfoField {
|
|
|
+ prop: keyof DeviceDetail | 'assetPropertyLabel' | 'responsibleNames'
|
|
|
+ label: string
|
|
|
+ value: string
|
|
|
+ icon: string
|
|
|
+}
|
|
|
|
|
|
-const defaultPicUrl = ref(
|
|
|
+const defaultDevicePic =
|
|
|
import.meta.env.VITE_BASE_URL + '/admin-api/infra/file/29/get/IntegratedSolution.png'
|
|
|
-) // 默认设备图片
|
|
|
-
|
|
|
-defineOptions({ name: 'DeviceDetailInfo' })
|
|
|
-
|
|
|
-const { t } = useI18n() // 国际化
|
|
|
-const message = useMessage() // 消息弹窗
|
|
|
-const { params } = useRoute() // 查询参数
|
|
|
-const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
|
|
-const activeName = ref('info') // Tag 激活的窗口
|
|
|
-const list = ref([])
|
|
|
-const id = params.id
|
|
|
-const fileRef = ref() // 搜索的表单
|
|
|
-// SPU 表单数据
|
|
|
-const formData = ref({
|
|
|
- id: undefined,
|
|
|
- code: undefined,
|
|
|
- name: undefined,
|
|
|
- classification: undefined,
|
|
|
- type: undefined,
|
|
|
- nature: undefined,
|
|
|
- creditCode: undefined,
|
|
|
- tin: undefined,
|
|
|
- corporation: undefined,
|
|
|
- incorporationDate: undefined,
|
|
|
- address: undefined,
|
|
|
- bizScope: undefined,
|
|
|
- registeredCapital: undefined,
|
|
|
- annualTurnove: undefined,
|
|
|
- size: undefined,
|
|
|
- status: undefined,
|
|
|
- remark: undefined,
|
|
|
- deptName: undefined,
|
|
|
- monthAmount: undefined,
|
|
|
- totalMonth: undefined,
|
|
|
- currency: undefined
|
|
|
+
|
|
|
+const { t } = useI18n()
|
|
|
+const { params, query } = useRoute()
|
|
|
+
|
|
|
+const formLoading = ref(false)
|
|
|
+const activeName = ref('info')
|
|
|
+const defaultPicUrl = ref(defaultDevicePic)
|
|
|
+const formData = ref<DeviceDetail>({})
|
|
|
+
|
|
|
+const deviceId = computed(() => {
|
|
|
+ const id = params.id ?? query.id
|
|
|
+ const normalizedId = Array.isArray(id) ? id[0] : id
|
|
|
+ return normalizedId ? Number(normalizedId) : undefined
|
|
|
+})
|
|
|
+
|
|
|
+const formatValue = (value?: string | number | null) => {
|
|
|
+ return value || value === 0 ? String(value) : '-'
|
|
|
+}
|
|
|
+
|
|
|
+const statusLabel = computed(() => {
|
|
|
+ return (
|
|
|
+ getDictLabel(DICT_TYPE.PMS_DEVICE_STATUS, formData.value.deviceStatus) ||
|
|
|
+ formData.value.deviceStatusName ||
|
|
|
+ '-'
|
|
|
+ )
|
|
|
+})
|
|
|
+
|
|
|
+const assetPropertyLabel = computed(() => {
|
|
|
+ return getDictLabel(DICT_TYPE.PMS_ASSET_PROPERTY, formData.value.assetProperty) || '-'
|
|
|
+})
|
|
|
+
|
|
|
+const responsibleNames = computed(() => {
|
|
|
+ return formData.value.responsibleNames || formData.value.devicePersons || '-'
|
|
|
+})
|
|
|
+
|
|
|
+const templateFields = computed<TemplateField[]>(() => {
|
|
|
+ if (!formData.value.templateJson) return []
|
|
|
+
|
|
|
+ try {
|
|
|
+ const fields = JSON.parse(formData.value.templateJson)
|
|
|
+ return Array.isArray(fields) ? fields : []
|
|
|
+ } catch {
|
|
|
+ return []
|
|
|
+ }
|
|
|
})
|
|
|
-const pics = ref([])
|
|
|
-const imgSrc = ref('')
|
|
|
-const loadedTabs = ref(['info']) // 记录已加载的标签
|
|
|
|
|
|
-/** 获得详情 */
|
|
|
+const mainInfoFields = computed<MainInfoField[]>(() => [
|
|
|
+ {
|
|
|
+ prop: 'yfDeviceCode',
|
|
|
+ label: t('iotDevice.yfCode'),
|
|
|
+ value: formatValue(formData.value.yfDeviceCode),
|
|
|
+ icon: 'ep:postcard'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'deviceCode',
|
|
|
+ label: t('iotDevice.code'),
|
|
|
+ value: formatValue(formData.value.deviceCode),
|
|
|
+ icon: 'ep:ticket'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'deviceName',
|
|
|
+ label: t('iotDevice.name'),
|
|
|
+ value: formatValue(formData.value.deviceName),
|
|
|
+ icon: 'ep:cpu'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'brandName',
|
|
|
+ label: t('iotDevice.brand'),
|
|
|
+ value: formatValue(formData.value.brandName),
|
|
|
+ icon: 'ep:medal'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'deptName',
|
|
|
+ label: t('iotDevice.dept'),
|
|
|
+ value: formatValue(formData.value.deptName),
|
|
|
+ icon: 'ep:office-building'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'assetClassName',
|
|
|
+ label: t('info.deviceClass'),
|
|
|
+ value: formatValue(formData.value.assetClassName),
|
|
|
+ icon: 'ep:grid'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'deviceStatus',
|
|
|
+ label: '施工状态',
|
|
|
+ value: statusLabel.value,
|
|
|
+ icon: 'ep:operation'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'assetPropertyLabel',
|
|
|
+ label: t('iotDevice.assets'),
|
|
|
+ value: assetPropertyLabel.value,
|
|
|
+ icon: 'ep:collection-tag'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'model',
|
|
|
+ label: t('deviceForm.model'),
|
|
|
+ value: formatValue(formData.value.model),
|
|
|
+ icon: 'ep:set-up'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'responsibleNames',
|
|
|
+ label: t('devicePerson.rp'),
|
|
|
+ value: responsibleNames.value,
|
|
|
+ icon: 'ep:user'
|
|
|
+ }
|
|
|
+])
|
|
|
+
|
|
|
const getDetail = async () => {
|
|
|
- if (id) {
|
|
|
- formLoading.value = true
|
|
|
- try {
|
|
|
- const res = (await IotDeviceApi.getIotDevice(id)) as IotDeviceVO
|
|
|
- formData.value = res
|
|
|
- pics.value.push(res.picUrl)
|
|
|
- if (res) {
|
|
|
- if (res.templateJson) {
|
|
|
- list.value = JSON.parse(res.templateJson)
|
|
|
- }
|
|
|
- }
|
|
|
- if (formData.value.picUrl) {
|
|
|
- defaultPicUrl.value = formData.value.picUrl
|
|
|
- }
|
|
|
- } finally {
|
|
|
- formLoading.value = false
|
|
|
- }
|
|
|
+ if (!deviceId.value) return
|
|
|
+
|
|
|
+ formLoading.value = true
|
|
|
+ try {
|
|
|
+ const detail = (await IotDeviceApi.getIotDevice(deviceId.value)) as DeviceDetail
|
|
|
+ formData.value = detail || {}
|
|
|
+ defaultPicUrl.value = detail?.picUrl || defaultDevicePic
|
|
|
+ } finally {
|
|
|
+ formLoading.value = false
|
|
|
}
|
|
|
}
|
|
|
|
|
|
const imagePreview = (imgUrl: string) => {
|
|
|
+ if (!imgUrl) return
|
|
|
+
|
|
|
const token = getAccessToken()
|
|
|
createImageViewer({
|
|
|
urlList: [imgUrl],
|
|
|
@@ -341,16 +312,274 @@ const imagePreview = (imgUrl: string) => {
|
|
|
})
|
|
|
}
|
|
|
|
|
|
-const handleTabClick = (tab) => {
|
|
|
- if (!loadedTabs.value.includes(tab.index)) {
|
|
|
- // 这里可以添加每个标签对应的加载逻辑,如果有的话
|
|
|
- loadedTabs.value.push(tab.index)
|
|
|
+onMounted(() => {
|
|
|
+ getDetail()
|
|
|
+})
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+.device-main-wrap {
|
|
|
+ overflow: hidden;
|
|
|
+ border: 0;
|
|
|
+ border-radius: 12px;
|
|
|
+ box-shadow: 0 8px 24px rgb(31 45 61 / 7%);
|
|
|
+}
|
|
|
+
|
|
|
+.device-detail-wrap {
|
|
|
+ margin-top: 14px;
|
|
|
+ overflow: hidden;
|
|
|
+ border: 0;
|
|
|
+ border-radius: 12px;
|
|
|
+ box-shadow: 0 8px 24px rgb(31 45 61 / 6%);
|
|
|
+}
|
|
|
+
|
|
|
+.device-main {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: 292px minmax(0, 1fr);
|
|
|
+ min-height: 286px;
|
|
|
+ padding: 24px;
|
|
|
+ background: radial-gradient(circle at 18px 22px, rgb(44 123 229 / 12%), transparent 30%),
|
|
|
+ linear-gradient(135deg, #f7fbff 0%, var(--el-bg-color) 42%, #fbfcfe 100%);
|
|
|
+}
|
|
|
+
|
|
|
+.device-media {
|
|
|
+ display: flex;
|
|
|
+ align-items: stretch;
|
|
|
+ justify-content: center;
|
|
|
+}
|
|
|
+
|
|
|
+.device-image-frame {
|
|
|
+ position: relative;
|
|
|
+ width: 260px;
|
|
|
+ height: 226px;
|
|
|
+ padding: 8px;
|
|
|
+ overflow: hidden;
|
|
|
+ font: inherit;
|
|
|
+ cursor: zoom-in;
|
|
|
+ background: var(--el-bg-color);
|
|
|
+ border: 1px solid rgb(44 123 229 / 14%);
|
|
|
+ border-radius: 12px;
|
|
|
+ box-shadow: 0 14px 32px rgb(39 83 135 / 15%);
|
|
|
+ appearance: none;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ .device-image {
|
|
|
+ transform: scale(1.035);
|
|
|
+ }
|
|
|
+
|
|
|
+ .image-preview-mask {
|
|
|
+ opacity: 1;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-/** 初始化 */
|
|
|
-onMounted(async () => {
|
|
|
- await getDetail()
|
|
|
-})
|
|
|
-</script>
|
|
|
-<style scoped></style>
|
|
|
+.device-image {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ overflow: hidden;
|
|
|
+ border-radius: 8px;
|
|
|
+ transition: transform 0.3s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.device-image__empty {
|
|
|
+ display: flex;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ font-size: 34px;
|
|
|
+ color: var(--el-text-color-placeholder);
|
|
|
+ background: var(--el-fill-color-light);
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+}
|
|
|
+
|
|
|
+.image-preview-mask {
|
|
|
+ position: absolute;
|
|
|
+ right: 18px;
|
|
|
+ bottom: 18px;
|
|
|
+ display: flex;
|
|
|
+ gap: 6px;
|
|
|
+ padding: 7px 12px;
|
|
|
+ font-size: 12px;
|
|
|
+ line-height: 16px;
|
|
|
+ color: #fff;
|
|
|
+ pointer-events: none;
|
|
|
+ background: rgb(20 29 43 / 74%);
|
|
|
+ border: 1px solid rgb(255 255 255 / 22%);
|
|
|
+ border-radius: 16px;
|
|
|
+ opacity: 0.88;
|
|
|
+ backdrop-filter: blur(6px);
|
|
|
+ align-items: center;
|
|
|
+ transition: opacity 0.2s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.device-content {
|
|
|
+ min-width: 0;
|
|
|
+ padding: 2px 4px 0 30px;
|
|
|
+}
|
|
|
+
|
|
|
+.device-head {
|
|
|
+ display: flex;
|
|
|
+ gap: 24px;
|
|
|
+ min-height: 78px;
|
|
|
+ padding-bottom: 18px;
|
|
|
+ border-bottom: 1px solid var(--el-border-color-lighter);
|
|
|
+ align-items: flex-start;
|
|
|
+ justify-content: space-between;
|
|
|
+}
|
|
|
+
|
|
|
+.device-head__main {
|
|
|
+ min-width: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.device-title-row {
|
|
|
+ display: flex;
|
|
|
+ gap: 12px;
|
|
|
+ min-width: 0;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.device-title {
|
|
|
+ max-width: 760px;
|
|
|
+ margin: 0;
|
|
|
+ overflow: hidden;
|
|
|
+ font-size: 24px;
|
|
|
+ font-weight: 700;
|
|
|
+ line-height: 34px;
|
|
|
+ color: var(--el-text-color-primary);
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+}
|
|
|
+
|
|
|
+.device-status {
|
|
|
+ flex: none;
|
|
|
+ padding: 0 12px;
|
|
|
+ font-weight: 600;
|
|
|
+}
|
|
|
+
|
|
|
+.device-subtitle {
|
|
|
+ display: flex;
|
|
|
+ gap: 6px;
|
|
|
+ margin-top: 9px;
|
|
|
+ font-size: 13px;
|
|
|
+ line-height: 20px;
|
|
|
+ color: #7a5a13;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.device-info-grid {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(5, minmax(132px, 1fr));
|
|
|
+ gap: 0 22px;
|
|
|
+ padding-top: 14px;
|
|
|
+}
|
|
|
+
|
|
|
+.device-info-item {
|
|
|
+ display: flex;
|
|
|
+ min-width: 0;
|
|
|
+ min-height: 68px;
|
|
|
+ padding: 11px 0;
|
|
|
+ border-bottom: 1px dashed var(--el-border-color-lighter);
|
|
|
+ flex-direction: column;
|
|
|
+ justify-content: center;
|
|
|
+
|
|
|
+ strong {
|
|
|
+ margin-top: 7px;
|
|
|
+ overflow: hidden;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 600;
|
|
|
+ line-height: 21px;
|
|
|
+ color: var(--el-text-color-primary);
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.device-info-item__label {
|
|
|
+ display: flex;
|
|
|
+ gap: 6px;
|
|
|
+ min-width: 0;
|
|
|
+ font-size: 12px;
|
|
|
+ line-height: 18px;
|
|
|
+ color: var(--el-text-color-secondary);
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.device-tabs {
|
|
|
+ :deep(.el-tabs__header) {
|
|
|
+ padding: 0 20px;
|
|
|
+ margin: 0 -18px 18px;
|
|
|
+ background: var(--el-bg-color);
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.el-tabs__nav-wrap::after) {
|
|
|
+ height: 1px;
|
|
|
+ background-color: var(--el-border-color-lighter);
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.el-tabs__item) {
|
|
|
+ height: 54px;
|
|
|
+ padding: 0 17px;
|
|
|
+ font-weight: 500;
|
|
|
+ color: var(--el-text-color-regular);
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.el-tabs__item.is-active) {
|
|
|
+ font-weight: 600;
|
|
|
+ color: var(--el-color-primary);
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.el-tabs__active-bar) {
|
|
|
+ height: 3px;
|
|
|
+ border-radius: 3px 3px 0 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@media (width <= 1399px) {
|
|
|
+ .device-main {
|
|
|
+ grid-template-columns: 260px minmax(0, 1fr);
|
|
|
+ }
|
|
|
+
|
|
|
+ .device-image-frame {
|
|
|
+ width: 232px;
|
|
|
+ height: 208px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .device-info-grid {
|
|
|
+ grid-template-columns: repeat(4, minmax(132px, 1fr));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@media (width <= 991px) {
|
|
|
+ .device-main {
|
|
|
+ grid-template-columns: 1fr;
|
|
|
+ }
|
|
|
+
|
|
|
+ .device-content {
|
|
|
+ padding: 24px 0 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .device-info-grid {
|
|
|
+ grid-template-columns: repeat(2, minmax(132px, 1fr));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@media (width <= 575px) {
|
|
|
+ .device-main {
|
|
|
+ padding: 18px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .device-head {
|
|
|
+ gap: 12px;
|
|
|
+ flex-direction: column;
|
|
|
+ }
|
|
|
+
|
|
|
+ .device-title {
|
|
|
+ font-size: 20px;
|
|
|
+ line-height: 30px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .device-info-grid {
|
|
|
+ grid-template-columns: 1fr;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|