feat: 标签页新增更多菜单功能

- 新增更多菜单按钮替代原浏览器打开按钮
  - 实现重新加载、复制链接地址、默认浏览器打开功能
  - 实现将标签页移至新窗口功能
  - 实现打印功能
  - 菜单支持根据当前 URL 类型动态启用/禁用选项
  - 添加相关国际化文案
This commit is contained in:
kuaifan 2026-01-10 16:35:19 +00:00
parent 3b1dce6d67
commit 731dbc5507
8 changed files with 56 additions and 47 deletions

View File

@ -23,11 +23,13 @@ const electronMenu = {
reload: "重新加载", reload: "重新加载",
print: "打印", print: "打印",
openInBrowser: "在浏览器中打开", openInBrowser: "在浏览器中打开",
openInDefaultBrowser: "默认浏览器打开",
saveImageAs: "图片存储为...", saveImageAs: "图片存储为...",
copyImage: "复制图片", copyImage: "复制图片",
copyEmailAddress: "复制电子邮件地址", copyEmailAddress: "复制电子邮件地址",
copyLinkAddress: "复制链接地址", copyLinkAddress: "复制链接地址",
copyImageAddress: "复制图片地址", copyImageAddress: "复制图片地址",
moveToNewWindow: "将标签页移至新窗口",
failedToSaveImage: "图片保存失败", failedToSaveImage: "图片保存失败",
theImageFailedToSave: "图片无法保存", theImageFailedToSave: "图片无法保存",
}, },

45
electron/electron.js vendored
View File

@ -1963,6 +1963,7 @@ ipcMain.on('webTabExternal', (event) => {
*/ */
ipcMain.on('webTabShowMenu', (event, args) => { ipcMain.on('webTabShowMenu', (event, args) => {
const windowId = args?.windowId const windowId = args?.windowId
const tabId = args?.tabId
const windowData = windowId ? webTabWindows.get(windowId) : null const windowData = windowId ? webTabWindows.get(windowId) : null
const webTabWindow = windowData?.window const webTabWindow = windowData?.window
@ -1972,45 +1973,65 @@ ipcMain.on('webTabShowMenu', (event, args) => {
} }
const item = currentWebTabInWindow(windowId) const item = currentWebTabInWindow(windowId)
const currentUrl = item?.view?.webContents?.getURL() || '' const webContents = item?.view?.webContents
const currentUrl = webContents?.getURL() || ''
const canBrowser = !utils.isLocalHost(currentUrl)
const menuTemplate = [ const menuTemplate = [
{ {
label: '重新加载', label: electronMenu.language.reload,
click: () => { click: () => {
// TODO: 实现重新加载 if (webContents && !webContents.isDestroyed()) {
webContents.reload()
}
} }
}, },
{ {
label: '复制链接地址', label: electronMenu.language.copyLinkAddress,
enabled: canBrowser,
click: () => { click: () => {
// TODO: 实现复制链接 if (currentUrl) {
clipboard.writeText(currentUrl)
}
} }
}, },
{ {
label: '默认浏览器打开', label: electronMenu.language.openInDefaultBrowser,
enabled: canBrowser,
click: () => { click: () => {
// TODO: 实现默认浏览器打开 if (currentUrl) {
renderer.openExternal(currentUrl).catch(() => {})
}
} }
}, },
{ type: 'separator' }, { type: 'separator' },
{ {
label: '将标签页移至新窗口', label: electronMenu.language.moveToNewWindow,
enabled: windowData?.views?.length > 1,
click: () => { click: () => {
// TODO: 实现移至新窗口 if (tabId) {
const bounds = webTabWindow.getBounds()
detachWebTab(windowId, tabId, bounds.x + 50, bounds.y + 50)
}
} }
}, },
{ type: 'separator' }, { type: 'separator' },
{ {
label: '打印', label: electronMenu.language.print,
click: () => { click: () => {
// TODO: 实现打印 if (webContents && !webContents.isDestroyed()) {
webContents.print()
}
} }
} }
] ]
const menu = Menu.buildFromTemplate(menuTemplate) const menu = Menu.buildFromTemplate(menuTemplate)
menu.popup({ window: webTabWindow }) menu.popup({
window: webTabWindow,
x: args?.x,
y: args?.y
})
event.returnValue = "ok" event.returnValue = "ok"
}) })

View File

