|
|
|
|
@ -7,10 +7,6 @@
|
|
|
|
|
</view>
|
|
|
|
|
<view class="header-content">
|
|
|
|
|
<text class="header-title">设备详情</text>
|
|
|
|
|
<text class="header-code">{{ equipmentData.code }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="status-badge" :class="'status-' + equipmentData.statusType">
|
|
|
|
|
<text>{{ equipmentData.status }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
@ -20,60 +16,190 @@
|
|
|
|
|
<view class="info-list">
|
|
|
|
|
<view class="info-row">
|
|
|
|
|
<text class="info-label">设备名称</text>
|
|
|
|
|
<text class="info-value">{{ equipmentData.name }}</text>
|
|
|
|
|
<text class="info-value">{{ getDetailField('deviceName') }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="info-row">
|
|
|
|
|
<text class="info-label">设备编号</text>
|
|
|
|
|
<text class="info-value">{{ getDetailField('deviceCode') }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="info-row">
|
|
|
|
|
<text class="info-label">设备状态</text>
|
|
|
|
|
<u-tag :text="statusLabel" :type="statusTagType" size="mini" />
|
|
|
|
|
</view>
|
|
|
|
|
<view class="info-row">
|
|
|
|
|
<text class="info-label">设备型号</text>
|
|
|
|
|
<text class="info-value">{{ getDetailField('deviceModel') }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="info-row">
|
|
|
|
|
<text class="info-label">设备规格</text>
|
|
|
|
|
<text class="info-value">{{ getDetailField('deviceSpec') }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="info-row">
|
|
|
|
|
<text class="info-label">设备类型</text>
|
|
|
|
|
<text class="info-value">{{ equipmentData.type }}</text>
|
|
|
|
|
<text class="info-value">{{ getDetailField('deviceType') }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="info-row">
|
|
|
|
|
<text class="info-label">规格型号</text>
|
|
|
|
|
<text class="info-value">{{ equipmentData.spec }}</text>
|
|
|
|
|
<text class="info-label">设备位置</text>
|
|
|
|
|
<text class="info-value">{{ getDetailField('deviceLocation') }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="info-row">
|
|
|
|
|
<text class="info-label">所属产线</text>
|
|
|
|
|
<text class="info-value">{{ equipmentData.lineName }}</text>
|
|
|
|
|
<text class="info-label">设备负责人</text>
|
|
|
|
|
<text class="info-value">{{ getDetailField('deviceManagerName') }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="info-row">
|
|
|
|
|
<text class="info-label">安装位置</text>
|
|
|
|
|
<text class="info-value">{{ equipmentData.location }}</text>
|
|
|
|
|
<text class="info-label">生产日期</text>
|
|
|
|
|
<text class="info-value">{{ productionDateLabel }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="info-row">
|
|
|
|
|
<text class="info-label">入厂日期</text>
|
|
|
|
|
<text class="info-value">{{ factoryEntryDateLabel }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<view class="info-card">
|
|
|
|
|
<view class="card-title">运行信息</view>
|
|
|
|
|
<view class="info-list">
|
|
|
|
|
<view class="info-row">
|
|
|
|
|
<text class="info-label">运行时长</text>
|
|
|
|
|
<text class="info-value highlight">{{ equipmentData.runTime }}</text>
|
|
|
|
|
<text class="info-label">创建人</text>
|
|
|
|
|
<text class="info-value">{{ getDetailField('creatorName') }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="info-row">
|
|
|
|
|
<text class="info-label">开机率</text>
|
|
|
|
|
<text class="info-value">{{ equipmentData.availability }}</text>
|
|
|
|
|
<text class="info-label">创建时间</text>
|
|
|
|
|
<text class="info-value">{{ createTimeLabel }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="info-row">
|
|
|
|
|
<text class="info-label">上次保养</text>
|
|
|
|
|
<text class="info-value">{{ equipmentData.lastMaintenance }}</text>
|
|
|
|
|
<text class="info-label">更新时间</text>
|
|
|
|
|
<text class="info-value">{{ updateTimeLabel }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="info-row">
|
|
|
|
|
<text class="info-label">下次保养</text>
|
|
|
|
|
<text class="info-value warning">{{ equipmentData.nextMaintenance }}</text>
|
|
|
|
|
<text class="info-label">备注</text>
|
|
|
|
|
<text class="info-value">{{ getDetailField('remark') }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<view class="info-card">
|
|
|
|
|
<view class="card-title">维护记录</view>
|
|
|
|
|
<view class="record-list">
|
|
|
|
|
<view v-for="(record, index) in maintenanceRecords" :key="index" class="record-item">
|
|
|
|
|
<view class="record-dot"></view>
|
|
|
|
|
<view class="record-content">
|
|
|
|
|
<text class="record-title">{{ record.title }}</text>
|
|
|
|
|
<text class="record-time">{{ record.time }}</text>
|
|
|
|
|
<view class="card-title">履历</view>
|
|
|
|
|
<view class="tabs-box">
|
|
|
|
|
<u-tabs activeColor="#1a3a5c" :list="tabList" :current="currentTab" :is-scroll="false" @change="handleTabChange" />
|
|
|
|
|
</view>
|
|
|
|
|
<view>
|
|
|
|
|
<view v-if="currentTab === 0">
|
|
|
|
|
<view v-if="!inspectionGroups.length" class="empty">暂无点检数据</view>
|
|
|
|
|
<view v-for="group in inspectionGroups" :key="group.key" class="history-group">
|
|
|
|
|
<view class="history-group-head">
|
|
|
|
|
<text class="history-group-time">[{{ group.time }}]</text>
|
|
|
|
|
<text class="history-group-operator">操作人: {{ group.operator }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view v-for="item in group.items" :key="item.key" class="history-item">
|
|
|
|
|
<view class="history-item-head">
|
|
|
|
|
<text class="result-badge" :class="'result-' + item.resultType">{{ item.resultLabel }}</text>
|
|
|
|
|
<text class="history-item-name">{{ item.name }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="history-item-body">
|
|
|
|
|
<view class="history-row">
|
|
|
|
|
<text class="history-label">点检方式</text>
|
|
|
|
|
<text class="history-value">{{ detailValue(item.method) }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="history-row">
|
|
|
|
|
<text class="history-label">判定标准</text>
|
|
|
|
|
<text class="history-value">{{ detailValue(item.criteria) }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="history-row">
|
|
|
|
|
<text class="history-label">点检时间</text>
|
|
|
|
|
<text class="history-value">{{ detailValue(item.taskTimeLabel) }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="history-row">
|
|
|
|
|
<text class="history-label">创建时间</text>
|
|
|
|
|
<text class="history-value">{{ detailValue(item.createTimeLabel) }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="history-row">
|
|
|
|
|
<text class="history-label">备注</text>
|
|
|
|
|
<text class="history-value">{{ detailValue(item.remark) }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view v-if="item.images && item.images.length" class="history-images">
|
|
|
|
|
<image v-for="img in item.images" :key="img" class="history-image" :src="img" mode="aspectFill"
|
|
|
|
|
@click="previewImages(item.images, img)" />
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="record-type" :class="'type-' + record.type">
|
|
|
|
|
<text>{{ record.typeText }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<view v-else-if="currentTab === 1">
|
|
|
|
|
<view v-if="!maintainGroups.length" class="empty">暂无保养数据</view>
|
|
|
|
|
<view v-for="group in maintainGroups" :key="group.key" class="history-group">
|
|
|
|
|
<view class="history-group-head">
|
|
|
|
|
<text class="history-group-time">[{{ group.time }}]</text>
|
|
|
|
|
<text class="history-group-operator">操作人: {{ group.operator }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view v-for="item in group.items" :key="item.key" class="history-item">
|
|
|
|
|
<view class="history-item-head">
|
|
|
|
|
<text class="result-badge" :class="'result-' + item.resultType">{{ item.resultLabel }}</text>
|
|
|
|
|
<text class="history-item-name">{{ item.name }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="history-item-body">
|
|
|
|
|
<view class="history-row">
|
|
|
|
|
<text class="history-label">保养方式</text>
|
|
|
|
|
<text class="history-value">{{ detailValue(item.method) }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="history-row">
|
|
|
|
|
<text class="history-label">判定标准</text>
|
|
|
|
|
<text class="history-value">{{ detailValue(item.criteria) }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="history-row">
|
|
|
|
|
<text class="history-label">保养时间</text>
|
|
|
|
|
<text class="history-value">{{ detailValue(item.taskTimeLabel) }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="history-row">
|
|
|
|
|
<text class="history-label">创建时间</text>
|
|
|
|
|
<text class="history-value">{{ detailValue(item.createTimeLabel) }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="history-row">
|
|
|
|
|
<text class="history-label">备注</text>
|
|
|
|
|
<text class="history-value">{{ detailValue(item.remark) }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view v-if="item.images && item.images.length" class="history-images">
|
|
|
|
|
<image v-for="img in item.images" :key="img" class="history-image" :src="img" mode="aspectFill"
|
|
|
|
|
@click="previewImages(item.images, img)" />
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<view v-else>
|
|
|
|
|
<view v-if="!repairGroups.length" class="empty">暂无维修数据</view>
|
|
|
|
|
<view v-for="group in repairGroups" :key="group.key" class="repair-group">
|
|
|
|
|
<view class="repair-group-head">
|
|
|
|
|
<text class="repair-group-name">{{ group.name }}</text>
|
|
|
|
|
<text class="repair-group-meta">共{{ group.items.length }}条</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view v-for="row in group.items" :key="row.key" class="repair-item">
|
|
|
|
|
<view class="repair-item-head">
|
|
|
|
|
<text class="repair-tag">{{ detailValue(row.subjectCode) }}</text>
|
|
|
|
|
<text class="repair-title">{{ detailValue(row.subjectName) }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="repair-item-body">
|
|
|
|
|
<view class="history-row">
|
|
|
|
|
<text class="history-label">项目内容</text>
|
|
|
|
|
<text class="history-value">{{ detailValue(row.subjectContent) }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="history-row">
|
|
|
|
|
<text class="history-label">维修结果</text>
|
|
|
|
|
<text class="history-value">
|
|
|
|
|
<text class="result-badge" :class="'result-' + row.resultType">{{ row.resultLabel }}</text>
|
|
|
|
|
</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="history-row">
|
|
|
|
|
<text class="history-label">备注</text>
|
|
|
|
|
<text class="history-value">{{ detailValue(row.remark) }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="history-row">
|
|
|
|
|
<text class="history-label">完成日期</text>
|
|
|
|
|
<text class="history-value">{{ detailValue(row.finishDateLabel) }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view v-if="row.images && row.images.length" class="history-images">
|
|
|
|
|
<image v-for="img in row.images" :key="img" class="history-image" :src="img" mode="aspectFill"
|
|
|
|
|
@click="previewImages(row.images, img)" />
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
@ -83,42 +209,305 @@
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
import { reactive, onMounted } from 'vue';
|
|
|
|
|
|
|
|
|
|
const equipmentData = reactive({
|
|
|
|
|
code: '',
|
|
|
|
|
name: '数控加工中心',
|
|
|
|
|
type: '加工设备',
|
|
|
|
|
spec: 'VMC-850',
|
|
|
|
|
lineName: '一号生产线',
|
|
|
|
|
location: 'A区-01工位',
|
|
|
|
|
status: '运行中',
|
|
|
|
|
statusType: 'running',
|
|
|
|
|
runTime: '2,560 小时',
|
|
|
|
|
availability: '92%',
|
|
|
|
|
lastMaintenance: '2024-03-01',
|
|
|
|
|
nextMaintenance: '2024-04-01'
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const maintenanceRecords = reactive([
|
|
|
|
|
{ title: '定期保养', time: '2024-03-01', type: 'maintenance', typeText: '保养' },
|
|
|
|
|
{ title: '故障维修', time: '2024-02-15', type: 'repair', typeText: '维修' },
|
|
|
|
|
{ title: '定期保养', time: '2024-01-15', type: 'maintenance', typeText: '保养' },
|
|
|
|
|
{ title: '设备调试', time: '2024-01-01', type: 'debug', typeText: '调试' }
|
|
|
|
|
]);
|
|
|
|
|
import { ref, computed, onMounted } from 'vue'
|
|
|
|
|
import {
|
|
|
|
|
getEquipmentDetail,
|
|
|
|
|
getEquipmentInspectionByDeviceId,
|
|
|
|
|
getEquipmentMaintenanceByDeviceId,
|
|
|
|
|
getEquipmentRepairListByDeviceId
|
|
|
|
|
} from '@/api/mes/equipment'
|
|
|
|
|
import { getDictLabel } from '@/utils/dict'
|
|
|
|
|
|
|
|
|
|
const loading = ref(false)
|
|
|
|
|
const deviceId = ref(undefined)
|
|
|
|
|
const detailData = ref(null)
|
|
|
|
|
const inspectionList = ref([])
|
|
|
|
|
const maintainList = ref([])
|
|
|
|
|
const repairList = ref([])
|
|
|
|
|
|
|
|
|
|
const tabList = ref([{ name: '点检履历' }, { name: '保养履历' }, { name: '维修履历' }])
|
|
|
|
|
const currentTab = ref(0)
|
|
|
|
|
|
|
|
|
|
const detailFieldCandidates = {
|
|
|
|
|
deviceName: ['deviceName', 'name'],
|
|
|
|
|
deviceCode: ['deviceCode', 'code', 'deviceNo'],
|
|
|
|
|
deviceStatus: ['deviceStatus', 'status'],
|
|
|
|
|
deviceModel: ['deviceModel', 'model'],
|
|
|
|
|
deviceSpec: ['deviceSpec', 'spec'],
|
|
|
|
|
deviceType: ['deviceTypeName', 'deviceType'],
|
|
|
|
|
deviceLocation: ['deviceLocation', 'location'],
|
|
|
|
|
deviceManagerName: ['deviceManagerName', 'managerName'],
|
|
|
|
|
productionDate: ['productionDate'],
|
|
|
|
|
factoryEntryDate: ['factoryEntryDate', 'inDate', 'entryDate'],
|
|
|
|
|
creatorName: ['creatorName', 'creator', 'createBy', 'createUserName'],
|
|
|
|
|
createTime: ['createTime'],
|
|
|
|
|
updateTime: ['updateTime'],
|
|
|
|
|
remark: ['remark', 'deviceRemark']
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getDetailField(field) {
|
|
|
|
|
const d = detailData.value
|
|
|
|
|
const candidates = detailFieldCandidates[field]
|
|
|
|
|
if (d && Array.isArray(candidates)) return detailValue(pickFirst(d, candidates))
|
|
|
|
|
return detailValue(d ? d[field] : undefined)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const statusMeta = computed(() => {
|
|
|
|
|
const d = detailData.value
|
|
|
|
|
const raw = d ? pickFirst(d, detailFieldCandidates.deviceStatus) : undefined
|
|
|
|
|
const label = getDictLabel('mes_tz_status', raw, detailValue(raw))
|
|
|
|
|
return formatStatus(label)
|
|
|
|
|
})
|
|
|
|
|
const statusLabel = computed(() => statusMeta.value.label)
|
|
|
|
|
const statusType = computed(() => statusMeta.value.type)
|
|
|
|
|
const statusTagType = computed(() => {
|
|
|
|
|
if (statusType.value === 'error') return 'error'
|
|
|
|
|
if (statusType.value === 'warning') return 'warning'
|
|
|
|
|
return 'success'
|
|
|
|
|
})
|
|
|
|
|
const productionDateLabel = computed(() => {
|
|
|
|
|
const d = detailData.value
|
|
|
|
|
return formatDateOnly(d ? pickFirst(d, detailFieldCandidates.productionDate) : undefined)
|
|
|
|
|
})
|
|
|
|
|
const factoryEntryDateLabel = computed(() => {
|
|
|
|
|
const d = detailData.value
|
|
|
|
|
return formatDateOnly(d ? pickFirst(d, detailFieldCandidates.factoryEntryDate) : undefined)
|
|
|
|
|
})
|
|
|
|
|
const createTimeLabel = computed(() => {
|
|
|
|
|
const d = detailData.value
|
|
|
|
|
return formatHistoryTime(d ? pickFirst(d, detailFieldCandidates.createTime) : undefined) || '-'
|
|
|
|
|
})
|
|
|
|
|
const updateTimeLabel = computed(() => {
|
|
|
|
|
const d = detailData.value
|
|
|
|
|
return formatHistoryTime(d ? pickFirst(d, detailFieldCandidates.updateTime) : undefined) || '-'
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const inspectionGroups = computed(() =>
|
|
|
|
|
buildStepGroups(inspectionList.value, {
|
|
|
|
|
timeFieldCandidates: ['taskTime', 'inspectionTime', 'createTime'],
|
|
|
|
|
nameFieldCandidates: ['inspectionItemName', 'name', 'itemName'],
|
|
|
|
|
resultFieldCandidates: ['inspectionResult', 'result'],
|
|
|
|
|
methodFieldCandidates: ['inspectionMethod', 'method'],
|
|
|
|
|
criteriaFieldCandidates: ['judgmentCriteria', 'criteria'],
|
|
|
|
|
imagesFieldCandidates: ['images'],
|
|
|
|
|
remarkFieldCandidates: ['remark']
|
|
|
|
|
})
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const maintainGroups = computed(() =>
|
|
|
|
|
buildStepGroups(maintainList.value, {
|
|
|
|
|
timeFieldCandidates: ['taskTime', 'inspectionTime', 'createTime'],
|
|
|
|
|
nameFieldCandidates: ['maintainItemName', 'inspectionItemName', 'name', 'itemName'],
|
|
|
|
|
resultFieldCandidates: ['maintainResult', 'inspectionResult', 'result'],
|
|
|
|
|
methodFieldCandidates: ['inspectionMethod', 'method'],
|
|
|
|
|
criteriaFieldCandidates: ['judgmentCriteria', 'criteria'],
|
|
|
|
|
imagesFieldCandidates: ['images'],
|
|
|
|
|
remarkFieldCandidates: ['remark']
|
|
|
|
|
})
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const repairGroups = computed(() => {
|
|
|
|
|
const groupsMap = new Map()
|
|
|
|
|
const rows = Array.isArray(repairList.value) ? repairList.value : []
|
|
|
|
|
for (const row of rows) {
|
|
|
|
|
const groupKey = String(
|
|
|
|
|
(row && (row.repairCode || row.repairId || row.subjectName || row.id)) ? (row.repairCode || row.repairId || row.subjectName || row.id) : '-'
|
|
|
|
|
)
|
|
|
|
|
if (!groupsMap.has(groupKey)) {
|
|
|
|
|
groupsMap.set(groupKey, {
|
|
|
|
|
key: groupKey,
|
|
|
|
|
name: String((row && (row.repairName || row.repairCode)) ? (row.repairName || row.repairCode) : groupKey),
|
|
|
|
|
items: []
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
const resultMeta = formatResult(row ? (row.repairResult !== undefined ? row.repairResult : row.result) : undefined)
|
|
|
|
|
groupsMap.get(groupKey).items.push({
|
|
|
|
|
key: String(row && row.id !== undefined && row.id !== null ? row.id : `${groupKey}_${Math.random()}`),
|
|
|
|
|
subjectCode: row ? row.subjectCode : undefined,
|
|
|
|
|
subjectName: row ? row.subjectName : undefined,
|
|
|
|
|
subjectContent: row ? row.subjectContent : undefined,
|
|
|
|
|
remark: row ? row.remark : undefined,
|
|
|
|
|
finishDateLabel: formatDateOnly(row ? row.finishDate : undefined),
|
|
|
|
|
resultLabel: resultMeta.label,
|
|
|
|
|
resultType: resultMeta.type,
|
|
|
|
|
images: parseImages(row ? (row.malfunctionImages || row.malfunctionUrl || row.images) : undefined)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
return Array.from(groupsMap.values())
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
function handleTabChange(e) {
|
|
|
|
|
const idx = e && typeof e === 'object' ? e.index : e
|
|
|
|
|
currentTab.value = Number(idx === 0 ? 0 : idx || 0)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function goBack() {
|
|
|
|
|
uni.navigateBack();
|
|
|
|
|
uni.navigateBack()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
const pages = getCurrentPages();
|
|
|
|
|
const currentPage = pages[pages.length - 1];
|
|
|
|
|
const options = currentPage.options || {};
|
|
|
|
|
if (options.code) {
|
|
|
|
|
equipmentData.code = decodeURIComponent(options.code);
|
|
|
|
|
const pages = getCurrentPages()
|
|
|
|
|
const currentPage = pages[pages.length - 1]
|
|
|
|
|
const options = currentPage && currentPage.options ? currentPage.options : {}
|
|
|
|
|
const rawId = options.id !== undefined ? options.id : options.code
|
|
|
|
|
const decoded = rawId ? decodeURIComponent(String(rawId)) : ''
|
|
|
|
|
deviceId.value = decoded ? decoded : undefined
|
|
|
|
|
fetchAll()
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
async function fetchAll() {
|
|
|
|
|
if (!deviceId.value) {
|
|
|
|
|
uni.showToast({ title: '缺少设备ID', icon: 'none' })
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
loading.value = true
|
|
|
|
|
try {
|
|
|
|
|
const [detailRes, inspectionRes, maintainRes, repairRes] = await Promise.all([
|
|
|
|
|
getEquipmentDetail(deviceId.value),
|
|
|
|
|
getEquipmentInspectionByDeviceId(deviceId.value),
|
|
|
|
|
getEquipmentMaintenanceByDeviceId(deviceId.value),
|
|
|
|
|
getEquipmentRepairListByDeviceId(deviceId.value)
|
|
|
|
|
])
|
|
|
|
|
detailData.value = normalizeDetail(detailRes)
|
|
|
|
|
inspectionList.value = normalizeList(inspectionRes)
|
|
|
|
|
maintainList.value = normalizeList(maintainRes)
|
|
|
|
|
repairList.value = normalizeList(repairRes)
|
|
|
|
|
} catch (e) {
|
|
|
|
|
uni.showToast({ title: '加载失败', icon: 'none' })
|
|
|
|
|
} finally {
|
|
|
|
|
loading.value = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function normalizeDetail(res) {
|
|
|
|
|
const data = res && res.data !== undefined ? res.data : res
|
|
|
|
|
if (data && !Array.isArray(data) && data.data && !Array.isArray(data.data)) return data.data
|
|
|
|
|
if (data && !Array.isArray(data)) return data
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function normalizeList(res) {
|
|
|
|
|
const data = res && res.data !== undefined ? res.data : res
|
|
|
|
|
if (Array.isArray(data)) return data
|
|
|
|
|
if (data && Array.isArray(data.data)) return data.data
|
|
|
|
|
if (data && data.data && Array.isArray(data.data.list)) return data.data.list
|
|
|
|
|
if (data && data.data && Array.isArray(data.data.rows)) return data.data.rows
|
|
|
|
|
if (data && data.data && Array.isArray(data.data.records)) return data.data.records
|
|
|
|
|
if (data && Array.isArray(data.list)) return data.list
|
|
|
|
|
if (data && Array.isArray(data.rows)) return data.rows
|
|
|
|
|
if (data && Array.isArray(data.records)) return data.records
|
|
|
|
|
return []
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function detailValue(v) {
|
|
|
|
|
if (v === 0) return '0'
|
|
|
|
|
if (v === false) return '否'
|
|
|
|
|
if (v === true) return '是'
|
|
|
|
|
if (v === null || v === undefined) return '-'
|
|
|
|
|
const s = String(v).trim()
|
|
|
|
|
return s ? s : '-'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function formatStatus(v) {
|
|
|
|
|
const raw = v === null || v === undefined ? '' : String(v).trim()
|
|
|
|
|
const upper = raw.toUpperCase()
|
|
|
|
|
if (!raw) return { label: '-', type: 'normal' }
|
|
|
|
|
if (raw === '1' || raw === '0' || upper === 'OK' || raw.includes('正常') || raw.includes('运行')) return { label: raw, type: 'normal' }
|
|
|
|
|
if (raw === '2' || upper === 'NG' || raw.includes('停') || raw.includes('禁') || raw.includes('坏') || raw.includes('修'))
|
|
|
|
|
return { label: raw, type: 'error' }
|
|
|
|
|
return { label: raw, type: 'warning' }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function formatResult(v) {
|
|
|
|
|
const raw = v === null || v === undefined ? '' : String(v).trim()
|
|
|
|
|
const upper = raw.toUpperCase()
|
|
|
|
|
if (!raw) return { label: '-', type: 'info' }
|
|
|
|
|
if (raw === '0') return { label: '待检测', type: 'info' }
|
|
|
|
|
if (raw === '1' || upper === 'OK') return { label: '通过', type: 'success' }
|
|
|
|
|
if (raw === '2' || upper === 'NG') return { label: '不通过', type: 'danger' }
|
|
|
|
|
return { label: raw, type: 'info' }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function formatHistoryTime(value) {
|
|
|
|
|
if (!value) return ''
|
|
|
|
|
if (Array.isArray(value) && value.length >= 3) {
|
|
|
|
|
const [y, m, d, hh, mm, ss] = value
|
|
|
|
|
const pad = (n) => String(n).padStart(2, '0')
|
|
|
|
|
if (hh !== undefined) return `${y}-${pad(m)}-${pad(d)} ${pad(hh)}:${pad(mm)}:${pad(ss)}`
|
|
|
|
|
return `${y}-${pad(m)}-${pad(d)}`
|
|
|
|
|
}
|
|
|
|
|
const s = String(value).trim()
|
|
|
|
|
if (!s) return ''
|
|
|
|
|
const num = Number(s)
|
|
|
|
|
if (Number.isFinite(num)) {
|
|
|
|
|
const ms = s.length === 10 ? num * 1000 : num
|
|
|
|
|
const d = new Date(ms)
|
|
|
|
|
if (!Number.isNaN(d.getTime())) return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')} ${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}:${String(d.getSeconds()).padStart(2, '0')}`
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
const d = new Date(s)
|
|
|
|
|
if (!Number.isNaN(d.getTime())) return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')} ${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}:${String(d.getSeconds()).padStart(2, '0')}`
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function formatDateOnly(value) {
|
|
|
|
|
const t = formatHistoryTime(value)
|
|
|
|
|
if (!t) return '-'
|
|
|
|
|
return String(t).split(' ')[0]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function parseImages(value) {
|
|
|
|
|
if (!value) return []
|
|
|
|
|
if (Array.isArray(value)) return value.map(String).filter(Boolean)
|
|
|
|
|
const cleaned = String(value).replace(/[`'"]/g, '').trim()
|
|
|
|
|
return cleaned
|
|
|
|
|
.split(',')
|
|
|
|
|
.map((v) => v.trim())
|
|
|
|
|
.filter(Boolean)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function pickFirst(obj, keys) {
|
|
|
|
|
for (const k of keys) {
|
|
|
|
|
if (obj && obj[k] !== undefined && obj[k] !== null && String(obj[k]).trim() !== '') return obj[k]
|
|
|
|
|
}
|
|
|
|
|
return undefined
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function buildStepGroups(rows, options) {
|
|
|
|
|
const groupsMap = new Map()
|
|
|
|
|
const list = Array.isArray(rows) ? rows : []
|
|
|
|
|
for (const row of list) {
|
|
|
|
|
const time = formatHistoryTime(pickFirst(row, options.timeFieldCandidates) || (row ? row.createTime : undefined))
|
|
|
|
|
const operator = detailValue(row ? (row.operator || row.creatorName || row.creator) : undefined)
|
|
|
|
|
const managementId = row && row.managementId !== undefined && row.managementId !== null ? row.managementId : ''
|
|
|
|
|
const groupKey = `${managementId}__${time}__${operator}`
|
|
|
|
|
const name = pickFirst(row, options.nameFieldCandidates) || '-'
|
|
|
|
|
const resultMeta = formatResult(pickFirst(row, options.resultFieldCandidates))
|
|
|
|
|
const item = {
|
|
|
|
|
key: String(row && row.id !== undefined && row.id !== null ? row.id : `${groupKey}_${String(name)}`),
|
|
|
|
|
name: detailValue(name),
|
|
|
|
|
resultLabel: resultMeta.label,
|
|
|
|
|
resultType: resultMeta.type,
|
|
|
|
|
method: pickFirst(row, options.methodFieldCandidates),
|
|
|
|
|
criteria: pickFirst(row, options.criteriaFieldCandidates),
|
|
|
|
|
remark: pickFirst(row, options.remarkFieldCandidates),
|
|
|
|
|
images: parseImages(pickFirst(row, options.imagesFieldCandidates)),
|
|
|
|
|
taskTimeLabel: formatHistoryTime(row ? (row.taskTime || row.inspectionTime) : undefined),
|
|
|
|
|
createTimeLabel: formatHistoryTime(row ? row.createTime : undefined)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!groupsMap.has(groupKey)) {
|
|
|
|
|
groupsMap.set(groupKey, { key: groupKey, time: time || '-', operator, items: [item] })
|
|
|
|
|
} else {
|
|
|
|
|
groupsMap.get(groupKey).items.push(item)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return Array.from(groupsMap.values())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function previewImages(list, current) {
|
|
|
|
|
if (!list || !list.length) return
|
|
|
|
|
uni.previewImage({ urls: list, current })
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
@ -137,17 +526,17 @@ onMounted(() => {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
margin-bottom: 20rpx;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
&:active {
|
|
|
|
|
opacity: 0.7;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.back-icon {
|
|
|
|
|
font-size: 48rpx;
|
|
|
|
|
color: #ffffff;
|
|
|
|
|
margin-right: 8rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.back-text {
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
color: #ffffff;
|
|
|
|
|
@ -155,6 +544,10 @@ onMounted(() => {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.header-content {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
|
|
|
|
|
.header-title {
|
|
|
|
|
display: block;
|
|
|
|
|
font-size: 44rpx;
|
|
|
|
|
@ -162,48 +555,17 @@ onMounted(() => {
|
|
|
|
|
color: #ffffff;
|
|
|
|
|
margin-bottom: 16rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.header-code {
|
|
|
|
|
display: block;
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
color: rgba(255, 255, 255, 0.7);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.status-badge {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 60rpx;
|
|
|
|
|
right: 30rpx;
|
|
|
|
|
padding: 12rpx 24rpx;
|
|
|
|
|
border-radius: 24rpx;
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.status-running {
|
|
|
|
|
background: rgba(24, 188, 55, 0.2);
|
|
|
|
|
color: #18bc37;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.status-stop {
|
|
|
|
|
background: rgba(255, 77, 79, 0.2);
|
|
|
|
|
color: #ff4d4f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.status-maintain {
|
|
|
|
|
background: rgba(255, 140, 0, 0.2);
|
|
|
|
|
color: #ff8c00;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.content-section {
|
|
|
|
|
padding: 0 30rpx 30rpx;
|
|
|
|
|
margin-top: -40rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.info-card {
|
|
|
|
|
background: #ffffff;
|
|
|
|
|
border-radius: 20rpx;
|
|
|
|
|
padding: 30rpx;
|
|
|
|
|
margin-bottom: 24rpx;
|
|
|
|
|
margin-top: 24rpx;
|
|
|
|
|
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -227,7 +589,7 @@ onMounted(() => {
|
|
|
|
|
align-items: center;
|
|
|
|
|
padding: 20rpx 0;
|
|
|
|
|
border-bottom: 1rpx solid #f5f7fa;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
&:last-child {
|
|
|
|
|
border-bottom: none;
|
|
|
|
|
}
|
|
|
|
|
@ -241,77 +603,163 @@ onMounted(() => {
|
|
|
|
|
.info-value {
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
color: #333333;
|
|
|
|
|
|
|
|
|
|
&.highlight {
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
color: #1a3a5c;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&.warning {
|
|
|
|
|
color: #ff8c00;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.record-list {
|
|
|
|
|
.tabs-box {
|
|
|
|
|
margin-bottom: 24rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.empty {
|
|
|
|
|
padding: 30rpx 0;
|
|
|
|
|
text-align: center;
|
|
|
|
|
color: #999;
|
|
|
|
|
font-size: 26rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.history-group {
|
|
|
|
|
margin-bottom: 24rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.history-group-head {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
align-items: center;
|
|
|
|
|
margin-bottom: 16rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.history-group-time {
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
color: #1a3a5c;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.history-group-operator {
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
color: #666;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.history-item {
|
|
|
|
|
border: 1rpx solid #eef1f5;
|
|
|
|
|
border-radius: 12rpx;
|
|
|
|
|
padding: 16rpx;
|
|
|
|
|
margin-bottom: 12rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.record-item {
|
|
|
|
|
.history-item-head {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
padding: 24rpx 0;
|
|
|
|
|
border-bottom: 1rpx solid #f5f7fa;
|
|
|
|
|
|
|
|
|
|
&:last-child {
|
|
|
|
|
border-bottom: none;
|
|
|
|
|
}
|
|
|
|
|
gap: 12rpx;
|
|
|
|
|
margin-bottom: 12rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.record-dot {
|
|
|
|
|
width: 16rpx;
|
|
|
|
|
height: 16rpx;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
background: #1a3a5c;
|
|
|
|
|
margin-right: 20rpx;
|
|
|
|
|
.history-item-name {
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
color: #333;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.record-content {
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
|
|
|
|
.record-title {
|
|
|
|
|
display: block;
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
color: #333333;
|
|
|
|
|
margin-bottom: 8rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.record-time {
|
|
|
|
|
display: block;
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
color: #999999;
|
|
|
|
|
}
|
|
|
|
|
.history-item-body {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 8rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.record-type {
|
|
|
|
|
padding: 8rpx 16rpx;
|
|
|
|
|
border-radius: 8rpx;
|
|
|
|
|
.history-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
gap: 20rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.history-label {
|
|
|
|
|
color: #999;
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.history-value {
|
|
|
|
|
color: #333;
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
text-align: right;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.history-images {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
gap: 12rpx;
|
|
|
|
|
margin-top: 10rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.history-image {
|
|
|
|
|
width: 120rpx;
|
|
|
|
|
height: 120rpx;
|
|
|
|
|
border-radius: 10rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.result-badge {
|
|
|
|
|
font-size: 22rpx;
|
|
|
|
|
padding: 4rpx 10rpx;
|
|
|
|
|
border-radius: 8rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.type-maintenance {
|
|
|
|
|
background: rgba(24, 188, 55, 0.1);
|
|
|
|
|
.result-info {
|
|
|
|
|
background: rgba(144, 147, 153, 0.15);
|
|
|
|
|
color: #606266;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.result-success {
|
|
|
|
|
background: rgba(24, 188, 55, 0.15);
|
|
|
|
|
color: #18bc37;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.type-repair {
|
|
|
|
|
background: rgba(255, 77, 79, 0.1);
|
|
|
|
|
.result-danger {
|
|
|
|
|
background: rgba(255, 77, 79, 0.15);
|
|
|
|
|
color: #ff4d4f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.type-debug {
|
|
|
|
|
background: rgba(74, 144, 194, 0.1);
|
|
|
|
|
color: #4a90c2;
|
|
|
|
|
.repair-group {
|
|
|
|
|
margin-bottom: 20rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.repair-group-head {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
margin-bottom: 12rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.repair-group-name {
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
color: #1a3a5c;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.repair-group-meta {
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
color: #999;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.repair-item {
|
|
|
|
|
border: 1rpx solid #eef1f5;
|
|
|
|
|
border-radius: 12rpx;
|
|
|
|
|
padding: 16rpx;
|
|
|
|
|
margin-bottom: 12rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.repair-item-head {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 12rpx;
|
|
|
|
|
margin-bottom: 12rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.repair-tag {
|
|
|
|
|
font-size: 22rpx;
|
|
|
|
|
color: #666;
|
|
|
|
|
background: #f5f7fa;
|
|
|
|
|
border-radius: 8rpx;
|
|
|
|
|
padding: 4rpx 10rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.repair-title {
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
color: #333;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
|