Files
Migu2.0/frontends/apps/simple-platform-vue/src/composables/useMapEditStream.ts
T

118 lines
3.8 KiB
TypeScript

import { onBeforeUnmount, onMounted } from 'vue'
import { useProjectionStream, type StreamEvent } from './useProjectionStream'
/**
* 编辑器 / 监控页对 SimpleLite SSE 的事件订阅封装,把以下 5 类事件转给业务回调:
*
* - `alarm` (来自 AlarmStreamService):报警新增/更新/解除
* - `object-created` (来自 MapEditApiController):对象创建后
* - `object-deleted` (来自 MapEditApiController):对象删除后
* - `object-patched` (来自 MapEditApiController):对象字段被 patch 后
* - `object-batch-changed`(来自 batch / ai 调用):一组对象变化,建议直接整页重拉
* - `pick-result` (来自 /pick):拾取会话完成
*
* 业务页只用关心自己感兴趣的事件回调,其它走默认忽略。
*/
export interface AlarmEvent {
action: 'raise' | 'update' | 'clear'
carId: number
carName: string
info?: string
x?: number
y?: number
timestamp: string
}
export interface ObjectChangeEvent {
kind: string
id: number
typeName?: string
source?: string
}
export interface PickResultEvent {
x: number
y: number
siteId: number
}
export interface SelectionDetailEvent {
/** 主选中 kind (site|track|car|null);当多选时取首个非空集合的 kind。 */
kind: 'site' | 'track' | 'car' | null
/** 主选中 id;多选时取首个。 */
id: number
siteIds: number[]
trackIds: number[]
carIds: number[]
/** 完整对象名列表,例如 ["UISite-12", "UITrack-7", "Car-3"]。 */
names: string[]
}
export interface WorkspaceShortcutEvent {
action: 'undo' | 'redo' | 'delete' | 'copy' | 'paste' | 'duplicate'
}
export interface MapEditStreamHandlers {
onAlarm?: (e: AlarmEvent) => void
onObjectCreated?: (e: ObjectChangeEvent) => void
onObjectDeleted?: (e: ObjectChangeEvent) => void
onObjectPatched?: (e: ObjectChangeEvent) => void
onObjectBatchChanged?: (e: { count: number; source?: string }) => void
onPickResult?: (e: PickResultEvent) => void
onSelectionDetail?: (e: SelectionDetailEvent) => void
/** iframe 画布聚焦时由 SimpleLite 经 SSE 转发的撤销/重做/删除。 */
onWorkspaceShortcut?: (e: WorkspaceShortcutEvent) => void
/** 视口样式(站点/路径 Painter)在 Web 或桌面对话框中变更后广播。 */
onViewportStyleUpdated?: () => void
}
export function useMapEditStream(handlers: MapEditStreamHandlers) {
const stream = useProjectionStream({ autoConnect: false })
function dispatch(ev: StreamEvent) {
const payload = ev?.payload as Record<string, unknown> | undefined
if (!payload) return
switch (ev.kind) {
case 'alarm':
handlers.onAlarm?.(payload as unknown as AlarmEvent)
break
case 'object-created':
handlers.onObjectCreated?.(payload as unknown as ObjectChangeEvent)
break
case 'object-deleted':
handlers.onObjectDeleted?.(payload as unknown as ObjectChangeEvent)
break
case 'object-patched':
handlers.onObjectPatched?.(payload as unknown as ObjectChangeEvent)
break
case 'object-batch-changed':
handlers.onObjectBatchChanged?.(payload as unknown as { count: number; source?: string })
break
case 'pick-result':
handlers.onPickResult?.(payload as unknown as PickResultEvent)
break
case 'selection-detail':
handlers.onSelectionDetail?.(payload as unknown as SelectionDetailEvent)
break
case 'workspace-shortcut':
handlers.onWorkspaceShortcut?.(payload as unknown as WorkspaceShortcutEvent)
break
case 'viewport-style-updated':
handlers.onViewportStyleUpdated?.()
break
}
}
onMounted(() => {
stream.on(dispatch)
stream.connect()
})
onBeforeUnmount(() => {
stream.off(dispatch)
stream.disconnect()
})
return { connected: stream.connected }
}