files.ts 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. import type { OpenDialogOptions, Event } from 'electron'
  2. import { dialog, ipcMain, shell, app } from 'electron'
  3. import * as fs from 'fs-extra'
  4. import * as path from 'path'
  5. /** 当前项目文件夹锁:持有一个项目内文件的句柄,防止用户删除项目文件夹 */
  6. let projectLockFd: number | null = null
  7. /**
  8. * 选择文件夹
  9. */
  10. export const choeseDirectory = async () => {
  11. const { canceled, filePaths } = await dialog.showOpenDialog({
  12. properties: ['openDirectory']
  13. })
  14. if (!canceled) {
  15. return filePaths[0]
  16. }
  17. return
  18. }
  19. /**
  20. * 选择文件
  21. */
  22. export const choeseFile = async (_e: Event, options: OpenDialogOptions = {}) => {
  23. const { canceled, filePaths } = await dialog.showOpenDialog({
  24. title: '选择文件',
  25. buttonLabel: '选择',
  26. ...options
  27. })
  28. if (!canceled) {
  29. return filePaths
  30. }
  31. return
  32. }
  33. /**
  34. * 保存文件
  35. */
  36. export const writeFile = async (_e: Event, filePath: string, content: string) => {
  37. return fs.writeFileSync(filePath, content)
  38. }
  39. /**
  40. * 读取文件
  41. */
  42. export const readFile = async (_e: Event, filePath: string, encoding: BufferEncoding) => {
  43. return fs.readFileSync(filePath, { encoding })
  44. }
  45. /**
  46. * 检查文件是否存在
  47. */
  48. export const checkFileExists = async (_e: Event, filePath: string) => {
  49. return fs.existsSync(filePath)
  50. }
  51. /**
  52. * 创建目录
  53. */
  54. export const createDirectory = async (_e: Event, directoryPath: string) => {
  55. return fs.mkdirSync(directoryPath, { recursive: true })
  56. }
  57. /**
  58. * 复制文件
  59. */
  60. export const copyFile = async (_e: Event, sourcePath: string, destinationPath: string) => {
  61. return fs.copyFileSync(sourcePath, destinationPath)
  62. }
  63. /**
  64. * 打开文件夹
  65. */
  66. export const openDir = async (
  67. _e: Event,
  68. directoryPath: string,
  69. cb: (err: NodeJS.ErrnoException | null, dir: fs.Dir) => void
  70. ) => {
  71. return fs.opendir(directoryPath, cb)
  72. }
  73. /**
  74. * 复制文件夹
  75. */
  76. export const cp = (_e: Event, sourcePath: string, destinationPath: string) => {
  77. return fs.cpSync(sourcePath, destinationPath)
  78. }
  79. /**
  80. * 删除文件
  81. */
  82. export const removeFile = (_e: Event, sourcePath: string) => {
  83. return fs.rmSync(sourcePath)
  84. }
  85. /**
  86. * 打开并锁定文件
  87. */
  88. export const openFile = (_e: Event, sourcePath: string, flag: string) => {
  89. return fs.openSync(sourcePath, flag || 'w')
  90. }
  91. /**
  92. * 关闭并解锁文件
  93. */
  94. export const closeFile = (_e: Event, fd: number) => {
  95. return fs.closeSync(fd)
  96. }
  97. /**
  98. * 修改文件名
  99. */
  100. export const renameFile = (_e: Event, sourcePath: string, targetPath: string) => {
  101. return fs.renameSync(sourcePath, targetPath)
  102. }
  103. /**
  104. * 资源管理器中打开
  105. * @param path 文件路径
  106. */
  107. export const openFileInExplorer = (_e: Event, path: string) => {
  108. return shell.showItemInFolder(path)
  109. }
  110. /**
  111. * 锁定项目文件夹:打开项目目录内的 project.ui 并保持句柄,防止用户在资源管理器中删除该文件夹
  112. * @param projectDir 项目根目录路径(如 D:\projects\myProject)
  113. */
  114. const lockProjectFolder = async (_e: Event, projectDir: string) => {
  115. if (projectLockFd !== null) {
  116. try {
  117. fs.closeSync(projectLockFd)
  118. } catch (_) {}
  119. projectLockFd = null
  120. }
  121. const lockFilePath = path.join(projectDir, 'project.ui')
  122. if (!fs.existsSync(lockFilePath)) return
  123. try {
  124. projectLockFd = fs.openSync(lockFilePath, 'r')
  125. } catch (_) {}
  126. }
  127. /**
  128. * 解锁项目文件夹:关闭句柄,允许用户删除项目文件夹
  129. */
  130. export const unlockProjectFolder = () => {
  131. if (projectLockFd !== null) {
  132. try {
  133. fs.closeSync(projectLockFd)
  134. } catch (_) {}
  135. projectLockFd = null
  136. }
  137. }
  138. export const copyTemplate = async (_e: Event, targetPath: string) => {
  139. const templatePaths = app.isPackaged
  140. ? [
  141. path.join(process.resourcesPath, 'template/src'),
  142. path.join(process.resourcesPath, 'app.asar.unpacked/resources/template/src')
  143. ]
  144. : [path.join(process.cwd(), 'resources/template/src')]
  145. const templatePath = templatePaths.find((item) => fs.existsSync(item))
  146. await fs.ensureDir(targetPath)
  147. if (templatePath) {
  148. await fs.copy(templatePath, targetPath)
  149. }
  150. await fs.ensureDir(path.join(targetPath, 'assets/images'))
  151. await fs.ensureDir(path.join(targetPath, 'assets/fonts'))
  152. await fs.ensureDir(path.join(targetPath, 'assets/others'))
  153. // .gitkeep占位文件 创建后移除
  154. await fs.remove(path.join(targetPath, 'assets/images/.gitkeep'))
  155. await fs.remove(path.join(targetPath, 'assets/others/.gitkeep'))
  156. }
  157. export function handleFile() {
  158. // 获取文件夹
  159. ipcMain.handle('get-directory', choeseDirectory)
  160. // 获取文件
  161. ipcMain.handle('get-file', choeseFile)
  162. // 创建文件夹
  163. ipcMain.handle('create-directory', createDirectory)
  164. // 复制文件夹
  165. ipcMain.handle('copy-directory', cp)
  166. // 写入文件
  167. ipcMain.handle('write-file', writeFile)
  168. // 读取文件
  169. ipcMain.handle('read-file', readFile)
  170. // 检查路径是否存在
  171. ipcMain.handle('check-file-path', checkFileExists)
  172. // 复制文件
  173. ipcMain.handle('copy-file', copyFile)
  174. // 删除文件
  175. ipcMain.handle('delete-file', removeFile)
  176. // 独占打开
  177. ipcMain.handle('exclusive-open', openFile)
  178. // 独占关闭
  179. ipcMain.handle('exclusive-close', closeFile)
  180. // 修改文件名
  181. ipcMain.handle('modify-file-name', renameFile)
  182. // 资源管理器中打开
  183. ipcMain.handle('open-file-in-explorer', openFileInExplorer)
  184. // 锁定/解锁项目文件夹(防止用户删除正在使用的项目目录)
  185. ipcMain.handle('lock-project-folder', lockProjectFolder)
  186. // 解锁项目文件夹
  187. ipcMain.handle('unlock-project-folder', unlockProjectFolder)
  188. // 复制模板文件
  189. ipcMain.handle('copy-template', copyTemplate)
  190. }