replay-startup.mjs 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. import { readFileSync } from 'node:fs'
  2. import { registerHooks } from 'node:module'
  3. import { parse, compileScript, compileTemplate } from '@vue/compiler-sfc'
  4. const SRC = '../../../../../src/modules/cockpit/'
  5. const ENV_STUB = 'const import_meta_env = { BASE_URL: "/", MODE: "development", DEV: true, PROD: false, SSR: false };'
  6. registerHooks({
  7. load(url, context, nextLoad) {
  8. if (url.endsWith('.json')) return { format: 'module', shortCircuit: true, source: 'export default ' + readFileSync(new URL(url), 'utf8') }
  9. if (url.endsWith('.css')) return { format: 'module', shortCircuit: true, source: 'export default {}' }
  10. if (url.endsWith('.js') && url.includes('/src/')) {
  11. const raw = readFileSync(new URL(url), 'utf8')
  12. if (raw.includes('import.meta.env')) return { format: 'module', shortCircuit: true, source: ENV_STUB + raw.replace(/import\.meta\.env/g, 'import_meta_env') }
  13. }
  14. if (url.endsWith('.vue')) {
  15. const source = readFileSync(new URL(url), 'utf8')
  16. const { descriptor } = parse(source, { filename: url })
  17. const script = compileScript(descriptor, { id: 'replay', genDefaultAs: '__sfc__' })
  18. const template = compileTemplate({ source: descriptor.template.content, filename: url, id: 'replay', ssr: true, compilerOptions: { bindingMetadata: script.bindings } })
  19. const body = script.content.replace(/import\.meta\.env/g, 'import_meta_env')
  20. return { format: 'module', shortCircuit: true, source: ENV_STUB + '\n' + body + '\n' + template.code + '\n__sfc__.ssrRender = ssrRender; export default __sfc__;' }
  21. }
  22. return nextLoad(url, context)
  23. },
  24. })
  25. // 最小 DOM/window stub(启动计划里只有 WS/字体/计时器需要它们)
  26. const noop = () => {}
  27. globalThis.window = globalThis.window || {
  28. addEventListener: noop, removeEventListener: noop, setTimeout, clearTimeout, setInterval, clearInterval,
  29. requestAnimationFrame: (cb) => setTimeout(() => cb(Date.now()), 16), cancelAnimationFrame: noop,
  30. devicePixelRatio: 1, innerWidth: 1680, innerHeight: 950,
  31. location: { href: 'http://127.0.0.1:8000/#/cockpit', search: '', hash: '#/cockpit', origin: 'http://127.0.0.1:8000' },
  32. matchMedia: () => ({ matches: false, addEventListener: noop, removeEventListener: noop }),
  33. getComputedStyle: () => ({ getPropertyValue: () => '' }),
  34. ResizeObserver: class { observe() {} unobserve() {} disconnect() {} },
  35. }
  36. globalThis.document = globalThis.document || {
  37. activeElement: null, visibilityState: 'visible', hidden: false,
  38. createElement: () => ({ style: {}, setAttribute: noop, appendChild: noop, addEventListener: noop, classList: { add: noop, remove: noop } }),
  39. createElementNS: () => ({ style: {}, setAttribute: noop, appendChild: noop, addEventListener: noop }),
  40. createTextNode: () => ({}), createComment: () => ({}),
  41. querySelector: () => null, querySelectorAll: () => [], getElementById: () => null,
  42. head: { appendChild: noop, children: [] }, body: { appendChild: noop, children: [] },
  43. addEventListener: noop, removeEventListener: noop,
  44. }
  45. if (!globalThis.navigator) Object.defineProperty(globalThis, 'navigator', { value: { userAgent: 'node' }, configurable: true })
  46. for (const k of ['ResizeObserver', 'matchMedia', 'getComputedStyle']) if (globalThis[k] === undefined) globalThis[k] = globalThis.window[k]
  47. const { createPinia, setActivePinia } = await import('pinia')
  48. setActivePinia(createPinia())
  49. const { loadCockpitConfig } = await import(SRC + 'services/cockpitConfig.js')
  50. const { planCockpitStartup } = await import(SRC + 'domain/startupPlan.js')
  51. const { STARTUP_ACTION } = await import(SRC + 'domain/startupPlan.js')
  52. const { registerMockTransport, resetMockTransportsForTest } = await import(SRC + 'services/cockpitApi.js')
  53. const { registerCockpitMocks } = await import(SRC + 'mocks/contractMocks.js')
  54. const { useMapStore } = await import(SRC + 'stores/mapStore.js')
  55. const { useDetailStore } = await import(SRC + 'stores/detailStore.js')
  56. const { usePipeNetworkStore } = await import(SRC + 'stores/pipeNetworkStore.js')
  57. const { useInfrastructureStore } = await import(SRC + 'stores/infrastructureStore.js')
  58. const { useMonitoringStore } = await import(SRC + 'stores/monitoringStore.js')
  59. const { useEventStore } = await import(SRC + 'stores/eventStore.js')
  60. const { useOverviewStore } = await import(SRC + 'stores/overviewStore.js')
  61. const { useRuntimeStore } = await import(SRC + 'stores/runtimeStore.js')
  62. const { useFilterStore } = await import(SRC + 'stores/filterStore.js')
  63. const requests = []
  64. resetMockTransportsForTest()
  65. registerCockpitMocks({
  66. scenario: 'success',
  67. handlers: {
  68. registerMockTransport: (path, handler) => registerMockTransport(path, (req) => { requests.push(typeof req === 'string' ? req : JSON.stringify(req).slice(0, 120)); return handler(req) }),
  69. resetMockTransportsForTest,
  70. },
  71. })
  72. const result = await loadCockpitConfig()
  73. const plan = planCockpitStartup(result.config)
  74. console.log('config state =', result.state, 'dataSource =', result.config.dataSource)
  75. console.log('plan.actions =', plan.actions.join(', '))
  76. const filterStore = useFilterStore()
  77. const mapStore = useMapStore()
  78. const detailStore = useDetailStore()
  79. const pipeNetworkStore = usePipeNetworkStore()
  80. const infrastructureStore = useInfrastructureStore()
  81. const monitoringStore = useMonitoringStore()
  82. const eventStore = useEventStore()
  83. const overviewStore = useOverviewStore()
  84. const runtimeStore = useRuntimeStore()
  85. const scope = { value: filterStore.scope }
  86. let failedAt = null
  87. for (const action of plan.actions) {
  88. try {
  89. if (action === STARTUP_ACTION.REGISTER_MOCKS) continue
  90. if (action === STARTUP_ACTION.LOAD_MAP_SUMMARY) { mapStore.setup({ config: result.config, industry: 'all', scope: 'all', deviceType: '', pointStatus: [] }); continue }
  91. if (action === STARTUP_ACTION.LOAD_SHARED_SECTIONS) { detailStore.loadSharedSections({ config: result.config }); pipeNetworkStore.load({ industry: 'all', scope: 'all', config: result.config }); continue }
  92. if (action === STARTUP_ACTION.LOAD_INFRASTRUCTURE_PANELS) { infrastructureStore.configure({ config: result.config }); infrastructureStore.loadPanels({ industry: 'all', scope: 'all' }); continue }
  93. if (action === STARTUP_ACTION.LOAD_MONITORING_PANELS) { monitoringStore.configure({ config: result.config }); monitoringStore.loadPanels({ industry: 'all', deviceType: '', pointStatus: [], scope: 'all' }); continue }
  94. if (action === STARTUP_ACTION.LOAD_EVENT_PANELS) { eventStore.configure({ config: result.config }); eventStore.loadPanels({ mode: 'WARNING', range: '7d', industry: 'all' }); continue }
  95. if (action === STARTUP_ACTION.LOAD_OVERVIEW_PANELS) { overviewStore.configure({ config: result.config }); overviewStore.loadPanels(); continue }
  96. if (action === STARTUP_ACTION.START_WEBSOCKET) { runtimeStore.startWithConfig(result.config, { scenario: 'success' }); continue }
  97. if (action === STARTUP_ACTION.START_FRESHNESS) { continue }
  98. } catch (error) {
  99. failedAt = action + ' → ' + (error && error.stack ? error.stack.split('\n').slice(0, 4).join(' | ') : error)
  100. break
  101. }
  102. }
  103. console.log('启动计划同步执行:', failedAt || '全部动作未抛错')
  104. await new Promise((r) => setTimeout(r, 300))
  105. console.log('请求数 =', requests.length)
  106. console.log('mapStore.filters =', JSON.stringify(mapStore.filters))
  107. console.log('mapStore.summary.phase =', mapStore.summary.phase)
  108. console.log('infrastructure 分区 loaded =', Object.entries(infrastructureStore.sections).map(([k, v]) => k + ':' + v.state + '/' + v.loaded).join(' '))
  109. process.exit(0)