@ -737,11 +737,14 @@ const utils = {
*/ */
isLocalHost(url) { isLocalHost(url) {
if (!url) { if (!url) {
return false return true
}
if (!/^https?:\/\//i.test(url)) {
return true
} }
try { try {
const uri = new URL(url) const uri = new URL(url)
return uri.hostname == "localhost" return uri.hostname === 'localhost'
} catch (e) { } catch (e) {
return false return false
} }

View File

@ -150,10 +150,10 @@ body {
} }
.nav-more svg { .nav-more svg {
width: 18px; width: 20px;
height: 18px; height: 20px;
color: #000000; color: #000000;
opacity: 0.7; opacity: 0.9;
} }
.nav-more:hover svg { .nav-more:hover svg {

View File

@ -1 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 296 B

View File

@ -30,7 +30,7 @@
</li> </li>
</ul> </ul>
<div class="nav-more" @click="onShowMenu"> <div class="nav-more" @click="onShowMenu">
<svg xmlns="http://www.w3.org/2000/svg" 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> <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>
</div> </div>
</div> </div>
</div> </div>
@ -214,13 +214,6 @@
pageTitle() { pageTitle() {
return this.activeItem ? this.activeItem.title : 'Untitled' return this.activeItem ? this.activeItem.title : 'Untitled'
}, },
/**
* 是否可以打开浏览器
* @returns {boolean}
*/
canBrowser() {
return !(this.activeItem && this.isLocalHost(this.activeItem.url))
},
/** /**
* 获取加载状态 * 获取加载状态
* @returns {boolean} * @returns {boolean}
@ -516,7 +509,14 @@
* 显示更多菜单 * 显示更多菜单
*/ */
onShowMenu() { onShowMenu() {
this.sendMessage('webTabShowMenu', {windowId: this.windowId, tabId: this.activeId}) const btn = document.querySelector('.nav-more')
const rect = btn.getBoundingClientRect()
this.sendMessage('webTabShowMenu', {
windowId: this.windowId,
tabId: this.activeId,
x: Math.round(rect.left),
y: Math.round(rect.bottom)
})
}, },
/** /**
@ -608,26 +608,6 @@
*/ */
updateNavigationState() { updateNavigationState() {
this.sendMessage('webTabGetNavigationState') this.sendMessage('webTabGetNavigationState')
},
/**
* 判断是否是本地URL
* @param url
* @returns {boolean}
*/
isLocalHost(url) {
if (!url) {
return true
}
if (!/^https?:\/\//i.test(url)) {
return true
}
try {
const uri = new URL(url)
return uri.hostname == "localhost"
} catch (e) {
return false
}
} }
}, },
} }

View File

@ -1270,6 +1270,8 @@ AI 机器人
状态[(*)]设置错误,设置剔除模式时必须填写状态负责人 状态[(*)]设置错误,设置剔除模式时必须填写状态负责人
键盘设置 键盘设置
在浏览器中打开 在浏览器中打开
默认浏览器打开
将标签页移至新窗口
图片存储为... 图片存储为...
复制电子邮件地址 复制电子邮件地址
复制链接地址 复制链接地址

View File

@ -636,11 +636,13 @@ export default {
reload: this.$L("重新加载"), reload: this.$L("重新加载"),
print: this.$L("打印"), print: this.$L("打印"),
openInBrowser: this.$L("在浏览器中打开"), openInBrowser: this.$L("在浏览器中打开"),
openInDefaultBrowser: this.$L("默认浏览器打开"),
saveImageAs: this.$L("图片存储为..."), saveImageAs: this.$L("图片存储为..."),
copyImage: this.$L("复制图片"), copyImage: this.$L("复制图片"),
copyEmailAddress: this.$L("复制电子邮件地址"), copyEmailAddress: this.$L("复制电子邮件地址"),
copyLinkAddress: this.$L("复制链接地址"), copyLinkAddress: this.$L("复制链接地址"),
copyImageAddress: this.$L("复制图片地址"), copyImageAddress: this.$L("复制图片地址"),
moveToNewWindow: this.$L("将标签页移至新窗口"),
failedToSaveImage: this.$L("图片保存失败"), failedToSaveImage: this.$L("图片保存失败"),
theImageFailedToSave: this.$L("图片无法保存"), theImageFailedToSave: this.$L("图片无法保存"),
}); });