windowCtl.ts 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. import { BrowserWindow } from 'electron';
  2. import type { WebContents } from 'electron';
  3. import { logger } from 'ee-core/log';
  4. import {
  5. closeSmartWindow,
  6. focusMainWindow,
  7. isSmartWindow,
  8. isSmartWindowOpen,
  9. openSmartWindow,
  10. } from '../service/window/smartWindow';
  11. /** 主进程 → 渲染进程的跨窗口状态事件通道 */
  12. export const SYNC_EVENT_CHANNEL = 'app:state';
  13. interface IpcEventLike {
  14. sender?: WebContents;
  15. }
  16. interface OpenSmartWindowParams {
  17. fullPath?: string;
  18. reloadIfOpen?: boolean;
  19. navigateIfOpen?: boolean;
  20. }
  21. interface SyncStateParams {
  22. event?: {
  23. type?: string;
  24. payload?: Record<string, unknown>;
  25. };
  26. }
  27. function toMessage(error: unknown): string {
  28. return error instanceof Error ? error.message : String(error);
  29. }
  30. /**
  31. * 窗口管理 IPC:通道名 controller/windowCtl/<方法>(ee-core 按文件名自动注册)。
  32. *
  33. * 统一返回 { success, data|message },失败不抛到 Electron 层。
  34. */
  35. class WindowCtl {
  36. /** 打开/聚焦智能工作台窗口;params { fullPath?, reloadIfOpen? } */
  37. async openSmartWindow(params: OpenSmartWindowParams = {}) {
  38. try {
  39. openSmartWindow({
  40. fullPath: params?.fullPath,
  41. reloadIfOpen: params?.reloadIfOpen === true,
  42. navigateIfOpen: params?.navigateIfOpen === true,
  43. });
  44. return { success: true, data: { open: true } };
  45. } catch (e) {
  46. const message = toMessage(e);
  47. logger.error('[windowCtl] openSmartWindow failed:', message);
  48. return { success: false, message };
  49. }
  50. }
  51. /** 关闭智能工作台窗口(已关闭时是 no-op) */
  52. async closeSmartWindow() {
  53. try {
  54. closeSmartWindow();
  55. return { success: true };
  56. } catch (e) {
  57. const message = toMessage(e);
  58. logger.error('[windowCtl] closeSmartWindow failed:', message);
  59. return { success: false, message };
  60. }
  61. }
  62. async isSmartWindowOpen() {
  63. return { success: true, data: { open: isSmartWindowOpen() } };
  64. }
  65. /** 智能窗口遇到登录失效时,把主窗口拉到前台负责提示 */
  66. async focusMainWindow() {
  67. try {
  68. focusMainWindow();
  69. return { success: true };
  70. } catch (e) {
  71. const message = toMessage(e);
  72. logger.error('[windowCtl] focusMainWindow failed:', message);
  73. return { success: false, message };
  74. }
  75. }
  76. /**
  77. * 渲染进程启动时问一次自己的角色。
  78. *
  79. * <p>ee-core 的 IPC 注册是 fn.call(controller, params, event),
  80. * event.sender 就是发起调用的 webContents。
  81. */
  82. async getWindowRole(_params: unknown, event?: IpcEventLike) {
  83. const win = event?.sender ? BrowserWindow.fromWebContents(event.sender) : null;
  84. return { success: true, data: { role: isSmartWindow(win) ? 'smart' : 'main' } };
  85. }
  86. /**
  87. * 跨窗口状态同步:把语义事件发给除发送者外的所有窗口。
  88. *
  89. * <p>只做转发,不解释业务;接收方负责按事件类型重读事实源或关闭自己。
  90. */
  91. async syncState(params: SyncStateParams, event?: IpcEventLike) {
  92. const payload = params?.event;
  93. if (!payload?.type) {
  94. return { success: false, message: '同步事件缺少 type' };
  95. }
  96. const senderId = event?.sender?.id;
  97. for (const win of BrowserWindow.getAllWindows()) {
  98. if (win.isDestroyed() || win.webContents.isDestroyed()) continue;
  99. if (senderId && win.webContents.id === senderId) continue;
  100. try {
  101. win.webContents.send(SYNC_EVENT_CHANNEL, payload);
  102. } catch (e) {
  103. logger.warn('[windowCtl] syncState send failed:', toMessage(e));
  104. }
  105. }
  106. return { success: true };
  107. }
  108. }
  109. WindowCtl.toString = () => 'WindowCtl';
  110. export default WindowCtl;