新增quickcommand.showLoadingBar,支持显示加载条

This commit is contained in:
fofolee
2025-02-17 22:38:19 +08:00
parent d7508c36a7
commit 3eca3b448e
14 changed files with 339 additions and 84 deletions

View File

@@ -361,12 +361,19 @@ document.addEventListener("DOMContentLoaded", () => {
case "process":
document.getElementById("process").style.display = "block";
document.body.classList.add("dialog-process");
// 创建进度条
const processBarInner = document.getElementById("process-bar-inner");
if (config.isLoading) {
// 如果是加载条模式,使用动画效果
processBarInner.className = "process-bar-loading";
} else {
// 如果是进度条模式,设置初始进度
processBarInner.className = "process-bar-inner";
processBarInner.style.width = `${config.value}%`;
}
// 设置初始文本和进度
// 设置初始文本
document.getElementById("process-text").textContent = config.text;
document.getElementById(
"process-bar-inner"
).style.width = `${config.value}%`;
// 如果需要显示暂停按钮
if (config.showPause) {
@@ -393,8 +400,13 @@ document.addEventListener("DOMContentLoaded", () => {
// 监听进度条更新事件
ipcRenderer.on("update-process", (event, data) => {
const { value, text } = data;
if (typeof value === "number") {
document.getElementById("process-bar-inner").style.width = `${value}%`;
const processBarInner = document.getElementById("process-bar-inner");
if (
processBarInner &&
processBarInner.className === "process-bar-inner" &&
typeof value === "number"
) {
processBarInner.style.width = `${value}%`;
}
if (text) {
document.getElementById("process-text").textContent = text;

View File

@@ -1,6 +1,25 @@
const { ipcRenderer } = require("electron");
const os = require("os");
const { createBrowserWindow } = utools;
const dialogPath = "lib/dialog/view.html";
const preloadPath = "lib/dialog/controller.js";
const commonBrowserWindowOptions = {
resizable: false,
minimizable: false,
maximizable: false,
fullscreenable: false,
skipTaskbar: true,
alwaysOnTop: true,
frame: false,
movable: true,
webPreferences: {
preload: preloadPath,
devTools: utools.isDev(),
},
};
/**
* 创建对话框窗口
* @param {object} config - 对话框配置
@@ -9,9 +28,6 @@ const { createBrowserWindow } = utools;
*/
const createDialog = (config, customDialogOptions = {}) => {
return new Promise((resolve) => {
const dialogPath = "lib/dialog/view.html";
const preloadPath = "lib/dialog/controller.js";
// linux 和 win32 都使用 win32 的样式
const platform = os.platform() === "darwin" ? "darwin" : "win32";
@@ -26,18 +42,8 @@ const createDialog = (config, customDialogOptions = {}) => {
title: config.title || "对话框",
width: dialogWidth,
height: 80,
resizable: false,
minimizable: false,
maximizable: false,
fullscreenable: false,
skipTaskbar: true,
alwaysOnTop: true,
frame: false,
opacity: 0,
webPreferences: {
preload: preloadPath,
devTools: utools.isDev(),
},
...commonBrowserWindowOptions,
...customDialogOptions, // 合并自定义选项
};
@@ -309,7 +315,6 @@ let lastProcessBar = null;
*/
const showProcessBar = async (options = {}) => {
const {
title = "进度",
text = "处理中...",
value = 0,
position = "bottom-right",
@@ -335,26 +340,15 @@ const showProcessBar = async (options = {}) => {
return new Promise((resolve) => {
const UBrowser = createBrowserWindow(
"lib/dialog/view.html",
dialogPath,
{
title,
width: windowWidth,
height: windowHeight,
x,
y,
resizable: false,
minimizable: false,
maximizable: false,
fullscreenable: false,
skipTaskbar: true,
alwaysOnTop: true,
frame: false,
opacity: 0,
movable: true,
webPreferences: {
preload: "lib/dialog/controller.js",
devTools: utools.isDev(),
},
focusable: false,
...commonBrowserWindowOptions,
},
() => {
const windowId = UBrowser.webContents.id;
@@ -362,7 +356,6 @@ const showProcessBar = async (options = {}) => {
// 发送配置到子窗口
ipcRenderer.sendTo(windowId, "dialog-config", {
type: "process",
title,
text,
value,
isDark: utools.isDarkColors(),
@@ -443,10 +436,103 @@ const updateProcessBar = (options = {}, processBar = null) => {
ipcRenderer.sendTo(processBar.id, "update-process", { value, text });
if (complete) {
processBar.close();
setTimeout(() => {
processBar.close();
}, 600);
}
};
let lastLoadingBar = null;
/**
* 显示一个加载条对话框
* @param {object} options - 配置选项
* @param {string} [options.text="加载中..."] - 加载条上方的文本
* @param {string} [options.position="bottom-right"] - 加载条位置可选值top-left, top-right, bottom-left, bottom-right
* @param {Function} [options.onClose] - 关闭按钮点击时的回调函数
* @returns {Promise<{id: number, close: Function}>} 返回加载条窗口ID和关闭函数
*/
const showLoadingBar = async (options = {}) => {
const { text = "加载中...", position = "bottom-right", onClose } = options;
const windowWidth = 250;
const windowHeight = 60;
// 计算窗口位置
const { x, y } = calculateWindowPosition({
position,
width: windowWidth,
height: windowHeight,
});
return new Promise((resolve) => {
const UBrowser = createBrowserWindow(
dialogPath,
{
width: windowWidth,
height: windowHeight,
x,
y,
opacity: 0,
focusable: false,
...commonBrowserWindowOptions,
},
() => {
const windowId = UBrowser.webContents.id;
// 发送配置到子窗口
ipcRenderer.sendTo(windowId, "dialog-config", {
type: "process",
text,
value: 0,
isDark: utools.isDarkColors(),
platform: process.platform,
isLoading: true, // 标记为加载条模式
});
// 监听窗口准备就绪
ipcRenderer.once("dialog-ready", () => {
UBrowser.setOpacity(1);
});
// 监听对话框结果
ipcRenderer.once("dialog-result", (event, result) => {
if (result === "close" && typeof onClose === "function") {
onClose();
}
UBrowser.destroy();
});
const loadingBar = {
id: windowId,
close: () => {
if (typeof onClose === "function") {
onClose();
}
lastLoadingBar = null;
UBrowser.destroy();
},
};
lastLoadingBar = loadingBar;
// 返回窗口ID和关闭函数
resolve(loadingBar);
}
);
});
};
const closeLoadingBar = (loadingBar) => {
if (!loadingBar) {
if (!lastLoadingBar) {
throw new Error("没有找到已创建的加载条");
}
loadingBar = lastLoadingBar;
}
loadingBar.close();
};
module.exports = {
showSystemMessageBox,
showSystemInputBox,
@@ -457,4 +543,6 @@ module.exports = {
showSystemWaitButton,
showProcessBar,
updateProcessBar,
showLoadingBar,
closeLoadingBar,
};

View File

@@ -614,6 +614,7 @@ textarea:focus {
background: rgba(255, 255, 255, 0.3);
border-radius: 2px;
overflow: hidden;
position: relative;
}
.process-bar-inner {
@@ -624,6 +625,25 @@ textarea:focus {
transition: width 0.3s ease;
}
@keyframes loading-bar-animation {
0% {
left: -80px;
}
100% {
left: calc(100% + 80px);
}
}
.process-bar-loading {
position: absolute;
width: 80px;
height: 100%;
background: white;
border-radius: 2px;
animation: loading-bar-animation 3s infinite linear;
}
/* 进度条按钮容器 */
.process-buttons {
position: absolute;

View File

@@ -60,7 +60,7 @@
</div>
<div class="process-text" id="process-text"></div>
<div class="process-bar">
<div class="process-bar-inner" id="process-bar-inner"></div>
<div id="process-bar-inner"></div>
</div>
</div>
</div>

View File

@@ -105,8 +105,27 @@ async function chat(apiConfig, content) {
throw new Error("不支持的模型类型");
}
const loadingBar = await quickcommand.showLoadingBar({
text: "AI思考中...",
onClose: () => {
// 取消请求
if (source) {
source.cancel("操作已取消");
}
},
});
// 创建取消令牌
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
// 发送请求
const response = await axios.post(url, requestData, config);
const response = await axios.post(url, requestData, {
...config,
cancelToken: source.token,
});
loadingBar.close();
// 解析不同模型的响应
let result;
@@ -129,6 +148,14 @@ async function chat(apiConfig, content) {
result,
};
} catch (error) {
// 如果是用户取消的请求,返回特定的错误信息
if (axios.isCancel(error)) {
return {
success: false,
error: "请求已取消",
cancelled: true,
};
}
return {
success: false,
error: error.response?.data?.error?.message || error.message,

View File

@@ -18,7 +18,6 @@ async function runFFmpeg(args, options = {}) {
return;
}
const {
title = "FFmpeg处理",
text = "处理中...",
position = "bottom-right",
onPause,
@@ -31,7 +30,6 @@ async function runFFmpeg(args, options = {}) {
// 显示进度条
const processBar = isShowProcessBar
? await quickcommand.showProcessBar({
title,
text,
value: 0,
position,

View File

@@ -1,13 +1,29 @@
const { runCsharpFeature } = require("../../csharp");
const child_process = require("child_process");
const stopMonitor = () => {
child_process.exec("taskkill /f /im monitor.exe");
};
// 监控剪贴板变化
const watchClipboard = async function () {
const args = ["-type", "clipboard", "-once"];
const result = await runCsharpFeature("monitor", args);
if (result && result.startsWith("Error:")) {
throw new Error(result.substring(7));
const loadingBar = await quickcommand.showLoadingBar({
text: "等待剪贴板变化...",
onClose: () => {
stopMonitor();
},
});
try {
const result = await runCsharpFeature("monitor", args);
loadingBar.close();
if (result && result.startsWith("Error:")) {
throw new Error(result.substring(7));
}
return JSON.parse(result);
} catch (error) {
return {};
}
return JSON.parse(result);
};
// 监控文件系统变化
@@ -28,11 +44,23 @@ const watchFileSystem = async function (watchPath, options = {}) {
args.push("-recursive", "false");
}
const result = await runCsharpFeature("monitor", args);
if (result && result.startsWith("Error:")) {
throw new Error(result.substring(7));
const loadingBar = await quickcommand.showLoadingBar({
text: "等待文件变化...",
onClose: () => {
stopMonitor();
},
});
try {
const result = await runCsharpFeature("monitor", args);
loadingBar.close();
if (result && result.startsWith("Error:")) {
throw new Error(result.substring(7));
}
return JSON.parse(result);
} catch (error) {
return {};
}
return JSON.parse(result);
};
module.exports = {