LaiTool_PRO/src/main/service/system/electronInterface.ts

322 lines
9.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { dialog, nativeTheme, shell } from 'electron'
import { CheckFileOrDirExist, CopyFileOrFolder } from '../../../define/Tools/file'
import path from 'path'
import fs from 'fs/promises'
import { errorMessage, successMessage } from '../../../public/generalTools'
import { ErrorItem, SuccessItem } from '@/define/model/generalResponse'
import { t } from '@/i18n'
/** 打开指定的文件夹的方法 */
export type OpenFolderParams = {
/** 是不是基于项目文件,是的话,会在项目文件夹的基础上进行拼接 */
baseProject: boolean
/** 判断是不是打开父文件夹 */
dirFolder: boolean
/** 文件路径baseProject 为false需要设置完整的文件路径 */
folderPath: string
}
/** 一些对electron接口的封装配合业务逻辑 */
export default class ElectronInterface {
constructor() { }
/**
* 打开指定的文件,试用默认的打开方式
* @param value
* @returns
*/
public async OpenFile(value: string): Promise<ErrorItem | SuccessItem> {
if (!(await CheckFileOrDirExist(value))) {
return errorMessage(t("目的文件/文件夹不存在,{data}", {
data: value
}), 'SystemIpc_OPEN_FILE')
}
await shell.openPath(value)
return successMessage(null, t("打开文件/文件夹成功", {
data: value
}), 'SystemIpc_OPEN_FILE')
}
/**
* 深度复制文件夹内容到目标文件夹
* @param source 源文件夹路径
* @param destination 目标文件夹路径
* @returns
*/
public async CopyFolderContents(
source: string,
destination: string
): Promise<ErrorItem | SuccessItem> {
try {
// 使用更完善的复制方法
await CopyFileOrFolder(source, destination, false)
return successMessage(null, t('复制文件夹成功'), 'SystemIpc_COPY_FOLDER_CONTENTS')
} catch (error: any) {
return errorMessage(
t("复制文件夹失败,{error}", {
error: error.message
}),
'SystemIpc_COPY_FOLDER_CONTENTS'
)
}
}
/**
* 打开对应的文件夹
* @param params
* @returns
*/
public async OpenFolder(params: OpenFolderParams) {
try {
let openFolder = ''
if (params.baseProject) {
openFolder = path.join(global.config.project_path, params.folderPath)
}
if (params.dirFolder) {
openFolder = path.dirname(params.folderPath)
}
if (!openFolder) {
openFolder = params.folderPath
}
// 判断文件夹是不是存在
let isExist = await CheckFileOrDirExist(openFolder)
if (!isExist) {
throw new Error(t("目的文件/文件夹不存在,{data}", {
data: openFolder
}))
}
shell.openPath(openFolder)
return successMessage(null, t('打开文件/文件夹成功'))
} catch (error: any) {
return errorMessage(t("打卡开文件/文件夹失败,{error}", {
error: error.message
}), 'SystemIpc_OPEN_FOLDER')
}
}
/**
* 选择单个指定文件后缀的文件
* @param value 后缀列表
*/
public async SelectSingleFile(value: string[]): Promise<SuccessItem | ErrorItem> {
try {
let { filePaths } = await dialog.showOpenDialog({
properties: ['openFile'],
filters: [{ name: 'fileName', extensions: value }]
})
if (filePaths.length === 0) {
throw new Error(t('没有选择的文件或文件夹'))
}
return successMessage(filePaths[0], t("选择文件/文件夹成功"), 'SystemIpc_SelectSingleFile')
} catch (error: any) {
return errorMessage(
t("选择文件/文件夹失败,{error}", {
error: error.message
}),
'SystemIpc_SelectSingleFile'
)
}
}
/**
* 选择多个指定文件后缀的文件
* @param value 文件后缀列表
* @returns
*/
public async SelectMultipleFile(value: string[]): Promise<SuccessItem | ErrorItem> {
try {
const { filePaths } = await dialog.showOpenDialog({
properties: ['openFile', 'multiSelections'],
filters: [{ name: 'fileName', extensions: value }]
})
if (filePaths.length === 0) {
throw new Error(t('没有选择的文件或文件夹'))
}
return successMessage(filePaths, t("选择文件/文件夹成功"), 'SystemIpc_SelectMultipleFile')
} catch (error: any) {
console.error('选择文件错误:', error) // 记录错误日志
return errorMessage(
t("选择文件/文件夹失败,{error}", {
error: error.message
}),
'SystemIpc_SelectMultipleFile'
)
}
}
/**
* 切换主题
* @param theme 主题名称
* @returns 返回当前是否使用深色模式
*/
public async ToggleTheme(theme: string) {
if (theme != 'light' && theme != 'dark') {
nativeTheme.themeSource = 'system'
} else {
nativeTheme.themeSource = theme
}
return nativeTheme.shouldUseDarkColors
}
/**
* 选择单个文件夹
* @param defaultPath 可选配置
* @returns 成功返回选择的文件夹路径,失败返回错误信息
*/
public async SelectSingleFolder(defaultPath: string): Promise<SuccessItem | ErrorItem> {
try {
const { filePaths } = await dialog.showOpenDialog({
properties: ['openDirectory'],
defaultPath: defaultPath,
title: t('选择文件夹'),
buttonLabel: t('选择文件夹')
})
if (filePaths.length === 0) {
throw new Error(t('没有选择的文件或文件夹'))
}
return successMessage(filePaths[0], t("选择文件/文件夹成功"), 'SystemIpc_SelectSingleFolder')
} catch (error: any) {
return errorMessage(
t("选择文件/文件夹失败,{error}", {
error: error.message
}),
'SystemIpc_SelectSingleFolder'
)
}
}
/**
* 选择文件夹或指定后缀的文件
* @param extensions 文件后缀列表(可选)
* @returns
*/
public async SelectFolderOrFile(extensions?: string[]): Promise<SuccessItem | ErrorItem> {
try {
// 使用消息框让用户选择类型
const choice = await dialog.showMessageBox({
type: 'question',
title: t('选择类型'),
message: t('请选择要选择的类型:'),
buttons: [t('选择文件'), t('选择文件夹'), t('取消')],
defaultId: 0,
cancelId: 2
})
if (choice.response === 2) {
throw new Error(t("取消操作"))
}
if (choice.response === 0) {
// 选择文件
const result = await dialog.showOpenDialog({
properties: ['openFile'],
filters:
extensions && extensions.length > 0
? [
{ name: 'Audio Files', extensions },
{ name: 'All Files', extensions: ['*'] }
]
: [{ name: 'All Files', extensions: ['*'] }],
title: t('选择文件')
})
if (result.filePaths.length === 0) {
throw new Error(t('没有选择的文件或文件夹'))
}
return successMessage(result.filePaths[0], t("选择文件/文件夹成功"), 'SystemIpc_SelectFolderOrFile')
} else {
// 选择文件夹
const result = await dialog.showOpenDialog({
properties: ['openDirectory'],
title: t('选择文件夹')
})
if (result.filePaths.length === 0) {
throw new Error(t('没有选择的文件或文件夹'))
}
return successMessage(result.filePaths[0], t("选择文件/文件夹成功"), 'SystemIpc_SelectFolderOrFile')
}
} catch (error: any) {
console.error('选择文件或文件夹错误:', error)
return errorMessage(
t("选择文件/文件夹失败,{error}", {
error: error.message
}),
'SystemIpc_SelectFolderOrFile'
)
}
}
/**
* 打开指定的URL
* @param url
*/
public OpenUrl(url: string) {
shell.openExternal(url)
}
/**
* 读取文件内容(仅支持常见的文本格式)
* @param filePath 文件路径
* @returns 返回文件内容或错误信息
*/
public async ReadTextFile(filePath: string): Promise<SuccessItem | ErrorItem> {
try {
// 定义支持的文本文件格式
const supportedExtensions = [
'.txt', '.json', '.xml', '.html', '.htm', '.css', '.js', '.ts',
'.jsx', '.tsx', '.vue', '.md', '.yml', '.yaml', '.csv', '.log',
'.ini', '.conf', '.config', '.py', '.java', '.c', '.cpp', '.h',
'.php', '.rb', '.go', '.rs', '.swift', '.kt', '.scala', '.sql',
'.sh', '.bat', '.ps1', '.dockerfile', '.gitignore', '.env'
]
// 检查文件是否存在
if (!(await CheckFileOrDirExist(filePath))) {
throw new Error(t('文件不存在'))
}
// 获取文件扩展名
const ext = path.extname(filePath).toLowerCase()
// 检查文件格式是否支持
if (!supportedExtensions.includes(ext)) {
throw new Error(t("不支持的文件格式: {ext}。支持的格式: {supportedExt}", {
ext: ext,
supportedExt: supportedExtensions.join(', ')
}))
}
// 读取文件内容
const content = await fs.readFile(filePath, 'utf-8')
return successMessage(
{
content: content,
filePath: filePath,
size: Buffer.byteLength(content, 'utf-8'),
extension: ext
},
t('读取文件成功!'),
'SystemIpc_ReadTextFile'
)
} catch (error: any) {
console.error('读取文件错误:', error)
return errorMessage(
t("读取文件失败,{error}", {
error: error.message
}),
'SystemIpc_ReadTextFile'
)
}
}
}