feat: 迁入 MiGu.Server、平台前端与车辆列表 reflection 回退
从 Simple-FR 拆出 Platform.Server 并重命名为 MiGu.Server;frontends 源码与构建脚本迁入本仓库。地图监控在 projection/cars 失败或为空时回退 reflection 车辆列表;Simple 仓库已移除旧 Platform.Server。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,620 @@
|
||||
import http from './http'
|
||||
import {
|
||||
mockReflectionAssemblies,
|
||||
mockReflectionBundle,
|
||||
mockReflectionDeleteField,
|
||||
mockReflectionExecute,
|
||||
mockReflectionFields,
|
||||
mockReflectionKinds,
|
||||
mockReflectionMethods,
|
||||
mockReflectionMethodsByType,
|
||||
mockReflectionObjects,
|
||||
mockReflectionSetField,
|
||||
mockReflectionStatus
|
||||
} from '@/mock/data/reflection'
|
||||
|
||||
// 与 auth.ts / config.ts / ops.ts 统一走 VITE_USE_MOCK 开关,避免 PROJECTION/USE 双命名造成
|
||||
// .env 改一个忘改另一个的诡异半 mock 状态。env.d.ts 已声明 VITE_USE_MOCK 类型。
|
||||
const MOCK = import.meta.env.VITE_USE_MOCK === 'true'
|
||||
|
||||
/**
|
||||
* 与 SimpleLite `ReflectionApiController` 一一对应的前端胶水。
|
||||
* 路径前缀:`/sl/projection/reflection`(迷榖平台 YARP 反代下游 → SimpleLite EmbedIO)。
|
||||
*
|
||||
* 该客户端覆盖了 SimpleLite 主体以及通过 plugins/*.dll 动态加载的 Standard 等
|
||||
* 插件中所有标注了 `MethodMember` 的方法与所有继承自 Car/Mission/CarProgram/Site/Track
|
||||
* 的子类型,平台前端可据此动态渲染列表、属性、动作按钮。
|
||||
*/
|
||||
|
||||
const BASE = '/sl/projection/reflection'
|
||||
|
||||
export type ReflectionKind =
|
||||
| 'car'
|
||||
| 'vehicle'
|
||||
| 'mission'
|
||||
| 'process'
|
||||
| 'site'
|
||||
| 'track'
|
||||
| 'map'
|
||||
| 'special'
|
||||
| 'script'
|
||||
// 顶级合成 kind:等于 site + track + special 的并集,由 SimpleLite 后端 /objects/scene 直接返回,
|
||||
// 每行带 subKind 字段,前端 bundle / execute / setField 走对应底层 kind。
|
||||
| 'scene'
|
||||
|
||||
export interface ReflectionEnvelope<T> {
|
||||
success: boolean
|
||||
code: number
|
||||
data: T | null
|
||||
message: string
|
||||
}
|
||||
|
||||
export interface ReflectionParam {
|
||||
name: string
|
||||
typeName: string
|
||||
hasDefault: boolean
|
||||
defaultValue?: string | null
|
||||
}
|
||||
|
||||
export interface ReflectionMethod {
|
||||
methodName: string
|
||||
label?: string | null
|
||||
description?: string | null
|
||||
hint?: string | null
|
||||
returnType: string
|
||||
hasParams: boolean
|
||||
params: ReflectionParam[]
|
||||
}
|
||||
|
||||
export interface ReflectionObject {
|
||||
id: number
|
||||
name: string
|
||||
typeName: string
|
||||
layer?: string | null
|
||||
status?: string | null
|
||||
summary?: string | null
|
||||
/** 当顶级 kind 是合成 kind(如 "scene")时,此字段标记底层真实 kind。 */
|
||||
subKind?: string | null
|
||||
}
|
||||
|
||||
export interface ReflectionKv {
|
||||
key: string
|
||||
value: string
|
||||
locked?: boolean
|
||||
/** "typed" = 强类型 [FieldMember],不可删;"dynamic" = Prop.fields 动态字段,可删。 */
|
||||
source?: 'typed' | 'dynamic'
|
||||
/** 后端字段的 .NET 类型名(String / Single / Int32 / Boolean 等),用于前端渲染对应控件。 */
|
||||
typeName?: string
|
||||
}
|
||||
|
||||
export interface ReflectionTypeMethods {
|
||||
typeName: string
|
||||
fullTypeName?: string
|
||||
typeLabel?: string
|
||||
assemblyName: string
|
||||
methods: ReflectionMethod[]
|
||||
}
|
||||
|
||||
export interface ReflectionAssembly {
|
||||
name: string
|
||||
version?: string
|
||||
location?: string
|
||||
}
|
||||
|
||||
/** 通过 GET /reflection/types/{kind} 拿到的可创建子类型行(进程 / 脚本 / 车辆管理面板的「新建」下拉用)。 */
|
||||
export interface ReflectionCreatableType {
|
||||
typeName: string
|
||||
shortName: string
|
||||
label: string
|
||||
assemblyName: string
|
||||
}
|
||||
|
||||
/** GET /reflection/plugins 返回的单个插件元信息。 */
|
||||
export interface PluginEntry {
|
||||
name: string
|
||||
dllPath: string
|
||||
assemblyName: string
|
||||
assemblyVersion: string
|
||||
collectible: boolean
|
||||
loadedTypes: number
|
||||
loadedAt: string
|
||||
missionTypes: number
|
||||
carTypes: number
|
||||
}
|
||||
|
||||
export interface ReflectionKindMeta {
|
||||
kind: ReflectionKind
|
||||
count: number
|
||||
label: string
|
||||
/** "primary" 表示顶级 5 分类;undefined / 其他视为底层 kind。 */
|
||||
group?: string
|
||||
}
|
||||
|
||||
export interface ReflectionSubKindMeta extends ReflectionKindMeta {
|
||||
parent: ReflectionKind
|
||||
}
|
||||
|
||||
export interface ReflectionSelection {
|
||||
kind: ReflectionKind | null
|
||||
id: number
|
||||
name: string
|
||||
typeName?: string
|
||||
}
|
||||
|
||||
export interface ScriptSourcePayload {
|
||||
id: number
|
||||
name: string
|
||||
typeName: string
|
||||
state?: string | null
|
||||
script: string
|
||||
}
|
||||
|
||||
export interface ScriptExceptionStatusPayload {
|
||||
id: number
|
||||
name: string
|
||||
typeName: string
|
||||
car: string
|
||||
state?: string | null
|
||||
exception: string
|
||||
notifies: string
|
||||
report: string
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
// 地图监控可见性配置(与后端 MonitorVisibilityConfig / MonitorVisibilityForKind 对应)
|
||||
//
|
||||
// `config` = 管理员勾选的白名单。空数组 = 显示全部;非空 = 仅显示列表中的 key。
|
||||
// `available` = 当前 SimpleLite 进程里能扫描到的全部可勾选项(基类 + 已加载子类 +
|
||||
// 运行时实例的 Prop.fields / status 反射键)。是 GET 返回的副产物,
|
||||
// POST 写入时不需要、也不该回传。
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
export type MonitorVisibilityKind = 'car' | 'site' | 'track'
|
||||
|
||||
export interface MonitorVisibilityForKind {
|
||||
fields: string[]
|
||||
status: string[]
|
||||
methods: string[]
|
||||
}
|
||||
|
||||
export interface MonitorVisibilityMap {
|
||||
car: MonitorVisibilityForKind
|
||||
site: MonitorVisibilityForKind
|
||||
track: MonitorVisibilityForKind
|
||||
}
|
||||
|
||||
/** POST /monitor-config 请求体:三组白名单 + 可选按车型动作。 */
|
||||
export interface MonitorConfigSaveBody extends MonitorVisibilityMap {
|
||||
carActionByType?: Record<string, string[]>
|
||||
}
|
||||
|
||||
export interface MonitorConfigPayload {
|
||||
config: MonitorVisibilityMap & { carActionByType?: Record<string, string[]> }
|
||||
available: MonitorVisibilityMap
|
||||
}
|
||||
|
||||
const MOCK_MONITOR_STORAGE_KEY = 'simple-platform-mock-monitor-config'
|
||||
|
||||
function loadMockMonitorConfig(): MonitorConfigSaveBody {
|
||||
try {
|
||||
const raw = localStorage.getItem(MOCK_MONITOR_STORAGE_KEY)
|
||||
if (raw) return JSON.parse(raw) as MonitorConfigSaveBody
|
||||
} catch { /* ignore */ }
|
||||
return emptyMonitorVisibilityMap()
|
||||
}
|
||||
|
||||
function saveMockMonitorConfig(cfg: MonitorConfigSaveBody) {
|
||||
try {
|
||||
localStorage.setItem(MOCK_MONITOR_STORAGE_KEY, JSON.stringify(cfg))
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
|
||||
export function emptyMonitorVisibilityForKind(): MonitorVisibilityForKind {
|
||||
return { fields: [], status: [], methods: [] }
|
||||
}
|
||||
|
||||
export function emptyMonitorVisibilityMap(): MonitorVisibilityMap {
|
||||
return {
|
||||
car: emptyMonitorVisibilityForKind(),
|
||||
site: emptyMonitorVisibilityForKind(),
|
||||
track: emptyMonitorVisibilityForKind()
|
||||
}
|
||||
}
|
||||
|
||||
// 语义上 available 与 config 同构(都是按 kind 分组的三组 key 列表),所以共用一个工厂。
|
||||
// 单独保留命名是为了表达"业务含义不同"——可勾选全集 vs 已勾选白名单。
|
||||
export const emptyMonitorAvailableMap = emptyMonitorVisibilityMap
|
||||
|
||||
async function get<T>(path: string): Promise<T> {
|
||||
const { data } = await http.get<ReflectionEnvelope<T>>(`${BASE}${path}`)
|
||||
if (!data?.success) throw new Error(data?.message ?? `reflection ${path} failed`)
|
||||
return data.data as T
|
||||
}
|
||||
|
||||
async function post<T>(path: string, params?: Record<string, string | number | boolean>): Promise<T> {
|
||||
const { data } = await http.post<ReflectionEnvelope<T>>(`${BASE}${path}`, null, { params })
|
||||
if (!data?.success) throw new Error(data?.message ?? `reflection ${path} failed`)
|
||||
return data.data as T
|
||||
}
|
||||
|
||||
async function del<T>(path: string): Promise<T> {
|
||||
const { data } = await http.delete<ReflectionEnvelope<T>>(`${BASE}${path}`)
|
||||
if (!data?.success) throw new Error(data?.message ?? `reflection ${path} failed`)
|
||||
return data.data as T
|
||||
}
|
||||
|
||||
export const reflectionApi = {
|
||||
listKinds: () => MOCK
|
||||
? Promise.resolve(mockReflectionKinds())
|
||||
: get<{ kinds: ReflectionKindMeta[]; subKinds?: ReflectionSubKindMeta[] }>('/kinds'),
|
||||
|
||||
listAssemblies: () => MOCK
|
||||
? Promise.resolve(mockReflectionAssemblies())
|
||||
: get<ReflectionAssembly[]>('/assemblies'),
|
||||
|
||||
listObjects: (kind: ReflectionKind) => MOCK
|
||||
? Promise.resolve(mockReflectionObjects(kind))
|
||||
: get<ReflectionObject[]>(`/objects/${kind}`),
|
||||
|
||||
/** 列出当前可实例化的子类型(用于「新建」下拉);mock 模式直接给空。 */
|
||||
listCreatableTypes: (kind: ReflectionKind): Promise<ReflectionCreatableType[]> => MOCK
|
||||
? Promise.resolve([])
|
||||
: get<ReflectionCreatableType[]>(`/types/${kind}`),
|
||||
|
||||
/**
|
||||
* 创建一条对象。底层 POST `/objects/{kind}?...`,所有 extras 字段都作为 query 传给后端。
|
||||
*
|
||||
* 三类典型用法:
|
||||
* - process/script + UiDiscoveryCache 子类:`createObject('process', 'TaskFlow')`
|
||||
* - car(DummyCar 兜底,typeName 可空):`createObject('car', '', { name: 'AGV-1', x: 0, y: 0 })`
|
||||
* - site/track/image/text/model:`createObject('site', '', { x: 100, y: 200, name: 'A' })`
|
||||
*
|
||||
* 后端 BuildAndPersist 会做完整的字段类型转换;前端只负责传字符串。
|
||||
*/
|
||||
createObject: (
|
||||
kind: ReflectionKind,
|
||||
typeName?: string,
|
||||
extras?: Record<string, string | number | boolean | undefined>
|
||||
) => {
|
||||
if (MOCK) return Promise.resolve({ kind, id: -1, typeName: typeName ?? '' })
|
||||
const params: Record<string, string | number | boolean> = {}
|
||||
if (typeName) params.typeName = typeName
|
||||
if (extras) {
|
||||
for (const [k, v] of Object.entries(extras)) {
|
||||
if (v === undefined || v === null || v === '') continue
|
||||
params[k] = v
|
||||
}
|
||||
}
|
||||
return post<{ kind: string; id: number; typeName: string }>(`/objects/${kind}`, params)
|
||||
},
|
||||
|
||||
/** 删除一条对象(process / script / car / site / track / special)。 */
|
||||
deleteObject: (kind: ReflectionKind, id: number) => MOCK
|
||||
? Promise.resolve({ kind, id, deleted: true })
|
||||
: del<{ kind: string; id: number; deleted: boolean }>(`/objects/${kind}/${id}`),
|
||||
|
||||
/**
|
||||
* 触发 SimpleLite 重新扫描 ./plugins 目录、加载新增 dll 并重建 UiDiscoveryCache。
|
||||
* 已加载的 dll 不会重复加载。
|
||||
* 返回:本次发现的 dll 总数、新加载的 assembly 数、当前可创建的 mission/car 类型计数。
|
||||
*/
|
||||
reloadPlugins: () => MOCK
|
||||
? Promise.resolve({ totalDlls: 0, newlyLoaded: 0, failed: 0, missionTypes: 0, carTypes: 0 })
|
||||
: post<{ totalDlls: number; newlyLoaded: number; failed: number; missionTypes: number; carTypes: number }>(
|
||||
'/plugins/reload'
|
||||
),
|
||||
|
||||
/** 列出当前已加载的所有 collectible 插件(PluginManager 跟踪范围内)。 */
|
||||
listPlugins: () => MOCK
|
||||
? Promise.resolve<PluginEntry[]>([])
|
||||
: get<PluginEntry[]>('/plugins'),
|
||||
|
||||
/**
|
||||
* 卸载一个 collectible 插件。
|
||||
* 失败原因常见:仍有 Mission / Car 实例占用插件类型 → 409。
|
||||
*/
|
||||
unloadPlugin: (name: string) => MOCK
|
||||
? Promise.resolve({ name, message: 'mock', missionTypes: 0, carTypes: 0 })
|
||||
: post<{ name: string; message: string; missionTypes: number; carTypes: number }>(
|
||||
`/plugins/${encodeURIComponent(name)}/unload`
|
||||
),
|
||||
|
||||
listMethods: (kind: ReflectionKind, id: number) => MOCK
|
||||
? Promise.resolve(mockReflectionMethods(kind))
|
||||
: get<ReflectionMethod[]>(`/methods/${kind}/${id}`),
|
||||
|
||||
listMethodsByType: (kind: ReflectionKind) => MOCK
|
||||
? Promise.resolve(mockReflectionMethodsByType(kind))
|
||||
: get<ReflectionTypeMethods[]>(`/methods-by-type/${kind}`),
|
||||
|
||||
getStatus: (kind: ReflectionKind, id: number) => MOCK
|
||||
? Promise.resolve(mockReflectionStatus(kind, id))
|
||||
: get<ReflectionKv[]>(`/status/${kind}/${id}`),
|
||||
|
||||
getFields: (kind: ReflectionKind, id: number) => MOCK
|
||||
? Promise.resolve(mockReflectionFields(kind, id))
|
||||
: get<ReflectionKv[]>(`/fields/${kind}/${id}`),
|
||||
|
||||
setField: (kind: ReflectionKind, id: number, field: string, value: string) => MOCK
|
||||
? Promise.resolve(mockReflectionSetField(kind, id, field, value))
|
||||
: post<{ kind: ReflectionKind; id: number; field: string; value: string }>(
|
||||
`/fields/${kind}/${id}/${encodeURIComponent(field)}`,
|
||||
{ value }
|
||||
),
|
||||
|
||||
deleteField: (kind: ReflectionKind, id: number, field: string) => MOCK
|
||||
? Promise.resolve(mockReflectionDeleteField(kind, id, field))
|
||||
: del<{ kind: ReflectionKind; id: number; field: string }>(
|
||||
`/fields/${kind}/${id}/${encodeURIComponent(field)}`
|
||||
),
|
||||
|
||||
getBundle: (kind: ReflectionKind, id: number) => MOCK
|
||||
? Promise.resolve(mockReflectionBundle(kind, id))
|
||||
: get<{
|
||||
kind: string
|
||||
id: number
|
||||
typeName: string
|
||||
fullTypeName?: string
|
||||
assembly: string
|
||||
summary: ReflectionObject
|
||||
methods: ReflectionMethod[]
|
||||
status: ReflectionKv[]
|
||||
/** 扁平 key→value 兼容旧组件。 */
|
||||
fields: Record<string, string>
|
||||
/** 带 source/locked/typeName 的字段表,新版「对象管理」面板用。 */
|
||||
fieldList?: ReflectionKv[]
|
||||
}>(`/bundle/${kind}/${id}`),
|
||||
|
||||
execute: (kind: ReflectionKind, id: number, method: string, params?: Record<string, string | number | boolean>) => MOCK
|
||||
? Promise.resolve(mockReflectionExecute(
|
||||
kind,
|
||||
id,
|
||||
method,
|
||||
Object.fromEntries(Object.entries(params ?? {}).map(([k, v]) => [k, String(v)]))
|
||||
))
|
||||
: post<{ returnValue: string }>(
|
||||
`/execute/${kind}/${id}/${encodeURIComponent(method)}`,
|
||||
params
|
||||
),
|
||||
|
||||
/** 车辆前往指定站点(Web「去某地」;不依赖 SimpleUI.GetPoint)。 */
|
||||
gotoCarSite: (carId: number, siteId: number) => MOCK
|
||||
? Promise.resolve({ carId, siteId, message: `mock goto ${carId} -> ${siteId}` })
|
||||
: post<{ carId: number; siteId: number; message: string }>(
|
||||
`/car/${carId}/goto-site`,
|
||||
{ siteId }
|
||||
),
|
||||
|
||||
getScriptSource: (id: number): Promise<ScriptSourcePayload> => MOCK
|
||||
? Promise.resolve({
|
||||
id,
|
||||
name: `MockScript#${id}`,
|
||||
typeName: 'CarProgram',
|
||||
state: 'Running',
|
||||
script: '// mock script'
|
||||
})
|
||||
: get<ScriptSourcePayload>(`/scripts/${id}/source`),
|
||||
|
||||
getScriptExceptionStatus: (id: number): Promise<ScriptExceptionStatusPayload> => MOCK
|
||||
? Promise.resolve({
|
||||
id,
|
||||
name: `MockScript#${id}`,
|
||||
typeName: 'CarProgram',
|
||||
car: 'AGV-1(#1)',
|
||||
state: 'Running',
|
||||
exception: '(无)',
|
||||
notifies: '(无)',
|
||||
report: '=== CarProgram 异常状态报告 ===\nstate : Running\n\n--- exception ---\n(无)'
|
||||
})
|
||||
: get<ScriptExceptionStatusPayload>(`/scripts/${id}/exception-status`),
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
// 地图监控配置(Configuration.conf.monitorVisibility)
|
||||
// /monitor-config GET 现有配置 + 各 kind 可勾选 fields/status/methods 全集
|
||||
// /monitor-config POST body JSON 全量覆盖
|
||||
// 空白名单 = 显示全部;非空 = 仅显示列表中的 key。
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
getMonitorConfig: (): Promise<MonitorConfigPayload> => MOCK
|
||||
? Promise.resolve({
|
||||
config: loadMockMonitorConfig(),
|
||||
available: emptyMonitorAvailableMap()
|
||||
})
|
||||
: get<MonitorConfigPayload>('/monitor-config'),
|
||||
|
||||
saveMonitorConfig: async (cfg: MonitorConfigSaveBody): Promise<MonitorConfigSaveBody> => {
|
||||
if (MOCK) {
|
||||
saveMockMonitorConfig(cfg)
|
||||
return cfg
|
||||
}
|
||||
const { data } = await http.post<ReflectionEnvelope<MonitorConfigSaveBody>>(
|
||||
`${BASE}/monitor-config`,
|
||||
cfg
|
||||
)
|
||||
if (!data?.success) throw new Error(data?.message ?? 'saveMonitorConfig failed')
|
||||
return data.data as MonitorConfigSaveBody
|
||||
},
|
||||
|
||||
// 选中同步:让 SimpleLite 3D 场景同步高亮被点击对象
|
||||
getSelection: () => MOCK
|
||||
? Promise.resolve<ReflectionSelection>({ kind: null, id: 0, name: '' })
|
||||
: get<ReflectionSelection>('/selection'),
|
||||
|
||||
setSelection: (kind: ReflectionKind, id: number) => MOCK
|
||||
? Promise.resolve({ kind, id, name: `mock(${kind}#${id})` })
|
||||
: post<{ kind: ReflectionKind; id: number; name: string }>('/selection', { kind, id }),
|
||||
|
||||
clearSelection: () => MOCK
|
||||
? Promise.resolve({ cleared: true })
|
||||
: post<{ cleared: boolean }>('/selection/clear'),
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
// 项目属性(Scene.conf 单例)
|
||||
// /project/fields GET → 列出 Scene.conf 全部 [FieldMember] 字段
|
||||
// /project/fields/{key} POST → ?value=xxx 写入单字段
|
||||
// /project/save POST → 把内存项目(含修改后的 conf)写回 JSON
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
getProjectFields: (): Promise<ProjectPropertiesPayload> => MOCK
|
||||
? Promise.resolve({
|
||||
target: 'Scene.conf',
|
||||
lastLoadedPath: null,
|
||||
autoloadPath: null,
|
||||
fields: []
|
||||
})
|
||||
: get<ProjectPropertiesPayload>('/project/fields'),
|
||||
|
||||
setProjectField: (field: string, value: string) => MOCK
|
||||
? Promise.resolve({ field, value })
|
||||
: post<{ field: string; value: string }>(
|
||||
`/project/fields/${encodeURIComponent(field)}`,
|
||||
{ value }
|
||||
),
|
||||
|
||||
/** 保存当前项目到磁盘。path 为空则后端用 LastLoadedPath / Configuration.conf.autoload。 */
|
||||
saveProject: (path?: string) => MOCK
|
||||
? Promise.resolve({ path: path ?? '(mock)' })
|
||||
: post<{ path: string }>('/project/save', path ? { path } : undefined),
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
// 核心配置(simple.json / Configuration.conf)
|
||||
// /app-config/fields GET → 列出 Configuration.conf 全部 [FieldMember]
|
||||
// /app-config/fields/{key} POST → ?value=xxx 写入单字段
|
||||
// /app-config/save POST → 调 Configuration.ToFile("simple.json")
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
getAppConfigFields: (): Promise<AppConfigPayload> => MOCK
|
||||
? Promise.resolve({ target: 'Configuration.conf', savePath: 'simple.json', fields: [] })
|
||||
: get<AppConfigPayload>('/app-config/fields'),
|
||||
|
||||
setAppConfigField: (field: string, value: string) => MOCK
|
||||
? Promise.resolve({ field, value })
|
||||
: post<{ field: string; value: string }>(
|
||||
`/app-config/fields/${encodeURIComponent(field)}`,
|
||||
{ value }
|
||||
),
|
||||
|
||||
saveAppConfig: () => MOCK
|
||||
? Promise.resolve({ path: 'simple.json' })
|
||||
: post<{ path: string }>('/app-config/save'),
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
// 车型样式(WorkspaceCarStylesByType / WorkspaceAlarmColorScheme)
|
||||
// /car-style/types GET 列出所有 Car 子类型样式
|
||||
// /car-style/{typeFullName} GET 单个车型当前样式
|
||||
// /car-style/{typeFullName} POST body JSON 全量覆盖
|
||||
// /car-style/{typeFullName} DELETE 恢复为默认
|
||||
// /car-style/alarm-colors GET 7 种报警键 → 颜色
|
||||
// /car-style/alarm-colors POST body JSON 全量覆盖映射
|
||||
// /car-style/save POST 写回 simple.json
|
||||
// ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
getCarStyleTypes: () => MOCK
|
||||
? Promise.resolve<CarStyleTypesPayload>({ globalDefault: defaultCarStyleDto(), types: [] })
|
||||
: get<CarStyleTypesPayload>('/car-style/types'),
|
||||
|
||||
getCarStyle: (typeFullName: string) => MOCK
|
||||
? Promise.resolve(defaultCarStyleDto())
|
||||
: get<CarStyleDto>(`/car-style/${encodeURIComponent(typeFullName)}`),
|
||||
|
||||
/** 用 axios 直接以 JSON body 提交,避免拼 query。 */
|
||||
putCarStyle: async (typeFullName: string, body: CarStyleDto) => {
|
||||
if (MOCK) return body
|
||||
const { data } = await http.post<ReflectionEnvelope<CarStyleDto>>(
|
||||
`${BASE}/car-style/${encodeURIComponent(typeFullName)}`,
|
||||
body
|
||||
)
|
||||
if (!data?.success) throw new Error(data?.message ?? 'putCarStyle failed')
|
||||
return data.data as CarStyleDto
|
||||
},
|
||||
|
||||
deleteCarStyle: (typeFullName: string) => MOCK
|
||||
? Promise.resolve({ typeFullName, removed: true })
|
||||
: del<{ typeFullName: string; removed: boolean }>(`/car-style/${encodeURIComponent(typeFullName)}`),
|
||||
|
||||
getAlarmColors: () => MOCK
|
||||
? Promise.resolve<AlarmColorsPayload>({ defaultKeys: [], entries: [] })
|
||||
: get<AlarmColorsPayload>('/car-style/alarm-colors'),
|
||||
|
||||
putAlarmColors: async (palette: Record<string, number>) => {
|
||||
if (MOCK) return { count: Object.keys(palette).length }
|
||||
const { data } = await http.post<ReflectionEnvelope<{ count: number }>>(
|
||||
`${BASE}/car-style/alarm-colors`,
|
||||
palette
|
||||
)
|
||||
if (!data?.success) throw new Error(data?.message ?? 'putAlarmColors failed')
|
||||
return data.data as { count: number }
|
||||
},
|
||||
|
||||
saveCarStyle: () => MOCK
|
||||
? Promise.resolve({ path: 'simple.json' })
|
||||
: post<{ path: string }>('/car-style/save')
|
||||
}
|
||||
|
||||
export interface CarStyleDto {
|
||||
bodyLengthM: number
|
||||
bodyWidthM: number
|
||||
bodyColorArgb: number
|
||||
outlineColorArgb: number
|
||||
labelColorArgb: number
|
||||
showLabel: boolean
|
||||
modelPath: string
|
||||
}
|
||||
|
||||
export interface CarStyleTypeRow {
|
||||
typeName: string
|
||||
shortName: string
|
||||
label: string
|
||||
assemblyName: string
|
||||
hasOverride: boolean
|
||||
style: CarStyleDto
|
||||
}
|
||||
|
||||
export interface CarStyleTypesPayload {
|
||||
globalDefault: CarStyleDto
|
||||
types: CarStyleTypeRow[]
|
||||
}
|
||||
|
||||
export interface AlarmColorEntry {
|
||||
key: string
|
||||
colorArgb: number
|
||||
isDefault: boolean
|
||||
}
|
||||
|
||||
export interface AlarmColorsPayload {
|
||||
defaultKeys: string[]
|
||||
entries: AlarmColorEntry[]
|
||||
}
|
||||
|
||||
function defaultCarStyleDto(): CarStyleDto {
|
||||
return {
|
||||
bodyLengthM: 0.64,
|
||||
bodyWidthM: 0.42,
|
||||
bodyColorArgb: 0xffffffff,
|
||||
outlineColorArgb: 0xff2a2a2a,
|
||||
labelColorArgb: 0xffffffff,
|
||||
showLabel: true,
|
||||
modelPath: ''
|
||||
}
|
||||
}
|
||||
|
||||
export interface ProjectPropertyRow {
|
||||
key: string
|
||||
label: string
|
||||
value: string
|
||||
typeName: string
|
||||
locked: boolean
|
||||
}
|
||||
|
||||
export interface ProjectPropertiesPayload {
|
||||
target: string
|
||||
lastLoadedPath: string | null
|
||||
autoloadPath: string | null
|
||||
fields: ProjectPropertyRow[]
|
||||
}
|
||||
|
||||
export interface AppConfigPayload {
|
||||
target: string
|
||||
savePath: string
|
||||
fields: ProjectPropertyRow[]
|
||||
}
|
||||
Reference in New Issue
Block a user