设备台账点检记录及保养记录及保修记录修改

master
liutao 2 weeks ago
parent 7a87f2178a
commit c67384ef79

@ -390,7 +390,6 @@ export default {
totalSuffix: '',
confirmDeleteHistory: 'Confirm delete this history record?',
operateTime: 'Operate Time',
operator: 'Operator'
},
moldInspectionItems: {
moduleName: 'Inspection Items',
@ -914,9 +913,17 @@ export default {
confirmDeleteContent: 'Confirm delete equipment [{name}]?',
checkHistory: 'Inspection',
maintainHistory: 'Maintenance',
maintainMethod: 'Maintenance Method',
maintainTime: 'Maintenance Time',
repairHistory: 'Repair',
repairProject: 'Repair Project',
projectContent: 'Project Content',
finishTime: 'Finish Time',
operator: 'Operator',
noHistoryData: 'No history data',
inspectionMethod: 'Inspection Method',
criteria: 'Judgment Criteria',
inspectionTime: 'Inspection Time',
resultPass: 'Pass',
resultFail: 'Fail',
repairPending: 'Pending',

@ -390,7 +390,6 @@ export default {
totalSuffix: ' 条',
confirmDeleteHistory: '确认删除该历史记录吗?',
operateTime: '操作时间',
operator: '操作人'
},
moldInspectionItems: {
moduleName: '点检项库',
@ -917,9 +916,17 @@ export default {
confirmDeleteContent: '确认删除设备【{name}】吗?',
checkHistory: '点检记录',
maintainHistory: '保养记录',
maintainMethod: '保养方法',
maintainTime: '保养时间',
repairHistory: '报修记录',
repairProject: '维修项目',
projectContent: '项目内容',
finishTime: '完成时间',
operator: '操作人',
noHistoryData: '暂无历史记录',
inspectionMethod: '点检方法',
criteria: '判定标准',
inspectionTime: '点检时间',
resultPass: '合格',
resultFail: '不合格',
repairPending: '待处理',
@ -1406,7 +1413,6 @@ export default {
purchaseUnit: '采购单位',
convertRatio: '换算关系',
defaultWarehouse: '默认仓库/库区',
selectSparepart: '选择备件',
selectedSpareparts: '已选备件',
noSelectedSparepart: '请扫码或选择备件',
alreadySelected: '该备件已添加',

@ -62,7 +62,7 @@
<text class="info-label">{{ t('equipmentLedger.factoryEntryDate') }}</text>
<text class="info-value">{{ formatDateValue(detailData?.factoryEntryDate) }}</text>
</view>
<view class="info-row">
<view class="info-row">
<text class="info-label">{{ t('equipmentLedger.productionDate') }}</text>
<text class="info-value">{{ formatDateValue(detailData?.outgoingTime) }}</text>
</view>
@ -108,45 +108,65 @@
<!-- 点检历史 -->
<view v-if="activeTab === 'check'" class="tab-content">
<view v-if="!inspectionList.length" class="empty-tip">{{ t('equipmentLedger.noHistoryData') }}</view>
<view v-for="(item, idx) in inspectionList" :key="idx" class="history-item">
<view class="history-header">
<text class="history-time">{{ formatDateTime(item.inspectionTime) }}</text>
<text :class="['history-result', getResultClass(item.inspectionResult)]">{{ getResultText(item.inspectionResult) }}</text>
<view v-if="inspectionLoading" class="empty-tip">{{ t('functionCommon.loading') }}</view>
<view v-else-if="!inspectionGroups.length" class="empty-tip">{{ t('equipmentLedger.noHistoryData') }}</view>
<view v-for="group in inspectionGroups" :key="group.key" class="record-card">
<view class="timeline-meta">
<text class="timeline-time">{{ group.time }}</text>
<text class="timeline-operator">{{ group.operator }}</text>
</view>
<view class="history-body">
<text class="history-name">{{ item.inspectionItemName || item.name || '-' }}</text>
<text class="history-operator">{{ t('equipmentLedger.operator') }}: {{ item.operatorName || item.inspectorName || '-' }}</text>
<view v-for="item in group.items" :key="item.key" class="history-item">
<view class="history-title-row">
<text class="history-item-name">{{ item.name }}</text>
<text :class="['result-badge', 'result-' + item.resultType]">{{ item.resultLabel }}</text>
</view>
<view class="record-row"><text class="record-label">{{ t('equipmentLedger.inspectionMethod') }}</text><text class="record-value">{{ item.method || '-' }}</text></view>
<view class="record-row"><text class="record-label">{{ t('equipmentLedger.criteria') }}</text><text class="record-value">{{ item.criteria || '-' }}</text></view>
<view class="record-row"><text class="record-label">{{ t('equipmentLedger.inspectionTime') }}</text><text class="record-value">{{ item.taskTimeLabel || '-' }}</text></view>
<view class="record-row"><text class="record-label">{{ t('equipmentLedger.remark') }}</text><text class="record-value">{{ 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 v-if="activeTab === 'maintain'" class="tab-content">
<view v-if="!maintainList.length" class="empty-tip">{{ t('equipmentLedger.noHistoryData') }}</view>
<view v-for="(item, idx) in maintainList" :key="idx" class="history-item">
<view class="history-header">
<text class="history-time">{{ formatDateTime(item.maintainTime || item.inspectionTime) }}</text>
<text :class="['history-result', getResultClass(item.maintainResult || item.inspectionResult)]">{{ getResultText(item.maintainResult || item.inspectionResult) }}</text>
<view v-if="maintainLoading" class="empty-tip">{{ t('functionCommon.loading') }}</view>
<view v-else-if="!maintainGroups.length" class="empty-tip">{{ t('equipmentLedger.noHistoryData') }}</view>
<view v-for="group in maintainGroups" :key="group.key" class="record-card">
<view class="timeline-meta">
<text class="timeline-time">{{ group.time }}</text>
<text class="timeline-operator">{{ group.operator }}</text>
</view>
<view class="history-body">
<text class="history-name">{{ item.maintainItemName || item.inspectionItemName || item.name || '-' }}</text>
<text class="history-operator">{{ t('equipmentLedger.operator') }}: {{ item.operatorName || item.inspectorName || '-' }}</text>
<view v-for="item in group.items" :key="item.key" class="history-item">
<view class="history-title-row">
<text class="history-item-name">{{ item.name }}</text>
<text :class="['result-badge', 'result-' + item.resultType]">{{ item.resultLabel }}</text>
</view>
<view class="record-row"><text class="record-label">{{ t('equipmentLedger.maintainMethod') }}</text><text class="record-value">{{ item.method || '-' }}</text></view>
<view class="record-row"><text class="record-label">{{ t('equipmentLedger.criteria') }}</text><text class="record-value">{{ item.criteria || '-' }}</text></view>
<view class="record-row"><text class="record-label">{{ t('equipmentLedger.maintainTime') }}</text><text class="record-value">{{ item.taskTimeLabel || '-' }}</text></view>
<view class="record-row"><text class="record-label">{{ t('equipmentLedger.remark') }}</text><text class="record-value">{{ 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 v-if="activeTab === 'repair'" class="tab-content">
<view v-if="!repairList.length" class="empty-tip">{{ t('equipmentLedger.noHistoryData') }}</view>
<view v-for="(item, idx) in repairList" :key="idx" class="history-item">
<view class="history-header">
<text class="history-time">{{ formatDateTime(item.createTime) }}</text>
<text :class="['history-result', getRepairStatusClass(item.status)]">{{ getRepairStatusText(item.status) }}</text>
<view v-if="repairLoading" class="empty-tip">{{ t('functionCommon.loading') }}</view>
<view v-else-if="!repairRecords.length" class="empty-tip">{{ t('equipmentLedger.noHistoryData') }}</view>
<view v-for="row in repairRecords" :key="row.key" class="record-card">
<view class="record-head">
<text class="record-title">{{ row.repairNo || row.description || '-' }}</text>
<text class="result-badge" :class="'result-' + row.resultType">{{ row.resultLabel }}</text>
</view>
<view class="history-body">
<text class="history-name">{{ item.repairNo || item.description || '-' }}</text>
<text class="history-operator">{{ t('equipmentLedger.operator') }}: {{ item.creatorName || '-' }}</text>
<view class="record-row"><text class="record-label">{{ t('equipmentLedger.repairProject') }}</text><text class="record-value">{{ row.subjectName || row.subjectCode || '-' }}</text></view>
<view class="record-row"><text class="record-label">{{ t('equipmentLedger.projectContent') }}</text><text class="record-value">{{ row.subjectContent || '-' }}</text></view>
<view class="record-row"><text class="record-label">{{ t('equipmentLedger.finishTime') }}</text><text class="record-value">{{ row.finishDateLabel || '-' }}</text></view>
<view class="record-row"><text class="record-label">{{ t('equipmentLedger.remark') }}</text><text class="record-value">{{ row.remark || '-' }}</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>
@ -174,6 +194,59 @@ const detailData = ref(null)
const deviceTypeList = ref([])
const activeTab = ref('check')
const inspectionList = ref([])
const inspectionLoading = ref(false)
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 maintainLoading = ref(false)
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 repairLoading = ref(false)
const repairRecords = computed(() => {
const rows = Array.isArray(repairList.value) ? repairList.value : []
return rows.map((row, index) => {
const resultMeta = formatRepairResult(row?.status)
return {
...row,
key: String(row?.id ?? row?.repairCode ?? index),
finishDateLabel: formatDateTime(row?.finishDate || row?.createTime),
resultLabel: resultMeta.label,
resultType: resultMeta.type,
images: parseImages(row?.malfunctionUrl || row?.malfunctionImages || row?.images)
}
})
})
function formatRepairResult(value) {
const raw = value === null || value === undefined ? '' : String(value).trim()
if (raw === '2') return { label: t('equipmentLedger.repairCompleted'), type: 'success' }
if (raw === '1') return { label: t('equipmentLedger.repairProcessing'), type: 'warning' }
if (raw === '0') return { label: t('equipmentLedger.repairPending'), type: 'info' }
return { label: textValue(value), type: 'info' }
}
const maintainList = ref([])
const repairList = ref([])
const statusUpdating = ref(false)
@ -256,29 +329,44 @@ function normalizeDetail(res) {
}
async function fetchInspectionHistory() {
if (!detailId.value) return
inspectionLoading.value = true
try {
const res = await request({ url: '/admin-api/mes/ticket-management/getInspectionByDeviceId', method: 'get', params: { deviceId: detailId.value } })
const data = res && res.data !== undefined ? res.data : res
inspectionList.value = Array.isArray(data) ? data : []
} catch (e) { inspectionList.value = [] }
inspectionList.value = normalizeListData(data)
} catch (e) {
inspectionList.value = []
} finally {
inspectionLoading.value = false
}
}
async function fetchMaintainHistory() {
if (!detailId.value) return
maintainLoading.value = true
try {
const res = await request({ url: '/admin-api/mes/ticket-management/getMaintenanceByDeviceId', method: 'get', params: { deviceId: detailId.value } })
const data = res && res.data !== undefined ? res.data : res
maintainList.value = Array.isArray(data) ? data : []
} catch (e) { maintainList.value = [] }
maintainList.value = normalizeListData(data)
} catch (e) {
maintainList.value = []
} finally {
maintainLoading.value = false
}
}
async function fetchRepairHistory() {
if (!detailId.value) return
repairLoading.value = true
try {
const res = await request({ url: '/admin-api/mes/dv-repair/getRepairListByDeviceId', method: 'get', params: { deviceId: detailId.value } })
const data = res && res.data !== undefined ? res.data : res
repairList.value = Array.isArray(data) ? data : []
} catch (e) { repairList.value = [] }
repairList.value = normalizeListData(data)
} catch (e) {
repairList.value = []
} finally {
repairLoading.value = false
}
}
function switchTab(key) {
activeTab.value = key
}
@ -321,6 +409,68 @@ async function onStatusChange(e) {
}
}
function normalizeListData(res) {
const root = res && res.data !== undefined ? res.data : res
if (Array.isArray(root?.records)) return root.records
if (Array.isArray(root?.data?.list)) return root.data.list
if (Array.isArray(root?.data?.rows)) return root.data.rows
if (Array.isArray(root?.data?.records)) return root.data.records
if (Array.isArray(root)) return root
return []
}
function pickFirst(row, candidates) {
if (!row || typeof row !== 'object') return undefined
for (const key of candidates) {
const v = row[key]
if (v !== undefined && v !== null && v !== '') return v
}
return undefined
}
function parseImages(value) {
if (!value) return []
if (Array.isArray(value)) return value.map(String).filter(Boolean)
return String(value).split(',').map(s => s.trim()).filter(Boolean)
}
function buildStepGroups(list, options) {
if (!Array.isArray(list) || !list.length) return []
const groupsMap = new Map()
list.forEach((row, index) => {
const operator = pickFirst(row, ['inspectorName', 'operatorName', 'creatorName', 'createByName']) || '-'
const time = pickFirst(row, options.timeFieldCandidates)
const groupKey = time ? String(time) + '_' + operator : 'g_' + index
const resultRaw = pickFirst(row, options.resultFieldCandidates)
const resultType = (resultRaw === 'PASS' || resultRaw === 'pass' || resultRaw === 'OK' || resultRaw === 'ok' || resultRaw === 1 || resultRaw === '1') ? 'pass' :
(resultRaw === 'FAIL' || resultRaw === 'fail' || resultRaw === 'NG' || resultRaw === 'ng' || resultRaw === 0 || resultRaw === '0') ? 'fail' : 'unknown'
const resultLabel = resultType === 'pass' ? t('equipmentLedger.resultPass') : resultType === 'fail' ? t('equipmentLedger.resultFail') : String(resultRaw || '-')
const item = {
key: 'i_' + index + '_' + (row.id || index),
name: pickFirst(row, options.nameFieldCandidates) || '-',
resultType,
resultLabel,
method: pickFirst(row, options.methodFieldCandidates),
criteria: pickFirst(row, options.criteriaFieldCandidates),
remark: pickFirst(row, options.remarkFieldCandidates),
images: parseImages(pickFirst(row, options.imagesFieldCandidates)),
taskTimeLabel: formatDateTime(time)
}
if (!groupsMap.has(groupKey)) {
groupsMap.set(groupKey, { key: groupKey, time: formatDateTime(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 })
}
function getResultText(result) {
if (result === 'PASS' || result === 'pass' || result === 'OK' || result === 'ok' || result === 1 || result === '1') return t('equipmentLedger.resultPass')
if (result === 'FAIL' || result === 'fail' || result === 'NG' || result === 'ng' || result === 0 || result === '0') return t('equipmentLedger.resultFail')
@ -422,14 +572,86 @@ function formatDateTime(value) {
.tab-text { font-size: 28rpx; color: #8a9099; }
.tab-item.active .tab-text { color: #1a3a5c; font-weight: 700; }
.tab-item.active::after { content: ''; position: absolute; bottom: -2rpx; left: 30%; right: 30%; height: 4rpx; background: #1a3a5c; border-radius: 2rpx; }
.record-card {
margin-bottom: 20rpx;
padding: 24rpx;
border-radius: 22rpx;
background: linear-gradient(180deg, #ffffff 0%, #fbfcfe 100%);
border: 1rpx solid #edf2f7;
}
.timeline-meta {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16rpx;
margin-bottom: 18rpx;
}
.timeline-time {
font-size: 28rpx;
color: #1f4f81;
font-weight: 700;
}
.timeline-operator {
font-size: 24rpx;
color: #64748b;
}
.history-title-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12rpx;
margin-bottom: 10rpx;
}
.history-item-name {
font-size: 28rpx;
color: #1f2937;
font-weight: 600;
flex: 1;
min-width: 0;
}
.result-badge {
font-size: 22rpx;
font-weight: 700;
padding: 4rpx 14rpx;
border-radius: 8rpx;
flex-shrink: 0;
}
.result-pass { color: #15803d; background: #dcfce7; }
.result-fail { color: #b91c1c; background: #fee2e2; }
.result-unknown { color: #92400e; background: #fef3c7; }
.record-row {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 20rpx;
margin-bottom: 10rpx;
}
.record-label {
font-size: 24rpx;
color: #94a3b8;
flex-shrink: 0;
min-width: 120rpx;
}
.record-value {
flex: 1;
font-size: 27rpx;
color: #1f2937;
font-weight: 500;
line-height: 1.4;
text-align: right;
}
.history-images {
display: flex;
flex-wrap: wrap;
gap: 12rpx;
margin-top: 14rpx;
}
.history-image {
width: 150rpx;
height: 150rpx;
border-radius: 14rpx;
background: #edf2f7;
}
.tab-content { min-height: 200rpx; }
.empty-tip { text-align: center; padding: 40rpx 0; color: #99a1aa; font-size: 26rpx; }
.history-item { padding: 20rpx 0; border-bottom: 1rpx solid #f0f2f5; }
.history-item:last-child { border-bottom: none; }
.history-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10rpx; }
.history-time { font-size: 24rpx; color: #8a9099; }
.history-result { font-size: 26rpx; font-weight: 600; }
.history-body { display: flex; justify-content: space-between; align-items: center; }
.history-name { font-size: 28rpx; color: #30363d; }
.history-operator { font-size: 24rpx; color: #8a9099; }
</style>

Loading…
Cancel
Save