From faf4ea1155ab2b966ad133e0d304750b916b4c6e Mon Sep 17 00:00:00 2001 From: Ironsp <1522278303@qq.com> Date: Thu, 17 Sep 2026 16:26:30 +0800 Subject: [PATCH] =?UTF-8?q?feat=20=E5=AF=B9=E9=BD=90=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/heatmap/heatUtils.ts | 49 ++++++++--------- src/components/heatmap/pointDetail.vue | 14 +++-- src/components/work/rtkCanvas.vue | 2 +- src/mock/rtkTrack.ts | 75 ++++++++++++++++---------- src/pages/heatmap/index.vue | 2 +- 5 files changed, 82 insertions(+), 60 deletions(-) diff --git a/src/components/heatmap/heatUtils.ts b/src/components/heatmap/heatUtils.ts index a1a3497..539416b 100644 --- a/src/components/heatmap/heatUtils.ts +++ b/src/components/heatmap/heatUtils.ts @@ -1,12 +1,14 @@ -/** 热力图图层与色阶工具 */ +/** 热力图图层与色阶工具(字段与 RtkTrack / MockRtkPoint 对齐) */ -export type HeatmapLayerKey = 'compaction' | 'speed' | 'temperature' | 'passCount' +import type { MockRtkPoint } from '@/mock/rtkTrack' + +export type HeatmapLayerKey = 'compaction_value' | 'speed' | 'steel_wheel_temp' | 'pass_count' export const HEATMAP_LAYERS: { key: HeatmapLayerKey, label: string }[] = [ - { key: 'compaction', label: '压实度' }, + { key: 'compaction_value', label: '压实度' }, { key: 'speed', label: '压实速度' }, - { key: 'temperature', label: '温度' }, - { key: 'passCount', label: '碾压遍数' }, + { key: 'steel_wheel_temp', label: '温度' }, + { key: 'pass_count', label: '碾压遍数' }, ] /** 色阶阈值:0–90 绿,90–95 黄,95–100 红 */ @@ -22,46 +24,37 @@ export function colorByScore(score: number): string { } /** 各图层原始值映射到 0–100 分,统一用 90/95 分色 */ -export function layerScore( - layer: HeatmapLayerKey, - point: { compaction: number, speed: number, temperature: number, passCount: number }, -): number { +export function layerScore(layer: HeatmapLayerKey, point: MockRtkPoint): number { switch (layer) { - case 'compaction': - return point.compaction + case 'compaction_value': + return point.compaction_value case 'speed': { - // 目标约 4~5 km/h 偏优,偏快偏慢降分 const ideal = 4.2 const diff = Math.abs(point.speed - ideal) return Math.max(0, Math.min(100, 100 - diff * 18)) } - case 'temperature': { - // 目标约 150℃ + case 'steel_wheel_temp': { const ideal = 150 - const diff = Math.abs(point.temperature - ideal) + const diff = Math.abs(point.steel_wheel_temp - ideal) return Math.max(0, Math.min(100, 100 - diff * 2.5)) } - case 'passCount': - // 目标遍数 6 - return Math.max(0, Math.min(100, (point.passCount / 6) * 100)) + case 'pass_count': + return Math.max(0, Math.min(100, (point.pass_count / 6) * 100)) default: return 0 } } -export function layerRawText( - layer: HeatmapLayerKey, - point: { compaction: number, speed: number, temperature: number, passCount: number }, -): string { +export function layerRawText(layer: HeatmapLayerKey, point: MockRtkPoint): string { switch (layer) { - case 'compaction': - return `${point.compaction}%` + case 'compaction_value': + return `${point.compaction_value}%` case 'speed': return `${point.speed}km/h` - case 'temperature': - return `${point.temperature}℃` - case 'passCount': - return `${point.passCount}遍` + case 'steel_wheel_temp': + return `${point.steel_wheel_temp}℃` + case 'pass_count': + return `${point.pass_count}遍` default: return '-' } diff --git a/src/components/heatmap/pointDetail.vue b/src/components/heatmap/pointDetail.vue index 546048a..4257421 100644 --- a/src/components/heatmap/pointDetail.vue +++ b/src/components/heatmap/pointDetail.vue @@ -56,7 +56,7 @@ const emit = defineEmits<{ 压实度 - {{ point.compaction }}% + {{ point.compaction_value }}% @@ -64,7 +64,7 @@ const emit = defineEmits<{ 温度 - {{ point.temperature }}℃ + {{ point.steel_wheel_temp }}℃ @@ -72,7 +72,15 @@ const emit = defineEmits<{ 碾压遍数 - {{ point.passCount }}遍 + {{ point.pass_count }}遍 + + + + + 合格率 + + + {{ point.qualified_rate }}% diff --git a/src/components/work/rtkCanvas.vue b/src/components/work/rtkCanvas.vue index 9f35151..e4bcfb3 100644 --- a/src/components/work/rtkCanvas.vue +++ b/src/components/work/rtkCanvas.vue @@ -142,7 +142,7 @@ function draw() { ctx.setLineWidth(ROLLER_WIDTH) for (let i = 1; i < coords.length; i++) { ctx.beginPath() - ctx.setStrokeStyle(compactionColor(path[i].compaction)) + ctx.setStrokeStyle(compactionColor(path[i].compaction_value)) ctx.moveTo(coords[i - 1].x, coords[i - 1].y) ctx.lineTo(coords[i].x, coords[i].y) ctx.stroke() diff --git a/src/mock/rtkTrack.ts b/src/mock/rtkTrack.ts index ea1405c..5b59a06 100644 --- a/src/mock/rtkTrack.ts +++ b/src/mock/rtkTrack.ts @@ -1,16 +1,25 @@ /** * RTK 轨迹假数据:蛇形碾压路径 + 持续推送 - * 字段:经纬度、航向、压实度、速度、温度、碾压遍数 + * 字段与 `@/service/rtkTrack` 的 RtkTrack 对齐 */ +import type { RtkTrack } from '@/service/rtkTrack' -export interface MockRtkPoint { - latitude: number // 纬度 - longitude: number // 经度 - heading: number // 航向角(度,0=北,顺时针) - compaction: number // 压实度 % - speed: number // 压实速度 km/h - temperature: number // 温度 ℃ - passCount: number // 碾压遍数 +/** 假数据点:与 rtk_track 表结构一致(坐标类字段必填便于绘制) */ +export type MockRtkPoint = Required< + Pick< + RtkTrack, + | 'latitude' + | 'longitude' + | 'speed' + | 'heading' + | 'steel_wheel_temp' + | 'compaction_value' + | 'pass_count' + | 'qualified_rate' + > +> & { + work_record_id?: number + created_at?: string } export interface MockRtkStreamOptions { @@ -18,14 +27,15 @@ export interface MockRtkStreamOptions { intervalMs?: number /** 结束后是否循环,默认 true */ loop?: boolean - /** 横向行数,默认 80(约 10 倍于早期 8 行) */ + /** 横向行数,默认 80 */ rows?: number /** 每行采样点数,默认 40 */ pointsPerRow?: number + /** 关联作业记录 id,默认 1 */ + workRecordId?: number } export interface MockRtkStream { - /** 完整路径(用于画布定界) */ readonly points: MockRtkPoint[] start: (onPoint: (point: MockRtkPoint, index: number) => void) => void stop: () => void @@ -33,12 +43,11 @@ export interface MockRtkStream { getIndex: () => number } -/** 航向归一化到 [0, 360) */ function normalizeHeading(deg: number) { return ((deg % 360) + 360) % 360 } -function makeCompaction(seed: number) { +function makeCompactionValue(seed: number) { const wave = Math.sin(seed / 4) * 4 return Number(Math.min(98, Math.max(78, 88 + wave + (Math.random() - 0.5) * 3)).toFixed(1)) } @@ -47,7 +56,7 @@ function makeSpeed(seed: number) { return Number((3.2 + Math.sin(seed / 5) * 1.2 + Math.random() * 0.8).toFixed(1)) } -function makeTemperature(seed: number) { +function makeSteelWheelTemp(seed: number) { return Number((148 + Math.sin(seed / 7) * 8 + (Math.random() - 0.5) * 6).toFixed(0)) } @@ -55,27 +64,36 @@ function makePassCount(seed: number) { return Math.max(1, Math.min(8, Math.round(2 + (seed % 9) / 2 + Math.random()))) } +function makeQualifiedRate(compaction: number) { + return Number(Math.min(100, Math.max(70, compaction - 2 + (Math.random() - 0.5) * 4)).toFixed(1)) +} + +function nowDateTime() { + return new Date().toISOString().replace('T', ' ').slice(0, 19) +} + function buildPoint( lat: number, lon: number, heading: number, seed: number, + workRecordId: number, ): MockRtkPoint { + const compaction_value = makeCompactionValue(seed) return { + work_record_id: workRecordId, latitude: Number(lat.toFixed(7)), longitude: Number(lon.toFixed(7)), - heading, - compaction: makeCompaction(seed), speed: makeSpeed(seed), - temperature: makeTemperature(seed), - passCount: makePassCount(seed), + heading, + steel_wheel_temp: makeSteelWheelTemp(seed), + compaction_value, + pass_count: makePassCount(seed), + qualified_rate: makeQualifiedRate(compaction_value), + created_at: nowDateTime(), } } -/** - * 行末三角掉头:行末 → 外侧顶点 → 下行起点 - * 航向先转 60°(到顶点),再转 120°(合计 180° 衔接下行) - */ function pushUTurnTriangle( points: MockRtkPoint[], startLat: number, @@ -84,6 +102,7 @@ function pushUTurnTriangle( latStep: number, eastward: boolean, seedRef: { value: number }, + workRecordId: number, ) { const nextLat = startLat + latStep const midLat = startLat + latStep / 2 @@ -99,19 +118,22 @@ function pushUTurnTriangle( tipLon, normalizeHeading(fromHeading + 60 * turnSign), seedRef.value++, + workRecordId, )) points.push(buildPoint( nextLat, startLon, normalizeHeading(fromHeading + 180 * turnSign), seedRef.value++, + workRecordId, )) } -/** 生成类似示意图的左右蛇形碾压路径 */ +/** 生成蛇形碾压路径假数据 */ export function generateSerpentineTrack(options: MockRtkStreamOptions = {}): MockRtkPoint[] { const rows = options.rows ?? 80 const pointsPerRow = options.pointsPerRow ?? 40 + const workRecordId = options.workRecordId ?? 1 const originLat = 39.0425 const originLon = 115.8620 @@ -129,13 +151,13 @@ export function generateSerpentineTrack(options: MockRtkStreamOptions = {}): Moc for (let col = 0; col < pointsPerRow; col++) { const t = eastward ? col : (pointsPerRow - 1 - col) const lon = originLon + t * lonStep - points.push(buildPoint(lat, lon, heading, seedRef.value++)) + points.push(buildPoint(lat, lon, heading, seedRef.value++, workRecordId)) } if (row < rows - 1) { const endT = eastward ? (pointsPerRow - 1) : 0 const endLon = originLon + endT * lonStep - pushUTurnTriangle(points, lat, endLon, heading, latStep, eastward, seedRef) + pushUTurnTriangle(points, lat, endLon, heading, latStep, eastward, seedRef, workRecordId) } } @@ -175,8 +197,7 @@ export function createMockRtkStream(options: MockRtkStreamOptions = {}): MockRtk return } } - const point = points[index] - onPoint(point, index) + onPoint(points[index], index) index += 1 }, intervalMs) } diff --git a/src/pages/heatmap/index.vue b/src/pages/heatmap/index.vue index d1dd350..843068c 100644 --- a/src/pages/heatmap/index.vue +++ b/src/pages/heatmap/index.vue @@ -10,7 +10,7 @@ definePage({ }, }) -const layer = ref('compaction') +const layer = ref('compaction_value') const layerVisible = ref(false) const detailVisible = ref(false) const selectedPoint = ref(null)