将调度内核标识从 SimpleLite 全面重命名为 Simple3。

配置段/环境变量、Launcher、健康检查 API、OpenAPI 与前后端文案同步;兼容探测旧 SimpleLite 进程名。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
黄兆尉
2026-08-26 17:46:52 +08:00
co-authored by Cursor
parent 912c44c9bb
commit 3686abdc78
79 changed files with 922 additions and 832 deletions
@@ -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/cars502 或空列表时回退 reflection 对象列表(与 3D 场景同源)。 */
/** 优先走 Simple3 /projection/cars502 或空列表时回退 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
/** 嵌入平台页时传 trueSimpleLite 仅显示底栏(对齐/选择/右击/图层/内容/录制回放) */
/** 嵌入平台页时传 trueSimple3 仅显示底栏(对齐/选择/右击/图层/内容/录制回放) */
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 :8223iframe 同源),失败再试 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
}
@@ -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
@@ -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: '站点、路径、装饰物',
@@ -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 iframeSimpleLite WebTerminal postMessage
* Workspace3D iframeSimple3 WebTerminal postMessage
*
* SimpleLite WebTerminal window.postMessage `workspace.pick`
* Simple3 WebTerminal window.postMessage `workspace.pick`
* `workspace.select` Workspace3D.vue emit
* vueiframe SimpleLite APIHTTP/SSE
* vueiframe Simple3 APIHTTP/SSE
* ** / / hover** iframevue
*/
@@ -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 SimpleLiteappsettings / exe /
* Process.Start / SimpleLite /api/sl/*
* - `WebEnabled`Simple3 LocalTerminal+ WebTerminal
* - `WebOnly`Simple3 WebTerminal使
* - `Detached` N+1 Platform.Server Simple3appsettings / exe /
* Process.Start / Simple3 /api/sl/*
*/
export type RunMode = 'WebOnly' | 'WebEnabled' | 'Detached'
/**
* ()
* - `DesktopAndWeb` + Web
* - `WebOnly` Web /
* - `WebOnly` WebSimple3
* - `DesktopAndWeb` + WebSimple3 使
*
* RunMode LaunchMode RunMode
* DesktopAndWeb WebEnabledWebOnly 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
/** 可选;不传时后端按 WebOnlySimple3)。 */
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.qrlidarscene.signal 仅 SPS / Pack)。 */
/** 由导航 + 业务场景推导的 Simple3 激活场景 id(如 scene.mag / scene.qrlidarscene.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 XY
* 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 }
}
/**
* SimpleLitesimple.json
* Simple3simple.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+2car-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()
}
}
})
@@ -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-onlyscope 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
}
]