feat(platform): integrate runtime amap vehicle maps
This commit is contained in:
@@ -6,6 +6,8 @@ import { DataEmpty } from '../components/DataEmpty';
|
||||
import { PageHeader } from '../components/PageHeader';
|
||||
import { SourceStatusTags } from '../components/SourceStatusTags';
|
||||
import { StatusTag } from '../components/StatusTag';
|
||||
import { VehicleMap, type VehicleMapPoint } from '../components/VehicleMap';
|
||||
import { isAMapConfigured } from '../config/appConfig';
|
||||
|
||||
function canOpenVehicle(vin?: string) {
|
||||
const value = vin?.trim();
|
||||
@@ -36,15 +38,6 @@ function isValidCoordinate(row: VehicleRealtimeRow) {
|
||||
return Number.isFinite(row.longitude) && Number.isFinite(row.latitude) && row.longitude !== 0 && row.latitude !== 0;
|
||||
}
|
||||
|
||||
function mapPointStyle(row: VehicleRealtimeRow, index: number) {
|
||||
if (!isValidCoordinate(row)) {
|
||||
return { left: `${18 + (index % 4) * 18}%`, top: `${28 + (index % 3) * 17}%` };
|
||||
}
|
||||
const left = Math.min(88, Math.max(10, ((row.longitude + 180) / 360) * 100));
|
||||
const top = Math.min(82, Math.max(12, ((90 - row.latitude) / 180) * 100));
|
||||
return { left: `${left}%`, top: `${top}%` };
|
||||
}
|
||||
|
||||
const onlineLabel: Record<string, string> = {
|
||||
online: '在线',
|
||||
offline: '离线'
|
||||
@@ -70,7 +63,7 @@ export function Realtime({
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [filters, setFilters] = useState<Record<string, string>>(initialFilters);
|
||||
const [pagination, setPagination] = useState({ currentPage: 1, pageSize: 50, total: 0 });
|
||||
const amapConfigured = Boolean(String(import.meta.env.VITE_AMAP_WEB_JS_KEY ?? '').trim());
|
||||
const amapConfigured = isAMapConfigured();
|
||||
|
||||
const load = (values: Record<string, string> = filters, page = pagination.currentPage, pageSize = pagination.pageSize) => {
|
||||
setLoading(true);
|
||||
@@ -108,6 +101,14 @@ export function Realtime({
|
||||
const locatedCount = rows.filter(isValidCoordinate).length;
|
||||
const degradedCount = rows.filter((row) => row.onlineSourceCount < row.sourceCount).length;
|
||||
const primaryProtocols = new Set(rows.map((row) => row.primaryProtocol).filter(Boolean));
|
||||
const mapPoints: VehicleMapPoint[] = rows.map((row, index) => ({
|
||||
id: row.vin || `${row.primaryProtocol}-${index}`,
|
||||
label: row.plate || row.vin || 'unknown',
|
||||
longitude: row.longitude,
|
||||
latitude: row.latitude,
|
||||
online: row.online,
|
||||
title: `${row.plate || row.vin || '-'} ${row.primaryProtocol || ''} ${row.lastSeen || ''}`
|
||||
}));
|
||||
|
||||
return (
|
||||
<div className="vp-page">
|
||||
@@ -161,16 +162,11 @@ export function Realtime({
|
||||
<Tag color="green">{onlineCount.toLocaleString()} 辆在线</Tag>
|
||||
</Space>
|
||||
</div>
|
||||
<div className="vp-map vp-map-monitor">
|
||||
{rows.slice(0, 80).map((row, index) => (
|
||||
<span
|
||||
key={`${row.vin}-${row.primaryProtocol}-${index}`}
|
||||
className={`vp-map-dot ${row.online ? 'vp-map-dot-online' : 'vp-map-dot-offline'}`}
|
||||
title={`${row.plate || row.vin} ${row.primaryProtocol || ''}`}
|
||||
style={mapPointStyle(row, index)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<VehicleMap
|
||||
points={mapPoints}
|
||||
maxFallbackPoints={80}
|
||||
fallbackLabel="高德地图未配置,显示实时坐标预览"
|
||||
/>
|
||||
</div>
|
||||
<div className="vp-monitor-side">
|
||||
{[
|
||||
@@ -251,16 +247,7 @@ export function Realtime({
|
||||
)}
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane tab="地图视图" itemKey="map">
|
||||
<div className="vp-map">
|
||||
{rows.map((row, index) => (
|
||||
<span
|
||||
key={row.vin}
|
||||
className="vp-map-dot"
|
||||
title={`${row.plate} ${row.primaryProtocol}`}
|
||||
style={mapPointStyle(row, index)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<VehicleMap points={mapPoints} heightClassName="" fallbackLabel="高德地图未配置,显示实时坐标预览" />
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
</Card>
|
||||
|
||||
Reference in New Issue
Block a user