perf: 优化下载工具

This commit is contained in:
kuaifan 2025-08-14 16:50:48 +08:00
parent f6850fc795
commit 0833018399
14 changed files with 459 additions and 192 deletions

View File

@ -1,149 +1,14 @@
const {BrowserWindow, screen} = require('electron')
const path = require('path');
const Store = require("electron-store");
const loger = require("electron-log");
const Store = require('electron-store');
const store = new Store({
name: 'download-manager',
defaults: {
downloadHistory: [],
}
});
const electronDl = require("@dootask/electron-dl").default;
class DownloadManager {
constructor() {
this.downloadHistory = store.get('downloadHistory', []);
}
/**
* 转换下载项格式
* @param {Electron.DownloadItem} downloadItem
*/
convertItem(downloadItem) {
return {
filename: downloadItem.getFilename(),
path: downloadItem.getSavePath(),
url: downloadItem.getURL(),
urls: downloadItem.getURLChain(),
mine: downloadItem.getMimeType(),
received: downloadItem.getReceivedBytes(),
total: downloadItem.getTotalBytes(),
percent: downloadItem.getPercentComplete(),
speed: downloadItem.getCurrentBytesPerSecond(),
state: downloadItem.getState(),
paused: downloadItem.isPaused(),
startTime: downloadItem.getStartTime(),
endTime: downloadItem.getEndTime(),
}
}
/**
* 添加下载项
* @param {Electron.DownloadItem} downloadItem
*/
addDownloadItem(downloadItem) {
this.downloadHistory.unshift({
...this.convertItem(downloadItem),
_source: downloadItem,
});
store.set('downloadHistory', this.downloadHistory.slice(0, 100)); // 限制最多100个下载项
loger.info(`Download item added: ${downloadItem.getSavePath()}`);
}
/**
* 更新下载项
* @param {string} path
*/
updateDownloadItem(path) {
const item = this.downloadHistory.find(d => d.path === path)
if (!item) {
loger.warn(`Download item not found for path: ${path}`);
return;
}
const downloadItem = item._source;
if (!downloadItem) {
loger.warn(`Download item not found for path: ${path}`);
return;
}
Object.assign(item, this.convertItem(downloadItem))
store.set('downloadHistory', this.downloadHistory);
loger.info(`Download item updated: ${path} - ${item.state} (${item.percent}%)`);
}
/**
* 暂停下载项
* @param {string} path
*/
pauseDownloadItem(path) {
const item = this.downloadHistory.find(d => d.path === path)
if (!item) {
loger.warn(`Download item not found for path: ${path}`);
return;
}
const downloadItem = item._source;
if (!downloadItem) {
loger.warn(`Download item not found for path: ${path}`);
return;
}
downloadItem.pause();
this.updateDownloadItem(path);
}
/**
* 恢复下载项
* @param {string} path
*/
resumeDownloadItem(path) {
const item = this.downloadHistory.find(d => d.path === path)
if (!item) {
loger.warn(`Download item not found for path: ${path}`);
return;
}
const downloadItem = item._source;
if (!downloadItem) {
loger.warn(`Download item not found for path: ${path}`);
return;
}
downloadItem.resume();
this.updateDownloadItem(path);
}
/**
* 取消下载项
* @param {string} path
*/
cancelDownloadItem(path) {
const item = this.downloadHistory.find(d => d.path === path)
if (!item) {
loger.warn(`Download item not found for path: ${path}`);
return;
}
const downloadItem = item._source;
if (!downloadItem) {
loger.warn(`Download item not found for path: ${path}`);
return;
}
downloadItem.cancel();
this.updateDownloadItem(path);
}
/**
* 取消所有下载项
*/
cancelAllDownloadItems() {
this.downloadHistory.forEach(item => {
this.cancelDownloadItem(item.path);
});
}
/**
* 清空下载历史
*/
clearHistory() {
this.downloadHistory = [];
store.set('downloadHistory', []);
}
}
const {default: electronDl, download} = require("@dootask/electron-dl");
const utils = require("./utils");
const {DownloadManager} = require("./utils/download");
const store = new Store();
const downloadManager = new DownloadManager();
let downloadWindow = null;
function initialize(options = {}) {
// 下载配置
@ -155,20 +20,181 @@ function initialize(options = {}) {
onStarted: (item) => {
downloadManager.addDownloadItem(item);
syncDownloadItems();
},
onProgress: (item) => {
downloadManager.updateDownloadItem(item.path);
syncDownloadItems();
},
onCancel: (item) => {
downloadManager.updateDownloadItem(item.getSavePath())
syncDownloadItems();
},
onCompleted: (item) => {
downloadManager.updateDownloadItem(item.path);
syncDownloadItems();
}
});
}
function syncDownloadItems() {
// 同步下载项到渲染进程
if (downloadWindow) {
downloadWindow.webContents.send('download-items', downloadManager.getDownloadItems());
}
}
function getLanguagePack(codeOrPack) {
if (codeOrPack && typeof codeOrPack === 'object') {
return codeOrPack;
}
const code = (codeOrPack || 'zh').toString();
return {
code,
title: '下载管理器',
// todo
}
}
async function open(language = 'zh', theme = 'light') {
// 获取语言包
const finalLanguage = getLanguagePack(language);
// 如果窗口已存在,直接显示
if (downloadWindow) {
// 更新窗口数据
await updateDownloadWindow(language, theme)
// 显示窗口并聚焦
downloadWindow.show();
downloadWindow.focus();
return;
}
// 窗口默认参数
const downloadWindowOptions = {
width: 700,
height: 480,
minWidth: 500,
minHeight: 350,
center: true,
show: false,
autoHideMenuBar: true,
title: finalLanguage.title,
backgroundColor: utils.getDefaultBackgroundColor(),
webPreferences: {
preload: path.join(__dirname, 'electron-preload.js'),
webSecurity: true,
nodeIntegration: true,
contextIsolation: true,
}
}
// 恢复窗口位置
const downloadWindowBounds = store.get('downloadWindowBounds', {});
if (
downloadWindowBounds.width !== undefined &&
downloadWindowBounds.height !== undefined &&
downloadWindowBounds.x !== undefined &&
downloadWindowBounds.y !== undefined
) {
// 获取所有显示器的可用区域
const displays = screen.getAllDisplays();
// 检查窗口是否在任意一个屏幕内
let isInScreen = false;
for (const display of displays) {
const area = display.workArea;
if (
downloadWindowBounds.x + downloadWindowBounds.width > area.x &&
downloadWindowBounds.x < area.x + area.width &&
downloadWindowBounds.y + downloadWindowBounds.height > area.y &&
downloadWindowBounds.y < area.y + area.height
) {
isInScreen = true;
break;
}
}
// 如果超出所有屏幕,则移动到主屏幕可见区域
if (!isInScreen) {
const primaryArea = screen.getPrimaryDisplay().workArea;
downloadWindowBounds.x = primaryArea.x + 50;
downloadWindowBounds.y = primaryArea.y + 50;
// 防止窗口太大超出屏幕
downloadWindowBounds.width = Math.min(downloadWindowBounds.width, primaryArea.width - 100);
downloadWindowBounds.height = Math.min(downloadWindowBounds.height, primaryArea.height - 100);
}
downloadWindowOptions.width = downloadWindowBounds.width;
downloadWindowOptions.height = downloadWindowBounds.height;
downloadWindowOptions.center = false;
downloadWindowOptions.x = downloadWindowBounds.x;
downloadWindowOptions.y = downloadWindowBounds.y;
}
// 创建窗口
downloadWindow = new BrowserWindow(downloadWindowOptions);
// 禁止修改窗口标题
downloadWindow.on('page-title-updated', (event) => {
event.preventDefault()
})
// 监听窗口关闭保存窗口位置
downloadWindow.on('close', () => {
const bounds = downloadWindow.getBounds();
store.set('downloadWindowBounds', bounds);
});
// 监听窗口关闭事件
downloadWindow.on('closed', () => {
downloadWindow = null;
});
// 加载下载管理器页面
const htmlPath = path.join(__dirname, 'render', 'download', 'index.html');
const themeParam = (theme === 'dark' ? 'dark' : 'light');
await downloadWindow.loadFile(htmlPath, {query: {theme: themeParam}});
// 将语言包发送到渲染进程
downloadWindow.webContents.once('dom-ready', () => {
updateDownloadWindow(language, theme)
});
// 显示窗口
downloadWindow.show();
}
function close() {
if (downloadWindow) {
downloadWindow.close();
downloadWindow = null;
}
}
function destroy() {
if (downloadWindow) {
downloadWindow.destroy();
downloadWindow = null;
}
}
async function updateDownloadWindow(language, theme) {
if (downloadWindow) {
try {
const finalLanguage = getLanguagePack(language);
downloadWindow.setTitle(finalLanguage.title);
downloadWindow.webContents.send('download-theme', theme);
downloadWindow.webContents.send('download-language', finalLanguage);
syncDownloadItems()
} catch (error) {
loger.error(error);
}
}
}
module.exports = {
initialize
initialize,
download,
open,
close,
destroy,
updateDownloadWindow
}

