You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

242 lines
8.0 KiB

  1. /**
  2. * 模拟客户端back server
  3. * 目前还不知道back的API及具体实现,暂时先抑制代码报错
  4. */
  5. import { fetchApi, wrapErrorHint } from '@/utils/request';
  6. import { notify, firstCharToUpperCase } from '@/utils/tool';
  7. import { identity } from 'lodash';
  8. import Vue from 'vue';
  9. export const isClient = !!global.electron; // process.env.IS_CLIENT;
  10. let socket;
  11. const noop = () => {};
  12. const io = (path) => {
  13. const sk = new WebSocket(`ws://127.0.0.1:7777/${path}`);
  14. sk.on = sk.addEventListener;
  15. return sk;
  16. }
  17. const requestBySocket = (socketIns, message) => new Promise((resolve, reject) => {
  18. socketIns.on('open', () => { socketIns.send(message); });
  19. socketIns.on('message', e => {resolve(e);socketIns.close();});
  20. socketIns.on('error', e => reject(e));
  21. });
  22. const safeCall = f => isClient ? f : noop;
  23. const safeSocket = f => (...args) => socket && f(...args);
  24. const system = {
  25. isClient,
  26. init: safeCall(() => {
  27. console.log('客户端 electron API 检测:', global.electron);
  28. }),
  29. // initialSocket: () => {
  30. // socket = io('ws://10.240.5.17:8000');
  31. // // socket = io('ws://127.0.0.1:3000');
  32. // debugger;
  33. // socket.on('connect', () => { console.log('本地socket服务连接成功') });
  34. // },
  35. /**
  36. * todo
  37. * 通知登录
  38. */
  39. login: () => {
  40. },
  41. /**
  42. * todo
  43. * 通知登出
  44. */
  45. logout: () => {
  46. },
  47. /**
  48. * 通知系统进入当前的工作空间
  49. */
  50. entryProject: safeCall((projName, userId, fileChangeHandler, initHandler, errorHandler = identity) => {
  51. requestBySocket(io('init'), [userId, projName].join('|'))
  52. .then(response => initHandler(response.data));
  53. const watchSocket = io('subscriptionFileChange');
  54. watchSocket.on('open', () => {
  55. watchSocket.send(projName);
  56. });
  57. watchSocket.on('message', fileChangeHandler);
  58. watchSocket.on('error', errorHandler);
  59. return watchSocket
  60. }),
  61. /**
  62. * todo
  63. * 离开工作空间时注销socket
  64. */
  65. leaveProject: safeCall((watchSocket) => {
  66. watchSocket.close();
  67. }),
  68. /**
  69. * 下载文件到工作空间
  70. */
  71. downloadFile: safeCall((fileIpfsId, projectName, fileName, dirName, onProcessHandler, onErrorHandler = identity) => {
  72. const socket = io('download');
  73. socket.on('open', () => {
  74. socket.send([fileIpfsId, projectName, fileName, dirName].join('|'));
  75. });
  76. socket.on('message', (e) => { onProcessHandler(e, socket); });
  77. socket.on('error', e => {
  78. onErrorHandler(e);
  79. });
  80. }),
  81. /**
  82. * 上传文件到工作空间
  83. * issue:
  84. * + 浏览器的文件上传不会带有本地的文件路径,本地服务是否可以唤起一个选择文件的弹窗?
  85. * 程序步骤:
  86. * + 用户选择本地文件
  87. * + 文件上传至本地ipfs节点
  88. * + 将文件的ipfsCid连同文件信息发送到远端服务器
  89. */
  90. uploadFile: safeCall(async (projectId, projectName, folderId, folderName, levelId, fileList, onSuccessHandler, onProgressHandler = identity, onErrorHandler = identity) => {
  91. const { ipcRenderer } = global.electron;
  92. const res = await ipcRenderer.invoke('project-upload-file');
  93. console.log('ipcRenderer project-selected-upload-file: ', res);
  94. const { canceled, filePaths } = res;
  95. if(canceled) return;
  96. const filePath = filePaths[0];
  97. const extensionedFileName = filePath.split(/\/|\\/g).pop();
  98. const tempFilePaths = extensionedFileName.split('.');
  99. const extension = tempFilePaths.length > 1 ? tempFilePaths.pop() : '';
  100. const fileName = tempFilePaths.join('.');
  101. const maybeFile = fileList.find(iFile => `${iFile.archName}${iFile.extension ? `.${iFile.extension}` : ''}` === extensionedFileName);
  102. // 检测当前工作目录中是否存在同名文件
  103. if(maybeFile) {
  104. let confirmRes = false;
  105. try {
  106. await Vue.prototype.$confirm('当前文件夹存在同名文件,是否上传并覆盖?');
  107. confirmRes = true;
  108. } catch(e) { console.log('user canceled'); }
  109. if(!confirmRes) return;
  110. }
  111. const uploadFile = maybeFile
  112. ? firstCharToUpperCase({ ...maybeFile, ModifyUserId: sessionStorage.userId })
  113. : {
  114. // 文件名称 不带扩展名
  115. ArchName: fileName,
  116. // CommonStatus: 0,
  117. // CreateTime: "string",
  118. // 文件上传者Id
  119. CreateUserId: sessionStorage.userId,
  120. // Deleted: 0,
  121. Extension: extension,
  122. // 文件大小 单位?
  123. // FileSize: +size,
  124. // 所处文件夹id
  125. FolderId: folderId,
  126. // 所处文件夹层级,拼接符:_
  127. FolderLevelId: levelId,
  128. // Id: 0,
  129. // IpfsCid: hash,
  130. // IsShowRecycle: 0,
  131. // Milestone: 0,
  132. // ModifyTime: "string",
  133. // ModifyUserId: 0,
  134. // 项目id
  135. ProjId: projectId,
  136. // ShowUrl: "string",
  137. // Status: 0,
  138. // Version: 0,
  139. // WorkStatus: 0
  140. };
  141. const socket = io('upload');
  142. socket.on('open', () => {
  143. const data = [filePath, extensionedFileName, projectName, folderName].join('|');
  144. socket.send(data);
  145. onProgressHandler({ process: 0 }, uploadFile);
  146. });
  147. socket.on('message', async (e) => {
  148. console.log('receive download file message:', e);
  149. try {
  150. const progressData = JSON.parse(e.data);
  151. const { size, process, hash } = progressData;
  152. onProgressHandler(progressData, uploadFile);
  153. if(process !== 100 || !hash) return;
  154. socket.close();
  155. // {"size":"88.69","currentSize":"88.69","unit":"KiB","process":100,"hash":""}
  156. // {"size":"","currentSize":"","unit":"","process":100,"hash":"QmPJ9i4z5UdoQpLH1DrkhZiTZra2rGicXiPabiLw4LvTmX"}
  157. // const maybeFile = fileList.find(iFile => `${iFile.archName}${iFile.extension ? `.${iFile.extension}` : ''}` === extensionedFileName);
  158. uploadFile.FileSize = +size;
  159. uploadFile.IpfsCid = hash;
  160. const res = await fetchApi(`file/${maybeFile ? 'updateFile' : 'addFile'}`, uploadFile);
  161. wrapErrorHint(res);
  162. if(res.Code !== 0) return;
  163. notify.success(maybeFile ? '上传成功, 已覆盖同名文件' : '上传成功');
  164. onSuccessHandler(uploadFile);
  165. } catch (e) {
  166. console.error('socket-upload-file parse data have error:', e);
  167. // todo 上传失败
  168. }
  169. });
  170. socket.on('error', e => {
  171. onErrorHandler(e, uploadFile);
  172. });
  173. }),
  174. /**
  175. * 更新本地文件
  176. */
  177. updateFile: safeCall((file, localFilePathPrefix, projectName, folderName, onSuccessHandler, onProgressHandler = identity, onErrorHandler = identity) => {
  178. const socket = io('upload');
  179. const { archName, extension, id: fileId } = file;
  180. const extensionedFileName = `${archName}${extension ? `.${extension}` : ''}`;
  181. const filePath = `${localFilePathPrefix}\\${folderName}\\${extensionedFileName}`;
  182. socket.on('open', () => {
  183. const data = [filePath, extensionedFileName, projectName, folderName].join('|');
  184. socket.send(data);
  185. });
  186. socket.on('message', async (e) => {
  187. try {
  188. const progressData = JSON.parse(e.data);
  189. const { size, process, hash } = progressData;
  190. onProgressHandler(progressData);
  191. if(process !== 100 || !hash) return;
  192. socket.close();
  193. const copyFile = firstCharToUpperCase({ ...file, ipfsCid: hash, ModifyUserId: sessionStorage.userId });
  194. const res = await fetchApi('file/updateFile', copyFile);
  195. wrapErrorHint(res);
  196. if(res.Code === 0) { notify.success(`${archName} 更新成功`); }
  197. onSuccessHandler(copyFile);
  198. return;
  199. } catch(err) {
  200. console.error('socket-update-file parse data have error:', e);
  201. // todo 上传失败
  202. }
  203. });
  204. socket.on('error', e => {
  205. onErrorHandler(e, file);
  206. });
  207. }),
  208. /**
  209. * 系统打开文件
  210. */
  211. openFile: safeCall((filePath) => {
  212. const { shell } = global.electron;
  213. shell.openPath(filePath);
  214. }),
  215. /**
  216. * 系统打开文件目录
  217. */
  218. openFolder: safeCall((filePath) => {
  219. const { shell } = global.electron;
  220. shell.showItemInFolder(filePath);
  221. }),
  222. }
  223. export default system;