|
|
import { ask, message } from "@tauri-apps/api/dialog";
|
|
|
import { invoke } from "@tauri-apps/api/tauri";
|
|
|
import { open } from "@tauri-apps/api/shell";
|
|
|
import type {
|
|
|
AppLogPaths,
|
|
|
NativeConfirmOptions,
|
|
|
ShowErrorNativeOptions,
|
|
|
} from "./types";
|
|
|
|
|
|
/**
|
|
|
* 统一封装原生确认框。
|
|
|
*/
|
|
|
export async function confirmNative(options: NativeConfirmOptions): Promise<boolean> {
|
|
|
try {
|
|
|
return await ask(options.message, {
|
|
|
title: options.title,
|
|
|
okLabel: options.okLabel,
|
|
|
cancelLabel: options.cancelLabel,
|
|
|
});
|
|
|
} catch (error) {
|
|
|
throw new Error(`打开确认框失败: ${String(error)}`);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
async function fetchLogPaths(): Promise<AppLogPaths> {
|
|
|
return await invoke<AppLogPaths>("get_log_paths");
|
|
|
}
|
|
|
|
|
|
const ERROR_DIALOG_MAX_LINES = 6;
|
|
|
|
|
|
/** 报错弹窗正文:最多 6 行;超过时保留前 5 行完整内容,第 6 行仅显示「...」。 */
|
|
|
function truncateErrorTextForDialog(text: string): string {
|
|
|
const normalized = text.replace(/\r\n/g, "\n");
|
|
|
const lines = normalized.split("\n");
|
|
|
if (lines.length <= ERROR_DIALOG_MAX_LINES) {
|
|
|
return normalized;
|
|
|
}
|
|
|
return `${lines.slice(0, ERROR_DIALOG_MAX_LINES - 1).join("\n")}\n...`;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 原生错误提示。`options.logActions === "file"` 时正文仅展示错误信息(最多 6 行,第 6 行可为「...」),
|
|
|
* 并提供「打开日志文件」/「确定」:前者用系统默认程序打开 app.log,后者仅关闭对话框。
|
|
|
*/
|
|
|
export async function showErrorNative(
|
|
|
content: string,
|
|
|
title = "错误",
|
|
|
options?: ShowErrorNativeOptions,
|
|
|
): Promise<void> {
|
|
|
const body = truncateErrorTextForDialog(content);
|
|
|
const logActions = options?.logActions ?? "none";
|
|
|
if (logActions === "none") {
|
|
|
try {
|
|
|
await message(body, { title, type: "error" });
|
|
|
} catch (error) {
|
|
|
throw new Error(`打开错误提示框失败: ${String(error)}`);
|
|
|
}
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
let paths: AppLogPaths;
|
|
|
try {
|
|
|
paths = await fetchLogPaths();
|
|
|
} catch {
|
|
|
try {
|
|
|
await message(body, { title, type: "error" });
|
|
|
} catch (error) {
|
|
|
throw new Error(`打开错误提示框失败: ${String(error)}`);
|
|
|
}
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
const openFile = await ask(body, {
|
|
|
title,
|
|
|
type: "error",
|
|
|
okLabel: "打开日志文件",
|
|
|
cancelLabel: "确定",
|
|
|
});
|
|
|
if (openFile) {
|
|
|
await open(paths.logFile);
|
|
|
}
|
|
|
} catch (error) {
|
|
|
try {
|
|
|
const fallback = truncateErrorTextForDialog(
|
|
|
`${content}\n(无法打开日志文件:${String(error)})`,
|
|
|
);
|
|
|
await message(fallback, {
|
|
|
title,
|
|
|
type: "error",
|
|
|
});
|
|
|
} catch (inner) {
|
|
|
throw new Error(`打开错误提示框失败: ${String(inner)}`);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 报错并可选择打开 app.log(正文仅错误摘要,等价于 `logActions: "file"`)。
|
|
|
*/
|
|
|
export async function showErrorNativeWithLog(
|
|
|
content: string,
|
|
|
title = "错误",
|
|
|
): Promise<void> {
|
|
|
return showErrorNative(content, title, { logActions: "file" });
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 原生信息提示(单按钮),用于非错误类说明。
|
|
|
*/
|
|
|
export async function showInfoNative(
|
|
|
content: string,
|
|
|
title = "提示",
|
|
|
): Promise<void> {
|
|
|
try {
|
|
|
await message(content, { title, type: "info" });
|
|
|
} catch (error) {
|
|
|
throw new Error(`打开提示框失败: ${String(error)}`);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 原生警告提示(非 Element 浮层),用于校验提示等。
|
|
|
*/
|
|
|
export async function showWarningNative(
|
|
|
content: string,
|
|
|
title = "提示",
|
|
|
): Promise<void> {
|
|
|
try {
|
|
|
await message(content, { title, type: "warning" });
|
|
|
} catch (error) {
|
|
|
throw new Error(`打开警告提示框失败: ${String(error)}`);
|
|
|
}
|
|
|
}
|