View File

@ -32,7 +32,13 @@ contextBridge.exposeInMainWorld(
'electron', {
request: (msg, callback, error) => {
msg.reqId = reqId++;
reqInfo[msg.reqId] = {callback: callback, error: error};
if (typeof callback !== "function") {
callback = function () {};
}
if (typeof error !== "function") {
error = function () {};
}
reqInfo[msg.reqId] = {callback, error};
if (msg.action == 'watchFile') {
fileChangedListeners[msg.path] = msg.listener;
delete msg.listener;

55
electron/electron.js vendored
View File

@ -123,9 +123,6 @@ if (!fs.existsSync(cacheDir)) {
fs.mkdirSync(cacheDir, { recursive: true });
}
// 初始化下载配置
electronDown.initialize()
/**
* 启动web服务
*/
@ -345,10 +342,10 @@ function createMainWindow() {
// 新窗口处理
mainWindow.webContents.setWindowOpenHandler(({url}) => {
if (allowedCalls.test(url)) {
openExternal(url)
openExternal(url).catch(() => {})
} else {
utils.onBeforeOpenWindow(mainWindow.webContents, url).then(() => {
openExternal(url)
openExternal(url).catch(() => {})
})
}
return {action: 'deny'}
@ -587,10 +584,10 @@ function createChildWindow(args) {
// 新窗口处理
browser.webContents.setWindowOpenHandler(({url}) => {
if (allowedCalls.test(url)) {
openExternal(url)
openExternal(url).catch(() => {})
} else {
utils.onBeforeOpenWindow(browser.webContents, url).then(() => {
openExternal(url)
openExternal(url).catch(() => {})
})
}
return {action: 'deny'}
@ -866,7 +863,7 @@ function createWebTabWindow(args) {
})
browserView.webContents.setWindowOpenHandler(({url}) => {
if (allowedCalls.test(url)) {
openExternal(url)
openExternal(url).catch(() => {})
} else {
createWebTabWindow({url})
}
@ -1102,6 +1099,8 @@ if (!getTheLock) {
}
// SameSite
utils.useCookie()
// 初始化下载
electronDown.initialize()
// 创建主窗口
createMainWindow()
// 预创建子窗口
@ -1293,7 +1292,7 @@ ipcMain.on('webTabExternal', (event) => {
if (!item) {
return
}
openExternal(item.view.webContents.getURL())
openExternal(item.view.webContents.getURL()).catch(() => {})
event.returnValue = "ok"
})
@ -1358,6 +1357,7 @@ ipcMain.on('childWindowCloseAll', (event) => {
})
preloadWindow?.close()
mediaWindow?.close()
electronDown.close()
event.returnValue = "ok"
})
@ -1370,6 +1370,7 @@ ipcMain.on('childWindowDestroyAll', (event) => {
})
preloadWindow?.destroy()
mediaWindow?.destroy()
electronDown.destroy()
event.returnValue = "ok"
})
@ -1714,6 +1715,7 @@ ipcMain.on('updateQuitAndInstall', (event, args) => {
})
preloadWindow?.destroy()
mediaWindow?.destroy()
electronDown.destroy()
// 启动更新子窗口
createUpdaterWindow(args.updateTitle)
@ -2611,7 +2613,7 @@ function getPluginFile(plugin) {
return null;
}
function uninstallPlugin(plugin) {
async function uninstallPlugin(plugin) {
const pluginFile = getPluginFile(plugin);
if (pluginFile != null) {
@ -2672,7 +2674,7 @@ async function deleteFile(file) {
}
}
function windowAction(method) {
async function windowAction(method) {
let win = BrowserWindow.getFocusedWindow();
if (win) {
@ -2692,16 +2694,14 @@ function windowAction(method) {
}
}
function openExternal(url) {
async function openExternal(url) {
//Only open http(s), mailto, tel, and callto links
if (allowedUrls.test(url)) {
shell.openExternal(url).catch(_ => {});
return true;
await shell.openExternal(url)
}
return false;
}
function watchFile(path) {
async function watchFile(path) {
let win = BrowserWindow.getFocusedWindow();
if (win) {
@ -2713,12 +2713,13 @@ function watchFile(path) {
prev: prev
});
} catch (e) {
} // Ignore
// Ignore
}
});
}
}
function unwatchFile(path) {
async function unwatchFile(path) {
fs.unwatchFile(path);
}
@ -2747,7 +2748,7 @@ ipcMain.on("rendererReq", async (event, args) => {
ret = await getDocumentsFolder();
break;
case 'checkFileExists':
ret = await checkFileExists(args.pathParts);
ret = checkFileExists(args.pathParts);
break;
case 'showOpenDialog':
dialogOpen = true;
@ -2768,7 +2769,7 @@ ipcMain.on("rendererReq", async (event, args) => {
ret = await uninstallPlugin(args.plugin);
break;
case 'getPluginFile':
ret = await getPluginFile(args.plugin);
ret = getPluginFile(args.plugin);
break;
case 'isPluginsEnabled':
ret = enablePlugins;
@ -2780,7 +2781,7 @@ ipcMain.on("rendererReq", async (event, args) => {
ret = await readFile(args.filename, args.encoding);
break;
case 'clipboardAction':
ret = await clipboardAction(args.method, args.data);
ret = clipboardAction(args.method, args.data);
break;
case 'deleteFile':
ret = await deleteFile(args.file);
@ -2797,6 +2798,15 @@ ipcMain.on("rendererReq", async (event, args) => {
case 'openExternal':
ret = await openExternal(args.url);
break;
case 'openDownloadWindow':
ret = await electronDown.open(args.language || 'zh', args.theme || 'light');
break;
case 'createDownloadTask':
ret = await electronDown.download(mainWindow, args.url, args.options || {});
break;
case 'updateDownloadWindow':
ret = await electronDown.updateDownloadWindow(args.language, args.theme);
break;
case 'watchFile':
ret = await watchFile(args.path);
break;
@ -2804,12 +2814,13 @@ ipcMain.on("rendererReq", async (event, args) => {
ret = await unwatchFile(args.path);
break;
case 'getCurDir':
ret = await getCurDir();
ret = getCurDir();
break;
}
event.reply('mainResp', {success: true, data: ret, reqId: args.reqId});
} catch (e) {
event.reply('mainResp', {error: true, msg: e.message, e: e, reqId: args.reqId});
loger.error('Renderer request error', e.message, e.stack);
}
});

View File

@ -79,10 +79,10 @@
"files": [
"render/**/*",
"public/**/*",
"utils/**/*",
"electron-menu.js",
"electron-preload.js",
"electron.js",
"utils.js"
"electron.js"
],
"extraFiles": [
{

View File

@ -0,0 +1 @@
123

161
electron/utils/download.js vendored Normal file
View File

@ -0,0 +1,161 @@
const loger = require("electron-log");
const Store = require('electron-store');
const store = new Store({
name: 'download-manager',
defaults: {
downloadHistory: [],
}
});
class DownloadManager {
constructor() {
this.downloadHistory = store.get('downloadHistory', []);
}
/**
* 转换下载项格式
* @param {Electron.DownloadItem} downloadItem
*/
convertItem(downloadItem) {
return {
filename: downloadItem.getFilename(),
path: downloadItem.getSavePath(),
url: downloadItem.getURL(),
urls: downloadItem.getURLChain(),
mine: downloadItem.getMimeType(),
received: downloadItem.getReceivedBytes(),
total: downloadItem.getTotalBytes(),
percent: downloadItem.getPercentComplete(),
speed: downloadItem.getCurrentBytesPerSecond(),
state: downloadItem.getState(),
paused: downloadItem.isPaused(),
startTime: downloadItem.getStartTime(),
endTime: downloadItem.getEndTime(),
}
}
/**
* 添加下载项
* @param {Electron.DownloadItem} downloadItem
*/
addDownloadItem(downloadItem) {
this.downloadHistory.unshift({
...this.convertItem(downloadItem),
_source: downloadItem,
});
if (this.downloadHistory.length > 1000) {
this.downloadHistory = this.downloadHistory.slice(0, 1000);
}
store.set('downloadHistory', this.downloadHistory);
loger.info(`Download item added: ${downloadItem.getSavePath()}`);
}
/**
* 获取下载列表
* @returns {*}
*/
getDownloadItems() {
return this.downloadHistory.map(item => {
return {
...item,
_source: undefined, // 移除源对象,避免序列化问题
};
});
}
/**
* 更新下载项
* @param {string} path
*/
updateDownloadItem(path) {
const item = this.downloadHistory.find(d => d.path === path)
if (!item) {
loger.warn(`Download item not found for path: ${path}`);
return;
}
const downloadItem = item._source;
if (!downloadItem) {
loger.warn(`Download item not found for path: ${path}`);
return;
}
Object.assign(item, this.convertItem(downloadItem))
store.set('downloadHistory', this.downloadHistory);
loger.info(`Download item updated: ${path} - ${item.state} (${item.percent}%)`);
}
/**
* 暂停下载项
* @param {string} path
*/
pauseDownloadItem(path) {
const item = this.downloadHistory.find(d => d.path === path)
if (!item) {
loger.warn(`Download item not found for path: ${path}`);
return;
}
const downloadItem = item._source;
if (!downloadItem) {
loger.warn(`Download item not found for path: ${path}`);
return;
}
downloadItem.pause();
this.updateDownloadItem(path);
}
/**
* 恢复下载项
* @param {string} path
*/
resumeDownloadItem(path) {
const item = this.downloadHistory.find(d => d.path === path)
if (!item) {
loger.warn(`Download item not found for path: ${path}`);
return;
}
const downloadItem = item._source;
if (!downloadItem) {
loger.warn(`Download item not found for path: ${path}`);
return;
}
downloadItem.resume();
this.updateDownloadItem(path);
}
/**
* 取消下载项
* @param {string} path
*/
cancelDownloadItem(path) {
const item = this.downloadHistory.find(d => d.path === path)
if (!item) {
loger.warn(`Download item not found for path: ${path}`);
return;
}
const downloadItem = item._source;
if (!downloadItem) {
loger.warn(`Download item not found for path: ${path}`);
return;
}
downloadItem.cancel();
this.updateDownloadItem(path);
}
/**
* 取消所有下载项
*/
cancelAllDownloadItems() {
this.downloadHistory.forEach(item => {
this.cancelDownloadItem(item.path);
});
}
/**
* 清空下载历史
*/
clearHistory() {
this.downloadHistory = [];
store.set('downloadHistory', []);
}
}
module.exports = { DownloadManager };

View File

@ -2150,3 +2150,4 @@ API URL
需要先设置 AI 助理
打开签到机器人
下载内容

View File

@ -401,20 +401,14 @@ export default {
if (this.isMeetingUrlStrict(url)) {
return 1;
}
//
if (this.isDownloadUrl(url)) {
return 1;
}
//
if ($A.getDomain(url) == $A.getDomain($A.mainUrl())) {
try {
const {pathname, searchParams} = new URL(url);
// uploads/
// api/dialog/msg/download
// api/project/task/filedown
if (/^\/(uploads|api\/dialog\/msg\/download|api\/project\/task\/filedown)/.test(pathname)) {
return 1;
}
// api/file/content?down=yes
if (/^\/api\/file\/content/.test(pathname) && searchParams.get('down') === 'yes') {
return 1;
}
// meeting/1234567890/xxxxx
if (/^\/meeting\/\d+\/\S+$/.test(pathname)) {
const meetingId = pathname.split('/')[2];
@ -438,6 +432,32 @@ export default {
return 0;
},
isDownloadUrl(url) {
if ($A.getDomain(url) == $A.getDomain($A.mainUrl())) {
try {
const {pathname, searchParams} = new URL(url);
//
const downloadPathPatterns = [
'/uploads', //
'/api/dialog/msg/download', //
'/api/project/task/filedown', //
'/api/file/download/pack', //
'/api/approve/down', //
'/api/project/task/down', //
'/api/system/checkin/down' //
];
if (downloadPathPatterns.some(pattern => $A.leftExists(pathname, pattern))) {
return true;
}
// /api/file/content down=yes
if ($A.leftExists(pathname, '/api/file/content') && searchParams.get('down') === 'yes') {
return true;
}
} catch (e) {}
}
return false;
},
isApplicationProtocol(url) {
const protocols = [
'thunder:', //
@ -576,6 +596,10 @@ export default {
return true;
} else if (urlType === 1) {
// 使
if (this.isDownloadUrl(url)) {
this.$store.dispatch('downUrl', url)
return true;
}
return false;
}
// 使

View File

@ -9,7 +9,7 @@
<!-- 胶囊工具栏 -->
<div v-if="capsuleMenuShow" class="micro-modal-cmask"></div>
<div class="micro-modal-capsule" :style="capsuleStyle">
<div class="micro-modal-capsule-item" v-touchclick="onCapsuleMore">
<div class="micro-modal-capsule-item" @click="onCapsuleMore">
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2 11C3.10457 11 4 10.1046 4 9C4 7.89543 3.10457 7 2 7C0.895431 7 0 7.89543 0 9C0 10.1046 0.895431 11 2 11Z" fill="currentColor"/>
<path d="M9 12C10.6569 12 12 10.6569 12 9C12 7.34315 10.6569 6 9 6C7.34315 6 6 7.34315 6 9C6 10.6569 7.34315 12 9 12Z" fill="currentColor"/>
@ -17,7 +17,7 @@
</svg>
</div>
<div class="micro-modal-capsule-line"></div>
<div class="micro-modal-capsule-item" v-touchclick="attemptClose">
<div class="micro-modal-capsule-item" @click="attemptClose">
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9 16C12.866 16 16 12.866 16 9C16 5.13401 12.866 2 9 2C5.13401 2 2 5.13401 2 9C2 12.866 5.13401 16 9 16Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9 12C10.6569 12 12 10.6569 12 9C12 7.34315 10.6569 6 9 6C7.34315 6 6 7.34315 6 9C6 10.6569 7.34315 12 9 12Z" fill="currentColor"/>
@ -48,13 +48,12 @@
<script>
import TransferDom from "../../directives/transfer-dom";
import touchclick from "../../directives/touchclick";
import ResizeLine from "../ResizeLine.vue";
export default {
name: 'MicroModal',
components: {ResizeLine},
directives: {TransferDom, touchclick},
directives: {TransferDom},
props: {
open: {
type: Boolean,

View File

@ -392,6 +392,7 @@ import notificationKoro from "notification-koro1";
import emitter from "../store/events";
import SearchBox from "../components/SearchBox.vue";
import transformEmojiToHtml from "../utils/emoji";
import {languageName} from "../language";
export default {
components: {
@ -516,6 +517,7 @@ export default {
'cacheDialogs',
'cacheProjects',
'projectTotal',
'themeName',
'wsOpenNum',
'columnTemplate',
@ -629,7 +631,8 @@ export default {
menu() {
const {userIsAdmin} = this;
const array = [
{path: 'taskBrowse', name: '最近打开的任务'}
{path: 'taskBrowse', name: '最近打开的任务'},
{path: 'download', name: '下载内容', visible: !!this.$Electron},
];
if (userIsAdmin) {
array.push(...[
@ -643,7 +646,6 @@ export default {
{path: 'archivedProject', name: '已归档的项目'},
{path: 'team', name: '团队管理', divided: true},
{path: 'complaint', name: '举报管理'},
])
} else {
array.push(...[
@ -727,6 +729,19 @@ export default {
this.$store.dispatch("getProjectByQueue", 600);
},
themeName: {
handler(theme) {
if (this.$Electron) {
$A.Electron.request({
action: 'updateDownloadWindow',
language: languageName,
theme,
});
}
},
immediate: true
},
'cacheProjects.length': {
handler() {
this.$nextTick(_ => {
@ -819,6 +834,13 @@ export default {
case 'complaint':
this.complaintShow = true;
return;
case 'download':
$A.Electron.request({
action: 'openDownloadWindow',
language: languageName,
theme: this.themeName,
});
return;
case 'logout':
$A.modalConfirm({
title: '退出登录',
@ -978,6 +1000,13 @@ export default {
this.onAddMenu('task')
break;
case 76: // L - + alt
if (e.altKey) {
e.preventDefault();
this.settingRoute('download')
}
break;
case 85: // U -
this.onCreateGroup([this.userId])
break;

View File

@ -88,7 +88,7 @@ export default {
]
if (this.$Electron || this.$isEEUIApp) {
menu.push({path: 'keyboard', name: '键盘设置', desc: ' (Beta)'})
menu.push({path: 'keyboard', name: '键盘设置'})
}
if ($A.isDooServer() && this.$isEEUIApp) {

View File

@ -7,6 +7,11 @@
{{mateName}}<div class="input-box-push">+</div>Shift<div class="input-box-push">+</div><Input class="input-box-key" v-model="formData.screenshot_key" :maxlength="2"/>
</div>
</FormItem>
<FormItem :label="$L('下载内容')" prop="download_key">
<div class="input-box">
{{mateName}}<div class="input-box-push">+</div>{{altName}}<div class="input-box-push">+</div>L
</div>
</FormItem>
<FormItem :label="$L('新建项目')">
<div class="input-box">
{{mateName}}<div class="input-box-push">+</div>B
@ -14,7 +19,7 @@
</FormItem>
<FormItem :label="$L('新建任务')">
<div class="input-box">
{{mateName}}<div class="input-box-push">+</div>N (K)
{{mateName}}<div class="input-box-push">+</div>N
</div>
</FormItem>
<FormItem :label="$L('新会议')">
@ -71,7 +76,7 @@ export default {
loadIng: 0,
mateName: /macintosh|mac os x/i.test(navigator.userAgent) ? 'Command' : 'Ctrl',
altName: /macintosh|mac os x/i.test(navigator.userAgent) ? 'Option' : 'Alt',
formData: {
screenshot_key: '',

View File

@ -465,10 +465,14 @@ export default {
url = $A.urlAddParams(url, params);
}
if ($A.Electron) {
$A.Electron.request({action: 'openExternal', url}, () => {
// 成功
}, () => {
// 失败
$A.Electron.request({
action: 'createDownloadTask',
url
});
$A.Electron.request({
action: 'openDownloadWindow',
language: languageName,
theme: state.themeName,
});
} else if ($A.isEEUIApp) {
$A.eeuiAppOpenWeb(url);