将调度内核标识从 SimpleLite 全面重命名为 Simple3。
配置段/环境变量、Launcher、健康检查 API、OpenAPI 与前后端文案同步;兼容探测旧 SimpleLite 进程名。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -3,7 +3,7 @@ import http from './http'
|
||||
/**
|
||||
* AI 助手 API:会话/工具走 axios(带鉴权拦截器);对话走原生 fetch 流式(SSE),
|
||||
* 因为 EventSource 只能 GET、且无法设置 Authorization header。fetch 这里手动对齐
|
||||
* axios 的双轨鉴权(Cookie + Bearer + X-Scope)。后端见 SimpleLite `Web/Assistant/AssistantApi.cs`。
|
||||
* axios 的双轨鉴权(Cookie + Bearer + X-Scope)。后端见 Simple3 `Web/Assistant/AssistantApi.cs`。
|
||||
*/
|
||||
|
||||
const API_BASE = (import.meta.env.VITE_API_BASE as string | undefined) ?? '/api'
|
||||
@@ -120,7 +120,7 @@ export async function streamChat(
|
||||
if (!resp.ok || !resp.body) {
|
||||
let msg = `请求失败(HTTP ${resp.status})`
|
||||
if (resp.status === 403) msg = '无权使用 AI 助手(需要 Platform 权限)。'
|
||||
else if (resp.status === 502 || resp.status === 504) msg = 'SimpleLite 未连接:请先启动后端(端口 8222)。'
|
||||
else if (resp.status === 502 || resp.status === 504) msg = 'Simple3 未连接:请先启动后端(端口 8222)。'
|
||||
else {
|
||||
try {
|
||||
const t = await resp.text()
|
||||
|
||||
@@ -32,7 +32,7 @@ export async function fetchFleetHealth(): Promise<FleetHealthRow[]> {
|
||||
await new Promise((r) => setTimeout(r, 120))
|
||||
return mockFleetHealth()
|
||||
}
|
||||
// 平台侧聚合:SimpleLite 指标 + WatchDog(:9776) TCP RTT。
|
||||
// 平台侧聚合:Simple3 指标 + WatchDog(:9776) TCP RTT。
|
||||
// 不再直打 /sl/projection/fleet/health(其探测车载 :8081,现场多数未开导致假超时 2000ms)。
|
||||
try {
|
||||
const { data } = await http.get<FleetHealthRow[]>('/fleet/health')
|
||||
|
||||
@@ -7,7 +7,7 @@ export interface HealthInfo {
|
||||
uptimeSec: number
|
||||
}
|
||||
|
||||
export interface SimpleLiteDiagnostics {
|
||||
export interface Simple3Diagnostics {
|
||||
enabled: boolean
|
||||
isRunning: boolean
|
||||
lastLaunchMode?: string | null
|
||||
@@ -18,7 +18,7 @@ export interface SimpleLiteDiagnostics {
|
||||
deployHint?: string | null
|
||||
}
|
||||
|
||||
export interface SimpleLiteLaunchResult {
|
||||
export interface Simple3LaunchResult {
|
||||
started: boolean
|
||||
status: string
|
||||
detail: string
|
||||
@@ -30,29 +30,26 @@ export function getHealth() {
|
||||
return http.get<HealthInfo>('/health')
|
||||
}
|
||||
|
||||
export function getSimpleLiteDiagnostics() {
|
||||
return http.get<SimpleLiteDiagnostics>('/health/simplelite')
|
||||
export function getSimple3Diagnostics() {
|
||||
return http.get<Simple3Diagnostics>('/health/simple3')
|
||||
}
|
||||
|
||||
export function stopSimpleLite() {
|
||||
return http.post<{ killed: number; diagnostics: SimpleLiteDiagnostics }>('/health/simplelite/stop')
|
||||
export function stopSimple3() {
|
||||
return http.post<{ killed: number; diagnostics: Simple3Diagnostics }>('/health/simple3/stop')
|
||||
}
|
||||
|
||||
export function restartSimpleLite(launchMode: LaunchMode) {
|
||||
return http.post<{ restart: SimpleLiteLaunchResult; diagnostics: SimpleLiteDiagnostics }>(
|
||||
'/health/simplelite/restart',
|
||||
export function restartSimple3(launchMode: LaunchMode) {
|
||||
return http.post<{ restart: Simple3LaunchResult; diagnostics: Simple3Diagnostics }>(
|
||||
'/health/simple3/restart',
|
||||
null,
|
||||
{ params: { launchMode }, timeout: 60_000 }
|
||||
)
|
||||
}
|
||||
|
||||
/** 从诊断/会话 runMode 推断重启时使用的 launchMode。 */
|
||||
/** Simple3 仅 Web;重启一律 WebOnly(保留函数签名供旧调用方)。 */
|
||||
export function resolveRestartLaunchMode(
|
||||
lastLaunchMode: string | null | undefined,
|
||||
runMode: RunMode | null | undefined
|
||||
_lastLaunchMode?: string | null,
|
||||
_runMode?: RunMode | null
|
||||
): LaunchMode {
|
||||
const mode = (lastLaunchMode ?? '').toLowerCase()
|
||||
if (mode === 'web') return 'WebOnly'
|
||||
if (mode === 'web+local') return 'DesktopAndWeb'
|
||||
return runMode === 'WebOnly' ? 'WebOnly' : 'DesktopAndWeb'
|
||||
return 'WebOnly'
|
||||
}
|
||||
|
||||
@@ -31,21 +31,21 @@ http.interceptors.request.use((config: InternalAxiosRequestConfig) => {
|
||||
})
|
||||
|
||||
/**
|
||||
* 把 axios 异常翻译成更友好的中文文案,特别是 SimpleLite 链路:
|
||||
* 把 axios 异常翻译成更友好的中文文案,特别是 Simple3 链路:
|
||||
*
|
||||
* /api/sl/projection/** → Platform.Server YARP → SimpleLite EmbedIO :8222
|
||||
* /api/sl/projection/** → Platform.Server YARP → Simple3 EmbedIO :8222
|
||||
*
|
||||
* 如果 SimpleLite 没启动 / 端口未监听,YARP 一定回 502;超时一般是 504。把这两种
|
||||
* 情况单独识别,写成「SimpleLite 未连接,请先启动 SimpleLite (端口 8222)」之类,
|
||||
* 如果 Simple3 没启动 / 端口未监听,YARP 一定回 502;超时一般是 504。把这两种
|
||||
* 情况单独识别,写成「Simple3 未连接,请先启动 Simple3 (端口 8222)」之类,
|
||||
* 比直接抛 `Request failed with status code 502` 友好得多,也避免用户怀疑前端 bug。
|
||||
*/
|
||||
function describeError(err: AxiosError): string {
|
||||
const status = err.response?.status
|
||||
const url = err.config?.url ?? ''
|
||||
const isSimpleLite = url.startsWith('/sl/') || url.startsWith('sl/')
|
||||
const isSimple3 = url.startsWith('/sl/') || url.startsWith('sl/')
|
||||
if (status === 502 || status === 504 || err.code === 'ECONNABORTED' || err.code === 'ERR_NETWORK') {
|
||||
if (isSimpleLite) {
|
||||
return 'SimpleLite 未连接:请先启动 SimpleLite 后端(监听端口 8222)后重试。'
|
||||
if (isSimple3) {
|
||||
return 'Simple3 未连接:请先启动 Simple3 后端(监听端口 8222)后重试。'
|
||||
}
|
||||
return `网关无法连接到下游服务(${status ?? err.code ?? 'network'})。`
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ import type { AxiosError } from 'axios'
|
||||
import http from './http'
|
||||
|
||||
/**
|
||||
* 与 SimpleLite `MapEditApiController` 对应的前端胶水。
|
||||
* 路径前缀:`/sl/projection/map-edit`(YARP → SimpleLite `/projection/map-edit`)。
|
||||
* 与 Simple3 `MapEditApiController` 对应的前端胶水。
|
||||
* 路径前缀:`/sl/projection/map-edit`(YARP → Simple3 `/projection/map-edit`)。
|
||||
*
|
||||
* 也覆盖 AI 服务配置 `/sl/projection/ai-config`(GET / POST),由 AiConfigController 提供。
|
||||
*
|
||||
@@ -205,7 +205,7 @@ export const mapEditApi = {
|
||||
unwrap<{ count: number; results: unknown[] }>(http.post(`${BASE}/objects/batch`, { ops })),
|
||||
|
||||
/**
|
||||
* CAD 相对包围盒对齐(SimpleLite CadAlignService)。
|
||||
* CAD 相对包围盒对齐(Simple3 CadAlignService)。
|
||||
* mode: left|right|top|bottom|centerH|centerV|center|distributeH|distributeV
|
||||
*/
|
||||
cadAlign: (mode: string, targets: Array<{ kind: string; id: number }>) =>
|
||||
@@ -264,8 +264,15 @@ export const mapEditApi = {
|
||||
),
|
||||
|
||||
// 拾取 / 仪表盘。snap=false 时落点用原始鼠标坐标(文本/图片/站点);布线取端点默认 true。
|
||||
pick: (opts?: { snap?: boolean }) =>
|
||||
unwrap<PickResult>(http.post(`${BASE}/pick`, { snap: opts?.snap ?? true })),
|
||||
pick: (opts?: { snap?: boolean; signal?: AbortSignal }) =>
|
||||
unwrap<PickResult>(http.post(
|
||||
`${BASE}/pick`,
|
||||
{ snap: opts?.snap ?? true },
|
||||
{ timeout: 0, signal: opts?.signal }
|
||||
)),
|
||||
|
||||
cancelPick: () =>
|
||||
unwrap<{ cancelled: boolean }>(http.post(`${BASE}/pick/cancel`)),
|
||||
|
||||
dashboardSummary: () =>
|
||||
unwrap<DashboardSummary>(http.get(`${BASE}/dashboard/summary`)),
|
||||
@@ -300,9 +307,9 @@ export const mapEditApi = {
|
||||
}>(http.get(`${BASE}/project/browse`, { params: dir ? { dir } : {} })),
|
||||
|
||||
/**
|
||||
* 在 SimpleLite 桌面进程上弹 Windows 原生「打开文件」对话框,
|
||||
* 在 Simple3 桌面进程上弹 Windows 原生「打开文件」对话框,
|
||||
* 让用户挑一个项目 JSON。返回 { path, cancelled };cancelled=true 时 path 为 null。
|
||||
* initialDir 默认 SimpleLite 程序工作目录。
|
||||
* initialDir 默认 Simple3 程序工作目录。
|
||||
*/
|
||||
projectNativePickOpen: (initialDir?: string) =>
|
||||
unwrap<{ path: string | null; cancelled: boolean }>(
|
||||
@@ -328,7 +335,7 @@ export const mapEditApi = {
|
||||
|
||||
/**
|
||||
* 读取后端「过滤」菜单当前的三组开关。编辑器载入时拉一次,让前端 reactive 状态
|
||||
* 与 SimpleLite 真实开关一致(避免「前端勾着、后端没改」的偏差)。
|
||||
* 与 Simple3 真实开关一致(避免「前端勾着、后端没改」的偏差)。
|
||||
*/
|
||||
getViewFilter: () => unwrap<ViewFilterSnapshot>(http.get(`${BASE}/view-filter`)),
|
||||
|
||||
@@ -348,7 +355,7 @@ export const aiConfigApi = {
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
// 地图管理(固定文件夹 + 按名称列表 / 保存 / 删除 / 使用 / 编辑打开)
|
||||
// 对应 SimpleLite MapEditApiController 的 /map-edit/maps* 接口。
|
||||
// 对应 Simple3 MapEditApiController 的 /map-edit/maps* 接口。
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
export interface MapListItem {
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { Mission, MissionStatus } from '@/types/mission'
|
||||
import { mockSites, mockTracks, mockCars, mockMissions } from '@/mock/server'
|
||||
import { applyRuntimeEnrichment, deriveCarState } from '@/utils/carRuntime'
|
||||
|
||||
/** 设为 true 时使用本地 Mock;默认走 YARP → SimpleLite :8222。
|
||||
/** 设为 true 时使用本地 Mock;默认走 YARP → Simple3 :8222。
|
||||
* 与 auth.ts / config.ts / ops.ts 统一走 VITE_USE_MOCK 开关(替代旧的 VITE_PROJECTION_MOCK)。 */
|
||||
const MOCK = import.meta.env.VITE_USE_MOCK === 'true'
|
||||
|
||||
@@ -114,7 +114,7 @@ async function enrichCarsRuntime(cars: Car[]): Promise<Car[]> {
|
||||
)
|
||||
}
|
||||
|
||||
/** 优先走 SimpleLite /projection/cars;502 或空列表时回退 reflection 对象列表(与 3D 场景同源)。 */
|
||||
/** 优先走 Simple3 /projection/cars;502 或空列表时回退 reflection 对象列表(与 3D 场景同源)。 */
|
||||
export async function listCars(): Promise<Car[]> {
|
||||
if (MOCK) return mockCars()
|
||||
try {
|
||||
|
||||
@@ -19,10 +19,10 @@ import {
|
||||
const MOCK = import.meta.env.VITE_USE_MOCK === 'true'
|
||||
|
||||
/**
|
||||
* 与 SimpleLite `ReflectionApiController` 一一对应的前端胶水。
|
||||
* 路径前缀:`/sl/projection/reflection`(迷榖平台 YARP 反代下游 → SimpleLite EmbedIO)。
|
||||
* 与 Simple3 `ReflectionApiController` 一一对应的前端胶水。
|
||||
* 路径前缀:`/sl/projection/reflection`(迷榖平台 YARP 反代下游 → Simple3 EmbedIO)。
|
||||
*
|
||||
* 该客户端覆盖了 SimpleLite 主体以及通过 plugins/*.dll 动态加载的 Standard 等
|
||||
* 该客户端覆盖了 Simple3 主体以及通过 plugins/*.dll 动态加载的 Standard 等
|
||||
* 插件中所有标注了 `MethodMember` 的方法与所有继承自 Car/Mission/CarProgram/Site/Track
|
||||
* 的子类型,平台前端可据此动态渲染列表、属性、动作按钮。
|
||||
*/
|
||||
@@ -39,7 +39,7 @@ export type ReflectionKind =
|
||||
| 'map'
|
||||
| 'special'
|
||||
| 'script'
|
||||
// 顶级合成 kind:等于 site + track + special 的并集,由 SimpleLite 后端 /objects/scene 直接返回,
|
||||
// 顶级合成 kind:等于 site + track + special 的并集,由 Simple3 后端 /objects/scene 直接返回,
|
||||
// 每行带 subKind 字段,前端 bundle / execute / setField 走对应底层 kind。
|
||||
| 'scene'
|
||||
|
||||
@@ -167,7 +167,7 @@ export interface ScriptExceptionStatusPayload {
|
||||
// 地图监控可见性配置(与后端 MonitorVisibilityConfig / MonitorVisibilityForKind 对应)
|
||||
//
|
||||
// `config` = 管理员勾选的白名单。空数组 = 显示全部;非空 = 仅显示列表中的 key。
|
||||
// `available` = 当前 SimpleLite 进程里能扫描到的全部可勾选项(基类 + 已加载子类 +
|
||||
// `available` = 当前 Simple3 进程里能扫描到的全部可勾选项(基类 + 已加载子类 +
|
||||
// 运行时实例的 Prop.fields / status 反射键)。是 GET 返回的副产物,
|
||||
// POST 写入时不需要、也不该回传。
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
@@ -253,7 +253,7 @@ export function formatReflectionExecuteMessage(
|
||||
): string {
|
||||
if (result.returnValue) return `已执行:${result.returnValue}`
|
||||
if (result.accepted && result.completed === false) {
|
||||
return `已受理:${label}(后台继续执行,请稍后在 SimpleLite 查看结果)`
|
||||
return `已受理:${label}(后台继续执行,请稍后在 Simple3 查看结果)`
|
||||
}
|
||||
if (result.accepted) return `已执行 ${label}`
|
||||
return `已执行 ${label}`
|
||||
@@ -367,7 +367,7 @@ export const reflectionApi = {
|
||||
: del<{ kind: string; id: number; deleted: boolean }>(`/objects/${kind}/${id}`),
|
||||
|
||||
/**
|
||||
* 触发 SimpleLite 重新扫描 ./plugins 目录、加载新增 dll 并重建 UiDiscoveryCache。
|
||||
* 触发 Simple3 重新扫描 ./plugins 目录、加载新增 dll 并重建 UiDiscoveryCache。
|
||||
* 已加载的 dll 不会重复加载。
|
||||
* 返回:本次发现的 dll 总数、新加载的 assembly 数、当前可创建的 mission/car 类型计数。
|
||||
*/
|
||||
@@ -515,7 +515,7 @@ export const reflectionApi = {
|
||||
return data.data as MonitorConfigSaveBody
|
||||
},
|
||||
|
||||
// 选中同步:让 SimpleLite 3D 场景同步高亮被点击对象
|
||||
// 选中同步:让 Simple3 3D 场景同步高亮被点击对象
|
||||
getSelection: () => MOCK
|
||||
? Promise.resolve<ReflectionSelection>({ kind: null, id: 0, name: '' })
|
||||
: get<ReflectionSelection>('/selection'),
|
||||
@@ -685,7 +685,7 @@ export const reflectionApi = {
|
||||
: post<{ path: string }>('/car-style/save')
|
||||
}
|
||||
|
||||
/** 与 SimpleLite 视口样式对话框 / Configuration.conf.viewport 同步。 */
|
||||
/** 与 Simple3 视口样式对话框 / Configuration.conf.viewport 同步。 */
|
||||
export interface ViewportStyleSite {
|
||||
drawScale: number
|
||||
dotRadiusNormal: number
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import http from './http'
|
||||
|
||||
/**
|
||||
* 工作区画布工具栏 API(对应 SimpleLite `WorkspaceToolbarApiController`)。
|
||||
* 工作区画布工具栏 API(对应 Simple3 `WorkspaceToolbarApiController`)。
|
||||
*
|
||||
* 历史背景(会话33):原 SimpleLite iframe 内 ImGui 底栏 (`Panel_4` /
|
||||
* 历史背景(会话33):原 Simple3 iframe 内 ImGui 底栏 (`Panel_4` /
|
||||
* `WorkspaceBottomBar.DefineForTerminalEmbedMinimal`) 提供 8 个按钮,平台 iframe
|
||||
* 嵌入时该底栏会盖在画布上影响交互。本会话把这条底栏整体迁到 Vue 端
|
||||
* `WorkspaceCanvasToolbar.vue` 渲染,状态读写改走该 HTTP API。
|
||||
*
|
||||
* 路径前缀:`/sl/projection/toolbar` (Platform.Server YARP → SimpleLite EmbedIO :8222)
|
||||
* 路径前缀:`/sl/projection/toolbar` (Platform.Server YARP → Simple3 EmbedIO :8222)
|
||||
*/
|
||||
|
||||
const BASE = '/sl/projection/toolbar'
|
||||
@@ -155,7 +155,7 @@ export const workspaceToolbarApi = {
|
||||
|
||||
/**
|
||||
* 一次性把地图相机定位(居中 + 2D 俯视)到指定车辆,不开启持续跟随。
|
||||
* 对应 SimpleLite `WorkspaceToolbarApiController.LocateCamera`(与原生「双击车辆行 = 选中+定位」一致)。
|
||||
* 对应 Simple3 `WorkspaceToolbarApiController.LocateCamera`(与原生「双击车辆行 = 选中+定位」一致)。
|
||||
*/
|
||||
locateCamera: (carId: number) =>
|
||||
unwrap<ToolbarState>(
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
<template>
|
||||
<PermissionGuard widget-id="ConfigCenter">
|
||||
<el-card shadow="never">
|
||||
<slot
|
||||
v-if="$slots.chrome"
|
||||
name="chrome"
|
||||
:payload="payload"
|
||||
:update="update"
|
||||
:save="save"
|
||||
:reload="reload"
|
||||
:saving="saving"
|
||||
:loading="loading"
|
||||
:envelope="envelope"
|
||||
:relative-time="relativeTime"
|
||||
/>
|
||||
<el-card v-else shadow="never">
|
||||
<template #header>
|
||||
<div class="cpb-header">
|
||||
<span class="cpb-title">{{ title }}</span>
|
||||
@@ -9,7 +21,7 @@
|
||||
<el-tag size="small" type="success" effect="plain" v-if="envelope?.updatedAt">{{ relativeTime }}</el-tag>
|
||||
<div class="spacer" />
|
||||
<el-button size="small" :icon="Refresh" :loading="loading" @click="reload(true)">重载</el-button>
|
||||
<el-button size="small" type="primary" :icon="Check" :loading="saving" @click="save">保存</el-button>
|
||||
<el-button size="small" type="primary" :icon="Check" :loading="saving" @click="save()">保存</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -50,11 +62,11 @@ const props = defineProps<{
|
||||
defaults: T
|
||||
/** 加载/保存前规范化 payload(如 ops 补全 monitor 字段) */
|
||||
normalizePayload?: (payload: T) => T
|
||||
/** 加载完成后二次合并(如从 SimpleLite 拉 monitor) */
|
||||
/** 加载完成后二次合并(如从 Simple3 拉 monitor) */
|
||||
afterLoad?: (payload: T, update: (next: T) => void) => void | Promise<void>
|
||||
/** 写入 Platform 之前先执行(如先落盘 SimpleLite monitor) */
|
||||
/** 写入 Platform 之前先执行(如先落盘 Simple3 monitor) */
|
||||
beforeSave?: (payload: T) => void | Promise<void>
|
||||
/** 保存成功后副作用(如再次同步 SimpleLite) */
|
||||
/** 保存成功后副作用(如再次同步 Simple3) */
|
||||
afterSave?: (payload: T) => void | Promise<void>
|
||||
}>()
|
||||
|
||||
@@ -98,7 +110,8 @@ async function reload(force = false) {
|
||||
await reloadFromServer(force, force)
|
||||
}
|
||||
|
||||
async function save() {
|
||||
async function save(opts?: { toast?: boolean }): Promise<boolean> {
|
||||
const showToast = typeof opts === 'object' && opts && 'toast' in opts ? opts.toast !== false : true
|
||||
saving.value = true
|
||||
let body = payload.value
|
||||
if (props.normalizePayload) body = props.normalizePayload(body)
|
||||
@@ -109,7 +122,7 @@ async function save() {
|
||||
} catch (e) {
|
||||
ElMessage.error(`地图监控配置保存失败:${e instanceof Error ? e.message : String(e)}`)
|
||||
saving.value = false
|
||||
return
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,14 +135,16 @@ async function save() {
|
||||
try {
|
||||
await props.afterSave(body)
|
||||
} catch (e) {
|
||||
monitorSyncWarn = `(平台配置已保存,但 SimpleLite 同步失败:${e instanceof Error ? e.message : String(e)})`
|
||||
monitorSyncWarn = `(平台配置已保存,但 Simple3 同步失败:${e instanceof Error ? e.message : String(e)})`
|
||||
}
|
||||
}
|
||||
await reloadFromServer(true, false)
|
||||
const extra = props.beforeSave || props.afterSave ? ',地图监控配置已写入 SimpleLite' : ''
|
||||
ElMessage.success(`已保存 v${env.version}${extra}${monitorSyncWarn}`)
|
||||
const extra = props.beforeSave || props.afterSave ? ',地图监控配置已写入 Simple3' : ''
|
||||
if (showToast) ElMessage.success(`已保存 v${env.version}${extra}${monitorSyncWarn}`)
|
||||
return true
|
||||
} catch (e) {
|
||||
ElMessage.error(`保存失败:${e instanceof Error ? e.message : String(e)}`)
|
||||
return false
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<div v-if="!loaded" class="workspace-3d-mask">
|
||||
<el-icon class="is-loading" size="32"><Loading /></el-icon>
|
||||
<span>{{ loadingHint }}</span>
|
||||
<span class="muted">如长时间未加载,请确认 SimpleLite 已以 Web-Enabled 模式启动且 8223 端口可达。</span>
|
||||
<span class="muted">如长时间未加载,请确认 Simple3 已以 Web-Enabled 模式启动且 8223 端口可达。</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -45,7 +45,7 @@ const props = withDefaults(defineProps<{
|
||||
scope?: Scope
|
||||
token?: string
|
||||
readOnly?: boolean
|
||||
/** 嵌入平台页时传 true:SimpleLite 仅显示底栏(对齐/选择/右击/图层/内容/录制回放) */
|
||||
/** 嵌入平台页时传 true:Simple3 仅显示底栏(对齐/选择/右击/图层/内容/录制回放) */
|
||||
embedUi?: boolean
|
||||
/**
|
||||
* 地图编辑器纯画布模式:比 embedUi 更裸,连底栏都不创建。
|
||||
@@ -116,34 +116,73 @@ function declareEndpoint() {
|
||||
return null
|
||||
}
|
||||
|
||||
/** 单次 HTTP 探测(带超时),用于 declare / vrenderReady。 */
|
||||
async function pingVrender(path: string): Promise<boolean> {
|
||||
try {
|
||||
const res = await fetch(`${vrenderBaseUrl()}${path}`, {
|
||||
method: 'GET',
|
||||
cache: 'no-store',
|
||||
credentials: 'omit',
|
||||
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
||||
})
|
||||
return res.ok
|
||||
} catch {
|
||||
return false
|
||||
/** 单次 HTTP 探测(带超时)。Simple3 把 declare* 挂在 Projection :8222,不再走 FairyView :8223。 */
|
||||
async function pingDeclare(path: string): Promise<boolean> {
|
||||
const token = localStorage.getItem('simple.auth.token')
|
||||
// 优先走平台 YARP → :8222;兼容 /projection/ui 前缀;最后回退旧 Simple3 的 :8223 LeastServer。
|
||||
const urls = [
|
||||
`/api/sl${path}`,
|
||||
`/api/sl/projection/ui${path}`,
|
||||
`http://${resolvedHost.value}${path}`
|
||||
]
|
||||
for (const url of urls) {
|
||||
try {
|
||||
const headers: Record<string, string> = {}
|
||||
if (token && url.startsWith('/')) headers.Authorization = `Bearer ${token}`
|
||||
const res = await fetch(url, {
|
||||
method: 'GET',
|
||||
cache: 'no-store',
|
||||
credentials: url.startsWith('/') ? 'include' : 'omit',
|
||||
headers,
|
||||
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
||||
})
|
||||
if (res.ok) return true
|
||||
} catch {
|
||||
/* try next */
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/** 画布就绪探测:仍优先打 FairyView :8223(iframe 同源),失败再试 Projection 镜像。 */
|
||||
async function pingVrender(path: string): Promise<boolean> {
|
||||
const candidates = [
|
||||
`http://${resolvedHost.value}${path}`,
|
||||
`/api/sl${path}`,
|
||||
`/api/sl/projection/ui${path}`
|
||||
]
|
||||
const token = localStorage.getItem('simple.auth.token')
|
||||
for (const url of candidates) {
|
||||
try {
|
||||
const headers: Record<string, string> = {}
|
||||
if (token && url.startsWith('/')) headers.Authorization = `Bearer ${token}`
|
||||
const res = await fetch(url, {
|
||||
method: 'GET',
|
||||
cache: 'no-store',
|
||||
credentials: url.startsWith('/') ? 'include' : 'omit',
|
||||
headers,
|
||||
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
||||
})
|
||||
if (res.ok) return true
|
||||
} catch {
|
||||
/* try next */
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* 尽力 declare(不阻塞 iframe)。SimpleLite 侧 WebTerminal 已默认 canvas-only,
|
||||
* declare 主要用于多 tab 显式标记;失败也不影响平台纯画布模式。
|
||||
* 尽力 declare。Simple3 须打 Projection :8222(经 /api/sl),不能只打 :8223。
|
||||
*/
|
||||
async function postDeclareBestEffort() {
|
||||
const endpoint = declareEndpoint()
|
||||
if (!endpoint) return
|
||||
|
||||
for (let attempt = 0; attempt < DECLARE_MAX_ATTEMPTS; attempt++) {
|
||||
if (await pingVrender(endpoint)) return
|
||||
if (await pingDeclare(endpoint)) return
|
||||
await sleep(80 * (attempt + 1))
|
||||
}
|
||||
console.warn(`[Workspace3D] ${endpoint} 未成功,将依赖 SimpleLite 默认 canvas-only 模式`)
|
||||
console.warn(`[Workspace3D] ${endpoint} 未成功,将依赖内核默认 canvas-only 模式`)
|
||||
}
|
||||
|
||||
/** 非嵌入模式:短轮询等待 8223 就绪后再加载 iframe。 */
|
||||
@@ -169,7 +208,7 @@ function startLoadWatchdog(generation: number) {
|
||||
clearLoadWatchdog()
|
||||
loadWatchdog = setTimeout(() => {
|
||||
if (generation !== prepareGeneration || loaded.value) return
|
||||
loadingHint.value = `连接超时 (http://${resolvedHost.value}),请确认 SimpleLite 已启动`
|
||||
loadingHint.value = `连接超时 (http://${resolvedHost.value}),请确认 Simple3 已启动`
|
||||
}, LOAD_WATCHDOG_MS)
|
||||
}
|
||||
|
||||
@@ -199,7 +238,7 @@ async function prepareAndLoad(forceReload = false) {
|
||||
}
|
||||
iframeSrc.value = buildIframeSrc(forceReload || !!iframeSrc.value)
|
||||
} else {
|
||||
loadingHint.value = '正在等待 SimpleLite webVRender 就绪 ...'
|
||||
loadingHint.value = '正在等待 Simple3 webVRender 就绪 ...'
|
||||
await waitForVrenderReady()
|
||||
if (generation !== prepareGeneration) return
|
||||
iframeSrc.value = buildIframeSrc(forceReload || !!iframeSrc.value)
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<div v-if="!collapsed">
|
||||
<el-empty
|
||||
v-if="!loading && plugins.length === 0"
|
||||
description="尚未通过 PluginManager 加载任何插件 (重启 SimpleLite 后会自动迁移现有 plugins/ 内的 dll)"
|
||||
description="尚未通过 PluginManager 加载任何插件 (重启 Simple3 后会自动迁移现有 plugins/ 内的 dll)"
|
||||
/>
|
||||
<el-table
|
||||
v-else
|
||||
@@ -44,7 +44,7 @@
|
||||
<el-tag v-if="row.collectible" size="small" type="success">可卸载</el-tag>
|
||||
<el-tooltip
|
||||
v-else
|
||||
content="此插件被加到 default ALC(旧路径加载),重启 SimpleLite 后会自动迁移到 collectible"
|
||||
content="此插件被加到 default ALC(旧路径加载),重启 Simple3 后会自动迁移到 collectible"
|
||||
placement="top"
|
||||
>
|
||||
<el-tag size="small" type="warning">不可卸载</el-tag>
|
||||
@@ -98,7 +98,7 @@ async function refresh() {
|
||||
try {
|
||||
plugins.value = await reflectionApi.listPlugins()
|
||||
} catch (err) {
|
||||
ElMessage.warning(`插件列表加载失败:${(err as Error).message}(旧版 SimpleLite 可能未带 /plugins 接口,请重启)`)
|
||||
ElMessage.warning(`插件列表加载失败:${(err as Error).message}(旧版 Simple3 可能未带 /plugins 接口,请重启)`)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
+3
-3
@@ -150,7 +150,7 @@
|
||||
>
|
||||
查看对象
|
||||
</el-button>
|
||||
<!-- 脚本管理专用:与 SimpleLite 工作台「脚本」表格行内按钮组对齐,
|
||||
<!-- 脚本管理专用:与 Simple3 工作台「脚本」表格行内按钮组对齐,
|
||||
选不选中都能直接点行内「查看脚本 / 查看异常状态」弹窗。 -->
|
||||
<template v-if="showScriptActions && kind === 'script'">
|
||||
<el-button
|
||||
@@ -492,7 +492,7 @@
|
||||
* site/track/image/text/model 走弹窗表单收集 x/y/siteA/siteB 等
|
||||
* - 删除:reflectionApi.deleteObject
|
||||
* - 方法调用:reflectionApi.execute(带参时弹 prompt 逐项收集)
|
||||
* - 3D 高亮:reflectionApi.setSelection 让 SimpleLite 工作台同步选中
|
||||
* - 3D 高亮:reflectionApi.setSelection 让 Simple3 工作台同步选中
|
||||
*
|
||||
* SSE 订阅:object-created/deleted/patched/batch-changed 都会自动 refresh。
|
||||
*/
|
||||
@@ -556,7 +556,7 @@ const props = defineProps<{
|
||||
* 列表状态列取自运行状态反射中的 key(如进程 Mission 的 status)。
|
||||
*/
|
||||
statusReflectionKey?: string
|
||||
/** 脚本管理专用:在动作区追加 SimpleLite 工作台同款「查看脚本 / 查看异常状态」。 */
|
||||
/** 脚本管理专用:在动作区追加 Simple3 工作台同款「查看脚本 / 查看异常状态」。 */
|
||||
showScriptActions?: boolean
|
||||
/** 增删改后是否自动写回项目 JSON。默认车辆/场景/进程启用。 */
|
||||
autoSaveProject?: boolean
|
||||
|
||||
+1
-1
@@ -363,7 +363,7 @@ async function loadActions() {
|
||||
async function onExecute(m: ReflectionMethod) {
|
||||
// 无 methodName = workbench 兜底 action,保留旧的"提示"语义,避免误导用户以为已执行。
|
||||
if (!m.methodName) {
|
||||
ElMessage.info(`动作「${m.label || m.methodName}」需在 SimpleLite 工作台执行(当前对象类型未接入反射 execute)。`)
|
||||
ElMessage.info(`动作「${m.label || m.methodName}」需在 Simple3 工作台执行(当前对象类型未接入反射 execute)。`)
|
||||
return
|
||||
}
|
||||
const rk = reflectionKind.value
|
||||
|
||||
@@ -65,7 +65,7 @@ const search = ref('')
|
||||
const selectedId = ref<string | null>(null)
|
||||
|
||||
const navHints: Record<WorkbenchNav, string> = {
|
||||
map: '地图/装饰对象列表(对齐 SimpleLite 工作台「地图」)',
|
||||
map: '地图/装饰对象列表(对齐 Simple3 工作台「地图」)',
|
||||
process: '自定义进程 Mission 列表',
|
||||
vehicle: '场景车辆列表(ID / 地址 / 名称 / 概况)',
|
||||
scene: '站点、路径、装饰物',
|
||||
|
||||
+3
-3
@@ -260,10 +260,10 @@ function publishState(next: ToolbarState | null) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 平台 iframe 画布工具栏(对应 SimpleLite `Panel_4` / `WorkspaceBottomBar.DefineForTerminalEmbedMinimal`)。
|
||||
* 平台 iframe 画布工具栏(对应 Simple3 `Panel_4` / `WorkspaceBottomBar.DefineForTerminalEmbedMinimal`)。
|
||||
*
|
||||
* 设计原则:
|
||||
* - 所有状态都通过 `/sl/projection/toolbar/*` 走 SimpleLite 全局状态,多 tab 同源。
|
||||
* - 所有状态都通过 `/sl/projection/toolbar/*` 走 Simple3 全局状态,多 tab 同源。
|
||||
* - `state` 是后端权威单一真值;本地不维护"乐观"切换:每次 toggle 都拿后端最新返回回写。
|
||||
* - 录制 / 回放期间 1Hz 轮询刷新 elapsed / 帧计数,避免页面打开后停留在初值。
|
||||
* - 错误用 ElMessage.error 提示,不阻断画布主操作。
|
||||
@@ -386,7 +386,7 @@ async function toggleLayer(name: string, visible: boolean) {
|
||||
}
|
||||
|
||||
function onContextMenuPlaceholder() {
|
||||
ElMessage.info('寻路右键动作尚未接入 SimpleLite;请使用 SegmentPlan.FindRoute / 工作台脚本。')
|
||||
ElMessage.info('寻路右键动作尚未接入 Simple3;请使用 SegmentPlan.FindRoute / 工作台脚本。')
|
||||
}
|
||||
|
||||
async function toggleRecording() {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { onBeforeUnmount, onMounted } from 'vue'
|
||||
|
||||
/**
|
||||
* 与 Workspace3D iframe(SimpleLite WebTerminal)的 postMessage 双向桥。
|
||||
* 与 Workspace3D iframe(Simple3 WebTerminal)的 postMessage 双向桥。
|
||||
*
|
||||
* 当前 SimpleLite WebTerminal 默认会通过 window.postMessage 抛出 `workspace.pick`
|
||||
* 当前 Simple3 WebTerminal 默认会通过 window.postMessage 抛出 `workspace.pick`
|
||||
* 与 `workspace.select` 事件给父窗口(Workspace3D.vue 收到后再 emit 给业务页)。
|
||||
* 反向的 vue→iframe 通道(设置工具、应用对齐等)走 SimpleLite 反射 API(HTTP/SSE),
|
||||
* 反向的 vue→iframe 通道(设置工具、应用对齐等)走 Simple3 反射 API(HTTP/SSE),
|
||||
* 这里的桥只承担**坐标 / 选中 / hover** 三类 iframe→vue 数据。
|
||||
*/
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import { ref, computed } from 'vue'
|
||||
*
|
||||
* 工具切换有两个层面:
|
||||
* - **前端切换**:仅影响 Vue 端的按钮高亮 + 状态栏 + 下一次画布点击的本地处理(如 pick 后调反射 API 创建对象)。
|
||||
* - **后端切换**:通过反射 API `/execute/...` 触发 SimpleLite 的 CAD action(吸附 / 对齐 / 框选等)。
|
||||
* - **后端切换**:通过反射 API `/execute/...` 触发 Simple3 的 CAD action(吸附 / 对齐 / 框选等)。
|
||||
*
|
||||
* 简化起见:本 composable 只负责前端状态,CAD action 由调用方决定何时触发。
|
||||
*/
|
||||
|
||||
@@ -2,7 +2,7 @@ import { onBeforeUnmount, onMounted } from 'vue'
|
||||
import { useProjectionStream, type StreamEvent } from './useProjectionStream'
|
||||
|
||||
/**
|
||||
* 编辑器 / 监控页对 SimpleLite SSE 的事件订阅封装,把以下 5 类事件转给业务回调:
|
||||
* 编辑器 / 监控页对 Simple3 SSE 的事件订阅封装,把以下 5 类事件转给业务回调:
|
||||
*
|
||||
* - `alarm` (来自 AlarmStreamService):报警新增/更新/解除
|
||||
* - `object-created` (来自 MapEditApiController):对象创建后
|
||||
@@ -38,14 +38,14 @@ export interface PickResultEvent {
|
||||
}
|
||||
|
||||
export interface SelectionDetailEvent {
|
||||
/** 主选中 kind (site|track|car|null);当多选时取首个非空集合的 kind。 */
|
||||
kind: 'site' | 'track' | 'car' | null
|
||||
/** 主选中 kind;当多选时取首个非空集合的 kind。 */
|
||||
kind: 'site' | 'track' | 'car' | 'special' | null
|
||||
/** 主选中 id;多选时取首个。 */
|
||||
id: number
|
||||
siteIds: number[]
|
||||
trackIds: number[]
|
||||
carIds: number[]
|
||||
/** 完整对象名列表,例如 ["UISite-12", "UITrack-7", "Car-3"]。 */
|
||||
/** 完整对象名列表,例如 ["UISite-12", "UITrack-7", "Car-3", "UIText-5"]。 */
|
||||
names: string[]
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ export interface MapEditStreamHandlers {
|
||||
onObjectBatchChanged?: (e: { count: number; source?: string }) => void
|
||||
onPickResult?: (e: PickResultEvent) => void
|
||||
onSelectionDetail?: (e: SelectionDetailEvent) => void
|
||||
/** iframe 画布聚焦时由 SimpleLite 经 SSE 转发的撤销/重做/删除。 */
|
||||
/** iframe 画布聚焦时由 Simple3 经 SSE 转发的撤销/重做/删除。 */
|
||||
onWorkspaceShortcut?: (e: WorkspaceShortcutEvent) => void
|
||||
/** 视口样式(站点/路径 Painter)在 Web 或桌面对话框中变更后广播。 */
|
||||
onViewportStyleUpdated?: () => void
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { onUnmounted, ref, type Ref } from 'vue'
|
||||
|
||||
/**
|
||||
* 与 SimpleLite `/projection/stream`(SSE)通信的轻量封装。
|
||||
* 与 Simple3 `/projection/stream`(SSE)通信的轻量封装。
|
||||
*
|
||||
* 服务器以 `text/event-stream` 推送 JSON 行:
|
||||
* ```
|
||||
@@ -33,7 +33,7 @@ export interface ProjectionStreamOptions {
|
||||
}
|
||||
|
||||
/**
|
||||
* SimpleLite 当前后端会广播的全部 SSE 事件名。新增后端事件时务必同步追加,
|
||||
* Simple3 当前后端会广播的全部 SSE 事件名。新增后端事件时务必同步追加,
|
||||
* 否则 EventSource 拿不到该事件 —— 这是历史上 alarm / object-* 被静默丢弃的根因。
|
||||
*/
|
||||
export const KNOWN_EVENT_KINDS = [
|
||||
@@ -123,7 +123,7 @@ export function useProjectionStream(opts: ProjectionStreamOptions = {}) {
|
||||
|
||||
// 命名事件:EventSource 默认只把"无 event: 头"的消息派发给 onmessage,
|
||||
// 任何 `event: foo` 都必须通过 addEventListener('foo', ...) 才能收到。
|
||||
// 必须覆盖 SimpleLite 后端可能广播的所有事件名 —— 漏一个就等于该事件被丢弃。
|
||||
// 必须覆盖 Simple3 后端可能广播的所有事件名 —— 漏一个就等于该事件被丢弃。
|
||||
// 当前后端事件源:
|
||||
// - ProjectionStreamModule: snapshot-tick (1Hz 心跳/计数)
|
||||
// - SimpleUI / ReflectionApiController: selection-detail
|
||||
|
||||
@@ -5,8 +5,7 @@ import type { ReflectionObject, ReflectionKind } from '@/api/reflection'
|
||||
* 编辑器内的多选集合 + 类型过滤工具。
|
||||
* 选中集是 `(kind, id)` 二元组集合:用 `${kind}:${id}` 作 key 去重。
|
||||
*
|
||||
* 与 SimpleLite 后端的同步:单选时把 selection 同步给 ReflectionApi(高亮 3D 场景),
|
||||
* 多选不同步(高亮不展开是因为反射 API 当前只支持单选;如需多选高亮以后再扩展)。
|
||||
* 选中集由画布 postMessage / SSE 写入。编辑页不要在 watch 里回写 SetSelection。
|
||||
*/
|
||||
|
||||
export interface SelectionItem extends ReflectionObject {
|
||||
|
||||
@@ -70,7 +70,6 @@
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<ThemeSwitcher />
|
||||
<ScopeSwitcher />
|
||||
<span class="runmode-tag" :class="auth.runMode">{{ runModeLabel }}</span>
|
||||
<el-dropdown trigger="click" @command="onUserCommand">
|
||||
<span class="user-link">
|
||||
@@ -91,6 +90,7 @@
|
||||
</el-header>
|
||||
|
||||
<el-main class="main mg-content">
|
||||
<WizardSetupBar />
|
||||
<!-- 不用 mode="out-in":懒加载子路由切换时易出现「离场后新组件未挂载」→ 主区全白,刷新才恢复。 -->
|
||||
<router-view v-slot="{ Component, route: rv }">
|
||||
<component :is="Component" v-if="Component" :key="rv.fullPath" class="route-page" />
|
||||
@@ -115,9 +115,9 @@ import { Fold, Expand, CaretBottom } from '@element-plus/icons-vue'
|
||||
import { ADMIN_MENU, MONITOR_MENU, type NavMenuItem } from '@/config/navMenu'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { useUiStore } from '@/stores/ui'
|
||||
import ScopeSwitcher from '@/components/ScopeSwitcher.vue'
|
||||
import ThemeSwitcher from '@/components/ThemeSwitcher.vue'
|
||||
import AiAssistantDrawer from '@/components/assistant/AiAssistantDrawer.vue'
|
||||
import WizardSetupBar from '@/components/wizard/WizardSetupBar.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
@@ -129,13 +129,13 @@ const apiBase = computed(() => (import.meta.env.VITE_API_BASE as string | undefi
|
||||
|
||||
const initial = computed(() => (auth.user?.displayName ?? auth.user?.username ?? '?').slice(0, 1).toUpperCase())
|
||||
|
||||
const scopeLabel = computed(() => (auth.scope === 'Platform' ? '管理员 (Platform)' : '运营 (RCSMonitor)'))
|
||||
const scopeLabel = computed(() => (auth.scope === 'Platform' ? '管理' : '运营'))
|
||||
|
||||
const runModeLabel = computed(() => {
|
||||
switch (auth.runMode) {
|
||||
case 'WebOnly': return 'Web-Only'
|
||||
case 'WebEnabled': return 'Web + Desktop'
|
||||
case 'Detached': return 'SimpleLite 未连接'
|
||||
case 'Detached': return 'Simple3 未连接'
|
||||
default: return String(auth.runMode ?? 'Unknown')
|
||||
}
|
||||
})
|
||||
@@ -498,7 +498,7 @@ function onUserCommand(cmd: string) {
|
||||
border-color: rgba(var(--mg-primary-hover-rgb), 0.45);
|
||||
color: var(--mg-accent);
|
||||
}
|
||||
/* 会话 N+1 增量:SimpleLite 未拉起时的降级展示,用警示色而非成功色,引导用户感知"实际只有 Platform"。 */
|
||||
/* 会话 N+1 增量:Simple3 未拉起时的降级展示,用警示色而非成功色,引导用户感知"实际只有 Platform"。 */
|
||||
.runmode-tag.Detached {
|
||||
background: rgba(var(--mg-status-warning-rgb), 0.18);
|
||||
border-color: rgba(var(--mg-status-warning-rgb), 0.45);
|
||||
|
||||
@@ -3,10 +3,10 @@ import type { Car } from '@/types/car'
|
||||
const NOW = new Date().toISOString()
|
||||
|
||||
export const CARS: Car[] = [
|
||||
{ id: 'C01', rawId: 1, name: 'AGV-001', typeName: 'SimpleLite.RCS.CarTypes.DummyCar', x: 1200, y: 2100, theta: 0, batterySoc: 0.86, state: 'running', missionId: 'M01', lastUpdate: NOW, group: 'A 区', ip: '10.0.1.11', onboardUrl: 'http://10.0.1.11:8081', lstatus: '运行' },
|
||||
{ id: 'C02', rawId: 2, name: 'AGV-002', typeName: 'SimpleLite.RCS.CarTypes.DummyCar', x: 2900, y: 2100, theta: 90, batterySoc: 0.42, state: 'running', missionId: 'M02', lastUpdate: NOW, group: 'A 区', ip: '10.0.1.12', onboardUrl: 'http://10.0.1.12:8081', lstatus: '运行' },
|
||||
{ id: 'C03', rawId: 3, name: 'AGV-003', typeName: 'SimpleLite.RCS.CarTypes.DummyCar', x: 1000, y: 4900, theta: 180, batterySoc: 1.0, state: 'charging', lastUpdate: NOW, group: 'A 区', ip: '10.0.1.13', onboardUrl: 'http://10.0.1.13:8081', lstatus: '充电' },
|
||||
{ id: 'C04', rawId: 4, name: 'AGV-004', typeName: 'SimpleLite.RCS.CarTypes.GhostCar', x: 7000, y: 2050, theta: 0, batterySoc: 0.71, state: 'idle', lastUpdate: NOW, group: 'B 区', ip: '10.0.1.14', onboardUrl: 'http://10.0.1.14:8081', lstatus: '空闲' },
|
||||
{ id: 'C05', rawId: 5, name: 'AGV-005', typeName: 'SimpleLite.RCS.CarTypes.GhostCar', x: 7050, y: 4000, theta: 270, batterySoc: 0.18, state: 'fault', lastUpdate: NOW, group: 'B 区', ip: '10.0.1.15', onboardUrl: 'http://10.0.1.15:8081', lstatus: '故障' },
|
||||
{ id: 'C06', rawId: 6, name: 'AGV-006', typeName: 'SimpleLite.RCS.CarTypes.DummyCar', x: 9000, y: 2000, theta: 0, batterySoc: 0.95, state: 'offline', lastUpdate: NOW, group: '维护', ip: '10.0.1.16', onboardUrl: 'http://10.0.1.16:8081', lstatus: '下线' }
|
||||
{ id: 'C01', rawId: 1, name: 'AGV-001', typeName: 'Simple3.RCS.CarTypes.DummyCar', x: 1200, y: 2100, theta: 0, batterySoc: 0.86, state: 'running', missionId: 'M01', lastUpdate: NOW, group: 'A 区', ip: '10.0.1.11', onboardUrl: 'http://10.0.1.11:8081', lstatus: '运行' },
|
||||
{ id: 'C02', rawId: 2, name: 'AGV-002', typeName: 'Simple3.RCS.CarTypes.DummyCar', x: 2900, y: 2100, theta: 90, batterySoc: 0.42, state: 'running', missionId: 'M02', lastUpdate: NOW, group: 'A 区', ip: '10.0.1.12', onboardUrl: 'http://10.0.1.12:8081', lstatus: '运行' },
|
||||
{ id: 'C03', rawId: 3, name: 'AGV-003', typeName: 'Simple3.RCS.CarTypes.DummyCar', x: 1000, y: 4900, theta: 180, batterySoc: 1.0, state: 'charging', lastUpdate: NOW, group: 'A 区', ip: '10.0.1.13', onboardUrl: 'http://10.0.1.13:8081', lstatus: '充电' },
|
||||
{ id: 'C04', rawId: 4, name: 'AGV-004', typeName: 'Simple3.RCS.CarTypes.GhostCar', x: 7000, y: 2050, theta: 0, batterySoc: 0.71, state: 'idle', lastUpdate: NOW, group: 'B 区', ip: '10.0.1.14', onboardUrl: 'http://10.0.1.14:8081', lstatus: '空闲' },
|
||||
{ id: 'C05', rawId: 5, name: 'AGV-005', typeName: 'Simple3.RCS.CarTypes.GhostCar', x: 7050, y: 4000, theta: 270, batterySoc: 0.18, state: 'fault', lastUpdate: NOW, group: 'B 区', ip: '10.0.1.15', onboardUrl: 'http://10.0.1.15:8081', lstatus: '故障' },
|
||||
{ id: 'C06', rawId: 6, name: 'AGV-006', typeName: 'Simple3.RCS.CarTypes.DummyCar', x: 9000, y: 2000, theta: 0, batterySoc: 0.95, state: 'offline', lastUpdate: NOW, group: '维护', ip: '10.0.1.16', onboardUrl: 'http://10.0.1.16:8081', lstatus: '下线' }
|
||||
]
|
||||
|
||||
@@ -4,7 +4,7 @@ const NOW = new Date().toISOString()
|
||||
|
||||
export const MISSIONS: Mission[] = [
|
||||
{
|
||||
id: 'M01', name: 'A 区送料 #1', typeName: 'SimpleLite.RCS.Missions.MoveMission',
|
||||
id: 'M01', name: 'A 区送料 #1', typeName: 'Simple3.RCS.Missions.MoveMission',
|
||||
priority: 50, status: 'running', carId: 'C01', createdAt: NOW,
|
||||
steps: [
|
||||
{ id: 'S1', action: 'pickup', targetSiteId: 'S001', status: 'completed' },
|
||||
@@ -13,7 +13,7 @@ export const MISSIONS: Mission[] = [
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'M02', name: 'A→B 缓存搬运', typeName: 'SimpleLite.RCS.Missions.MoveMission',
|
||||
id: 'M02', name: 'A→B 缓存搬运', typeName: 'Simple3.RCS.Missions.MoveMission',
|
||||
priority: 60, status: 'running', carId: 'C02', createdAt: NOW,
|
||||
steps: [
|
||||
{ id: 'S1', action: 'pickup', targetSiteId: 'S002', status: 'completed' },
|
||||
@@ -21,7 +21,7 @@ export const MISSIONS: Mission[] = [
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'M03', name: 'B 区工位 W1 投料', typeName: 'SimpleLite.RCS.Missions.MoveMission',
|
||||
id: 'M03', name: 'B 区工位 W1 投料', typeName: 'Simple3.RCS.Missions.MoveMission',
|
||||
priority: 70, status: 'queued', createdAt: NOW,
|
||||
steps: [
|
||||
{ id: 'S1', action: 'pickup', targetSiteId: 'S003', status: 'queued' },
|
||||
@@ -29,7 +29,7 @@ export const MISSIONS: Mission[] = [
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'M04', name: 'W2→维护检修', typeName: 'SimpleLite.RCS.Missions.MaintenanceMission',
|
||||
id: 'M04', name: 'W2→维护检修', typeName: 'Simple3.RCS.Missions.MaintenanceMission',
|
||||
priority: 30, status: 'paused', carId: 'C05', createdAt: NOW,
|
||||
steps: [
|
||||
{ id: 'S1', action: 'goto', targetSiteId: 'S010', status: 'paused' }
|
||||
|
||||
@@ -254,7 +254,7 @@ export function mockReflectionKinds(): { kinds: ReflectionKindMeta[]; subKinds:
|
||||
export function mockReflectionAssemblies(): ReflectionAssembly[] {
|
||||
return [
|
||||
{ name: 'SimpleCore', version: '1.0.0' },
|
||||
{ name: 'SimpleLite', version: '1.0.0' },
|
||||
{ name: 'Simple3', version: '1.0.0' },
|
||||
{ name: 'StandardScene', version: '1.4.0', location: 'plugins/StandardScene.dll' },
|
||||
{ name: 'CustomFlightDeck', version: '0.1.0', location: 'plugins/CustomFlightDeck.dll' }
|
||||
]
|
||||
@@ -273,7 +273,7 @@ export function mockReflectionMethodsByType(kind: ReflectionKind): ReflectionTyp
|
||||
if (kind === 'car' || kind === 'vehicle' || kind === 'script') {
|
||||
return [
|
||||
{ typeName: 'GhostCar', assemblyName: 'StandardScene', methods: ms },
|
||||
{ typeName: 'DummyCar', assemblyName: 'SimpleLite', methods: ms.slice(0, 2) }
|
||||
{ typeName: 'DummyCar', assemblyName: 'Simple3', methods: ms.slice(0, 2) }
|
||||
]
|
||||
}
|
||||
if (kind === 'mission' || kind === 'process') {
|
||||
@@ -282,7 +282,7 @@ export function mockReflectionMethodsByType(kind: ReflectionKind): ReflectionTyp
|
||||
{ typeName: 'PatrolMission', assemblyName: 'CustomFlightDeck', methods: ms.slice(0, 2) }
|
||||
]
|
||||
}
|
||||
return [{ typeName: kind, assemblyName: 'SimpleLite', methods: ms }]
|
||||
return [{ typeName: kind, assemblyName: 'Simple3', methods: ms }]
|
||||
}
|
||||
|
||||
export function mockReflectionStatus(kind: ReflectionKind, id: number) {
|
||||
|
||||
@@ -2,20 +2,19 @@ export type Scope = 'Platform' | 'RCSMonitor'
|
||||
|
||||
/**
|
||||
* 启动后实际运行模式(由后端在登录响应中告知,仅作展示用)。
|
||||
* - `WebEnabled`:SimpleLite 同时启动了 LocalTerminal(本地桌面窗口)+ WebTerminal(浏览器端)。
|
||||
* - `WebOnly`:SimpleLite 只启动了 WebTerminal,没有本地桌面窗口,全程浏览器使用。
|
||||
* - `Detached`:会话 N+1 增量字段。Platform.Server 未能拉起 SimpleLite(appsettings 关闭 / exe 找不到 /
|
||||
* Process.Start 失败 / 启动异常),前端应该提示「SimpleLite 未连接」并降级 /api/sl/* 调用。
|
||||
* - `WebEnabled`:Simple3 同时启动了 LocalTerminal(本地桌面窗口)+ WebTerminal(浏览器端)。
|
||||
* - `WebOnly`:Simple3 只启动了 WebTerminal,没有本地桌面窗口,全程浏览器使用。
|
||||
* - `Detached`:会话 N+1 增量字段。Platform.Server 未能拉起 Simple3(appsettings 关闭 / exe 找不到 /
|
||||
* Process.Start 失败 / 启动异常),前端应该提示「Simple3 未连接」并降级 /api/sl/* 调用。
|
||||
*/
|
||||
export type RunMode = 'WebOnly' | 'WebEnabled' | 'Detached'
|
||||
|
||||
/**
|
||||
* 登录时用户在登录页选择的「启动模式」(请求字段)。
|
||||
* - `DesktopAndWeb`:希望本地桌面窗口 + Web 都启动(功能最完整)。
|
||||
* - `WebOnly`:希望只起 Web,不要弹本地桌面窗口(远程办公 / 服务器部署)。
|
||||
* - `WebOnly`:仅 Web(Simple3 默认;登录页不再提供选择)。
|
||||
* - `DesktopAndWeb`:历史兼容(本地桌面 + Web);Simple3 无本地端,一般不再使用。
|
||||
*
|
||||
* 与 RunMode 的区别:LaunchMode 是「期望」(前端 → 后端),RunMode 是「事实」(后端 → 前端)。
|
||||
* 正常情况下二者一一映射:DesktopAndWeb → WebEnabled,WebOnly → WebOnly。
|
||||
*/
|
||||
export type LaunchMode = 'DesktopAndWeb' | 'WebOnly'
|
||||
|
||||
@@ -50,8 +49,9 @@ export interface AuthUser {
|
||||
export interface LoginRequest {
|
||||
username: string
|
||||
password: string
|
||||
scope: Scope
|
||||
/** 会话 N+1:可选;不传时后端按 DesktopAndWeb 处理(向后兼容历史前端)。 */
|
||||
/** 已废弃:登录入口由账号角色决定,客户端不再挑选管理/运营。 */
|
||||
scope?: Scope
|
||||
/** 可选;不传时后端按 WebOnly(Simple3)。 */
|
||||
launchMode?: LaunchMode
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ export interface LoginResponse {
|
||||
effectivePermissions: EffectivePermissions
|
||||
runMode: RunMode
|
||||
/**
|
||||
* 会话 N+1 增量字段:SimpleLiteLauncher.LaunchResult.Status 字符串枚举。
|
||||
* 会话 N+1 增量字段:Simple3Launcher.LaunchResult.Status 字符串枚举。
|
||||
* 可能值:Disabled / AlreadyRunning / ReusingExisting / PortOccupied / Ready / StartedButNotReady /
|
||||
* ExecutableNotFound / ProcessStartFailed / Exception。
|
||||
* 字段缺失说明后端没有附带该字段(兼容旧后端)。
|
||||
@@ -70,7 +70,7 @@ export interface LoginResponse {
|
||||
launchStatus?: string
|
||||
/**
|
||||
* 会话 N+1 增量字段:可选告警文本。非空时前端应该用消息条提示用户
|
||||
* (比如「检测到 SimpleLite 已经在端口上运行,本次选择的启动模式未生效」)。
|
||||
* (比如「检测到 Simple3 已经在端口上运行,本次选择的启动模式未生效」)。
|
||||
*/
|
||||
launchWarning?: string
|
||||
/**
|
||||
@@ -92,4 +92,6 @@ export interface MeResponse {
|
||||
effectivePermissions: EffectivePermissions
|
||||
/** 部署配置向导是否待完成(与 LoginResponse.needsWizard 同义)。 */
|
||||
needsWizard?: boolean
|
||||
/** 历史「切运营端」残留 token 被纠正时,后端重发的新 JWT。 */
|
||||
token?: string
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ export interface DeploymentProfileDto {
|
||||
navigationKinds: string[]
|
||||
scenarios: string[]
|
||||
updatedBy: string
|
||||
/** 由导航 + 业务场景推导的 SimpleLite 激活场景 id(如 scene.mag / scene.qrlidar;scene.signal 仅 SPS / Pack)。 */
|
||||
/** 由导航 + 业务场景推导的 Simple3 激活场景 id(如 scene.mag / scene.qrlidar;scene.signal 仅 SPS / Pack)。 */
|
||||
activeSceneIds: string[]
|
||||
/** 被部署画像裁剪隐藏的页面 Key。 */
|
||||
hiddenPages: string[]
|
||||
|
||||
@@ -30,7 +30,7 @@ export async function loadSitePickOptions(): Promise<SitePickOption[]> {
|
||||
.sort((a, b) => a.id - b.id)
|
||||
}
|
||||
|
||||
/** 在地图上拾取目标站点(调用 SimpleLite pick 会话)。 */
|
||||
/** 在地图上拾取目标站点(调用 Simple3 pick 会话)。 */
|
||||
export async function pickTargetSiteOnMap(): Promise<number | null> {
|
||||
try {
|
||||
ElMessage.info({ message: '请在 3D 地图上点击目标站点附近', duration: 4000 })
|
||||
@@ -77,7 +77,7 @@ export async function executeCarGotoSite(carId: number, siteId: number): Promise
|
||||
/WebGotoSite|goto-site|不存在可调用方法/i.test(msg2)
|
||||
ElMessage.error(
|
||||
needDeploy
|
||||
? `${msg2}。当前 SimpleLite 仍是旧版 DLL:请先完全关闭 SimpleLite 窗口,再在 PowerShell 执行 Migu2.0/scripts/redeploy-simplelite.ps1,然后重新登录;或调用 POST /api/health/simplelite/restart-for-update`
|
||||
? `${msg2}。当前 Simple3 仍是旧版 DLL:请先完全关闭 Simple3 窗口,再在 PowerShell 执行 Migu2.0/scripts/redeploy-simple3.ps1,然后重新登录;或调用 POST /api/health/simple3/restart-for-update`
|
||||
: `前往站点失败:${msg2}`
|
||||
)
|
||||
return false
|
||||
|
||||
@@ -43,7 +43,7 @@ function compactJsonList(raw: string, maxItems = 3): string {
|
||||
}
|
||||
|
||||
/**
|
||||
* SimpleLite /projection/cars 会把「正常但未初始化」「导航失联」等一律标成 fault,
|
||||
* Simple3 /projection/cars 会把「正常但未初始化」「导航失联」等一律标成 fault,
|
||||
* 与地图标签/车体 AlarmLevel 不一致。这里用 lstatus + 车体 status 重新推导。
|
||||
*/
|
||||
export function deriveCarState(car: Pick<Car, 'state' | 'lstatus'>, statusRows?: Kv[] | null): CarState {
|
||||
@@ -93,7 +93,7 @@ export function deriveCarState(car: Pick<Car, 'state' | 'lstatus'>, statusRows?:
|
||||
}
|
||||
|
||||
/**
|
||||
* 与 SimpleLite 车体浮窗 CarVisual.FormatTrafficStatus 对齐:
|
||||
* 与 Simple3 车体浮窗 CarVisual.FormatTrafficStatus 对齐:
|
||||
* 1) TCStat 含 BeforeLock → 互锁
|
||||
* 2) blockedBy 有车 →「被 X、Y 车阻塞」;仅非车阻塞 →「阻塞中」
|
||||
* 3) 有 holdingLocks → 正常拿锁;否则 → 畅通
|
||||
|
||||
@@ -35,7 +35,7 @@ export function normalizeOpsConfig(raw?: Partial<OpsConfig> | null): OpsConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/** 深拷贝车型动作表,避免与 SimpleLite 返回对象共享引用导致勾选 UI 不同步。 */
|
||||
/** 深拷贝车型动作表,避免与 Simple3 返回对象共享引用导致勾选 UI 不同步。 */
|
||||
export function cloneCarActionByType(
|
||||
src?: Record<string, string[] | readonly string[]> | null
|
||||
): Record<string, string[]> {
|
||||
@@ -57,12 +57,12 @@ function pickMonitorRoot(cfg: MonitorConfigRoot) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 SimpleLite simple.json 加载地图监控配置(运行时唯一可信源)。
|
||||
* Platform ops 里的 monitor 仅作备份;以 SimpleLite 为准覆盖表单。
|
||||
* 从 Simple3 simple.json 加载地图监控配置(运行时唯一可信源)。
|
||||
* Platform ops 里的 monitor 仅作备份;以 Simple3 为准覆盖表单。
|
||||
*/
|
||||
export interface LoadMonitorSettingsResult {
|
||||
config: OpsConfig
|
||||
/** 与 SimpleLite 对齐的 monitor-config 快照,供保存时合并 fields/status,避免每次 POST 前再 GET。 */
|
||||
/** 与 Simple3 对齐的 monitor-config 快照,供保存时合并 fields/status,避免每次 POST 前再 GET。 */
|
||||
cache: MonitorConfigRoot | null
|
||||
}
|
||||
|
||||
@@ -80,13 +80,13 @@ export async function loadMonitorSettingsIntoOps(
|
||||
next.monitor.site.actionKeys = picked.siteActionKeys
|
||||
next.monitor.track.actionKeys = picked.trackActionKeys
|
||||
} catch {
|
||||
// SimpleLite 未连接:退回 Platform payload 中已有的 monitor(若有)
|
||||
// Simple3 未连接:退回 Platform payload 中已有的 monitor(若有)
|
||||
}
|
||||
return { config: next, cache }
|
||||
}
|
||||
|
||||
/**
|
||||
* 将运营维护表单中的地图监控配置写入 SimpleLite(simple.json)。
|
||||
* 将运营维护表单中的地图监控配置写入 Simple3(simple.json)。
|
||||
* 保留「地图监控配置」页面对 fields/status 的既有设置,只更新动作相关字段。
|
||||
*/
|
||||
export interface SaveMonitorSettingsOptions {
|
||||
@@ -132,7 +132,7 @@ export async function saveMonitorSettingsFromOps(
|
||||
}
|
||||
|
||||
/** @deprecated 使用 loadMonitorSettingsIntoOps */
|
||||
export const mergeMonitorFromSimpleLite = loadMonitorSettingsIntoOps
|
||||
export const mergeMonitorFromSimple3 = loadMonitorSettingsIntoOps
|
||||
|
||||
/** @deprecated 使用 saveMonitorSettingsFromOps */
|
||||
export const syncOpsMonitorToSimpleLite = saveMonitorSettingsFromOps
|
||||
export const syncOpsMonitorToSimple3 = saveMonitorSettingsFromOps
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* webVRender (SimpleLite 3D 视口, 默认 :8223) 的 host 解析。
|
||||
* webVRender (Simple3 3D 视口, 默认 :8223) 的 host 解析。
|
||||
*
|
||||
* 优先级:显式 VITE_VRENDER_HOST > 当前页面 hostname:8223。
|
||||
* 不能写死 localhost —— 从远程浏览器访问平台时 iframe 会去连访问者本机而非服务器。
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<el-descriptions-item label="运行时长" :span="2">{{ serverUptime || '—' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<el-descriptions v-if="sl" :column="2" border title="SimpleLite" class="status-block">
|
||||
<el-descriptions v-if="sl" :column="2" border title="Simple3" class="status-block">
|
||||
<el-descriptions-item label="托管启用">
|
||||
<el-tag :type="sl.enabled ? 'success' : 'info'" size="small">{{ sl.enabled ? '是' : '否' }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
@@ -43,15 +43,15 @@
|
||||
|
||||
<el-alert v-if="error" :title="error" type="warning" :closable="false" class="status-block" />
|
||||
|
||||
<div v-if="canControlSimpleLite" class="status-block sl-control">
|
||||
<div class="sl-control-title">SimpleLite 操作</div>
|
||||
<div v-if="canControlSimple3" class="status-block sl-control">
|
||||
<div class="sl-control-title">Simple3 操作</div>
|
||||
<div class="sl-control-actions">
|
||||
<el-button
|
||||
type="warning"
|
||||
:loading="actionLoading === 'restart'"
|
||||
:disabled="!!actionLoading"
|
||||
@click="onRestart">
|
||||
重启 SimpleLite
|
||||
重启 Simple3
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
@@ -59,10 +59,10 @@
|
||||
:loading="actionLoading === 'stop'"
|
||||
:disabled="!!actionLoading"
|
||||
@click="onStop">
|
||||
关闭 SimpleLite
|
||||
关闭 Simple3
|
||||
</el-button>
|
||||
</div>
|
||||
<p class="sl-control-hint">将终止本机全部 SimpleLite 进程;重启后按当前启动模式重新拉起。</p>
|
||||
<p class="sl-control-hint">将终止本机全部 Simple3 进程;重启后按当前启动模式重新拉起。</p>
|
||||
</div>
|
||||
|
||||
<div class="status-actions">
|
||||
@@ -79,12 +79,11 @@ import { useRouter } from 'vue-router'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import {
|
||||
getHealth,
|
||||
getSimpleLiteDiagnostics,
|
||||
restartSimpleLite,
|
||||
resolveRestartLaunchMode,
|
||||
stopSimpleLite,
|
||||
getSimple3Diagnostics,
|
||||
restartSimple3,
|
||||
stopSimple3,
|
||||
type HealthInfo,
|
||||
type SimpleLiteDiagnostics
|
||||
type Simple3Diagnostics
|
||||
} from '@/api/health'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
|
||||
@@ -94,10 +93,10 @@ const auth = useAuthStore()
|
||||
const loading = ref(false)
|
||||
const actionLoading = ref<'stop' | 'restart' | null>(null)
|
||||
const health = ref<HealthInfo | null>(null)
|
||||
const sl = ref<SimpleLiteDiagnostics | null>(null)
|
||||
const sl = ref<Simple3Diagnostics | null>(null)
|
||||
const error = ref('')
|
||||
|
||||
const canControlSimpleLite = computed(() => auth.token && auth.scope === 'Platform')
|
||||
const canControlSimple3 = computed(() => auth.token && auth.scope === 'Platform')
|
||||
|
||||
let timer: number | undefined
|
||||
|
||||
@@ -132,16 +131,16 @@ async function refresh() {
|
||||
loading.value = true
|
||||
error.value = ''
|
||||
try {
|
||||
// /status 是 public 页:未登录只拉匿名 /health,不调需登录的 simplelite 诊断
|
||||
// /status 是 public 页:未登录只拉匿名 /health,不调需登录的 simple3 诊断
|
||||
// (401 会触发全局拦截器强制跳转登录页)。
|
||||
const [h, d] = await Promise.allSettled([
|
||||
getHealth(),
|
||||
auth.token ? getSimpleLiteDiagnostics() : Promise.reject(new Error('skipped'))
|
||||
auth.token ? getSimple3Diagnostics() : Promise.reject(new Error('skipped'))
|
||||
])
|
||||
health.value = h.status === 'fulfilled' ? h.value.data : null
|
||||
sl.value = d.status === 'fulfilled' ? d.value.data : null
|
||||
if (h.status === 'rejected') error.value = '无法连接 MiGu.Server(/api/health)'
|
||||
else if (auth.token && d.status === 'rejected') error.value = 'SimpleLite 诊断获取失败(/api/health/simplelite)'
|
||||
else if (auth.token && d.status === 'rejected') error.value = 'Simple3 诊断获取失败(/api/health/simple3)'
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
@@ -150,8 +149,8 @@ async function refresh() {
|
||||
async function onStop() {
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
'将终止本机全部 SimpleLite 进程,地图监控与 /api/sl/* 功能将不可用,直到重新登录或手动重启。',
|
||||
'关闭 SimpleLite',
|
||||
'将终止本机全部 Simple3 进程,地图监控与 /api/sl/* 功能将不可用,直到重新登录或手动重启。',
|
||||
'关闭 Simple3',
|
||||
{ type: 'warning', confirmButtonText: '关闭', cancelButtonText: '取消' }
|
||||
)
|
||||
} catch {
|
||||
@@ -160,24 +159,21 @@ async function onStop() {
|
||||
|
||||
actionLoading.value = 'stop'
|
||||
try {
|
||||
const { data } = await stopSimpleLite()
|
||||
const { data } = await stopSimple3()
|
||||
sl.value = data.diagnostics
|
||||
ElMessage.success(data.killed > 0 ? `已关闭 ${data.killed} 个 SimpleLite 进程` : '当前没有运行中的 SimpleLite 进程')
|
||||
ElMessage.success(data.killed > 0 ? `已关闭 ${data.killed} 个 Simple3 进程` : '当前没有运行中的 Simple3 进程')
|
||||
} catch (e) {
|
||||
ElMessage.error(e instanceof Error ? e.message : '关闭 SimpleLite 失败')
|
||||
ElMessage.error(e instanceof Error ? e.message : '关闭 Simple3 失败')
|
||||
} finally {
|
||||
actionLoading.value = null
|
||||
}
|
||||
}
|
||||
|
||||
async function onRestart() {
|
||||
const launchMode = resolveRestartLaunchMode(sl.value?.lastLaunchMode, auth.runMode)
|
||||
const modeLabel = launchMode === 'WebOnly' ? '仅 Web' : '本地 + Web'
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`将关闭本机全部 SimpleLite 并按「${modeLabel}」模式重新拉起,期间 /api/sl/* 可能短暂不可用。`,
|
||||
'重启 SimpleLite',
|
||||
'将关闭本机全部 Simple3 并以 Web 模式重新拉起,期间 /api/sl/* 可能短暂不可用。',
|
||||
'重启内核',
|
||||
{ type: 'warning', confirmButtonText: '重启', cancelButtonText: '取消' }
|
||||
)
|
||||
} catch {
|
||||
@@ -186,14 +182,14 @@ async function onRestart() {
|
||||
|
||||
actionLoading.value = 'restart'
|
||||
try {
|
||||
const { data } = await restartSimpleLite(launchMode)
|
||||
const { data } = await restartSimple3('WebOnly')
|
||||
sl.value = data.diagnostics
|
||||
const r = data.restart
|
||||
if (r.warning) ElMessage.warning(r.warning)
|
||||
else if (r.started) ElMessage.success('SimpleLite 已重新拉起')
|
||||
else if (r.started) ElMessage.success('内核已重新拉起')
|
||||
else ElMessage.error(`重启未完成:${r.status}`)
|
||||
} catch (e) {
|
||||
ElMessage.error(e instanceof Error ? e.message : '重启 SimpleLite 失败')
|
||||
ElMessage.error(e instanceof Error ? e.message : '重启失败')
|
||||
} finally {
|
||||
actionLoading.value = null
|
||||
void refresh()
|
||||
@@ -202,7 +198,7 @@ async function onRestart() {
|
||||
|
||||
onMounted(() => {
|
||||
void refresh()
|
||||
// 10s 轮询:服务端对 goto-site 探测有 60s 缓存,此频率不会对 SimpleLite 产生压力。
|
||||
// 10s 轮询:服务端对 goto-site 探测有 60s 缓存,此频率不会对 Simple3 产生压力。
|
||||
timer = window.setInterval(() => void refresh(), 10_000)
|
||||
})
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
show-icon
|
||||
:closable="false"
|
||||
class="am-offline"
|
||||
title="SimpleLite 未连接:以下为平台最近一次采集的报警快照。"
|
||||
title="Simple3 未连接:以下为平台最近一次采集的报警快照。"
|
||||
:description="lastSyncAt ? `最近同步:${formatTime(lastSyncAt)}` : '暂无同步记录'"
|
||||
/>
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
|
||||
<el-empty v-if="!loading && types.length === 0" description="未发现任何 Car 子类(请确认 SimpleLite 已加载相应插件 dll)" />
|
||||
<el-empty v-if="!loading && types.length === 0" description="未发现任何 Car 子类(请确认 Simple3 已加载相应插件 dll)" />
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
@@ -161,7 +161,7 @@ async function refresh() {
|
||||
loading.value = true
|
||||
try {
|
||||
// 会话 N+2:car-types 与 alarm-colors 独立 await,避免一边失败把另一边的成功也吞掉。
|
||||
// 任意一边 SimpleLite 未启动 / 返回了非预期结构(YARP 502 / null payload),都不要让整个面板白屏崩。
|
||||
// 任意一边 Simple3 未启动 / 返回了非预期结构(YARP 502 / null payload),都不要让整个面板白屏崩。
|
||||
const carTypesResp = await reflectionApi.getCarStyleTypes().catch((err) => {
|
||||
console.warn('[CarStyleEditor] getCarStyleTypes failed:', err)
|
||||
return null
|
||||
@@ -196,7 +196,7 @@ async function refresh() {
|
||||
}
|
||||
|
||||
if (!carTypesResp || !paletteResp) {
|
||||
ElMessage.warning('车型样式 / 报警颜色加载部分失败,请确认 SimpleLite 已启动(端口 8222)。')
|
||||
ElMessage.warning('车型样式 / 报警颜色加载部分失败,请确认 Simple3 已启动(端口 8222)。')
|
||||
}
|
||||
} catch (err) {
|
||||
ElMessage.error(`加载车型样式失败:${(err as Error).message}`)
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
</div>
|
||||
<p class="cse-model-hint">
|
||||
现版本仅记录文件路径作为元信息。3D 渲染替代矩形将在后续阶段实现(CycleGUI Workspace LoadModel)。
|
||||
如需上传文件到服务器请先用其它方式将模型放到 SimpleLite 可访问的相对路径。
|
||||
如需上传文件到服务器请先用其它方式将模型放到 Simple3 可访问的相对路径。
|
||||
</p>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
@@ -260,7 +260,7 @@ import { fetchMonitorConfigCached, invalidateMonitorConfigCache } from '@/utils/
|
||||
import type { MapFocusKind } from '@/utils/mapObjectFocus'
|
||||
import { defaultVrHost } from '@/utils/vrender'
|
||||
defineProps<{
|
||||
/** 只读模式(运营端复用 MapMonitorView 时传 true):3D 不可编辑,选中信息面板动作改用运维白名单。 */
|
||||
/** 只读:3D 不可编辑,站点/路径编辑区隐藏。车辆动作一律走运维网关并记审计。 */
|
||||
readOnly?: boolean
|
||||
}>()
|
||||
|
||||
@@ -513,7 +513,7 @@ function onPick(_p: { x: number; y: number }) {
|
||||
// Workspace3D pick 坐标事件;监控页侧栏选中走 SSE selection-detail / select
|
||||
}
|
||||
|
||||
// SimpleLite 3D 端发送的对象命名规则(见 SimpleUI.cs / UISite.cs / TrackUiHelper):
|
||||
// Simple3 3D 端发送的对象命名规则(见 SimpleUI.cs / UISite.cs / TrackUiHelper):
|
||||
// - 车辆:`Car-{id}` 或 `UICar-{id}`(不同入口不同前缀,正则同时兼容)
|
||||
// - 站点:`UISite-{id}`
|
||||
// - 路径:`UITrack-{id}` / `UICircularArcTrack-{id}` / `UIBezierTrack-{id}` / `UINurbsTrack-{id}`
|
||||
@@ -596,7 +596,7 @@ function applySelectionFromKindId(kind: 'site' | 'track' | 'car', id: number, na
|
||||
}
|
||||
|
||||
/**
|
||||
* SimpleLite 画布点选后通过 SSE `selection-detail` 广播(见 SimpleUI.BroadcastSelectionToWeb)。
|
||||
* Simple3 画布点选后通过 SSE `selection-detail` 广播(见 SimpleUI.BroadcastSelectionToWeb)。
|
||||
* embed 模式通常不会 postMessage `workspace.select`,因此必须在此同步侧栏。
|
||||
*/
|
||||
function onSelectionDetailFromStream(e: SelectionDetailEvent) {
|
||||
@@ -619,10 +619,14 @@ function onSelectionDetailFromStream(e: SelectionDetailEvent) {
|
||||
}
|
||||
}
|
||||
|
||||
if (e.kind && e.id) {
|
||||
if (e.kind === 'site' || e.kind === 'track' || e.kind === 'car') {
|
||||
applySelectionFromKindId(e.kind, e.id, e.names)
|
||||
return
|
||||
}
|
||||
if (e.kind === 'special' && e.id) {
|
||||
const next = { kind: 'special', id: String(e.id), name: `装饰物 ${e.id}` }
|
||||
if (!sameSelection(selection.value, next)) selection.value = next
|
||||
}
|
||||
|
||||
if (siteN > 0) {
|
||||
applySelectionFromKindId('site', e.siteIds![0]!, e.names)
|
||||
@@ -727,7 +731,7 @@ async function applyPendingPlayback() {
|
||||
if (!fileName || !workspaceReady.value) return
|
||||
pendingPlayback.value = null
|
||||
try {
|
||||
// iframe 终端就绪后再启动:SimpleLite StartPlayback 会把回放控制条投影到该 webVRender 终端。
|
||||
// iframe 终端就绪后再启动:Simple3 StartPlayback 会把回放控制条投影到该 webVRender 终端。
|
||||
await workspaceToolbarApi.startPlayback(fileName)
|
||||
ElMessage.success({ message: `已开始回放:${fileName}`, grouping: true })
|
||||
} catch (err) {
|
||||
@@ -759,7 +763,7 @@ async function fallbackSelectionFromBackend() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 选中并定位到指定车辆:① 同步 SimpleLite 3D 场景高亮;② 立即把相机定位(居中 + 2D 俯视)
|
||||
* 选中并定位到指定车辆:① 同步 Simple3 3D 场景高亮;② 立即把相机定位(居中 + 2D 俯视)
|
||||
* 到该车,与原生「双击车辆行 = 选中+定位」一致。两个请求互不阻塞,各自独立提示错误
|
||||
* (例如 iframe 未连接时相机定位会 503,但高亮仍可成功)。
|
||||
*/
|
||||
@@ -872,10 +876,10 @@ async function refreshAll() {
|
||||
// - snapshot-tick:后端 1Hz 心跳,仅含 cars/missions/sites/tracks 数字,不带实际状态。**不要**
|
||||
// 在这里触发 refreshAll,否则会等价"1s 全量轮询 listCars+listMissions",比原来的 3s 轮询还
|
||||
// 重。snapshot-tick 仅用于 `stream.connected` 心跳判定。
|
||||
// - selection-detail:画布点选后 SimpleLite 广播,驱动右侧「选中信息」面板(embed 模式主路径)。
|
||||
// - selection-detail:画布点选后 Simple3 广播,驱动右侧「选中信息」面板(embed 模式主路径)。
|
||||
// - 其它 object-* / pick-result / project-*:本页不处理,由地图编辑页订阅。
|
||||
//
|
||||
// 实时通道未连接(旧 SimpleLite / 离线)时退回 3s 轮询保底。
|
||||
// 实时通道未连接(旧 Simple3 / 离线)时退回 3s 轮询保底。
|
||||
const stream = useProjectionStream({ autoConnect: true })
|
||||
const streamConnected = computed(() => stream.connected.value)
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
* (UiDiscoveryCache.MissionCreatableTypes 同源),这里直接用 ReflectionManagerPanel
|
||||
* 接入,按下「新建任务」会列出所有 MissionType 子类(含 taskflow / trigger)。
|
||||
*
|
||||
* 旧版本的 DataTablePro + listMissions mock 已经下线,所有数据走 SimpleLite 反射 API。
|
||||
* 旧版本的 DataTablePro + listMissions mock 已经下线,所有数据走 Simple3 反射 API。
|
||||
*/
|
||||
import ReflectionManagerPanel from '@/components/reflection/ReflectionManagerPanel.vue'
|
||||
</script>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
v-if="activeTab === 'project'"
|
||||
ref="projectEditor"
|
||||
target-label="项目属性"
|
||||
description="对应 SimpleLite 桌面端主菜单「项目属性…」。编辑字段后点「保存」即可一次完成内存生效 + 写回项目 JSON 文件(含全部场景/车辆/路径/属性)。"
|
||||
description="对应 Simple3 桌面端主菜单「项目属性…」。编辑字段后点「保存」即可一次完成内存生效 + 写回项目 JSON 文件(含全部场景/车辆/路径/属性)。"
|
||||
:loader="loadProject"
|
||||
:on-set="setProjectField"
|
||||
:on-save="saveProject"
|
||||
@@ -18,7 +18,7 @@
|
||||
v-if="activeTab === 'app-config'"
|
||||
ref="appConfigEditor"
|
||||
target-label="核心配置"
|
||||
description="对应 SimpleLite 桌面端主菜单「核心配置 → 编辑配置…」。修改后点「保存」即可一次完成内存生效 + 写入可执行目录下的 simple.json。"
|
||||
description="对应 Simple3 桌面端主菜单「核心配置 → 编辑配置…」。修改后点「保存」即可一次完成内存生效 + 写入可执行目录下的 simple.json。"
|
||||
:loader="loadAppConfig"
|
||||
:on-set="setAppConfigField"
|
||||
:on-save="saveAppConfig"
|
||||
@@ -48,7 +48,7 @@ async function loadProject(): Promise<FieldMemberEditorLoadResult> {
|
||||
return {
|
||||
fields: r.fields,
|
||||
pathInfos: [
|
||||
{ label: '最近加载/保存的项目 JSON', value: r.lastLoadedPath, emptyHint: '尚未通过 SimpleLite 加载或保存过项目' },
|
||||
{ label: '最近加载/保存的项目 JSON', value: r.lastLoadedPath, emptyHint: '尚未通过 Simple3 加载或保存过项目' },
|
||||
{ label: 'simple.json autoload', value: r.autoloadPath, emptyHint: '未配置' }
|
||||
],
|
||||
saveButtonLabel: '保存(写回项目 JSON)'
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div class="script-page">
|
||||
<!--
|
||||
平台「脚本管理」= SimpleLite 工作台「脚本」页(ComposerDockPanel.RenderScriptTable)的 Web 镜像:
|
||||
平台「脚本管理」= Simple3 工作台「脚本」页(ComposerDockPanel.RenderScriptTable)的 Web 镜像:
|
||||
- 列表 = CarProgram.GetPrograms(),最多 300 条(含历史),由 Mission 调度运行时自动产生与回收。
|
||||
- 列:ID / 名称(任务名) / 类型(CarProgram 子类) / 车辆(plans[0].usingCar) / 状态(ProgramStatus.state)。
|
||||
- 不渲染「新建」按钮:CarProgram 无法手动 new,由 Mission 调度运行时自动产生(在 SimpleLite 工作台新建 Mission 后生成)。
|
||||
- 不渲染「新建」按钮:CarProgram 无法手动 new,由 Mission 调度运行时自动产生(在 Simple3 工作台新建 Mission 后生成)。
|
||||
- 不渲染「删除」按钮:CarProgram 由调度器在环形队列里维护,删除一律会被后端拒。
|
||||
右侧详情面板沿用通用 ReflectionManagerPanel 渲染(fields 多为 typed/locked,只读 status 通过反射)。
|
||||
-->
|
||||
@@ -12,7 +12,7 @@
|
||||
kind="script"
|
||||
kind-label="脚本"
|
||||
title="脚本管理"
|
||||
empty-text="当前无 CarProgram 实例。CarProgram 是 Mission 运行时编译用户脚本后产生的对象,请在 SimpleLite 工作台新建 Mission(任务)后由调度运行时自动生成。"
|
||||
empty-text="当前无 CarProgram 实例。CarProgram 是 Mission 运行时编译用户脚本后产生的对象,请在 Simple3 工作台新建 Mission(任务)后由调度运行时自动生成。"
|
||||
disable-create
|
||||
disable-delete
|
||||
show-summary-column
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<h2>字段管理</h2>
|
||||
<p>「默认」从 SimpleLite 拉取全部车型字段;「刷新」从数据库读取;「保存」将全部字段写入数据库。</p>
|
||||
<p>「默认」从 Simple3 拉取全部车型字段;「刷新」从数据库读取;「保存」将全部字段写入数据库。</p>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<el-button :loading="initializing || loadingDefaults" @click="loadDefaults">默认</el-button>
|
||||
@@ -339,7 +339,7 @@ function allCarsToRows(cars: CarTypeCoderFieldsRow[]): FieldRow[] {
|
||||
return cars.flatMap(carTypeToRows)
|
||||
}
|
||||
|
||||
/** 从已加载的数据库记录推导车型下拉(不请求 SimpleLite)。 */
|
||||
/** 从已加载的数据库记录推导车型下拉(不请求 Simple3)。 */
|
||||
function syncCarTypesFromFieldRows() {
|
||||
const labelByKey = new Map(
|
||||
carTypes.value.map((c) => [buildCarType(c.assemblyName, c.shortName), c.label])
|
||||
@@ -363,7 +363,7 @@ function syncCarTypesFromFieldRows() {
|
||||
carTypes.value = options
|
||||
}
|
||||
|
||||
/** 从 SimpleLite 补全车型中文名(轻量接口,不拉字段定义)。 */
|
||||
/** 从 Simple3 补全车型中文名(轻量接口,不拉字段定义)。 */
|
||||
async function enrichCarTypeLabels() {
|
||||
try {
|
||||
const types = await reflectionApi.listCreatableTypes('car')
|
||||
@@ -376,7 +376,7 @@ async function enrichCarTypeLabels() {
|
||||
return label ? { ...c, label } : c
|
||||
})
|
||||
} catch {
|
||||
/* SimpleLite 未连接时保留现有显示 */
|
||||
/* Simple3 未连接时保留现有显示 */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -423,7 +423,7 @@ async function loadDefaults() {
|
||||
try {
|
||||
const cars = await reflectionApi.getCarTypeCoderFields()
|
||||
if (!cars.length) {
|
||||
ElMessage.warning('未获取到车型列表,请确认 SimpleLite 已启动')
|
||||
ElMessage.warning('未获取到车型列表,请确认 Simple3 已启动')
|
||||
return
|
||||
}
|
||||
carTypes.value = cars.map((c) => ({
|
||||
@@ -434,7 +434,7 @@ async function loadDefaults() {
|
||||
}))
|
||||
ensureDefaultCarType()
|
||||
fieldRows.value = allCarsToRows(cars)
|
||||
ElMessage.success(`已从 SimpleLite 加载 ${cars.length} 种车型、共 ${fieldRows.value.length} 条默认字段(请点击保存写入数据库)`)
|
||||
ElMessage.success(`已从 Simple3 加载 ${cars.length} 种车型、共 ${fieldRows.value.length} 条默认字段(请点击保存写入数据库)`)
|
||||
} catch (e) {
|
||||
ElMessage.error(e instanceof Error ? e.message : '加载默认字段失败')
|
||||
} finally {
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
show-icon
|
||||
:closable="false"
|
||||
class="tm-offline"
|
||||
title="SimpleLite 未连接:以下为平台最近一次快照;取消/重发/新建等操作需 SimpleLite 在线,离线执行会提示失败。"
|
||||
title="Simple3 未连接:以下为平台最近一次快照;取消/重发/新建等操作需 Simple3 在线,离线执行会提示失败。"
|
||||
:description="lastSyncAt ? `最近同步:${formatTime(lastSyncAt)}` : '暂无同步记录'"
|
||||
/>
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<p class="mmc-desc">
|
||||
勾选「地图监控」右侧选中信息面板要展示的内容白名单(按对象类型 - 车辆 / 站点 / 路径 分组)。
|
||||
三组分别控制 <b>属性</b>、<b>状态</b>、<b>动作</b> Tab 里可见的字段 / 状态项 / 可执行方法。
|
||||
每组留空 = 显示全部;勾选任何一项即进入白名单模式(仅显示已勾选)。保存后直接写入 SimpleLite 的
|
||||
每组留空 = 显示全部;勾选任何一项即进入白名单模式(仅显示已勾选)。保存后直接写入 Simple3 的
|
||||
<code>simple.json</code> (<code>Configuration.conf.monitorVisibility</code>)。
|
||||
</p>
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
title="属性字段(Properties)"
|
||||
:available="available[k.value].fields"
|
||||
:selected="config[k.value].fields"
|
||||
empty-tip="该类型暂未发现任何 [FieldMember]。在 SimpleLite 端添加字段后点重载会刷新。"
|
||||
empty-tip="该类型暂未发现任何 [FieldMember]。在 Simple3 端添加字段后点重载会刷新。"
|
||||
@update:selected="(v: string[]) => onUpdate(k.value, 'fields', v)" />
|
||||
|
||||
<MapMonitorConfigGroup
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
<el-option v-for="s in displayedSites" :key="s.id" :label="s.label" :value="s.id" />
|
||||
</el-select>
|
||||
<span v-if="sitesLoadError" class="field-hint field-hint-warn">{{ sitesLoadError }}</span>
|
||||
<span v-else class="field-hint">主数据来自 SimpleLite,保存值为站点 id</span>
|
||||
<span v-else class="field-hint">主数据来自 Simple3,保存值为站点 id</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="条码"><el-input v-model="storageForm.barcode" /></el-form-item>
|
||||
<el-form-item label="优先级"><el-input-number v-model="storageForm.priority" :min="0" /></el-form-item>
|
||||
@@ -155,7 +155,7 @@ import {
|
||||
AREA_TYPES, AREA_LAYOUT_MODES, STORAGE_TYPES, LOCATION_KINDS, CONTAINER_TYPES,
|
||||
CONTAINER_LOCATION_STATUSES, MATERIAL_UNITS, MATERIAL_LIFECYCLES
|
||||
} from './wmsOptions'
|
||||
import { useSimpleLiteSites } from './useSimpleLiteSites'
|
||||
import { useSimple3Sites } from './useSimple3Sites'
|
||||
|
||||
export type DialogKind = 'area' | 'storage' | 'container' | 'materialType' | 'material' | 'location' | 'containerMaterial' | 'rule'
|
||||
|
||||
@@ -186,7 +186,7 @@ const emit = defineEmits<{
|
||||
const formRef = ref<FormInstance>()
|
||||
const areaLayoutMode = defineModel<string>('areaLayoutMode', { default: 'Flat' })
|
||||
const containerBarcode = defineModel<string>('containerBarcode', { default: '' })
|
||||
const { sites: simpleLiteSites, loading: sitesLoading, loadError: sitesLoadError, load: loadSimpleLiteSites } = useSimpleLiteSites()
|
||||
const { sites: simple3Sites, loading: sitesLoading, loadError: sitesLoadError, load: loadSimple3Sites } = useSimple3Sites()
|
||||
const siteSearch = ref('')
|
||||
|
||||
function onSiteChange(id: string) {
|
||||
@@ -203,10 +203,10 @@ function onMaterialTypeChange(code: string) {
|
||||
}
|
||||
|
||||
const siteOptionsWithCurrent = computed(() => {
|
||||
const opts = [...simpleLiteSites.value]
|
||||
const opts = [...simple3Sites.value]
|
||||
const current = props.storageForm.siteId?.trim()
|
||||
if (current && !opts.some((o) => o.id === current)) {
|
||||
opts.unshift({ id: current, name: '', label: `#${current}(未在 SimpleLite 列表中)` })
|
||||
opts.unshift({ id: current, name: '', label: `#${current}(未在 Simple3 列表中)` })
|
||||
}
|
||||
return opts
|
||||
})
|
||||
@@ -289,7 +289,7 @@ watch(() => props.visible, (v) => {
|
||||
formRef.value?.clearValidate()
|
||||
if (props.kind === 'storage') {
|
||||
siteSearch.value = ''
|
||||
void loadSimpleLiteSites()
|
||||
void loadSimple3Sites()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
+6
-6
@@ -2,13 +2,13 @@ import { ref } from 'vue'
|
||||
import { reflectionApi } from '@/api/reflection'
|
||||
import { listSites } from '@/api/projection'
|
||||
|
||||
export interface SimpleLiteSiteOption {
|
||||
export interface Simple3SiteOption {
|
||||
id: string
|
||||
name: string
|
||||
label: string
|
||||
}
|
||||
|
||||
export async function fetchSimpleLiteSites(): Promise<SimpleLiteSiteOption[]> {
|
||||
export async function fetchSimple3Sites(): Promise<Simple3SiteOption[]> {
|
||||
try {
|
||||
const rows = await reflectionApi.listObjects('site')
|
||||
if (rows.length > 0) {
|
||||
@@ -33,8 +33,8 @@ export async function fetchSimpleLiteSites(): Promise<SimpleLiteSiteOption[]> {
|
||||
.sort((a, b) => Number(a.id) - Number(b.id) || a.name.localeCompare(b.name))
|
||||
}
|
||||
|
||||
export function useSimpleLiteSites() {
|
||||
const sites = ref<SimpleLiteSiteOption[]>([])
|
||||
export function useSimple3Sites() {
|
||||
const sites = ref<Simple3SiteOption[]>([])
|
||||
const loading = ref(false)
|
||||
const loadError = ref('')
|
||||
|
||||
@@ -42,9 +42,9 @@ export function useSimpleLiteSites() {
|
||||
loading.value = true
|
||||
loadError.value = ''
|
||||
try {
|
||||
sites.value = await fetchSimpleLiteSites()
|
||||
sites.value = await fetchSimple3Sites()
|
||||
} catch (e) {
|
||||
loadError.value = e instanceof Error ? e.message : '加载 SimpleLite 站点失败'
|
||||
loadError.value = e instanceof Error ? e.message : '加载 Simple3 站点失败'
|
||||
sites.value = []
|
||||
} finally {
|
||||
loading.value = false
|
||||
@@ -9,11 +9,10 @@
|
||||
* 只读模式下:
|
||||
* - 3D 画布不可编辑(Workspace3D read-only),scope 由 MapMonitorView 内部按 auth.scope 取,
|
||||
* 运营端登录后即 RCSMonitor。
|
||||
* - 右侧「选中信息」面板的车辆动作改用运维白名单(OPS_WHITELIST + executeOp 审计 + 二次确认,
|
||||
* 按当前账号 hasOp 过滤),站点/路径的编辑动作区隐藏。
|
||||
* - 右侧「选中信息」车辆动作与管理端相同(monitor-config 按车型勾选),经 /api/sl/ops/execute
|
||||
* (ops.car.execute)转发 Simple3,站点/路径的编辑动作区隐藏。
|
||||
*
|
||||
* 备注:原运营端地图页内的「任务」白名单动作(暂停/取消/重派等)请走 /monitor/ops 运维操作页,
|
||||
* 地图监控页聚焦于地图对象(车/站/路)的监视与车辆运维动作。
|
||||
* 备注:任务白名单动作(暂停/取消/重派等)请走 /monitor/ops 运维操作页。
|
||||
*/
|
||||
import MapMonitorView from '@/views/admin/MapMonitorView.vue'
|
||||
</script>
|
||||
|
||||
@@ -115,7 +115,7 @@
|
||||
<FleetAllocationPanel :cars="cardModels" :can-write="canWrite" @saved="onFleetSaved" />
|
||||
|
||||
<p class="footnote">
|
||||
延迟 = 本机到车辆 WatchDog(:9776) 的 TCP 往返;故障率 = 报警占用时长 ÷ 自上线以来运行时长(SimpleLite 进程内累计)
|
||||
延迟 = 本机到车辆 WatchDog(:9776) 的 TCP 往返;故障率 = 报警占用时长 ÷ 自上线以来运行时长(Simple3 进程内累计)
|
||||
</p>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
@@ -32,7 +32,7 @@ export function assignCarParams(): ParamFieldDef[] {
|
||||
label: '车辆类型',
|
||||
type: 'string',
|
||||
required: true,
|
||||
placeholder: '如 Forklift / SimpleLite.RCS.Cars.AgvCar',
|
||||
placeholder: '如 Forklift / Simple3.RCS.Cars.AgvCar',
|
||||
when: BY_CAR_TYPE
|
||||
}
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user