新增任务管理与报警管理前端,并接入车队健康数据。
运营菜单下挂地图监控/任务/报警入口,车辆卡片与任务列表同步展示运行态。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
import http from './http'
|
||||
import type { AlarmFeed } from '@/types/alarm'
|
||||
|
||||
const MOCK = import.meta.env.VITE_USE_MOCK === 'true'
|
||||
|
||||
/** 报警管理数据源:平台记录库 /fleet/alarms(离线可读 + 历史保留)。 */
|
||||
export async function fetchAlarmFeed(limit = 2000): Promise<AlarmFeed> {
|
||||
if (MOCK) {
|
||||
const { mockAlarms } = await import('@/mock/data/alarms')
|
||||
return { online: true, lastSyncAt: new Date().toISOString(), alarms: await mockAlarms() }
|
||||
}
|
||||
const { data } = await http.get<AlarmFeed>('/fleet/alarms', { params: { limit } })
|
||||
return {
|
||||
online: !!data?.online,
|
||||
lastSyncAt: data?.lastSyncAt ?? null,
|
||||
alarms: Array.isArray(data?.alarms) ? data.alarms : []
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import http from './http'
|
||||
import type { DeliveryTask } from '@/types/delivery'
|
||||
import type { CreateDeliveryPayload, DeliveryTask } from '@/types/delivery'
|
||||
|
||||
const MOCK = import.meta.env.VITE_USE_MOCK === 'true'
|
||||
|
||||
@@ -20,17 +20,72 @@ export async function listDeliveries(opts?: {
|
||||
return Array.isArray(data) ? data : []
|
||||
}
|
||||
|
||||
export async function cancelDelivery(id: number): Promise<void> {
|
||||
if (MOCK) return
|
||||
await http.post(`${BASE}/${id}/cancel`)
|
||||
/** CDM 任务快照订阅结果:来自平台库 cdm_tasks(SimpleLite 关闭时仍可读,含完整历史)。 */
|
||||
export interface CdmTaskFeed {
|
||||
online: boolean
|
||||
lastSyncAt: string | null
|
||||
tasks: DeliveryTask[]
|
||||
}
|
||||
|
||||
export async function resendDelivery(id: number): Promise<void> {
|
||||
if (MOCK) return
|
||||
await http.post(`${BASE}/${id}/resend`)
|
||||
/**
|
||||
* 任务页数据源:优先读平台快照库 /fleet/tasks(离线可读 + 历史保留);
|
||||
* 若平台端点不可用则回退到实时投影 /sl/projection/deliveries。
|
||||
*/
|
||||
export async function fetchCdmTaskFeed(limit = 1000): Promise<CdmTaskFeed> {
|
||||
if (MOCK) {
|
||||
const { mockDeliveries } = await import('@/mock/data/deliveries')
|
||||
return { online: true, lastSyncAt: new Date().toISOString(), tasks: await mockDeliveries() }
|
||||
}
|
||||
try {
|
||||
const { data } = await http.get<CdmTaskFeed>('/fleet/tasks', { params: { limit } })
|
||||
return {
|
||||
online: !!data?.online,
|
||||
lastSyncAt: data?.lastSyncAt ?? null,
|
||||
tasks: Array.isArray(data?.tasks) ? data.tasks : []
|
||||
}
|
||||
} catch {
|
||||
const tasks = await listDeliveries({ includeFinished: true, includeAborted: true })
|
||||
return { online: true, lastSyncAt: new Date().toISOString(), tasks }
|
||||
}
|
||||
}
|
||||
|
||||
export async function forceCompleteDelivery(id: number): Promise<void> {
|
||||
export async function cancelDelivery(id: string): Promise<void> {
|
||||
if (MOCK) return
|
||||
await http.post(`${BASE}/${id}/force-complete`)
|
||||
await http.post(`${BASE}/${encodeURIComponent(id)}/cancel`)
|
||||
}
|
||||
|
||||
export async function resendDelivery(id: string): Promise<void> {
|
||||
if (MOCK) return
|
||||
await http.post(`${BASE}/${encodeURIComponent(id)}/resend`)
|
||||
}
|
||||
|
||||
export async function forceCompleteDelivery(id: string): Promise<void> {
|
||||
if (MOCK) return
|
||||
await http.post(`${BASE}/${encodeURIComponent(id)}/force-complete`)
|
||||
}
|
||||
|
||||
export async function pauseDelivery(id: string): Promise<void> {
|
||||
if (MOCK) return
|
||||
await http.post(`${BASE}/${encodeURIComponent(id)}/pause`)
|
||||
}
|
||||
|
||||
export async function resumeDelivery(id: string): Promise<void> {
|
||||
if (MOCK) return
|
||||
await http.post(`${BASE}/${encodeURIComponent(id)}/resume`)
|
||||
}
|
||||
|
||||
export async function changeCarDelivery(id: string): Promise<void> {
|
||||
if (MOCK) return
|
||||
await http.post(`${BASE}/${encodeURIComponent(id)}/change-car`)
|
||||
}
|
||||
|
||||
export async function setDeliveryPriority(id: string, value: number): Promise<void> {
|
||||
if (MOCK) return
|
||||
await http.post(`${BASE}/${encodeURIComponent(id)}/priority`, { value })
|
||||
}
|
||||
|
||||
export async function createDelivery(payload: CreateDeliveryPayload): Promise<{ id: string }> {
|
||||
if (MOCK) return { id: `MOCK-${Date.now()}` }
|
||||
const { data } = await http.post<{ success: boolean; id: string }>(BASE, payload)
|
||||
return { id: data?.id ?? '' }
|
||||
}
|
||||
|
||||
@@ -32,6 +32,14 @@ export async function fetchFleetHealth(): Promise<FleetHealthRow[]> {
|
||||
await new Promise((r) => setTimeout(r, 120))
|
||||
return mockFleetHealth()
|
||||
}
|
||||
// 平台侧聚合:SimpleLite 指标 + WatchDog(:9776) TCP RTT。
|
||||
// 不再直打 /sl/projection/fleet/health(其探测车载 :8081,现场多数未开导致假超时 2000ms)。
|
||||
try {
|
||||
const { data } = await http.get<FleetHealthRow[]>('/fleet/health')
|
||||
if (Array.isArray(data) && data.length > 0) return data
|
||||
} catch {
|
||||
/* fall through */
|
||||
}
|
||||
const { data } = await http.get<FleetHealthRow[]>('/sl/projection/fleet/health')
|
||||
return Array.isArray(data) ? data : []
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import http from '@/api/http'
|
||||
import type { LaunchMode, RunMode } from '@/types/auth'
|
||||
|
||||
export interface HealthInfo {
|
||||
status: string
|
||||
startTime: string
|
||||
uptimeSec: number
|
||||
}
|
||||
|
||||
export interface SimpleLiteDiagnostics {
|
||||
enabled: boolean
|
||||
isRunning: boolean
|
||||
lastLaunchMode?: string | null
|
||||
projectionPort: number
|
||||
projectionPortReachable: boolean
|
||||
gotoSiteApiAvailable?: boolean | null
|
||||
executableExists: boolean
|
||||
deployHint?: string | null
|
||||
}
|
||||
|
||||
export interface SimpleLiteLaunchResult {
|
||||
started: boolean
|
||||
status: string
|
||||
detail: string
|
||||
displayMode?: string | null
|
||||
warning?: string | null
|
||||
}
|
||||
|
||||
export function getHealth() {
|
||||
return http.get<HealthInfo>('/health')
|
||||
}
|
||||
|
||||
export function getSimpleLiteDiagnostics() {
|
||||
return http.get<SimpleLiteDiagnostics>('/health/simplelite')
|
||||
}
|
||||
|
||||
export function stopSimpleLite() {
|
||||
return http.post<{ killed: number; diagnostics: SimpleLiteDiagnostics }>('/health/simplelite/stop')
|
||||
}
|
||||
|
||||
export function restartSimpleLite(launchMode: LaunchMode) {
|
||||
return http.post<{ restart: SimpleLiteLaunchResult; diagnostics: SimpleLiteDiagnostics }>(
|
||||
'/health/simplelite/restart',
|
||||
null,
|
||||
{ params: { launchMode }, timeout: 60_000 }
|
||||
)
|
||||
}
|
||||
|
||||
/** 从诊断/会话 runMode 推断重启时使用的 launchMode。 */
|
||||
export function resolveRestartLaunchMode(
|
||||
lastLaunchMode: string | null | undefined,
|
||||
runMode: RunMode | null | undefined
|
||||
): LaunchMode {
|
||||
const mode = (lastLaunchMode ?? '').toLowerCase()
|
||||
if (mode === 'web') return 'WebOnly'
|
||||
if (mode === 'web+local') return 'DesktopAndWeb'
|
||||
return runMode === 'WebOnly' ? 'WebOnly' : 'DesktopAndWeb'
|
||||
}
|
||||
Reference in New Issue
Block a user