refactor: 统一 Electron 子窗口与标签页窗口管理

将原有独立子窗口 (childWindow) 和标签页窗口 (webTabWindow) 合并为统一的
  窗口管理系统,通过 mode 参数区分窗口类型:
  - mode='tab': 标签页模式(有导航栏,默认)
  - mode='window': 独立窗口模式(无导航栏)

  主要变更:
  - 移除 createChildWindow、preCreateChildWindow 等独立窗口相关代码
  - 扩展 createWebTabWindow 支持 mode 参数
  - 简化前端 openWindow 调用,将 config 对象扁平化为顶层参数
  - 更新所有调用点使用新的统一接口
This commit is contained in:
kuaifan 2026-01-11 20:28:45 +00:00
parent 731dbc5507
commit 790f5d4838
13 changed files with 257 additions and 473 deletions

523
electron/electron.js vendored
View File

@ -72,14 +72,10 @@ let screenshotObj = null,
// 窗口实例变量 // 窗口实例变量
let mainWindow = null, let mainWindow = null,
mainTray = null, mainTray = null,
preloadWindow = null,
mediaWindow = null; mediaWindow = null;
// 独立子窗口管理
let childWindow = [];
// 多窗口 Tab 管理 // 多窗口 Tab 管理
// Map<windowId, {window, views: [{id, view, name, favicon}], activeTabId}> // Map<windowId, {window, views: [{id, view, name, favicon}], activeTabId, mode: 'tab'|'window'}>
let webTabWindows = new Map(); let webTabWindows = new Map();
let webTabWindowIdCounter = 1; let webTabWindowIdCounter = 1;
// 标签名称到标签位置的映射,用于复用已存在的标签 // 标签名称到标签位置的映射,用于复用已存在的标签
@ -435,241 +431,6 @@ function createUpdaterWindow(updateTitle) {
} }
} }
/**
* 创建预窗口
*/
function preCreateChildWindow() {
if (preloadWindow) {
return;
}
const browser = new BrowserWindow({
width: 360,
height: 360,
minWidth: 360,
minHeight: 360,
center: true,
show: false,
autoHideMenuBar: true,
backgroundColor: utils.getDefaultBackgroundColor(),
webPreferences: {
preload: path.join(__dirname, 'electron-preload.js'),
webSecurity: true,
nodeIntegration: true,
contextIsolation: true,
}
});
// 关闭事件
browser.addListener('closed', () => {
preloadWindow = null;
})
// 设置 UA
const originalUA = browser.webContents.session.getUserAgent() || browser.webContents.getUserAgent()
browser.webContents.setUserAgent(originalUA + " SubTaskWindow/" + process.platform + "/" + os.arch() + "/1.0");
utils.loadUrl(browser, serverUrl, '/preload')
preloadWindow = browser;
}
/**
* 创建子窗口
* @param args {name, path, hash, force, userAgent, config, webPreferences}
* - config: {title, titleFixed, ...BrowserWindowConstructorOptions}
*/
function createChildWindow(args) {
if (!args) {
return;
}
if (!utils.isJson(args)) {
args = {path: args, config: {}}
}
args.path = args.path || args.url;
const name = args.name || "auto_" + utils.randomString(6);
const wind = childWindow.find(item => item.name == name);
let browser = wind ? wind.browser : null;
let isPreload = false;
// 清理已销毁但仍被引用的窗口,避免对失效对象调用方法
if (browser && browser.isDestroyed && browser.isDestroyed()) {
const index = childWindow.findIndex(item => item.name == name);
if (index > -1) {
childWindow.splice(index, 1);
}
browser = null;
}
if (browser) {
browser.focus();
if (args.force === false) {
return;
}
} else {
const config = args.config || {};
const webPreferences = args.webPreferences || {};
const options = Object.assign({
width: 1280,
height: 800,
minWidth: 360,
minHeight: 360,
center: true,
show: false,
autoHideMenuBar: true,
backgroundColor: utils.getDefaultBackgroundColor(),
webPreferences: Object.assign({
preload: path.join(__dirname, 'electron-preload.js'),
webSecurity: true,
nodeIntegration: true,
contextIsolation: true,
}, webPreferences),
}, config)
options.width = utils.normalizeSize(options.width, 1280)
options.height = utils.normalizeSize(options.height, 800)
options.minWidth = utils.normalizeSize(options.minWidth, 360)
options.minHeight = utils.normalizeSize(options.minHeight, 360)
if (!options.webPreferences.contextIsolation) {
delete options.webPreferences.preload;
}
if (options.parent) {
options.parent = mainWindow
}
if (preloadWindow && !preloadWindow.isDestroyed?.() && Object.keys(webPreferences).length === 0) {
// 使用预加载窗口
browser = preloadWindow;
preloadWindow = null;
isPreload = true;
options.title && browser.setTitle(options.title);
options.parent && browser.setParentWindow(options.parent);
browser.setSize(options.width, options.height);
browser.setMinimumSize(options.minWidth, options.minHeight);
browser.center();
browser.setAutoHideMenuBar(options.autoHideMenuBar);
browser.removeAllListeners("closed");
setTimeout(() => onShowWindow(browser), 300)
process.nextTick(() => setTimeout(() => onShowWindow(browser), 50));
} else {
// 创建新窗口
browser = new BrowserWindow(options)
loger.info("create new window")
}
browser.on('page-title-updated', (event, title) => {
if (title == "index.html" || options.titleFixed === true) {
event.preventDefault()
}
})
browser.on('focus', () => {
browser.webContents.send("browserWindowFocus", {})
})
browser.on('blur', () => {
browser.webContents.send("browserWindowBlur", {})
})
browser.on('close', event => {
if (!willQuitApp) {
utils.onBeforeUnload(event, browser).then(() => {
browser.hide()
setTimeout(() => {
browser.destroy()
}, 100)
})
}
})
browser.on('closed', () => {
const index = childWindow.findIndex(item => item.browser === browser);
if (index > -1) {
childWindow.splice(index, 1)
}
})
browser.once('ready-to-show', () => {
onShowWindow(browser);
})
browser.webContents.once('dom-ready', () => {
onShowWindow(browser);
})
childWindow.push({ name, browser })
}
// 设置 UA
const originalUA = browser.webContents.session.getUserAgent() || browser.webContents.getUserAgent()
browser.webContents.setUserAgent(originalUA + " SubTaskWindow/" + process.platform + "/" + os.arch() + "/1.0" + (args.userAgent ? (" " + args.userAgent) : ""));
// 新窗口处理
browser.webContents.setWindowOpenHandler(({url}) => {
if (allowedCalls.test(url)) {
renderer.openExternal(url).catch(() => {})
} else {
utils.onBeforeOpenWindow(browser.webContents, url).then(() => {
renderer.openExternal(url).catch(() => {})
})
}
return {action: 'deny'}
})
// 设置右键菜单
electronMenu.webContentsMenu(browser.webContents)
// 设置导航快捷键(返回/前进)
navigation.setup(browser)
// 加载地址
const hash = `${args.hash || args.path}`;
if (/^https?:/i.test(hash)) {
// 完整 URL 直接加载
browser.loadURL(hash).then(_ => { }).catch(_ => { })
} else if (isPreload) {
// preload 窗口尝试调用 __initializeApp失败则 loadUrl
browser
.webContents
.executeJavaScript(`if(typeof window.__initializeApp === 'function'){window.__initializeApp('${hash}')}else{throw new Error('no function')}`, true)
.catch(() => {
utils.loadUrl(browser, serverUrl, hash)
});
} else {
// 相对路径使用 loadUrl
utils.loadUrl(browser, serverUrl, hash)
}
// 预创建下一个窗口
preCreateChildWindow();
}
/**
* 更新子窗口
* @param browser
* @param args
*/
function updateChildWindow(browser, args) {
if (!args) {
return;
}
if (!utils.isJson(args)) {
args = {path: args, name: null}
}
const hash = args.hash || args.path;
if (hash) {
utils.loadUrl(browser, serverUrl, hash)
}
if (args.name) {
const er = childWindow.find(item => item.browser == browser);
if (er) {
er.name = args.name;
}
}
}
/** /**
* 创建媒体浏览器窗口 * 创建媒体浏览器窗口
* @param args * @param args
@ -745,7 +506,10 @@ function createMediaWindow(args, type = 'image') {
/** /**
* 创建内置浏览器窗口支持多窗口 * 创建内置浏览器窗口支持多窗口
* @param args {url, windowId, position, afterId, insertIndex, name, force, userAgent, title, titleFixed, webPreferences, ...} * @param args {url, windowId, position, afterId, insertIndex, name, force, userAgent, title, titleFixed, webPreferences, mode, ...}
* - mode: 'tab' | 'window'
* - 'window': 独立窗口模式无导航栏
* - 'tab': 标签页模式默认有导航栏
* @returns {number} 窗口ID * @returns {number} 窗口ID
*/ */
function createWebTabWindow(args) { function createWebTabWindow(args) {
@ -757,7 +521,10 @@ function createWebTabWindow(args) {
args = {url: args} args = {url: args}
} }
// 如果有 name先查找是否已存在同名标签 const mode = args.mode || 'tab';
const isWindowMode = mode === 'window';
// 如果有 name先查找是否已存在同名标签/窗口
if (args.name) { if (args.name) {
const existing = webTabNameMap.get(args.name); const existing = webTabNameMap.get(args.name);
if (existing) { if (existing) {
@ -765,7 +532,7 @@ function createWebTabWindow(args) {
if (existingWindowData && existingWindowData.window && !existingWindowData.window.isDestroyed()) { if (existingWindowData && existingWindowData.window && !existingWindowData.window.isDestroyed()) {
const viewItem = existingWindowData.views.find(v => v.id === existing.tabId); const viewItem = existingWindowData.views.find(v => v.id === existing.tabId);
if (viewItem && viewItem.view && !viewItem.view.webContents.isDestroyed()) { if (viewItem && viewItem.view && !viewItem.view.webContents.isDestroyed()) {
// 激活已存在的标签 // 激活已存在的标签/窗口
if (existingWindowData.window.isMinimized()) { if (existingWindowData.window.isMinimized()) {
existingWindowData.window.restore(); existingWindowData.window.restore();
} }
@ -790,12 +557,12 @@ function createWebTabWindow(args) {
let windowData = windowId ? webTabWindows.get(windowId) : null; let windowData = windowId ? webTabWindows.get(windowId) : null;
let webTabWindow = windowData ? windowData.window : null; let webTabWindow = windowData ? windowData.window : null;
// 如果没有指定窗口或窗口不存在,查找第一个可用窗口或创建新窗口 // 如果没有指定窗口或窗口不存在,查找可用窗口或创建新窗口
if (!webTabWindow) { if (!webTabWindow) {
// 如果没有指定窗口,尝试使用第一个可用窗口 // window 模式总是创建新窗口tab 模式尝试使用第一个可用的 tab 窗口
if (!windowId) { if (!isWindowMode && !windowId) {
for (const [id, data] of webTabWindows) { for (const [id, data] of webTabWindows) {
if (data.window && !data.window.isDestroyed()) { if (data.window && !data.window.isDestroyed() && data.mode !== 'window') {
windowId = id; windowId = id;
windowData = data; windowData = data;
webTabWindow = data.window; webTabWindow = data.window;
@ -807,11 +574,21 @@ function createWebTabWindow(args) {
// 如果还是没有窗口,创建新窗口 // 如果还是没有窗口,创建新窗口
if (!webTabWindow) { if (!webTabWindow) {
windowId = webTabWindowIdCounter++; windowId = webTabWindowIdCounter++;
webTabWindow = createWebTabWindowInstance(windowId, args.position); // 从 args 中提取窗口尺寸
const position = {
x: args.x,
y: args.y,
width: args.width,
height: args.height,
minWidth: args.minWidth,
minHeight: args.minHeight,
};
webTabWindow = createWebTabWindowInstance(windowId, position, mode);
windowData = { windowData = {
window: webTabWindow, window: webTabWindow,
views: [], views: [],
activeTabId: null activeTabId: null,
mode: mode
}; };
webTabWindows.set(windowId, windowData); webTabWindows.set(windowId, windowData);
} }
@ -853,14 +630,17 @@ function createWebTabWindow(args) {
}); });
} }
utils.onDispatchEvent(webTabWindow.webContents, { // tab 模式通知标签栏创建标签window 模式不需要
event: 'create', if (!isWindowMode) {
id: browserView.webContents.id, utils.onDispatchEvent(webTabWindow.webContents, {
url: args.url, event: 'create',
afterId: args.afterId, id: browserView.webContents.id,
windowId: windowId, url: args.url,
title: args.title, afterId: args.afterId,
}).then(_ => { }); windowId: windowId,
title: args.title,
}).then(_ => { });
}
activateWebTabInWindow(windowId, browserView.webContents.id); activateWebTabInWindow(windowId, browserView.webContents.id);
return windowId; return windowId;
@ -870,30 +650,40 @@ function createWebTabWindow(args) {
* 创建 WebTabWindow 实例 * 创建 WebTabWindow 实例
* @param windowId * @param windowId
* @param position {x, y, width, height} * @param position {x, y, width, height}
* @param mode 'tab' | 'window'
* @returns {BrowserWindow} * @returns {BrowserWindow}
*/ */
function createWebTabWindowInstance(windowId, position) { function createWebTabWindowInstance(windowId, position, mode = 'tab') {
const titleBarOverlay = { const isWindowMode = mode === 'window';
height: webTabHeight
}; // mode='window': 根据屏幕分辨率动态计算默认值
if (nativeTheme.shouldUseDarkColors) { // mode='tab': 使用 userConf 保存值或固定默认值
titleBarOverlay.color = '#3B3B3D'; let defaultWidth, defaultHeight, defaultMinWidth, defaultMinHeight;
titleBarOverlay.symbolColor = '#C5C5C5'; if (isWindowMode) {
const { width: screenWidth } = screen.getPrimaryDisplay().workAreaSize;
const isHighRes = screenWidth >= 2560;
defaultWidth = isHighRes ? 1920 : 1024;
defaultHeight = isHighRes ? 1080 : 768;
defaultMinWidth = 400;
defaultMinHeight = 300;
} else {
const savedBounds = userConf.get('webTabWindow') || {};
defaultWidth = savedBounds.width ?? 1280;
defaultHeight = savedBounds.height ?? 800;
defaultMinWidth = 360;
defaultMinHeight = 360;
} }
const defaultBounds = userConf.get('webTabWindow') || {};
const hasExplicitPosition = position?.x !== undefined && position?.y !== undefined; const hasExplicitPosition = position?.x !== undefined && position?.y !== undefined;
const windowOptions = { const windowOptions = {
width: position?.width ?? defaultBounds.width ?? 1280, width: position?.width ?? defaultWidth,
height: position?.height ?? defaultBounds.height ?? 800, height: position?.height ?? defaultHeight,
minWidth: 360, minWidth: position?.minWidth ?? defaultMinWidth,
minHeight: 360, minHeight: position?.minHeight ?? defaultMinHeight,
center: !hasExplicitPosition, center: !hasExplicitPosition,
show: false, show: false,
autoHideMenuBar: true, autoHideMenuBar: true,
titleBarStyle: 'hidden', backgroundColor: isWindowMode ? utils.getDefaultBackgroundColor() : (nativeTheme.shouldUseDarkColors ? '#575757' : '#FFFFFF'),
titleBarOverlay,
backgroundColor: nativeTheme.shouldUseDarkColors ? '#575757' : '#FFFFFF',
webPreferences: { webPreferences: {
preload: path.join(__dirname, 'electron-preload.js'), preload: path.join(__dirname, 'electron-preload.js'),
webSecurity: true, webSecurity: true,
@ -902,6 +692,19 @@ function createWebTabWindowInstance(windowId, position) {
}, },
}; };
// tab 模式使用隐藏标题栏 + titleBarOverlay
if (!isWindowMode) {
const titleBarOverlay = {
height: webTabHeight
};
if (nativeTheme.shouldUseDarkColors) {
titleBarOverlay.color = '#3B3B3D';
titleBarOverlay.symbolColor = '#C5C5C5';
}
windowOptions.titleBarStyle = 'hidden';
windowOptions.titleBarOverlay = titleBarOverlay;
}
// 有明确位置时设置 x/y // 有明确位置时设置 x/y
if (hasExplicitPosition) { if (hasExplicitPosition) {
windowOptions.x = position.x; windowOptions.x = position.x;
@ -944,7 +747,11 @@ function createWebTabWindowInstance(windowId, position) {
return; return;
} }
} }
userConf.set('webTabWindow', webTabWindow.getBounds()); // 只有 tab 模式才保存 bounds
const windowData = webTabWindows.get(windowId);
if (windowData && windowData.mode !== 'window') {
userConf.set('webTabWindow', webTabWindow.getBounds());
}
}); });
webTabWindow.on('closed', () => { webTabWindow.on('closed', () => {
@ -996,7 +803,10 @@ function createWebTabWindowInstance(windowId, position) {
return item ? item.view.webContents : null; return item ? item.view.webContents : null;
}); });
webTabWindow.loadFile('./render/tabs/index.html', {query: {windowId: String(windowId)}}).then(_ => { }).catch(_ => { }); // tab 模式加载标签栏界面window 模式不需要
if (!isWindowMode) {
webTabWindow.loadFile('./render/tabs/index.html', {query: {windowId: String(windowId)}}).then(_ => { }).catch(_ => { });
}
return webTabWindow; return webTabWindow;
} }
@ -1012,12 +822,16 @@ function createWebTabView(windowId, args) {
if (!windowData) return null; if (!windowData) return null;
const webTabWindow = windowData.window; const webTabWindow = windowData.window;
const viewOptions = args.config || {}; const isWindowMode = windowData.mode === 'window';
viewOptions.webPreferences = Object.assign({ const effectiveTabHeight = isWindowMode ? 0 : webTabHeight;
preload: path.join(__dirname, 'electron-preload.js'),
nodeIntegration: true, const viewOptions = {
contextIsolation: true webPreferences: Object.assign({
}, args.webPreferences || {}); preload: path.join(__dirname, 'electron-preload.js'),
nodeIntegration: true,
contextIsolation: true
}, args.webPreferences || {})
};
if (!viewOptions.webPreferences.contextIsolation) { if (!viewOptions.webPreferences.contextIsolation) {
delete viewOptions.webPreferences.preload; delete viewOptions.webPreferences.preload;
} }
@ -1025,6 +839,8 @@ function createWebTabView(windowId, args) {
const browserView = new WebContentsView(viewOptions); const browserView = new WebContentsView(viewOptions);
if (args.backgroundColor) { if (args.backgroundColor) {
browserView.setBackgroundColor(args.backgroundColor); browserView.setBackgroundColor(args.backgroundColor);
} else if (isWindowMode) {
browserView.setBackgroundColor(utils.getDefaultBackgroundColor());
} else if (nativeTheme.shouldUseDarkColors) { } else if (nativeTheme.shouldUseDarkColors) {
browserView.setBackgroundColor('#575757'); browserView.setBackgroundColor('#575757');
} else { } else {
@ -1032,9 +848,9 @@ function createWebTabView(windowId, args) {
} }
browserView.setBounds({ browserView.setBounds({
x: 0, x: 0,
y: webTabHeight, y: effectiveTabHeight,
width: webTabWindow.getContentBounds().width || 1280, width: webTabWindow.getContentBounds().width || 1280,
height: (webTabWindow.getContentBounds().height || 800) - webTabHeight, height: (webTabWindow.getContentBounds().height || 800) - effectiveTabHeight,
}); });
// 保存所属窗口ID和元数据 // 保存所属窗口ID和元数据
@ -1063,7 +879,13 @@ function createWebTabView(windowId, args) {
browserView.webContents.setWindowOpenHandler(({url}) => { browserView.webContents.setWindowOpenHandler(({url}) => {
if (allowedCalls.test(url)) { if (allowedCalls.test(url)) {
renderer.openExternal(url).catch(() => {}); renderer.openExternal(url).catch(() => {});
} else if (isWindowMode) {
// window 模式下打开外部浏览器
utils.onBeforeOpenWindow(browserView.webContents, url).then(() => {
renderer.openExternal(url).catch(() => {});
});
} else { } else {
// tab 模式下创建新标签
createWebTabWindow({url, afterId: browserView.webContents.id, windowId}); createWebTabWindow({url, afterId: browserView.webContents.id, windowId});
} }
return {action: 'deny'}; return {action: 'deny'};
@ -1323,15 +1145,18 @@ function resizeWebTabInWindow(windowId, id) {
if (!windowData || !windowData.window) return; if (!windowData || !windowData.window) return;
const webTabWindow = windowData.window; const webTabWindow = windowData.window;
const isWindowMode = windowData.mode === 'window';
const effectiveTabHeight = isWindowMode ? 0 : webTabHeight;
const item = id === 0 ? currentWebTabInWindow(windowId) : windowData.views.find(item => item.id == id); const item = id === 0 ? currentWebTabInWindow(windowId) : windowData.views.find(item => item.id == id);
if (!item) { if (!item) {
return; return;
} }
item.view.setBounds({ item.view.setBounds({
x: 0, x: 0,
y: webTabHeight, y: effectiveTabHeight,
width: webTabWindow.getContentBounds().width || 1280, width: webTabWindow.getContentBounds().width || 1280,
height: (webTabWindow.getContentBounds().height || 800) - webTabHeight, height: (webTabWindow.getContentBounds().height || 800) - effectiveTabHeight,
}); });
} }
@ -1373,10 +1198,22 @@ function closeWebTabInWindow(windowId, id) {
const webTabView = windowData.views; const webTabView = windowData.views;
const webTabWindow = windowData.window; const webTabWindow = windowData.window;
const isWindowMode = windowData.mode === 'window';
const item = id === 0 ? currentWebTabInWindow(windowId) : webTabView.find(item => item.id == id); const item = id === 0 ? currentWebTabInWindow(windowId) : webTabView.find(item => item.id == id);
if (!item) { if (!item) {
return; return;
} }
// window 模式下直接关闭整个窗口
if (isWindowMode) {
webTabView.forEach(({name}) => {
if (name) webTabNameMap.delete(name);
});
webTabWindow.destroy();
return;
}
if (webTabView.length === 1) { if (webTabView.length === 1) {
webTabWindow.hide(); webTabWindow.hide();
} }
@ -1668,9 +1505,7 @@ function monitorThemeChanges() {
// 更新背景 // 更新背景
const backgroundColor = utils.getDefaultBackgroundColor() const backgroundColor = utils.getDefaultBackgroundColor()
mainWindow?.setBackgroundColor(backgroundColor); mainWindow?.setBackgroundColor(backgroundColor);
preloadWindow?.setBackgroundColor(backgroundColor);
mediaWindow?.setBackgroundColor(backgroundColor); mediaWindow?.setBackgroundColor(backgroundColor);
childWindow.some(({browser}) => browser.setBackgroundColor(backgroundColor))
// 更新所有 webTab 窗口背景 // 更新所有 webTab 窗口背景
for (const [, windowData] of webTabWindows) { for (const [, windowData] of webTabWindows) {
windowData.window?.setBackgroundColor(nativeTheme.shouldUseDarkColors ? '#575757' : '#FFFFFF'); windowData.window?.setBackgroundColor(nativeTheme.shouldUseDarkColors ? '#575757' : '#FFFFFF');
@ -1706,8 +1541,6 @@ if (!getTheLock) {
utils.useCookie() utils.useCookie()
// 创建主窗口 // 创建主窗口
createMainWindow() createMainWindow()
// 预创建子窗口
preCreateChildWindow()
// 监听主题变化 // 监听主题变化
monitorThemeChanges() monitorThemeChanges()
// 创建托盘 // 创建托盘
@ -1803,42 +1636,39 @@ ipcMain.on('windowQuit', (event) => {
}) })
/** /**
* 显示预加载窗口用于调试 * 获取路由窗口信息 webTabWindows 中查找 mode='window' 的窗口
*/
ipcMain.on('showPreloadWindow', (event) => {
if (preloadWindow) {
onShowWindow(preloadWindow)
}
event.returnValue = "ok"
})
/**
* 更新路由窗口
* @param args {?name, ?path} // name: 不是要更改的窗口名,是要把窗口名改成什么, path: 地址
*/
ipcMain.on('updateChildWindow', (event, args) => {
const browser = BrowserWindow.fromWebContents(event.sender);
updateChildWindow(browser, args)
event.returnValue = "ok"
})
/**
* 获取路由窗口信息
*/ */
ipcMain.handle('getChildWindow', (event, args) => { ipcMain.handle('getChildWindow', (event, args) => {
let child; let windowData, viewItem;
if (!args) { if (!args) {
const browser = BrowserWindow.fromWebContents(event.sender); // 通过发送者查找
child = childWindow.find(({browser: win}) => win === browser) const sender = event.sender;
} else { for (const [, data] of webTabWindows) {
child = childWindow.find(({name}) => name === args) if (data.mode === 'window') {
} const found = data.views.find(v => v.view.webContents === sender);
if (child) { if (found) {
return { windowData = data;
name: child.name, viewItem = found;
id: child.browser.webContents.id, break;
url: child.browser.webContents.getURL() }
}
} }
} else {
// 通过名称查找
const location = webTabNameMap.get(args);
if (location) {
windowData = webTabWindows.get(location.windowId);
if (windowData && windowData.mode === 'window') {
viewItem = windowData.views.find(v => v.id === location.tabId);
}
}
}
if (windowData && viewItem) {
return {
name: viewItem.name,
id: viewItem.view.webContents.id,
url: viewItem.view.webContents.getURL()
};
} }
return null; return null;
}); });
@ -1857,17 +1687,12 @@ ipcMain.on('openMediaViewer', (event, args) => {
* - url: 要打开的地址 * - url: 要打开的地址
* - name: 窗口/标签名称 * - name: 窗口/标签名称
* - mode: 'tab' | 'window' * - mode: 'tab' | 'window'
* - 'window': 独立窗口模式 * - 'window': 独立窗口模式无导航栏
* - 'tab': 标签页模式默认 * - 'tab': 标签页模式默认有导航栏
*/ */
ipcMain.on('openWindow', (event, args) => { ipcMain.on('openWindow', (event, args) => {
if (args.mode === 'window') { // 统一使用 createWebTabWindow通过 mode 区分窗口类型
// 独立窗口模式 createWebTabWindow(args)
createChildWindow(args)
} else {
// 标签页模式
createWebTabWindow(args)
}
event.returnValue = "ok" event.returnValue = "ok"
}) })
@ -2263,40 +2088,33 @@ ipcMain.on('windowDestroy', (event) => {
}) })
/** /**
* 关闭所有子窗口 * 关闭所有子窗口mode='window' 的窗口
*/ */
ipcMain.on('childWindowCloseAll', (event) => { ipcMain.on('childWindowCloseAll', (event) => {
childWindow.some(({browser}) => { for (const [, data] of webTabWindows) {
browser && browser.close() if (data.mode === 'window' && data.window && !data.window.isDestroyed()) {
}) data.window.close();
preloadWindow?.close() }
}
mediaWindow?.close() mediaWindow?.close()
electronDown.close() electronDown.close()
event.returnValue = "ok" event.returnValue = "ok"
}) })
/** /**
* 销毁所有子窗口 * 销毁所有子窗口mode='window' 的窗口
*/ */
ipcMain.on('childWindowDestroyAll', (event) => { ipcMain.on('childWindowDestroyAll', (event) => {
childWindow.some(({browser}) => { for (const [, data] of webTabWindows) {
browser && browser.destroy() if (data.mode === 'window' && data.window && !data.window.isDestroyed()) {
}) data.window.destroy();
preloadWindow?.destroy() }
}
mediaWindow?.destroy() mediaWindow?.destroy()
electronDown.destroy() electronDown.destroy()
event.returnValue = "ok" event.returnValue = "ok"
}) })
/**
* 刷新预加载窗口用于更换语言和主题时触发
*/
ipcMain.on('reloadPreloadWindow', (event) => {
if (preloadWindow) {
preloadWindow.webContents.reload()
}
event.returnValue = "ok"
})
/** /**
* 设置窗口尺寸 * 设置窗口尺寸
@ -2637,10 +2455,11 @@ ipcMain.on('updateQuitAndInstall', (event, args) => {
// 关闭所有子窗口 // 关闭所有子窗口
willQuitApp = true willQuitApp = true
childWindow.some(({browser}) => { for (const [, data] of webTabWindows) {
browser && browser.destroy() if (data.mode === 'window' && data.window && !data.window.isDestroyed()) {
}) data.window.destroy();
preloadWindow?.destroy() }
}
mediaWindow?.destroy() mediaWindow?.destroy()
electronDown.destroy() electronDown.destroy()

View File

@ -276,6 +276,19 @@ export default {
params.path = params.url params.path = params.url
delete params.url delete params.url
} }
// config
if ($A.isJson(params.config)) {
const config = params.config
delete params.config
params = Object.assign({
title: config.title,
titleFixed: config.titleFixed,
width: config.width,
height: config.height,
minWidth: config.minWidth,
minHeight: config.minHeight,
}, params)
}
this.$store.dispatch('openWindow', params); this.$store.dispatch('openWindow', params);
}, },
openTabWindow: (url) => { openTabWindow: (url) => {
@ -482,16 +495,17 @@ export default {
await $A.IDBSet("cacheMicroApps", $A.cloneJSON(apps)); await $A.IDBSet("cacheMicroApps", $A.cloneJSON(apps));
if (this.$Electron) { if (this.$Electron) {
const mergedConfig = Object.assign({
title: appConfig.title || ' ',
width: Math.min(window.screen.availWidth, 1440),
height: Math.min(window.screen.availHeight, 900),
}, $A.isJson(windowConfig) ? windowConfig : {});
await this.$store.dispatch('openWindow', { await this.$store.dispatch('openWindow', {
name: `single-apps-${$A.randomString(6)}`, name: `single-apps-${$A.randomString(6)}`,
path: path, path: path,
force: false, title: mergedConfig.title,
config: Object.assign({ width: mergedConfig.width,
title: appConfig.title || ' ', height: mergedConfig.height,
parent: null,
width: Math.min(window.screen.availWidth, 1440),
height: Math.min(window.screen.availHeight, 900),
}, $A.isJson(windowConfig) ? windowConfig : {}),
}); });
} else if (this.$isEEUIApp) { } else if (this.$isEEUIApp) {
await this.$store.dispatch('openAppChildPage', { await this.$store.dispatch('openAppChildPage', {
@ -517,13 +531,9 @@ export default {
await this.$store.dispatch('openWindow', { await this.$store.dispatch('openWindow', {
name: `external-apps-${$A.randomString(6)}`, name: `external-apps-${$A.randomString(6)}`,
path: config.url, path: config.url,
force: false, title: config.title || ' ',
config: { width: Math.min(window.screen.availWidth, 1440),
title: config.title || ' ', height: Math.min(window.screen.availHeight, 900),
parent: null,
width: Math.min(window.screen.availWidth, 1440),
height: Math.min(window.screen.availHeight, 900),
},
}); });
} else if (this.$isEEUIApp) { } else if (this.$isEEUIApp) {
await this.$store.dispatch('openAppChildPage', { await this.$store.dispatch('openAppChildPage', {

View File

@ -95,7 +95,7 @@ function setLanguage(language, silence = false) {
if (silence) { if (silence) {
utils.saveLanguage(language); utils.saveLanguage(language);
(async () => { (async () => {
await $A.IDBDel("callAt") $A.IDBDel("callAt")
$A.Electron?.sendMessage('reloadPreloadWindow'); $A.Electron?.sendMessage('reloadPreloadWindow');
$A.reloadUrl() $A.reloadUrl()
})() })()

View File

@ -3784,15 +3784,11 @@ export default {
this.$store.dispatch('openWindow', { this.$store.dispatch('openWindow', {
name: `file-msg-${data.id}`, name: `file-msg-${data.id}`,
path: path, path: path,
title,
titleFixed: true,
width: Math.min(window.screen.availWidth, 1440),
height: Math.min(window.screen.availHeight, 900),
userAgent: "/hideenOfficeTitle/", userAgent: "/hideenOfficeTitle/",
force: false,
config: {
title,
titleFixed: true,
parent: null,
width: Math.min(window.screen.availWidth, 1440),
height: Math.min(window.screen.availHeight, 900),
},
}); });
} else if (this.$isEEUIApp) { } else if (this.$isEEUIApp) {
this.$store.dispatch('openAppChildPage', { this.$store.dispatch('openAppChildPage', {

View File

@ -184,15 +184,11 @@ export default {
this.$store.dispatch('openWindow', { this.$store.dispatch('openWindow', {
name: `file-${this.fileId}-${row.id}`, name: `file-${this.fileId}-${row.id}`,
path: path, path: path,
title,
titleFixed: true,
width: Math.min(window.screen.availWidth, 1440),
height: Math.min(window.screen.availHeight, 900),
userAgent: "/hideenOfficeTitle/", userAgent: "/hideenOfficeTitle/",
force: false,
config: {
title,
titleFixed: true,
parent: null,
width: Math.min(window.screen.availWidth, 1440),
height: Math.min(window.screen.availHeight, 900),
},
}); });
} else if (this.$isEEUIApp) { } else if (this.$isEEUIApp) {
this.$store.dispatch('openAppChildPage', { this.$store.dispatch('openAppChildPage', {

View File

@ -394,13 +394,6 @@ export default {
}, },
}).then(linkRes => { }).then(linkRes => {
// 使 // 使
const config = {
title: this.addData.name,
titleFixed: true,
parent: null,
width: Math.min(window.screen.availWidth, 1440),
height: Math.min(window.screen.availHeight, 900),
}
const meetingPath = $A.urlAddParams($A.removeMainUrlPrefix(linkRes.data), { const meetingPath = $A.urlAddParams($A.removeMainUrlPrefix(linkRes.data), {
type: 'direct', type: 'direct',
nickname: data.nickname, nickname: data.nickname,
@ -412,9 +405,10 @@ export default {
this.$store.dispatch('openWindow', { this.$store.dispatch('openWindow', {
name: `meeting-window`, name: `meeting-window`,
path: meetingPath, path: meetingPath,
mode: 'window', title: this.addData.name,
force: false, titleFixed: true,
config width: Math.min(window.screen.availWidth, 1440),
height: Math.min(window.screen.availHeight, 900),
}); });
// //
this.addShow = false; this.addShow = false;

View File

@ -222,13 +222,9 @@ export default {
this.$store.dispatch('openWindow', { this.$store.dispatch('openWindow', {
name: `project-log-${id}`, name: `project-log-${id}`,
path: path, path: path,
force: false, title: this.$L(title),
config: { width: Math.min(window.screen.availWidth, 1440),
title: this.$L(title), height: Math.min(window.screen.availHeight, 900),
parent: null,
width: Math.min(window.screen.availWidth, 1440),
height: Math.min(window.screen.availHeight, 900),
},
}); });
} else if (this.$isEEUIApp) { } else if (this.$isEEUIApp) {
e.preventDefault() e.preventDefault()

View File

@ -104,18 +104,13 @@ export default {
this.detailData = row; this.detailData = row;
this.$emit("on-read"); this.$emit("on-read");
if (this.$Electron) { if (this.$Electron) {
let config = {
title: row.title,
titleFixed: true,
parent: null,
width: Math.min(window.screen.availWidth, 1440),
height: Math.min(window.screen.availHeight, 900),
}
this.$store.dispatch('openWindow', { this.$store.dispatch('openWindow', {
name: `report-detail-${row.id}`, name: `report-detail-${row.id}`,
path: `/single/report/detail/${row.id}`, path: `/single/report/detail/${row.id}`,
force: false, title: row.title,
config titleFixed: true,
width: Math.min(window.screen.availWidth, 1440),
height: Math.min(window.screen.availHeight, 900),
}); });
} else { } else {
this.showDetailDrawer = true; this.showDetailDrawer = true;
@ -128,17 +123,12 @@ export default {
onEditReport(id) { onEditReport(id) {
if (this.$Electron) { if (this.$Electron) {
let config = {
title: this.$L(id > 0 ? '修改报告' : '新增报告'),
parent: null,
width: Math.min(window.screen.availWidth, 1440),
height: Math.min(window.screen.availHeight, 900),
}
this.$store.dispatch('openWindow', { this.$store.dispatch('openWindow', {
name: `report-edit-${id}`, name: `report-edit-${id}`,
path: `/single/report/edit/${id}`, path: `/single/report/edit/${id}`,
force: false, title: this.$L(id > 0 ? '修改报告' : '新增报告'),
config width: Math.min(window.screen.availWidth, 1440),
height: Math.min(window.screen.availHeight, 900),
}); });
} else { } else {
this.reportId = id; this.reportId = id;

View File

@ -172,14 +172,10 @@ export default {
this.$store.dispatch('openWindow', { this.$store.dispatch('openWindow', {
name: `task-content-${this.taskId}-${row.id}`, name: `task-content-${this.taskId}-${row.id}`,
path: path, path: path,
force: false, title: title,
config: { titleFixed: true,
title: title, width: Math.min(window.screen.availWidth, 1440),
titleFixed: true, height: Math.min(window.screen.availHeight, 900),
parent: null,
width: Math.min(window.screen.availWidth, 1440),
height: Math.min(window.screen.availHeight, 900),
},
}); });
} else if (this.$isEEUIApp) { } else if (this.$isEEUIApp) {
this.$store.dispatch('openAppChildPage', { this.$store.dispatch('openAppChildPage', {

View File

@ -1891,26 +1891,16 @@ export default {
}, },
openNewWin() { openNewWin() {
const config = {
title: this.taskDetail.name,
titleFixed: true,
parent: null,
width: Math.min(window.screen.availWidth * 0.8, this.$el.clientWidth + 72),
height: Math.min(window.screen.availHeight * 0.8, this.$el.clientHeight + 72),
minWidth: 600,
minHeight: 450,
autoZoom: true,
};
if (this.hasOpenDialog) {
config.minWidth = 800;
config.minHeight = 600;
}
this.$store.dispatch('openWindow', { this.$store.dispatch('openWindow', {
name: `task-${this.taskDetail.id}`, name: `task-${this.taskDetail.id}`,
path: `/single/task/${this.taskDetail.id}?navActive=${this.navActive}`, path: `/single/task/${this.taskDetail.id}?navActive=${this.navActive}`,
mode: 'window', mode: 'window',
force: false, title: this.taskDetail.name,
config titleFixed: true,
width: Math.min(window.screen.availWidth * 0.8, this.$el.clientWidth + 72),
height: Math.min(window.screen.availHeight * 0.8, this.$el.clientHeight + 72),
minWidth: this.hasOpenDialog ? 800 : 600,
minHeight: this.hasOpenDialog ? 600 : 450,
}); });
this.$store.dispatch('openTask', 0); this.$store.dispatch('openTask', 0);
}, },
@ -1971,15 +1961,11 @@ export default {
this.$store.dispatch('openWindow', { this.$store.dispatch('openWindow', {
name: `file-task-${file.id}`, name: `file-task-${file.id}`,
path: path, path: path,
title: `${file.name} (${$A.bytesToSize(file.size)})`,
titleFixed: true,
width: Math.min(window.screen.availWidth, 1440),
height: Math.min(window.screen.availHeight, 900),
userAgent: "/hideenOfficeTitle/", userAgent: "/hideenOfficeTitle/",
force: false,
config: {
title: `${file.name} (${$A.bytesToSize(file.size)})`,
titleFixed: true,
parent: null,
width: Math.min(window.screen.availWidth, 1440),
height: Math.min(window.screen.availHeight, 900),
},
}); });
} else if (this.$isEEUIApp) { } else if (this.$isEEUIApp) {
this.$store.dispatch('openAppChildPage', { this.$store.dispatch('openAppChildPage', {

View File

@ -1523,15 +1523,11 @@ export default {
this.$store.dispatch('openWindow', { this.$store.dispatch('openWindow', {
name: `file-${item.id}`, name: `file-${item.id}`,
path: path, path: path,
title: $A.getFileName(item),
titleFixed: true,
width: Math.min(window.screen.availWidth, 1440),
height: Math.min(window.screen.availHeight, 900),
userAgent: "/hideenOfficeTitle/", userAgent: "/hideenOfficeTitle/",
force: false, //
config: {
title: $A.getFileName(item),
titleFixed: true,
parent: null,
width: Math.min(window.screen.availWidth, 1440),
height: Math.min(window.screen.availHeight, 900),
},
}); });
} else if (this.$isEEUIApp) { } else if (this.$isEEUIApp) {
this.$store.dispatch('openAppChildPage', { this.$store.dispatch('openAppChildPage', {

View File

@ -1385,12 +1385,15 @@ export default {
/** /**
* 打开窗口客户端 * 打开窗口客户端
* @param dispatch * @param dispatch
* @param params {path, name, mode, force, config, userAgent, webPreferences} * @param params {path, name, mode, force, title, titleFixed, width, height, minWidth, minHeight, userAgent, webPreferences}
* - path: 要打开的地址或直接传 URL 字符串 * - path: 要打开的地址或直接传 URL 字符串
* - name: 窗口/标签名称 * - name: 窗口/标签名称
* - mode: 'tab' | 'window'默认 'tab' * - mode: 'tab' | 'window'默认 'tab'
* - force: 是否强制刷新 * - force: 是否强制刷新
* - config: 窗口配置独立窗口模式有效 * - title: 窗口标题
* - titleFixed: 是否固定标题
* - width/height: 窗口尺寸mode='window' 有效
* - minWidth/minHeight: 最小尺寸mode='window' 有效
* - userAgent: 自定义 UserAgent * - userAgent: 自定义 UserAgent
* - webPreferences: 网页偏好设置 * - webPreferences: 网页偏好设置
*/ */
@ -1410,14 +1413,17 @@ export default {
} }
$A.Electron.sendMessage('openWindow', { $A.Electron.sendMessage('openWindow', {
url: params.path,
name: params.name, name: params.name,
url: params.path,
mode: params.mode, mode: params.mode,
force: params.force, title: params.title,
config: params.config, titleFixed: params.titleFixed,
width: params.width,
height: params.height,
minWidth: params.minWidth,
minHeight: params.minHeight,
userAgent: params.userAgent, userAgent: params.userAgent,
title: params.config?.title, force: params.force,
titleFixed: params.config?.titleFixed,
webPreferences: params.webPreferences, webPreferences: params.webPreferences,
}) })
}, },
@ -3594,13 +3600,9 @@ export default {
name: `dialog-${dialogId}`, name: `dialog-${dialogId}`,
path: `/single/dialog/${dialogId}`, path: `/single/dialog/${dialogId}`,
mode: 'window', mode: 'window',
force: false, title: dialogData.name,
config: { width: Math.min(window.screen.availWidth, 1024),
title: dialogData.name, height: Math.min(window.screen.availHeight, 768),
parent: null,
width: Math.min(window.screen.availWidth, 1024),
height: Math.min(window.screen.availHeight, 768),
},
}); });
}, },

View File

@ -68,9 +68,12 @@ export function openFileInClient(vm, item, options = {}) {
vm.$store.dispatch('openWindow', { vm.$store.dispatch('openWindow', {
name: windowName, name: windowName,
path, path,
title: windowConfig.title,
titleFixed: windowConfig.titleFixed,
width: windowConfig.width,
height: windowConfig.height,
userAgent: "/hideenOfficeTitle/", userAgent: "/hideenOfficeTitle/",
force: options.force === undefined ? false : options.force, force: options.force === undefined ? false : options.force,
config: windowConfig,
}); });
return; return;
} }