mirror of
https://github.com/kuaifan/dootask.git
synced 2026-01-11 16:28:11 +00:00
refactor: 优化标签页加载状态管理与 URL 加载逻辑
- 新增 loadContentUrl 方法统一处理完整 URL 和相对路径的加载 - 优化标签页加载状态,忽略 SPA 路由切换(isSameDocument),避免频繁闪烁 - 添加定时检查器确保加载状态正确停止 - windowClose/windowDestroy 支持识别 tab 页面发送者,仅关闭对应标签 - 子窗口重启过程中不再意外销毁窗口 - 微应用打开标签页时传递标题信息 - isLocalHost 对空 URL 和相对路径返回 true
This commit is contained in:
parent
ce42c2a660
commit
4929d44ce7
102
electron/electron.js
vendored
102
electron/electron.js
vendored
@ -625,10 +625,10 @@ function createChildWindow(args) {
|
||||
// 加载地址
|
||||
const hash = `${args.hash || args.path}`;
|
||||
if (/^https?:/i.test(hash)) {
|
||||
browser.loadURL(hash)
|
||||
.then(_ => { })
|
||||
.catch(_ => { })
|
||||
// 完整 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)
|
||||
@ -636,6 +636,7 @@ function createChildWindow(args) {
|
||||
utils.loadUrl(browser, serverUrl, hash)
|
||||
});
|
||||
} else {
|
||||
// 相对路径使用 loadUrl
|
||||
utils.loadUrl(browser, serverUrl, hash)
|
||||
}
|
||||
|
||||
@ -774,7 +775,7 @@ function createWebTabWindow(args) {
|
||||
|
||||
// force=true 时重新加载
|
||||
if (args.force === true && args.url) {
|
||||
viewItem.view.webContents.loadURL(args.url).catch(_ => {});
|
||||
utils.loadContentUrl(viewItem.view.webContents, serverUrl, args.url);
|
||||
}
|
||||
return existing.windowId;
|
||||
}
|
||||
@ -1050,10 +1051,13 @@ function createWebTabView(windowId, args) {
|
||||
}
|
||||
|
||||
browserView.webContents.on('destroyed', () => {
|
||||
// 清理 name 映射
|
||||
if (browserView.tabName) {
|
||||
webTabNameMap.delete(browserView.tabName);
|
||||
}
|
||||
if (browserView._loadingChecker) {
|
||||
clearInterval(browserView._loadingChecker);
|
||||
browserView._loadingChecker = null;
|
||||
}
|
||||
closeWebTabInWindow(windowId, browserView.webContents.id);
|
||||
});
|
||||
browserView.webContents.setWindowOpenHandler(({url}) => {
|
||||
@ -1134,27 +1138,46 @@ function createWebTabView(windowId, args) {
|
||||
favicon: base64Favicon || ''
|
||||
}).then(_ => { });
|
||||
});
|
||||
browserView.webContents.on('did-start-loading', _ => {
|
||||
// 使用动态窗口ID,支持标签在窗口间转移
|
||||
const currentWindowId = browserView.webTabWindowId;
|
||||
const wd = webTabWindows.get(currentWindowId);
|
||||
if (!wd || !wd.window) return;
|
||||
utils.onDispatchEvent(wd.window.webContents, {
|
||||
event: 'start-loading',
|
||||
id: browserView.webContents.id,
|
||||
}).then(_ => { });
|
||||
// 页面加载状态管理,忽略SPA路由切换(isSameDocument)
|
||||
browserView._loadingActive = false;
|
||||
browserView._loadingChecker = null;
|
||||
const dispatchLoading = (event) => {
|
||||
const wd = webTabWindows.get(browserView.webTabWindowId);
|
||||
if (wd && wd.window) {
|
||||
utils.onDispatchEvent(wd.window.webContents, {
|
||||
event,
|
||||
id: browserView.webContents.id,
|
||||
}).then(_ => { });
|
||||
}
|
||||
};
|
||||
const startLoading = () => {
|
||||
if (browserView._loadingActive) return;
|
||||
browserView._loadingActive = true;
|
||||
dispatchLoading('start-loading');
|
||||
if (!browserView._loadingChecker) {
|
||||
browserView._loadingChecker = setInterval(() => {
|
||||
if (browserView.webContents.isDestroyed() || !browserView.webContents.isLoading()) {
|
||||
stopLoading();
|
||||
}
|
||||
}, 3000);
|
||||
}
|
||||
};
|
||||
const stopLoading = () => {
|
||||
if (browserView._loadingChecker) {
|
||||
clearInterval(browserView._loadingChecker);
|
||||
browserView._loadingChecker = null;
|
||||
}
|
||||
if (!browserView._loadingActive) return;
|
||||
browserView._loadingActive = false;
|
||||
dispatchLoading('stop-loading');
|
||||
};
|
||||
browserView.webContents.on('did-start-navigation', (_, _url, _isInPlace, isMainFrame, _frameProcessId, _frameRoutingId, _navigationId, isSameDocument) => {
|
||||
if (isMainFrame && !isSameDocument) {
|
||||
startLoading();
|
||||
}
|
||||
});
|
||||
browserView.webContents.on('did-stop-loading', _ => {
|
||||
// 使用动态窗口ID,支持标签在窗口间转移
|
||||
const currentWindowId = browserView.webTabWindowId;
|
||||
const wd = webTabWindows.get(currentWindowId);
|
||||
if (!wd || !wd.window) return;
|
||||
utils.onDispatchEvent(wd.window.webContents, {
|
||||
event: 'stop-loading',
|
||||
id: browserView.webContents.id,
|
||||
}).then(_ => { });
|
||||
|
||||
// 加载完成暗黑模式下把窗口背景色改成白色,避免透明网站背景色穿透
|
||||
stopLoading();
|
||||
if (nativeTheme.shouldUseDarkColors) {
|
||||
browserView.setBackgroundColor('#FFFFFF');
|
||||
}
|
||||
@ -1179,7 +1202,8 @@ function createWebTabView(windowId, args) {
|
||||
|
||||
electronMenu.webContentsMenu(browserView.webContents, true);
|
||||
|
||||
browserView.webContents.loadURL(args.url).then(_ => { }).catch(_ => { });
|
||||
// 加载地址
|
||||
utils.loadContentUrl(browserView.webContents, serverUrl, args.url);
|
||||
|
||||
browserView.setVisible(true);
|
||||
|
||||
@ -2128,20 +2152,36 @@ ipcMain.on('windowHidden', (event) => {
|
||||
})
|
||||
|
||||
/**
|
||||
* 关闭窗口
|
||||
* 关闭窗口(或关闭 tab,如果发送者是 tab 中的页面)
|
||||
*/
|
||||
ipcMain.on('windowClose', (event) => {
|
||||
const win = BrowserWindow.fromWebContents(event.sender);
|
||||
win.close()
|
||||
const tabId = event.sender.id;
|
||||
const windowId = findWindowIdByTabId(tabId);
|
||||
if (windowId !== null) {
|
||||
// 发送者是 tab 中的页面,只关闭这个 tab
|
||||
closeWebTabInWindow(windowId, tabId);
|
||||
} else {
|
||||
// 发送者是独立窗口,关闭整个窗口
|
||||
const win = BrowserWindow.fromWebContents(event.sender);
|
||||
win?.close()
|
||||
}
|
||||
event.returnValue = "ok"
|
||||
})
|
||||
|
||||
/**
|
||||
* 销毁窗口
|
||||
* 销毁窗口(或销毁 tab,如果发送者是 tab 中的页面)
|
||||
*/
|
||||
ipcMain.on('windowDestroy', (event) => {
|
||||
const win = BrowserWindow.fromWebContents(event.sender);
|
||||
win.destroy()
|
||||
const tabId = event.sender.id;
|
||||
const windowId = findWindowIdByTabId(tabId);
|
||||
if (windowId !== null) {
|
||||
// 发送者是 tab 中的页面,只关闭这个 tab
|
||||
closeWebTabInWindow(windowId, tabId);
|
||||
} else {
|
||||
// 发送者是独立窗口,销毁整个窗口
|
||||
const win = BrowserWindow.fromWebContents(event.sender);
|
||||
win?.destroy()
|
||||
}
|
||||
event.returnValue = "ok"
|
||||
})
|
||||
|
||||
|
||||
17
electron/lib/utils.js
vendored
17
electron/lib/utils.js
vendored
@ -768,6 +768,23 @@ const utils = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 加载内容 URL(自动判断完整 URL 或相对路径)
|
||||
* @param webContents - BrowserWindow 或 WebContents 对象
|
||||
* @param serverUrl - 服务器地址
|
||||
* @param url - 要加载的 URL(完整 URL 或相对路径)
|
||||
*/
|
||||
loadContentUrl(webContents, serverUrl, url) {
|
||||
if (!url) return;
|
||||
if (/^https?:/i.test(url)) {
|
||||
// 完整 URL 直接加载
|
||||
webContents.loadURL(url).then(_ => { }).catch(_ => { })
|
||||
} else {
|
||||
// 相对路径使用 loadUrl 处理
|
||||
utils.loadUrl(webContents, serverUrl, url)
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取主题名称
|
||||
* @returns {string|*}
|
||||
|
||||
1
electron/render/tabs/assets/image/more.svg
Normal file
1
electron/render/tabs/assets/image/more.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32"><g fill="none"><path d="M9.5 16a2.5 2.5 0 1 1-5 0a2.5 2.5 0 0 1 5 0zm9 0a2.5 2.5 0 1 1-5 0a2.5 2.5 0 0 1 5 0zm6.5 2.5a2.5 2.5 0 1 0 0-5a2.5 2.5 0 0 0 0 5z" fill="currentColor"></path></g></svg>
|
||||
|
After Width: | Height: | Size: 296 B |
@ -617,7 +617,10 @@
|
||||
*/
|
||||
isLocalHost(url) {
|
||||
if (!url) {
|
||||
return false
|
||||
return true
|
||||
}
|
||||
if (!/^https?:\/\//i.test(url)) {
|
||||
return true
|
||||
}
|
||||
try {
|
||||
const uri = new URL(url)
|
||||
|
||||
@ -119,6 +119,7 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
assistShow: false,
|
||||
isRestarting: false,
|
||||
userSelectOptions: {value: [], config: {}},
|
||||
|
||||
backupConfigs: {},
|
||||
@ -160,8 +161,8 @@ export default {
|
||||
this.unmountAllMicroApp()
|
||||
},
|
||||
assistShow(show) {
|
||||
if (!show && $A.isSubElectron) {
|
||||
// 如果是子 Electron 窗口,关闭窗口助理时销毁窗口
|
||||
if (!show && $A.isSubElectron && !this.isRestarting) {
|
||||
// 如果是子 Electron 窗口,关闭窗口助理时销毁窗口(但是重启过程中不销毁)
|
||||
$A.Electron.sendMessage('windowDestroy');
|
||||
}
|
||||
},
|
||||
@ -486,7 +487,7 @@ export default {
|
||||
path: path,
|
||||
force: false,
|
||||
config: Object.assign({
|
||||
title: ' ',
|
||||
title: appConfig.title || ' ',
|
||||
parent: null,
|
||||
width: Math.min(window.screen.availWidth, 1440),
|
||||
height: Math.min(window.screen.availHeight, 900),
|
||||
@ -518,7 +519,7 @@ export default {
|
||||
path: config.url,
|
||||
force: false,
|
||||
config: {
|
||||
title: ' ',
|
||||
title: config.title || ' ',
|
||||
parent: null,
|
||||
width: Math.min(window.screen.availWidth, 1440),
|
||||
height: Math.min(window.screen.availHeight, 900),
|
||||
@ -706,15 +707,20 @@ export default {
|
||||
* @param name
|
||||
*/
|
||||
async onRestartApp(name) {
|
||||
this.closeMicroApp(name, true)
|
||||
await new Promise(resolve => setTimeout(resolve, 300));
|
||||
this.isRestarting = true
|
||||
try {
|
||||
this.closeMicroApp(name, true)
|
||||
await new Promise(resolve => setTimeout(resolve, 300));
|
||||
|
||||
const app = this.backupConfigs[name];
|
||||
if (!app) {
|
||||
$A.modalError("应用不存在");
|
||||
return
|
||||
const app = this.backupConfigs[name];
|
||||
if (!app) {
|
||||
$A.modalError("应用不存在");
|
||||
return
|
||||
}
|
||||
await this.onOpen(app)
|
||||
} finally {
|
||||
this.isRestarting = false
|
||||
}
|
||||
await this.onOpen(app)
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
1
resources/assets/js/store/actions.js
vendored
1
resources/assets/js/store/actions.js
vendored
@ -5120,6 +5120,7 @@ export default {
|
||||
const config = {
|
||||
id: microAppId,
|
||||
name: data.name,
|
||||
title: data.label || data.title || data.name,
|
||||
url: $A.mainUrl(url),
|
||||
type: data.type || data.url_type,
|
||||
background: data.background || null,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user