feat 历史报告
This commit is contained in:
@@ -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 }}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user