feat 联调报告上传接口
This commit is contained in:
10
pnpm-lock.yaml
generated
10
pnpm-lock.yaml
generated
@@ -3240,8 +3240,8 @@ packages:
|
||||
resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
caniuse-lite@1.0.30001737:
|
||||
resolution: {integrity: sha512-BiloLiXtQNrY5UyF0+1nSJLXUENuhka2pzy2Fx5pGxqavdrxSCW4U6Pn/PoG3Efspi2frRbHpBV2XsrPE6EDlw==}
|
||||
caniuse-lite@1.0.30001812:
|
||||
resolution: {integrity: sha512-qN+QNNBr93TCmFrmte0bBCjSDMuRvt78VlHT99qIGPszm4QsqCX8lnyWUFkHi8B7ZBAPq8SH+UqyXNy/odMdng==}
|
||||
|
||||
ccount@2.0.1:
|
||||
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
|
||||
@@ -10260,7 +10260,7 @@ snapshots:
|
||||
autoprefixer@10.4.20(postcss@8.5.6):
|
||||
dependencies:
|
||||
browserslist: 4.25.3
|
||||
caniuse-lite: 1.0.30001737
|
||||
caniuse-lite: 1.0.30001812
|
||||
fraction.js: 4.3.7
|
||||
normalize-range: 0.1.2
|
||||
picocolors: 1.1.1
|
||||
@@ -10410,7 +10410,7 @@ snapshots:
|
||||
|
||||
browserslist@4.25.3:
|
||||
dependencies:
|
||||
caniuse-lite: 1.0.30001737
|
||||
caniuse-lite: 1.0.30001812
|
||||
electron-to-chromium: 1.5.209
|
||||
node-releases: 2.0.19
|
||||
update-browserslist-db: 1.1.3(browserslist@4.25.3)
|
||||
@@ -10482,7 +10482,7 @@ snapshots:
|
||||
|
||||
camelcase@6.3.0: {}
|
||||
|
||||
caniuse-lite@1.0.30001737: {}
|
||||
caniuse-lite@1.0.30001812: {}
|
||||
|
||||
ccount@2.0.1: {}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ApiResult, PageResult, ReportStatus } from '../types'
|
||||
import type { ApiResult, ReportStatus } from '../types'
|
||||
import { request } from '../request'
|
||||
|
||||
export interface ReportItem {
|
||||
@@ -70,11 +70,10 @@ export interface UploadReportParams {
|
||||
}
|
||||
|
||||
/** GET /api/report/list 报告列表 */
|
||||
export function getReportList(params?: Record<string, unknown>) {
|
||||
return request<PageResult<ReportItem>>({
|
||||
export function getReportList() {
|
||||
return request({
|
||||
url: '/api/report/list',
|
||||
method: 'GET',
|
||||
params,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -86,7 +85,7 @@ export function getReportDetail(reportId: number | string) {
|
||||
})
|
||||
}
|
||||
|
||||
/** POST /api/report/upload 上传报告(JSON) */
|
||||
/** POST /api/report/upload 上传报告数据(JSON) */
|
||||
export function uploadReport(data: UploadReportParams) {
|
||||
return request<ApiResult>({
|
||||
url: '/api/report/upload',
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import storage from '@/utils/storage'
|
||||
|
||||
const props = defineProps<{
|
||||
id?: number
|
||||
reportId?: number
|
||||
title: string
|
||||
deviceCode: string
|
||||
status: string
|
||||
@@ -14,25 +12,13 @@ const props = defineProps<{
|
||||
}>()
|
||||
|
||||
function goToDetail() {
|
||||
const key = `report_${Date.now()}`
|
||||
storage.set(key, {
|
||||
id: props.id,
|
||||
title: props.title,
|
||||
deviceCode: props.deviceCode,
|
||||
status: props.status,
|
||||
time: props.time,
|
||||
area: props.area,
|
||||
duration: props.duration,
|
||||
areaCount: props.areaCount,
|
||||
passRate: props.passRate,
|
||||
})
|
||||
|
||||
const query = [`key=${key}`]
|
||||
if (props.id != null)
|
||||
query.push(`id=${props.id}`)
|
||||
if (props.reportId == null) {
|
||||
uni.showToast({ title: '报告ID缺失', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
uni.navigateTo({
|
||||
url: `/pages/report/detail/index?${query.join('&')}`,
|
||||
url: `/pages/report/detail/index?reportId=${props.reportId}`,
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import type { Project } from '@/service/project'
|
||||
import { uploadReport } from '@/api/report'
|
||||
import { useCurrentProject } from '@/composables/useCurrentProject'
|
||||
import { historyReportService, projectService, workRecordService } from '@/service'
|
||||
import { useDbStore } from '@/store/modules/db'
|
||||
import { getDeviceId } from '@/utils/device'
|
||||
import storage from '@/utils/storage'
|
||||
|
||||
/** 本地时间字符串,格式与 seed 一致:YYYY-MM-DD HH:mm:ss */
|
||||
@@ -127,7 +129,7 @@ export function useProjectLifecycle() {
|
||||
})
|
||||
}
|
||||
|
||||
/** 结束项目 → 已结束,生成历史报告 */
|
||||
/** 结束项目 → 先上传报告,成功后再改本地状态并生成历史报告 */
|
||||
async function endProject(projectId: number) {
|
||||
await withBusy(async () => {
|
||||
const project = await projectService.selectById(projectId)
|
||||
@@ -136,34 +138,71 @@ export function useProjectLifecycle() {
|
||||
return
|
||||
}
|
||||
if (project.status === 1) {
|
||||
uni.showToast({ title: '项目已结束', icon: 'none' })
|
||||
uni.showToast({ title: '项目已经结束', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
const endTime = nowDateTime()
|
||||
const open = await findOpenWorkRecord(projectId)
|
||||
if (open?.id) {
|
||||
await workRecordService.update(open.id, {
|
||||
end_time: endTime,
|
||||
duration: calcDurationHours(open.start_time, endTime),
|
||||
})
|
||||
}
|
||||
|
||||
const latest = await projectService.selectById(projectId)
|
||||
if (!latest) {
|
||||
uni.showToast({ title: '项目不存在', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
const deviceId = getDeviceId() || storage.get<string>('device_unique_id') || ''
|
||||
if (!deviceId || !latest.project_name) {
|
||||
uni.showToast({ title: '上传失败,请重新点击结束项目', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
const endTime = nowDateTime()
|
||||
const workStartTime = latest.start_time || endTime
|
||||
const workRecords = await workRecordService.selectByProjectId(projectId)
|
||||
const exceptionCount = workRecords.reduce((sum, item) => sum + (item.exception_count ?? 0), 0)
|
||||
|
||||
let uploaded = false
|
||||
try {
|
||||
const res = await uploadReport({
|
||||
deviceId,
|
||||
projectName: latest.project_name,
|
||||
reportNo: `RTK-${projectId}`,
|
||||
workStartTime,
|
||||
workEndTime: endTime,
|
||||
constructionSection: latest.stake_range,
|
||||
compactArea: latest.total_compaction_area ?? 0,
|
||||
qualificationRate: latest.qualified_rate ?? 0,
|
||||
workDuration: latest.total_duration ?? 0,
|
||||
passCount: latest.total_pass_count ?? 0,
|
||||
avgSpeed: latest.avg_speed ?? 0,
|
||||
avgTemperature: latest.avg_paving_temp ?? 0,
|
||||
avgCompaction: latest.avg_compaction ?? 0,
|
||||
exceptionCount,
|
||||
})
|
||||
uploaded = res.code === 200
|
||||
}
|
||||
catch {
|
||||
uploaded = false
|
||||
}
|
||||
|
||||
if (!uploaded) {
|
||||
uni.showToast({ title: '上传失败,请重新点击结束项目', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
// 上传成功后再改本地状态
|
||||
const open = await findOpenWorkRecord(projectId)
|
||||
if (open?.id) {
|
||||
await workRecordService.update(open.id, {
|
||||
end_time: endTime,
|
||||
duration: calcDurationHours(open.start_time, endTime),
|
||||
})
|
||||
}
|
||||
|
||||
await historyReportService.insert({
|
||||
project_id: projectId,
|
||||
report_name: `${latest.project_name} 碾压报告`,
|
||||
device_id: storage.get<string>('device_unique_id') || undefined,
|
||||
device_id: deviceId,
|
||||
construction_area: latest.stake_range,
|
||||
start_time: latest.start_time,
|
||||
start_time: workStartTime,
|
||||
end_time: endTime,
|
||||
duration: latest.total_duration ?? 0,
|
||||
compaction_area: latest.total_compaction_area ?? 0,
|
||||
@@ -184,7 +223,7 @@ export function useProjectLifecycle() {
|
||||
setCurrentProject(null)
|
||||
|
||||
await refreshAfterChange(projectId)
|
||||
uni.showToast({ title: '已结束并生成报告', icon: 'success' })
|
||||
uni.showToast({ title: '项目已经结束', icon: 'success' })
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
"navigationBarTitleText": "Road-Roller"
|
||||
},
|
||||
"pages": [
|
||||
// GENERATED BY UNI-PAGES, PLATFORM: H5 || APP
|
||||
// GENERATED BY UNI-PAGES, PLATFORM: APP || H5
|
||||
{
|
||||
"path": "pages/Ahome/index",
|
||||
"type": "home",
|
||||
@@ -121,7 +121,7 @@
|
||||
"color": "@tabFontColor",
|
||||
"selectedColor": "@tabSelectedColor",
|
||||
"list": [
|
||||
// GENERATED BY UNI-PAGES, PLATFORM: H5 || APP
|
||||
// GENERATED BY UNI-PAGES, PLATFORM: APP || H5
|
||||
{
|
||||
"pagePath": "pages/Ahome/index",
|
||||
"text": "首页",
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import type { ReportItem } from '@/api/report'
|
||||
import type { HistoryReport } from '@/service/historyReport'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { onLoad, onShow } from '@dcloudio/uni-app'
|
||||
import { ref } from 'vue'
|
||||
import { uploadReport } from '@/api/report'
|
||||
import { getReportDetail } from '@/api/report'
|
||||
import { useDbStore } from '@/store/modules/db'
|
||||
import { getDeviceId } from '@/utils/device'
|
||||
import storage from '@/utils/storage'
|
||||
|
||||
definePage({
|
||||
layout: false,
|
||||
@@ -31,11 +30,13 @@ interface ReportDetailView {
|
||||
}
|
||||
|
||||
const db = useDbStore()
|
||||
const reportId = ref<number | null>(null)
|
||||
const reportData = ref<ReportDetailView | null>(null)
|
||||
const apiReport = ref<ReportItem | null>(null)
|
||||
const rawReport = ref<HistoryReport | null>(null)
|
||||
const uploading = ref(false)
|
||||
|
||||
function parseDate(value?: string) {
|
||||
function parseDate(value?: string | null) {
|
||||
if (!value)
|
||||
return null
|
||||
const date = new Date(value.replace(/-/g, '/'))
|
||||
@@ -46,7 +47,7 @@ function pad(value: number, length = 2) {
|
||||
return String(value).padStart(length, '0')
|
||||
}
|
||||
|
||||
function formatDateTimeRange(start?: string, end?: string) {
|
||||
function formatDateTimeRange(start?: string | null, end?: string | null) {
|
||||
const startDate = parseDate(start)
|
||||
const endDate = parseDate(end)
|
||||
if (!startDate)
|
||||
@@ -64,36 +65,45 @@ function formatDateTimeRange(start?: string, end?: string) {
|
||||
return `${datePart}${startHm}-${endDate.getFullYear()}年${pad(endDate.getMonth() + 1)}月${pad(endDate.getDate())}日${endHm}`
|
||||
}
|
||||
|
||||
/** 接口时间格式 yyyy-MM-dd HH:mm:ss */
|
||||
function formatApiDateTime(value?: string) {
|
||||
const date = parseDate(value)
|
||||
if (!date)
|
||||
return ''
|
||||
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`
|
||||
}
|
||||
|
||||
/** 报告编号:RTK-项目id */
|
||||
function buildReportNo(item: HistoryReport) {
|
||||
return `RTK-${item.project_id}`
|
||||
}
|
||||
|
||||
function formatDuration(value?: number) {
|
||||
function formatDuration(value?: number | null) {
|
||||
const hours = value ?? 0
|
||||
const text = Number.isInteger(hours) ? String(hours) : hours.toFixed(1)
|
||||
return `${text}H`
|
||||
}
|
||||
|
||||
function formatArea(value?: number) {
|
||||
function formatArea(value?: number | null) {
|
||||
return `${value ?? 0}m²`
|
||||
}
|
||||
|
||||
function formatRate(value?: number) {
|
||||
function formatRate(value?: number | null) {
|
||||
const rate = value ?? 0
|
||||
const text = Number.isInteger(rate) ? String(rate) : rate.toFixed(1)
|
||||
return `${text}%`
|
||||
}
|
||||
|
||||
function mapReportDetail(item: HistoryReport): ReportDetailView {
|
||||
function formatMetric(value: number | null | undefined, unit: string) {
|
||||
return value != null ? `${value}${unit}` : '-'
|
||||
}
|
||||
|
||||
function mapApiReportDetail(item: ReportItem): ReportDetailView {
|
||||
return {
|
||||
deviceCode: item.deviceId || '-',
|
||||
time: formatDateTimeRange(item.workStartTime, item.workEndTime),
|
||||
status: String(item.reportStatus) === '1' ? '有异常' : '作业已完成',
|
||||
duration: formatDuration(item.workDuration),
|
||||
areaCount: formatArea(item.compactArea),
|
||||
passCount: `${item.passCount ?? 0}遍`,
|
||||
passRate: formatRate(item.qualificationRate),
|
||||
avgSpeed: formatMetric(item.avgSpeed, 'km/h'),
|
||||
avgFrequency: formatMetric(item.avgFrequency, 'Hz'),
|
||||
avgAmplitude: formatMetric(item.avgAmplitude, 'mm'),
|
||||
avgTemperature: formatMetric(item.avgTemperature, '℃'),
|
||||
avgCompaction: item.avgCompaction != null ? formatRate(item.avgCompaction) : '-',
|
||||
exceptionCount: String(item.exceptionCount ?? 0),
|
||||
}
|
||||
}
|
||||
|
||||
function mapLocalReportDetail(item: HistoryReport): ReportDetailView {
|
||||
return {
|
||||
deviceCode: item.device_id || '-',
|
||||
time: formatDateTimeRange(item.start_time, item.end_time),
|
||||
@@ -102,120 +112,76 @@ function mapReportDetail(item: HistoryReport): ReportDetailView {
|
||||
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` : '-',
|
||||
avgSpeed: formatMetric(item.avg_speed, 'km/h'),
|
||||
avgFrequency: '-',
|
||||
avgAmplitude: '-',
|
||||
avgTemperature: item.avg_paving_temp != null ? `${item.avg_paving_temp}℃` : '-',
|
||||
avgTemperature: formatMetric(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 || '-',
|
||||
}
|
||||
/** 兼容 data 嵌套 / 扁平结构 */
|
||||
function pickReportPayload(res: unknown): ReportItem | null {
|
||||
if (!res || typeof res !== 'object')
|
||||
return null
|
||||
const raw = res as Record<string, unknown>
|
||||
const nested = raw.data
|
||||
if (nested && typeof nested === 'object' && 'reportId' in (nested as object))
|
||||
return nested as ReportItem
|
||||
if ('reportId' in raw)
|
||||
return raw as unknown as ReportItem
|
||||
return null
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
async function loadLocalFallback(id: number) {
|
||||
if (!db.history_report.length || !db.project.length)
|
||||
await db.loadAll()
|
||||
|
||||
const item = db.history_report.find(report => report.id === id)
|
||||
if (item) {
|
||||
rawReport.value = item
|
||||
reportData.value = mapReportDetail(item)
|
||||
apiReport.value = null
|
||||
reportData.value = mapLocalReportDetail(item)
|
||||
}
|
||||
}
|
||||
|
||||
/** 优先请求详情接口,失败回退本地库 */
|
||||
async function loadReportDetail() {
|
||||
const id = reportId.value
|
||||
if (!id)
|
||||
return
|
||||
|
||||
try {
|
||||
const res = await getReportDetail(id)
|
||||
if (res.code === 200) {
|
||||
const payload = pickReportPayload(res)
|
||||
if (payload) {
|
||||
apiReport.value = payload
|
||||
rawReport.value = null
|
||||
reportData.value = mapApiReportDetail(payload)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
catch {
|
||||
// 接口不通,走本地
|
||||
}
|
||||
|
||||
await loadLocalFallback(id)
|
||||
}
|
||||
|
||||
onLoad((options) => {
|
||||
const id = Number(options?.reportId || options?.id)
|
||||
reportId.value = id || null
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
loadReportDetail()
|
||||
})
|
||||
|
||||
async function handleDownload() {
|
||||
if (uploading.value)
|
||||
return
|
||||
|
||||
const item = rawReport.value
|
||||
if (!item) {
|
||||
uni.showToast({ title: '暂无报告数据', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
if (!db.project.length)
|
||||
await db.loadAll()
|
||||
|
||||
const project = db.project.find(p => p.id === item.project_id)
|
||||
const projectName = project?.project_name
|
||||
if (!projectName) {
|
||||
uni.showToast({ title: '项目信息不完整', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
const workStartTime = formatApiDateTime(item.start_time)
|
||||
const workEndTime = formatApiDateTime(item.end_time)
|
||||
if (!workStartTime || !workEndTime) {
|
||||
uni.showToast({ title: '作业时间不完整', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
const deviceId = getDeviceId() || item.device_id || ''
|
||||
if (!deviceId) {
|
||||
uni.showToast({ title: '设备号缺失', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
uploading.value = true
|
||||
uni.showLoading({ title: '上传中...', mask: true })
|
||||
try {
|
||||
const res = await uploadReport({
|
||||
deviceId,
|
||||
projectName,
|
||||
reportNo: buildReportNo(item),
|
||||
workStartTime,
|
||||
workEndTime,
|
||||
constructionSection: item.construction_area,
|
||||
compactArea: item.compaction_area,
|
||||
qualificationRate: item.qualified_rate,
|
||||
workDuration: item.duration,
|
||||
passCount: item.pass_count,
|
||||
avgSpeed: item.avg_speed,
|
||||
avgTemperature: item.avg_paving_temp,
|
||||
avgCompaction: item.avg_compaction,
|
||||
exceptionCount: item.exception_count,
|
||||
})
|
||||
const ok = res.code === 200 || res.code === 0
|
||||
if (!ok)
|
||||
throw new Error(res.msg || '上传失败')
|
||||
|
||||
uni.showToast({ title: '上传成功', icon: 'success' })
|
||||
}
|
||||
catch (e: any) {
|
||||
uni.showToast({ title: e?.message || '上传失败', icon: 'none' })
|
||||
}
|
||||
finally {
|
||||
uploading.value = false
|
||||
uni.hideLoading()
|
||||
}
|
||||
console.log('下载报告')
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import type { ReportItem } from '@/api/report'
|
||||
import type { HistoryReport } from '@/service/historyReport'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { onShow } from '@dcloudio/uni-app'
|
||||
import { computed, ref } from 'vue'
|
||||
import { getReportList } from '@/api/report'
|
||||
import { useDbStore } from '@/store/modules/db'
|
||||
|
||||
definePage({
|
||||
@@ -10,8 +13,27 @@ definePage({
|
||||
},
|
||||
})
|
||||
|
||||
/** 列表统一结构,便于接口 / 本地双数据源筛选展示 */
|
||||
interface ReportListRow {
|
||||
id?: number
|
||||
title: string
|
||||
deviceCode: string
|
||||
status: string
|
||||
time: string
|
||||
area: string
|
||||
duration: string
|
||||
areaCount: string
|
||||
passRate: string
|
||||
projectId: string
|
||||
startTime?: string
|
||||
reportStatus: string
|
||||
}
|
||||
|
||||
const db = useDbStore()
|
||||
|
||||
/** 非 null 表示接口可用,优先用接口数据 */
|
||||
const apiReports = ref<ReportItem[] | null>(null)
|
||||
|
||||
const datetimerange = ref<string[]>([])
|
||||
const reportStatus = ref('')
|
||||
const area = ref('')
|
||||
@@ -30,24 +52,48 @@ const reportStatusOptions = [
|
||||
{ value: 'pending', text: '待审核' },
|
||||
]
|
||||
|
||||
const listRows = computed<ReportListRow[]>(() => {
|
||||
if (apiReports.value) {
|
||||
return apiReports.value
|
||||
.slice()
|
||||
.sort((a, b) => (b.reportId ?? 0) - (a.reportId ?? 0))
|
||||
.map(mapApiReportItem)
|
||||
}
|
||||
return db.history_report
|
||||
.slice()
|
||||
.sort((a, b) => (b.id ?? 0) - (a.id ?? 0))
|
||||
.map(mapLocalReportItem)
|
||||
})
|
||||
|
||||
const areaOptions = computed(() => {
|
||||
const areas = [...new Set(db.history_report.map(item => item.construction_area).filter(Boolean))] as string[]
|
||||
const areas = [...new Set(listRows.value.map(item => item.area).filter(v => v && v !== '-'))]
|
||||
return [
|
||||
{ value: 'all', text: '全部' },
|
||||
...areas.map(text => ({ value: text, text })),
|
||||
]
|
||||
})
|
||||
|
||||
const projectOptions = computed(() => [
|
||||
{ value: 'all', text: '全部' },
|
||||
...db.project.map(item => ({ value: String(item.id), text: item.project_name })),
|
||||
])
|
||||
const projectOptions = computed(() => {
|
||||
if (apiReports.value) {
|
||||
const map = new Map<string, string>()
|
||||
for (const item of apiReports.value) {
|
||||
if (item.projectId != null)
|
||||
map.set(String(item.projectId), item.projectName || String(item.projectId))
|
||||
}
|
||||
return [
|
||||
{ value: 'all', text: '全部' },
|
||||
...[...map.entries()].map(([value, text]) => ({ value, text })),
|
||||
]
|
||||
}
|
||||
return [
|
||||
{ value: 'all', text: '全部' },
|
||||
...db.project.map(item => ({ value: String(item.id), text: item.project_name })),
|
||||
]
|
||||
})
|
||||
|
||||
const reports = computed(() => {
|
||||
const query = appliedQuery.value
|
||||
return db.history_report
|
||||
.filter(item => matchReport(item, query))
|
||||
.map(mapReportItem)
|
||||
return listRows.value.filter(item => matchReport(item, query))
|
||||
})
|
||||
|
||||
function isAll(value: string) {
|
||||
@@ -55,16 +101,18 @@ function isAll(value: string) {
|
||||
}
|
||||
|
||||
function matchReport(
|
||||
item: HistoryReport,
|
||||
item: ReportListRow,
|
||||
query: { datetimerange: string[], reportStatus: string, area: string, project: string },
|
||||
) {
|
||||
if (!isAll(query.project) && String(item.project_id) !== query.project)
|
||||
if (!isAll(query.project) && item.projectId !== query.project)
|
||||
return false
|
||||
if (!isAll(query.area) && item.construction_area !== query.area)
|
||||
if (!isAll(query.area) && item.area !== query.area)
|
||||
return false
|
||||
if (query.reportStatus === 'pending')
|
||||
return false
|
||||
if (!matchDateRange(item.start_time, query.datetimerange))
|
||||
if (query.reportStatus === 'completed' && item.reportStatus === '1')
|
||||
return false
|
||||
if (!matchDateRange(item.startTime, query.datetimerange))
|
||||
return false
|
||||
return true
|
||||
}
|
||||
@@ -109,23 +157,46 @@ function formatDateTimeRange(start?: string, end?: string) {
|
||||
return `${datePart}${startHm}-${endDate.getFullYear()}年${pad(endDate.getMonth() + 1)}月${pad(endDate.getDate())}日${endHm}`
|
||||
}
|
||||
|
||||
function formatDuration(value?: number) {
|
||||
function formatDuration(value?: number | null) {
|
||||
const hours = value ?? 0
|
||||
const text = Number.isInteger(hours) ? String(hours) : hours.toFixed(1)
|
||||
return `${text}H`
|
||||
}
|
||||
|
||||
function formatArea(value?: number) {
|
||||
function formatArea(value?: number | null) {
|
||||
return `${value ?? 0}m²`
|
||||
}
|
||||
|
||||
function formatRate(value?: number) {
|
||||
function formatRate(value?: number | null) {
|
||||
const rate = value ?? 0
|
||||
const text = Number.isInteger(rate) ? String(rate) : rate.toFixed(1)
|
||||
return `${text}%`
|
||||
}
|
||||
|
||||
function mapReportItem(item: HistoryReport) {
|
||||
function mapReportStatus(status?: ReportItem['reportStatus']) {
|
||||
if (String(status) === '1')
|
||||
return '有异常'
|
||||
return '已完成'
|
||||
}
|
||||
|
||||
function mapApiReportItem(item: ReportItem): ReportListRow {
|
||||
return {
|
||||
id: item.reportId,
|
||||
title: item.reportNo || item.projectName || '碾压报告',
|
||||
deviceCode: item.deviceId || '-',
|
||||
status: mapReportStatus(item.reportStatus),
|
||||
time: formatDateTimeRange(item.workStartTime, item.workEndTime),
|
||||
area: item.constructionSection || '-',
|
||||
duration: formatDuration(item.workDuration),
|
||||
areaCount: formatArea(item.compactArea),
|
||||
passRate: formatRate(item.qualificationRate),
|
||||
projectId: item.projectId != null ? String(item.projectId) : '',
|
||||
startTime: item.workStartTime,
|
||||
reportStatus: String(item.reportStatus ?? '0'),
|
||||
}
|
||||
}
|
||||
|
||||
function mapLocalReportItem(item: HistoryReport): ReportListRow {
|
||||
return {
|
||||
id: item.id,
|
||||
title: item.report_name,
|
||||
@@ -136,6 +207,9 @@ function mapReportItem(item: HistoryReport) {
|
||||
duration: formatDuration(item.duration),
|
||||
areaCount: formatArea(item.compaction_area),
|
||||
passRate: formatRate(item.qualified_rate),
|
||||
projectId: item.project_id != null ? String(item.project_id) : '',
|
||||
startTime: item.start_time,
|
||||
reportStatus: '0',
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,9 +222,30 @@ function handleQuery() {
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
if (!db.history_report.length)
|
||||
await db.loadAll()
|
||||
/** 优先拉接口;code 非 200 或请求失败则回退本地 SQLite */
|
||||
async function loadReports() {
|
||||
// 本地库并行准备,不阻塞接口请求
|
||||
const localTask = (!db.history_report.length || !db.project.length)
|
||||
? db.loadAll().catch(() => undefined)
|
||||
: Promise.resolve()
|
||||
|
||||
try {
|
||||
const res = await getReportList()
|
||||
if (res.code === 200 && Array.isArray(res.rows)) {
|
||||
apiReports.value = res.rows
|
||||
return
|
||||
}
|
||||
}
|
||||
catch {
|
||||
// 接口不通,走本地
|
||||
}
|
||||
|
||||
apiReports.value = null
|
||||
await localTask
|
||||
}
|
||||
|
||||
onShow(() => {
|
||||
loadReports()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -213,7 +308,7 @@ onMounted(async () => {
|
||||
<!-- 报告列表 -->
|
||||
<view class="report-header">
|
||||
<text class="report-title">
|
||||
报告列表
|
||||
报告列表11111
|
||||
</text>
|
||||
<text class="report-count">
|
||||
共{{ reports.length }}条
|
||||
@@ -226,8 +321,8 @@ onMounted(async () => {
|
||||
</view>
|
||||
<report-item
|
||||
v-for="item in reports"
|
||||
:id="item.id"
|
||||
:key="item.id"
|
||||
:report-id="item.id"
|
||||
:title="item.title"
|
||||
:device-code="item.deviceCode"
|
||||
:status="item.status"
|
||||
|
||||
Reference in New Issue
Block a user