|
|
|
|
@ -94,59 +94,119 @@
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<!-- 历史记录Tabs -->
|
|
|
|
|
<view class="info-card">
|
|
|
|
|
<view class="tab-bar">
|
|
|
|
|
<view
|
|
|
|
|
v-for="tab in tabs"
|
|
|
|
|
:key="tab.key"
|
|
|
|
|
:class="['tab-item', activeTab === tab.key ? 'active' : '']"
|
|
|
|
|
@click="switchTab(tab.key)"
|
|
|
|
|
>
|
|
|
|
|
<text class="tab-text">{{ tab.label }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="tab-card">
|
|
|
|
|
<view class="tabs-wrap">
|
|
|
|
|
<u-tabs
|
|
|
|
|
activeColor="#2463eb"
|
|
|
|
|
:list="tabList"
|
|
|
|
|
:current="currentTab"
|
|
|
|
|
:is-scroll="false"
|
|
|
|
|
@change="handleTabChange"
|
|
|
|
|
/>
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<!-- 点检历史 -->
|
|
|
|
|
<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="currentTab === 0">
|
|
|
|
|
<view v-if="!inspectionGroups.length" class="hint">{{ 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" :class="`result-${item.resultType}`">{{ item.resultLabel }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="record-row"><text class="record-label">{{ t('equipmentLedger.inspectionMethod') }}</text><text class="record-value">{{ textValue(item.method) }}</text></view>
|
|
|
|
|
<view class="record-row"><text class="record-label">{{ t('equipmentLedger.criteria') }}</text><text class="record-value">{{ textValue(item.criteria) }}</text></view>
|
|
|
|
|
<view class="record-row"><text class="record-label">{{ t('equipmentLedger.inspectionTime') }}</text><text class="record-value">{{ textValue(item.taskTimeLabel) }}</text></view>
|
|
|
|
|
<view class="record-row"><text class="record-label">{{ t('equipmentLedger.remark') }}</text><text class="record-value">{{ textValue(item.remark) }}</text></view>
|
|
|
|
|
<view v-if="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-else-if="currentTab === 1">
|
|
|
|
|
<view v-if="!maintainGroups.length" class="hint">{{ 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" :class="`result-${item.resultType}`">{{ item.resultLabel }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="record-row"><text class="record-label">{{ t('equipmentLedger.maintainMethod') }}</text><text class="record-value">{{ textValue(item.method) }}</text></view>
|
|
|
|
|
<view class="record-row"><text class="record-label">{{ t('equipmentLedger.criteria') }}</text><text class="record-value">{{ textValue(item.criteria) }}</text></view>
|
|
|
|
|
<view class="record-row"><text class="record-label">{{ t('equipmentLedger.maintainTime') }}</text><text class="record-value">{{ textValue(item.taskTimeLabel) }}</text></view>
|
|
|
|
|
<view class="record-row"><text class="record-label">{{ t('equipmentLedger.remark') }}</text><text class="record-value">{{ textValue(item.remark) }}</text></view>
|
|
|
|
|
<view v-if="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-else-if="currentTab === 2">
|
|
|
|
|
<view v-if="!repairRecords.length" class="hint">{{ t('equipmentLedger.noHistoryData') }}</view>
|
|
|
|
|
<view v-for="row in repairRecords" :key="row.key" class="record-card">
|
|
|
|
|
<view class="record-head">
|
|
|
|
|
<text class="record-title">{{ textValue(row.repairCode || row.repairName) }}</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.repairName') }}</text><text class="record-value">{{ textValue(row.repairName) }}</text></view>
|
|
|
|
|
<view class="record-row"><text class="record-label">{{ t('equipmentLedger.faultPhenomenon') }}</text><text class="record-value">{{ textValue(row.faultPhenomenon) }}</text></view>
|
|
|
|
|
<view class="record-row"><text class="record-label">{{ t('equipmentLedger.faultDescription') }}</text><text class="record-value">{{ textValue(row.faultDescription) }}</text></view>
|
|
|
|
|
<view class="record-row"><text class="record-label">{{ t('equipmentLedger.replacementParts') }}</text><text class="record-value">{{ textValue(row.replacementParts) }}</text></view>
|
|
|
|
|
<view class="record-row"><text class="record-label">{{ t('equipmentLedger.repairContent') }}</text><text class="record-value">{{ textValue(row.repairContent) }}</text></view>
|
|
|
|
|
<view class="record-row"><text class="record-label">{{ t('equipmentLedger.finishDate') }}</text><text class="record-value">{{ textValue(row.finishDateLabel) }}</text></view>
|
|
|
|
|
<view class="record-row"><text class="record-label">{{ t('equipmentLedger.remark') }}</text><text class="record-value">{{ textValue(row.remark) }}</text></view>
|
|
|
|
|
<view v-if="row.faultImageList.length" class="image-block">
|
|
|
|
|
<text class="image-title">{{ t('equipmentLedger.faultImages') }}</text>
|
|
|
|
|
<view class="history-images">
|
|
|
|
|
<image
|
|
|
|
|
v-for="img in row.faultImageList"
|
|
|
|
|
:key="img"
|
|
|
|
|
class="history-image"
|
|
|
|
|
:src="img"
|
|
|
|
|
mode="aspectFill"
|
|
|
|
|
@click="previewImages(row.faultImageList, img)"
|
|
|
|
|
/>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
<view v-if="row.repairedImageList.length" class="image-block">
|
|
|
|
|
<text class="image-title">{{ t('equipmentLedger.repairedImages') }}</text>
|
|
|
|
|
<view class="history-images">
|
|
|
|
|
<image
|
|
|
|
|
v-for="img in row.repairedImageList"
|
|
|
|
|
:key="img"
|
|
|
|
|
class="history-image"
|
|
|
|
|
:src="img"
|
|
|
|
|
mode="aspectFill"
|
|
|
|
|
@click="previewImages(row.repairedImageList, img)"
|
|
|
|
|
/>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
@ -172,16 +232,16 @@ const dictStore = useDictStore()
|
|
|
|
|
const detailId = ref(undefined)
|
|
|
|
|
const detailData = ref(null)
|
|
|
|
|
const deviceTypeList = ref([])
|
|
|
|
|
const activeTab = ref('check')
|
|
|
|
|
const currentTab = ref(0)
|
|
|
|
|
const inspectionList = ref([])
|
|
|
|
|
const maintainList = ref([])
|
|
|
|
|
const repairList = ref([])
|
|
|
|
|
const statusUpdating = ref(false)
|
|
|
|
|
|
|
|
|
|
const tabs = computed(() => [
|
|
|
|
|
{ key: 'check', label: t('equipmentLedger.checkHistory') },
|
|
|
|
|
{ key: 'maintain', label: t('equipmentLedger.maintainHistory') },
|
|
|
|
|
{ key: 'repair', label: t('equipmentLedger.repairHistory') }
|
|
|
|
|
const tabList = computed(() => [
|
|
|
|
|
{ name: t('equipmentLedger.checkHistory') },
|
|
|
|
|
{ name: t('equipmentLedger.maintainHistory') },
|
|
|
|
|
{ name: t('equipmentLedger.repairHistory') }
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
const statusOptions = computed(() => {
|
|
|
|
|
@ -198,6 +258,46 @@ const scheduledText = computed(() => {
|
|
|
|
|
return Number(val) === 1 ? t('equipmentLedger.yes') : t('equipmentLedger.no')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
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', 'maintainTime', 'inspectionTime', 'createTime'],
|
|
|
|
|
nameFieldCandidates: ['maintainItemName', 'inspectionItemName', 'name', 'itemName'],
|
|
|
|
|
resultFieldCandidates: ['maintainResult', 'inspectionResult', 'result'],
|
|
|
|
|
methodFieldCandidates: ['inspectionMethod', 'maintainMethod', 'method'],
|
|
|
|
|
criteriaFieldCandidates: ['judgmentCriteria', 'criteria'],
|
|
|
|
|
imagesFieldCandidates: ['images'],
|
|
|
|
|
remarkFieldCandidates: ['remark']
|
|
|
|
|
})
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const repairRecords = computed(() => {
|
|
|
|
|
const rows = Array.isArray(repairList.value) ? repairList.value : []
|
|
|
|
|
return rows.map((row, index) => {
|
|
|
|
|
const resultMeta = formatRepairResult(row?.repairResult !== undefined ? row.repairResult : row?.result)
|
|
|
|
|
return {
|
|
|
|
|
...row,
|
|
|
|
|
key: String(row?.id ?? row?.repairCode ?? index),
|
|
|
|
|
finishDateLabel: formatDateTime(row?.finishDate || row?.createTime),
|
|
|
|
|
resultLabel: resultMeta.label,
|
|
|
|
|
resultType: resultMeta.type,
|
|
|
|
|
faultImageList: parseImages(row?.faultImages),
|
|
|
|
|
repairedImageList: parseImages(row?.repairedImages)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
onLoad(async (query) => {
|
|
|
|
|
const id = query?.id ? decodeURIComponent(String(query.id)) : ''
|
|
|
|
|
detailId.value = id || undefined
|
|
|
|
|
@ -255,32 +355,43 @@ function normalizeDetail(res) {
|
|
|
|
|
return {}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function normalizeListData(res) {
|
|
|
|
|
const root = res && res.data !== undefined ? res.data : res
|
|
|
|
|
if (Array.isArray(root)) return root
|
|
|
|
|
if (Array.isArray(root?.data)) return root.data
|
|
|
|
|
if (Array.isArray(root?.list)) return root.list
|
|
|
|
|
if (Array.isArray(root?.rows)) return root.rows
|
|
|
|
|
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
|
|
|
|
|
return []
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function fetchInspectionHistory() {
|
|
|
|
|
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 : []
|
|
|
|
|
inspectionList.value = normalizeListData(res)
|
|
|
|
|
} catch (e) { inspectionList.value = [] }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function fetchMaintainHistory() {
|
|
|
|
|
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 : []
|
|
|
|
|
maintainList.value = normalizeListData(res)
|
|
|
|
|
} catch (e) { maintainList.value = [] }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function fetchRepairHistory() {
|
|
|
|
|
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 : []
|
|
|
|
|
repairList.value = normalizeListData(res)
|
|
|
|
|
} catch (e) { repairList.value = [] }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function switchTab(key) {
|
|
|
|
|
activeTab.value = key
|
|
|
|
|
function handleTabChange(e) {
|
|
|
|
|
const idx = e && typeof e === 'object' ? e.index : e
|
|
|
|
|
currentTab.value = Number(idx === undefined ? 0 : idx)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getStatusText(status) {
|
|
|
|
|
@ -321,32 +432,6 @@ async function onStatusChange(e) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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')
|
|
|
|
|
return textValue(result)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getResultClass(result) {
|
|
|
|
|
if (result === 'PASS' || result === 'pass' || result === 'OK' || result === 'ok' || result === 1 || result === '1') return 'text-success'
|
|
|
|
|
if (result === 'FAIL' || result === 'fail' || result === 'NG' || result === 'ng' || result === 0 || result === '0') return 'text-danger'
|
|
|
|
|
return ''
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getRepairStatusText(status) {
|
|
|
|
|
if (status === 0 || status === '0') return t('equipmentLedger.repairPending')
|
|
|
|
|
if (status === 1 || status === '1') return t('equipmentLedger.repairProcessing')
|
|
|
|
|
if (status === 2 || status === '2') return t('equipmentLedger.repairCompleted')
|
|
|
|
|
return textValue(status)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getRepairStatusClass(status) {
|
|
|
|
|
if (status === 0 || status === '0') return 'text-warning'
|
|
|
|
|
if (status === 1 || status === '1') return 'text-primary'
|
|
|
|
|
if (status === 2 || status === '2') return 'text-success'
|
|
|
|
|
return ''
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function fieldValue(field) {
|
|
|
|
|
return textValue(detailData.value ? detailData.value[field] : undefined)
|
|
|
|
|
}
|
|
|
|
|
@ -360,6 +445,77 @@ function textValue(value) {
|
|
|
|
|
return text || '-'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function parseImages(value) {
|
|
|
|
|
if (!value) return []
|
|
|
|
|
if (Array.isArray(value)) return value.map(String).filter(Boolean)
|
|
|
|
|
return String(value)
|
|
|
|
|
.replace(/[`'"]/g, '')
|
|
|
|
|
.split(',')
|
|
|
|
|
.map((item) => item.trim())
|
|
|
|
|
.filter(Boolean)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function pickFirst(obj, keys) {
|
|
|
|
|
for (const key of keys) {
|
|
|
|
|
if (obj && obj[key] !== undefined && obj[key] !== null && String(obj[key]).trim() !== '') {
|
|
|
|
|
return obj[key]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return undefined
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function formatResult(value) {
|
|
|
|
|
const raw = value === null || value === undefined ? '' : String(value).trim()
|
|
|
|
|
const upper = raw.toUpperCase()
|
|
|
|
|
if (!raw) return { label: '-', type: 'info' }
|
|
|
|
|
if (raw === '0') return { label: t('equipmentLedger.resultPending'), type: 'info' }
|
|
|
|
|
if (raw === '1' || upper === 'OK' || upper === 'PASS') return { label: t('equipmentLedger.resultPass'), type: 'success' }
|
|
|
|
|
if (raw === '2' || upper === 'NG' || upper === 'FAIL') return { label: t('equipmentLedger.resultFail'), type: 'danger' }
|
|
|
|
|
return { label: raw, type: 'info' }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function formatRepairResult(value) {
|
|
|
|
|
const raw = value === null || value === undefined ? '' : String(value).trim()
|
|
|
|
|
if (raw === '1') return { label: t('equipmentLedger.repairCompleted'), type: 'success' }
|
|
|
|
|
if (raw === '2') return { label: t('equipmentLedger.repairAbnormal'), type: 'danger' }
|
|
|
|
|
if (raw === '0') return { label: t('equipmentLedger.repairProcessing'), type: 'warning' }
|
|
|
|
|
return { label: textValue(value), type: 'info' }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function buildStepGroups(rows, options) {
|
|
|
|
|
const groupsMap = new Map()
|
|
|
|
|
const list = Array.isArray(rows) ? rows : []
|
|
|
|
|
for (const row of list) {
|
|
|
|
|
const time = formatDateTime(pickFirst(row, options.timeFieldCandidates) || row?.createTime)
|
|
|
|
|
const operator = textValue(row?.operator || row?.operatorName || row?.inspectorName || row?.creatorName || row?.creator)
|
|
|
|
|
const groupKey = `${row?.managementId || ''}_${time}_${operator}`
|
|
|
|
|
const name = pickFirst(row, options.nameFieldCandidates) || '-'
|
|
|
|
|
const resultMeta = formatResult(pickFirst(row, options.resultFieldCandidates))
|
|
|
|
|
const item = {
|
|
|
|
|
key: String(row?.id ?? `${groupKey}_${name}`),
|
|
|
|
|
name: textValue(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: formatDateTime(row?.taskTime || row?.maintainTime || row?.inspectionTime || row?.createTime)
|
|
|
|
|
}
|
|
|
|
|
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 })
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function formatDateValue(value) {
|
|
|
|
|
if (!value) return '-'
|
|
|
|
|
if (Array.isArray(value) && value.length >= 3) {
|
|
|
|
|
@ -417,19 +573,30 @@ function formatDateTime(value) {
|
|
|
|
|
.text-primary { color: #1a3a5c; }
|
|
|
|
|
.remark-row { border-bottom: none; }
|
|
|
|
|
.remark-value { white-space: pre-wrap; }
|
|
|
|
|
.tab-bar { display: flex; border-bottom: 2rpx solid #edf0f3; margin-bottom: 20rpx; }
|
|
|
|
|
.tab-item { flex: 1; text-align: center; padding: 16rpx 0; position: relative; }
|
|
|
|
|
.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; }
|
|
|
|
|
.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; }
|
|
|
|
|
.tab-card { margin-top: 20rpx; background: #ffffff; border-radius: 20rpx; padding: 28rpx; box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05); }
|
|
|
|
|
.tabs-wrap { margin-bottom: 20rpx; }
|
|
|
|
|
.hint { padding: 40rpx 0; text-align: center; font-size: 28rpx; color: #94a3b8; }
|
|
|
|
|
.record-card { margin-bottom: 20rpx; padding: 24rpx; border-radius: 22rpx; background: linear-gradient(180deg, #ffffff 0%, #fbfcfe 100%); border: 1rpx solid #edf2f7; }
|
|
|
|
|
.record-head { display: flex; align-items: center; justify-content: space-between; gap: 16rpx; margin-bottom: 16rpx; }
|
|
|
|
|
.record-title { flex: 1; min-width: 0; font-size: 30rpx; color: #1f2937; font-weight: 700; }
|
|
|
|
|
.record-label { font-size: 24rpx; color: #94a3b8; flex-shrink: 0; min-width: 150rpx; }
|
|
|
|
|
.record-value { flex: 1; font-size: 27rpx; color: #1f2937; font-weight: 500; line-height: 1.4; word-break: break-all; text-align: right; }
|
|
|
|
|
.record-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 20rpx; margin-bottom: 10rpx; }
|
|
|
|
|
.record-row:last-child { margin-bottom: 0; }
|
|
|
|
|
.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-item { padding: 20rpx; border-radius: 18rpx; background: #f8fbff; margin-bottom: 16rpx; }
|
|
|
|
|
.history-item:last-child { margin-bottom: 0; }
|
|
|
|
|
.history-title-row { display: flex; align-items: center; justify-content: space-between; gap: 16rpx; margin-bottom: 12rpx; }
|
|
|
|
|
.history-item-name { flex: 1; min-width: 0; font-size: 28rpx; color: #1f2937; font-weight: 700; }
|
|
|
|
|
.result-badge { padding: 8rpx 18rpx; border-radius: 999rpx; font-size: 22rpx; font-weight: 600; }
|
|
|
|
|
.result-success { background: rgba(34, 197, 94, 0.12); color: #16a34a; }
|
|
|
|
|
.result-warning { background: rgba(245, 158, 11, 0.14); color: #d97706; }
|
|
|
|
|
.result-danger { background: rgba(239, 68, 68, 0.12); color: #dc2626; }
|
|
|
|
|
.result-info { background: rgba(148, 163, 184, 0.16); color: #64748b; }
|
|
|
|
|
.image-block { margin-top: 14rpx; }
|
|
|
|
|
.image-title { display: block; margin-bottom: 10rpx; font-size: 24rpx; color: #94a3b8; }
|
|
|
|
|
.history-images { display: flex; flex-wrap: wrap; gap: 12rpx; margin-top: 14rpx; }
|
|
|
|
|
.history-image { width: 150rpx; height: 150rpx; border-radius: 14rpx; background: #edf2f7; }
|
|
|
|
|
</style>
|
|
|
|
|
|