feat 历史报告

This commit is contained in:
Ironsp
2026-09-14 16:17:53 +08:00
parent f53a3da87c
commit 2a775ab5e3
4 changed files with 306 additions and 81 deletions

View File

@@ -10,7 +10,7 @@ defineProps<{
<view class="device-header"> <view class="device-header">
<view class="device-info"> <view class="device-info">
<text class="device-code"> <text class="device-code">
备ID: {{ deviceCode }} 设备ID: {{ deviceCode }}
</text> </text>
<text class="device-time"> <text class="device-time">
{{ time }} {{ time }}

View File

@@ -2,6 +2,7 @@
import storage from '@/utils/storage' import storage from '@/utils/storage'
const props = defineProps<{ const props = defineProps<{
id?: number
title: string title: string
deviceCode: string deviceCode: string
status: string status: string
@@ -15,6 +16,7 @@ const props = defineProps<{
function goToDetail() { function goToDetail() {
const key = `report_${Date.now()}` const key = `report_${Date.now()}`
storage.set(key, { storage.set(key, {
id: props.id,
title: props.title, title: props.title,
deviceCode: props.deviceCode, deviceCode: props.deviceCode,
status: props.status, status: props.status,
@@ -24,8 +26,13 @@ function goToDetail() {
areaCount: props.areaCount, areaCount: props.areaCount,
passRate: props.passRate, passRate: props.passRate,
}) })
const query = [`key=${key}`]
if (props.id != null)
query.push(`id=${props.id}`)
uni.navigateTo({ uni.navigateTo({
url: `/pages/report/detail/index?key=${key}`, url: `/pages/report/detail/index?${query.join('&')}`,
}) })
} }
</script> </script>

View File

@@ -1,4 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import type { HistoryReport } from '@/service/historyReport'
import { onLoad } from '@dcloudio/uni-app'
import { ref } from 'vue'
import { useDbStore } from '@/store/modules/db'
import storage from '@/utils/storage' import storage from '@/utils/storage'
definePage({ definePage({
@@ -8,35 +12,129 @@ definePage({
}, },
}) })
const reportData = ref<any>({ interface ReportDetailView {
deviceCode: 'ABC123', deviceCode: string
time: '2025-12-15 08:30-14:30', time: string
status: '作业已完成', status: string
duration: '6H', duration: string
areaCount: '2580m²', areaCount: string
passCount: '355', passCount: string
passRate: '96.8%', passRate: string
avgSpeed: '3.8km/h', avgSpeed: string
avgFrequency: '34Hz', avgFrequency: string
avgAmplitude: '2.1mm', avgAmplitude: string
avgTemperature: '42℃', avgTemperature: string
avgCompaction: '96%', avgCompaction: string
exceptionCount: '2', exceptionCount: string
}
const db = useDbStore()
const reportData = ref<ReportDetailView | null>(null)
function parseDate(value?: string) {
if (!value)
return null
const date = new Date(value.replace(/-/g, '/'))
return Number.isNaN(date.getTime()) ? null : date
}
function pad(value: number) {
return String(value).padStart(2, '0')
}
function formatDateTimeRange(start?: string, end?: string) {
const startDate = parseDate(start)
const endDate = parseDate(end)
if (!startDate)
return '-'
const datePart = `${startDate.getFullYear()}年${pad(startDate.getMonth() + 1)}月${pad(startDate.getDate())}日`
const startHm = `${pad(startDate.getHours())}:${pad(startDate.getMinutes())}`
if (!endDate)
return `${datePart}${startHm}`
const endHm = `${pad(endDate.getHours())}:${pad(endDate.getMinutes())}`
if (startDate.toDateString() === endDate.toDateString())
return `${datePart}${startHm}-${endHm}`
return `${datePart}${startHm}-${endDate.getFullYear()}年${pad(endDate.getMonth() + 1)}月${pad(endDate.getDate())}日${endHm}`
}
function formatDuration(value?: number) {
const hours = value ?? 0
const text = Number.isInteger(hours) ? String(hours) : hours.toFixed(1)
return `${text}H`
}
function formatArea(value?: number) {
return `${value ?? 0}m²`
}
function formatRate(value?: number) {
const rate = value ?? 0
const text = Number.isInteger(rate) ? String(rate) : rate.toFixed(1)
return `${text}%`
}
function mapReportDetail(item: HistoryReport): ReportDetailView {
return {
deviceCode: item.device_id || '-',
time: formatDateTimeRange(item.start_time, item.end_time),
status: '作业已完成',
duration: formatDuration(item.duration),
areaCount: formatArea(item.compaction_area),
passCount: `${item.pass_count ?? 0}遍`,
passRate: formatRate(item.qualified_rate),
avgSpeed: item.avg_speed != null ? `${item.avg_speed}km/h` : '-',
avgFrequency: '-',
avgAmplitude: '-',
avgTemperature: item.avg_paving_temp != null ? `${item.avg_paving_temp}℃` : '-',
avgCompaction: item.avg_compaction != null ? formatRate(item.avg_compaction) : '-',
exceptionCount: String(item.exception_count ?? 0),
}
}
function applyStoragePayload(data: Record<string, any>) {
reportData.value = {
deviceCode: data.deviceCode || '-',
time: data.time || '-',
status: data.status || '作业已完成',
duration: data.duration || '-',
areaCount: data.areaCount || '-',
passCount: data.passCount || '-',
passRate: data.passRate || '-',
avgSpeed: data.avgSpeed || '-',
avgFrequency: data.avgFrequency || '-',
avgAmplitude: data.avgAmplitude || '-',
avgTemperature: data.avgTemperature || '-',
avgCompaction: data.avgCompaction || '-',
exceptionCount: data.exceptionCount || '-',
}
}
onLoad(async (options) => {
if (options?.key) {
const data = storage.get(options.key)
if (data)
applyStoragePayload(data)
storage.remove(options.key)
}
const id = Number(options?.id)
if (!id)
return
if (!db.history_report.length)
await db.loadAll()
const item = db.history_report.find(report => report.id === id)
if (item)
reportData.value = mapReportDetail(item)
}) })
function handleDownload() { function handleDownload() {
console.log('点击下载') console.log('点击下载')
} }
onLoad((options) => {
if (options?.key) {
const data = storage.get(options.key)
if (data) {
reportData.value = data
storage.remove(options.key)
}
}
})
</script> </script>
<template> <template>
@@ -46,30 +144,33 @@ onLoad((options) => {
</back-nav> </back-nav>
<view class="content"> <view class="content">
<report-device-header <view v-if="!reportData" class="empty-tip">
v-if="reportData" 暂无报告详情
:device-code="reportData.deviceCode" </view>
:time="reportData.time"
:status="reportData.status"
/>
<report-stats-card <template v-else>
v-if="reportData" <report-device-header
:duration="reportData.duration" :device-code="reportData.deviceCode"
:area-count="reportData.areaCount" :time="reportData.time"
:pass-count="reportData.passCount" :status="reportData.status"
:pass-rate="reportData.passRate" />
/>
<report-detail-metrics <report-stats-card
v-if="reportData" :duration="reportData.duration"
:avg-speed="reportData.avgSpeed" :area-count="reportData.areaCount"
:avg-frequency="reportData.avgFrequency" :pass-count="reportData.passCount"
:avg-amplitude="reportData.avgAmplitude" :pass-rate="reportData.passRate"
:avg-temperature="reportData.avgTemperature" />
:avg-compaction="reportData.avgCompaction"
:exception-count="reportData.exceptionCount" <report-detail-metrics
/> :avg-speed="reportData.avgSpeed"
:avg-frequency="reportData.avgFrequency"
:avg-amplitude="reportData.avgAmplitude"
:avg-temperature="reportData.avgTemperature"
:avg-compaction="reportData.avgCompaction"
:exception-count="reportData.exceptionCount"
/>
</template>
</view> </view>
</view> </view>
</template> </template>
@@ -87,4 +188,11 @@ onLoad((options) => {
flex-direction: column; flex-direction: column;
gap: 12px; gap: 12px;
} }
.empty-tip {
font-size: 14px;
color: #999;
text-align: center;
padding: 24px 0;
}
</style> </style>

View File

@@ -1,4 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import type { HistoryReport } from '@/service/historyReport'
import { computed, onMounted, ref } from 'vue'
import { useDbStore } from '@/store/modules/db'
definePage({ definePage({
layout: false, layout: false,
style: { style: {
@@ -6,53 +10,148 @@ definePage({
}, },
}) })
const datetimerange = ref([]) const db = useDbStore()
const datetimerange = ref<string[]>([])
const reportStatus = ref('') const reportStatus = ref('')
const area = ref('') const area = ref('')
const project = ref('') const project = ref('')
const appliedQuery = ref({
datetimerange: [] as string[],
reportStatus: '',
area: '',
project: '',
})
const reportStatusOptions = [ const reportStatusOptions = [
{ value: 'all', text: '全部' }, { value: 'all', text: '全部' },
{ value: 'completed', text: '已完成' }, { value: 'completed', text: '已完成' },
{ value: 'pending', text: '待审核' }, { value: 'pending', text: '待审核' },
] ]
const areaOptions = [ const areaOptions = computed(() => {
{ value: 'area1', text: '区域1' }, const areas = [...new Set(db.history_report.map(item => item.construction_area).filter(Boolean))] as string[]
{ value: 'area2', text: '区域2' }, return [
] { value: 'all', text: '全部' },
...areas.map(text => ({ value: text, text })),
]
})
const projectOptions = [ const projectOptions = computed(() => [
{ value: 'project1', text: '项目1' }, { value: 'all', text: '全部' },
{ value: 'project2', text: '项目2' }, ...db.project.map(item => ({ value: String(item.id), text: item.project_name })),
] ])
const reports = [ const reports = computed(() => {
{ const query = appliedQuery.value
title: 'k1+200-k1+800压实作业报告', return db.history_report
deviceCode: 'ABC123', .filter(item => matchReport(item, query))
.map(mapReportItem)
})
function isAll(value: string) {
return !value || value === 'all'
}
function matchReport(
item: HistoryReport,
query: { datetimerange: string[], reportStatus: string, area: string, project: string },
) {
if (!isAll(query.project) && String(item.project_id) !== query.project)
return false
if (!isAll(query.area) && item.construction_area !== query.area)
return false
if (query.reportStatus === 'pending')
return false
if (!matchDateRange(item.start_time, query.datetimerange))
return false
return true
}
function parseDate(value?: string) {
if (!value)
return null
const date = new Date(value.replace(/-/g, '/'))
return Number.isNaN(date.getTime()) ? null : date
}
function matchDateRange(startTime: string | undefined, range: string[]) {
if (!range || range.length < 2)
return true
const start = parseDate(startTime)
const from = parseDate(range[0])
const to = parseDate(range[1])
if (!start || !from || !to)
return false
return start.getTime() >= from.getTime() && start.getTime() <= to.getTime()
}
function pad(value: number) {
return String(value).padStart(2, '0')
}
function formatDateTimeRange(start?: string, end?: string) {
const startDate = parseDate(start)
const endDate = parseDate(end)
if (!startDate)
return '-'
const datePart = `${startDate.getFullYear()}年${pad(startDate.getMonth() + 1)}月${pad(startDate.getDate())}日`
const startHm = `${pad(startDate.getHours())}:${pad(startDate.getMinutes())}`
if (!endDate)
return `${datePart}${startHm}`
const endHm = `${pad(endDate.getHours())}:${pad(endDate.getMinutes())}`
if (startDate.toDateString() === endDate.toDateString())
return `${datePart}${startHm}-${endHm}`
return `${datePart}${startHm}-${endDate.getFullYear()}年${pad(endDate.getMonth() + 1)}月${pad(endDate.getDate())}日${endHm}`
}
function formatDuration(value?: number) {
const hours = value ?? 0
const text = Number.isInteger(hours) ? String(hours) : hours.toFixed(1)
return `${text}H`
}
function formatArea(value?: number) {
return `${value ?? 0}m²`
}
function formatRate(value?: number) {
const rate = value ?? 0
const text = Number.isInteger(rate) ? String(rate) : rate.toFixed(1)
return `${text}%`
}
function mapReportItem(item: HistoryReport) {
return {
id: item.id,
title: item.report_name,
deviceCode: item.device_id || '-',
status: '已完成', status: '已完成',
time: '2025年12月15日08:30-14:30', time: formatDateTimeRange(item.start_time, item.end_time),
area: 'G6高速-3标段 基层', area: item.construction_area || '-',
duration: '6H', duration: formatDuration(item.duration),
areaCount: '2580m²', areaCount: formatArea(item.compaction_area),
passRate: '96.8%', passRate: formatRate(item.qualified_rate),
}, }
{ }
title: 'k1+200-k1+800压实作业报告',
deviceCode: 'ABC123',
status: '已完成',
time: '2025年12月15日08:30-14:30',
area: 'G6高速-3标段 基层',
duration: '6H',
areaCount: '2580m²',
passRate: '96.8%',
},
]
function handleQuery() { function handleQuery() {
console.log('查询') appliedQuery.value = {
datetimerange: [...(datetimerange.value || [])],
reportStatus: reportStatus.value,
area: area.value,
project: project.value,
}
} }
onMounted(async () => {
if (!db.history_report.length)
await db.loadAll()
})
</script> </script>
<template> <template>
@@ -122,9 +221,13 @@ function handleQuery() {
</view> </view>
<view class="report-list"> <view class="report-list">
<view v-if="!reports.length" class="empty-tip">
暂无历史报告
</view>
<report-item <report-item
v-for="(item, index) in reports" v-for="item in reports"
:key="index" :id="item.id"
:key="item.id"
:title="item.title" :title="item.title"
:device-code="item.deviceCode" :device-code="item.deviceCode"
:status="item.status" :status="item.status"
@@ -242,4 +345,11 @@ function handleQuery() {
flex-direction: column; flex-direction: column;
gap: 12px; gap: 12px;
} }
.empty-tip {
font-size: 14px;
color: #999;
text-align: center;
padding: 24px 0;
}
</style> </style>