| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- import { Injectable } from '@angular/core';
-
- /**
- * 浏览器窗口管理服务
- * 封装 window.open 操作,提供项目标签页管理功能
- *
- * 功能:
- * 1. 生成正确的绝对 URL
- * 2. 管理标签页状态(localStorage 存储)
- * 3. 实现窗口复用(已打开则聚焦)
- * 4. 标签页数量限制(最多7个,自动关闭最旧)
- * 5. 错误处理和诊断日志
- */
- @Injectable({
- providedIn: 'root'
- })
- export class WindowService {
- private readonly MAX_TABS = 7;
- private readonly TAB_STORAGE_KEY = 'browser_tabs';
- private windowReferences = new Map<string, Window>();
-
- /**
- * 打开项目标签页
- * @param projectId 项目ID
- * @returns 是否成功打开或聚焦窗口
- */
- openProjectTab(projectId: string): boolean {
- console.log('🔍 [WindowService] 打开项目标签页:', projectId);
-
- // 生成窗口名称和URL
- const windowName = this.generateWindowName(projectId);
- const url = this.generateProjectUrl(projectId);
-
- console.log('🔍 [WindowService] 参数:', { windowName, url });
-
- // 检查内存中是否已跟踪此窗口
- const existingWindow = this.windowReferences.get(windowName);
- if (existingWindow && !existingWindow.closed) {
- console.log('🔍 [WindowService] 窗口已在内存中跟踪,聚焦:', windowName);
- existingWindow.focus();
- return true;
- }
-
- // 清理无效的窗口引用
- this.cleanupWindowReferences();
-
- // 检查标签页数量限制
- const openTabCount = this.windowReferences.size;
- console.log('🔍 [WindowService] 当前跟踪的标签页数量:', openTabCount, '限制:', this.MAX_TABS);
-
- if (openTabCount >= this.MAX_TABS) {
- console.error('🔍 [WindowService] 标签页数量超限');
- this.showError('已达到最大标签页数量(7个),请手动关闭一个标签页后再试');
- return false;
- }
-
- // 使用 _blank 目标打开新窗口
- console.log('🔍 [WindowService] 使用 _blank 目标打开新窗口');
- const newWindow = window.open(url, '_blank');
-
- console.log('🔍 [WindowService] window.open 结果:', !!newWindow);
-
- if (!newWindow) {
- console.error('🔍 [WindowService] 无法打开新标签页');
- this.showError('无法打开新标签页,可能被浏览器阻止,请检查浏览器弹出窗口设置');
- return false;
- }
-
- // 在内存中跟踪此窗口
- this.windowReferences.set(windowName, newWindow);
-
- // 设置关闭监听器
- const checkClosed = () => {
- if (newWindow.closed) {
- console.log('🔍 [WindowService] 检测到窗口已关闭:', windowName);
- this.windowReferences.delete(windowName);
- } else {
- // 继续检查
- setTimeout(checkClosed, 1000);
- }
- };
- setTimeout(checkClosed, 1000);
-
- console.log('🔍 [WindowService] 窗口已打开并跟踪');
- return true;
- }
-
- /**
- * 生成窗口名称
- */
- private generateWindowName(projectId: string): string {
- return `project-${projectId}`;
- }
-
- /**
- * 生成项目URL(绝对路径)
- */
- private generateProjectUrl(projectId: string): string {
- // 生成绝对URL确保在新标签页中可靠加载
- const origin = window.location.origin;
- const url = `${origin}/project/${projectId}`;
-
- console.log('🔍 [WindowService] 生成的URL:', { origin, url });
-
- return url;
- }
-
-
- /**
- * 清理无效的窗口引用
- */
- private cleanupWindowReferences(): void {
- for (const [name, win] of this.windowReferences.entries()) {
- if (win.closed) {
- console.log('🔍 [WindowService] 清理已关闭的窗口引用:', name);
- this.windowReferences.delete(name);
- }
- }
- }
-
- /**
- * 根据名称获取已存在的窗口(简化版,不依赖window.open('', name))
- */
- private getWindowByName(name: string): Window | null {
- // 只从内存引用中获取
- const win = this.windowReferences.get(name);
- if (win && !win.closed) {
- return win;
- }
- return null;
- }
-
- /**
- * 获取标签页数据
- */
- private getTabData(): Record<string, number> {
- try {
- const tabsJson = localStorage.getItem(this.TAB_STORAGE_KEY);
- return tabsJson ? JSON.parse(tabsJson) : {};
- } catch (e) {
- console.error('🔍 [WindowService] 解析标签页数据失败:', e);
- return {};
- }
- }
-
- /**
- * 保存标签页数据
- */
- private saveTabData(tabData: Record<string, number>): void {
- try {
- localStorage.setItem(this.TAB_STORAGE_KEY, JSON.stringify(tabData));
- } catch (e) {
- console.error('🔍 [WindowService] 保存标签页数据失败:', e);
- }
- }
-
- /**
- * 获取当前应用的基URL(用于诊断)
- */
- getBaseUrl(): string {
- return window.location.origin;
- }
-
- /**
- * 显示错误提示给用户
- */
- private showError(message: string): void {
- console.error('🔍 [WindowService] 错误:', message);
- // 使用alert简单提示用户,实际项目中可替换为toast等UI组件
- alert(`打开标签页失败: ${message}`);
- }
-
- /**
- * 诊断方法:输出当前窗口状态信息
- */
- diagnose(): {
- baseUrl: string;
- openTabCount: number;
- maxTabs: number;
- trackedWindows: string[];
- windowLocation: string;
- } {
- return {
- baseUrl: this.getBaseUrl(),
- openTabCount: this.windowReferences.size,
- maxTabs: this.MAX_TABS,
- trackedWindows: Array.from(this.windowReferences.keys()),
- windowLocation: window.location.href
- };
- }
- }
|