@spacecorexdev/core 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/package.json +2 -36
  2. package/src/api/_base.ts +243 -0
  3. package/src/api/index.ts +17 -0
  4. package/src/api/project.ts +1345 -0
  5. package/src/api/user.ts +1174 -0
  6. package/src/contract.ts +50 -0
  7. package/src/hooks/index.ts +2 -0
  8. package/{dist/hooks/index.js → src/hooks/useLongPress.ts} +53 -49
  9. package/src/hooks/useOnlineStatus.ts +22 -0
  10. package/src/index.ts +14 -0
  11. package/src/types/id.ts +2 -0
  12. package/src/types/index.ts +5 -0
  13. package/src/types/project/base.ts +63 -0
  14. package/src/types/project/contract.ts +79 -0
  15. package/src/types/project/contractStatus.tsx +15 -0
  16. package/src/types/project/index.ts +5 -0
  17. package/src/types/project/member.ts +46 -0
  18. package/src/types/project/progress.ts +43 -0
  19. package/src/types/projectRole.ts +17 -0
  20. package/src/types/theme.ts +1 -0
  21. package/src/types/user/index.ts +1 -0
  22. package/src/types/user/role.ts +7 -0
  23. package/src/ui/CommentTimeline/index.tsx +330 -0
  24. package/src/ui/FileManager/FileContextMenu.tsx +628 -0
  25. package/src/ui/FileManager/FileGridView.tsx +529 -0
  26. package/src/ui/FileManager/FileTableView.tsx +405 -0
  27. package/src/ui/FileManager/FileToolbar.tsx +119 -0
  28. package/src/ui/FileManager/FileVersionHistoryModal.tsx +175 -0
  29. package/src/ui/FileManager/MoveCopyToProjectModal.tsx +119 -0
  30. package/src/ui/FileManager/ShareToWechatModal.tsx +419 -0
  31. package/src/ui/FileManager/hooks/useFileManagerActions.ts +2532 -0
  32. package/src/ui/FileManager/hooks/useFileManagerState.ts +419 -0
  33. package/src/ui/FileManager/index.tsx +580 -0
  34. package/src/ui/FileManager/projectFileManagerApi.ts +57 -0
  35. package/src/ui/FileManager/types.ts +184 -0
  36. package/src/ui/FileManager/utils/fileDeltaWorker.ts +193 -0
  37. package/src/ui/FileManager/utils/fileTree.ts +178 -0
  38. package/src/ui/FileManager/utils/fileUpload.ts +714 -0
  39. package/src/ui/FileManager/utils/fileUtils.ts +65 -0
  40. package/src/ui/InvoiceRecordsList/index.tsx +813 -0
  41. package/src/ui/MyFileSelectorModal.tsx +261 -0
  42. package/src/ui/ProjectSelector/index.tsx +472 -0
  43. package/src/ui/ProjectSelector/projectCache.ts +40 -0
  44. package/src/ui/RoleSelector/index.tsx +473 -0
  45. package/src/ui/RoleSelector/roleCache.ts +40 -0
  46. package/src/ui/UIAlert.tsx +399 -0
  47. package/src/ui/UIAlertExample.tsx +129 -0
  48. package/src/ui/UIConfirm.tsx +335 -0
  49. package/src/ui/UIConfirmExample.tsx +165 -0
  50. package/src/ui/UIDatePicker.tsx +146 -0
  51. package/src/ui/UIDateRangePicker.tsx +38 -0
  52. package/src/ui/UIFilePreview/ExcelEditor/index.tsx +617 -0
  53. package/src/ui/UIFilePreview/ExcelEditor/types.ts +24 -0
  54. package/src/ui/UIFilePreview/ExportDropdown.css +85 -0
  55. package/src/ui/UIFilePreview/ExportDropdown.tsx +109 -0
  56. package/src/ui/UIFilePreview/IndexedDBUtils.ts +564 -0
  57. package/src/ui/UIFilePreview/MarkdownEditor/exportMarkdownPdf.ts +576 -0
  58. package/src/ui/UIFilePreview/MarkdownEditor/index.tsx +677 -0
  59. package/src/ui/UIFilePreview/MarkdownEditor/markdownEditor.css +1005 -0
  60. package/src/ui/UIFilePreview/MarkdownEditor/types.ts +15 -0
  61. package/src/ui/UIFilePreview/PPTXEditor/index.tsx +602 -0
  62. package/src/ui/UIFilePreview/RichDocEditor/exportRDocMarkdown.ts +77 -0
  63. package/src/ui/UIFilePreview/RichDocEditor/exportRDocPdf.ts +374 -0
  64. package/src/ui/UIFilePreview/RichDocEditor/index.tsx +730 -0
  65. package/src/ui/UIFilePreview/RichDocEditor/richdoc-editor.css +59 -0
  66. package/src/ui/UIFilePreview/UIFilePreview.tsx +611 -0
  67. package/src/ui/UIFilePreview/WordEditor/index.tsx +3933 -0
  68. package/src/ui/UIFilePreview/WordEditor/style.ts +1213 -0
  69. package/src/ui/UIFilePreview/WordEditor/word-editor.css +999 -0
  70. package/src/ui/UIFilePreview/filePreview.css +193 -0
  71. package/src/ui/UIFilePreview/filetypes/archive.tsx +66 -0
  72. package/src/ui/UIFilePreview/filetypes/cad.tsx +287 -0
  73. package/src/ui/UIFilePreview/filetypes/excel.tsx +355 -0
  74. package/src/ui/UIFilePreview/filetypes/image.tsx +20 -0
  75. package/src/ui/UIFilePreview/filetypes/imageLoader.ts +63 -0
  76. package/src/ui/UIFilePreview/filetypes/pdf.tsx +156 -0
  77. package/src/ui/UIFilePreview/filetypes/ppt.tsx +535 -0
  78. package/src/ui/UIFilePreview/filetypes/pptExportPdf.ts +523 -0
  79. package/src/ui/UIFilePreview/filetypes/video.tsx +482 -0
  80. package/src/ui/UIFilePreview/filetypes/word.tsx +245 -0
  81. package/src/ui/UIFilePreview/filetypes/wordExportMarkdown.ts +86 -0
  82. package/src/ui/UIFilePreview/filetypes/wordExportPdf.tsx +427 -0
  83. package/src/ui/UIFilePreview/index.ts +35 -0
  84. package/src/ui/UIFilePreview/pdfPagination.ts +143 -0
  85. package/src/ui/UIFilePreview/previewUtils.tsx +531 -0
  86. package/src/ui/UIFileUpload.tsx +382 -0
  87. package/src/ui/UIImageUpload.tsx +195 -0
  88. package/src/ui/UILoading.tsx +255 -0
  89. package/src/ui/UILoadingExample.tsx +139 -0
  90. package/src/ui/UISelect.tsx +105 -0
  91. package/src/ui/UIToast.tsx +381 -0
  92. package/src/ui/UIToastExample.tsx +69 -0
  93. package/src/ui/UIToastSingle.tsx +279 -0
  94. package/src/ui/UIViewToggle.tsx +64 -0
  95. package/src/ui/UserSelector/index.tsx +583 -0
  96. package/src/ui/index.ts +84 -0
  97. package/src/ui/upload/image.ts +135 -0
  98. package/src/ui/upload/index.ts +73 -0
  99. package/src/ui/upload/submitFile.ts +106 -0
  100. package/src/ui/upload/submitForm.ts +124 -0
  101. package/src/ui/upload/upload.err.ts +15 -0
  102. package/src/ui/upload/upload.ts +250 -0
  103. package/src/utils/CacheDepartmentInfo.ts +372 -0
  104. package/src/utils/CacheFileContent.ts +315 -0
  105. package/src/utils/CachePermissionInfo.ts +235 -0
  106. package/src/utils/CacheRoleInfo.ts +303 -0
  107. package/src/utils/CacheUserInfo.ts +818 -0
  108. package/src/utils/CacheViewToggleInfo.ts +182 -0
  109. package/src/utils/MessageContentUtils.ts +268 -0
  110. package/src/utils/clipboard.ts +70 -0
  111. package/src/utils/companyEmail.ts +19 -0
  112. package/src/utils/forbidDebugger.ts +59 -0
  113. package/src/utils/formatters.ts +94 -0
  114. package/src/utils/htmlUtils.tsx +59 -0
  115. package/src/utils/index.ts +18 -0
  116. package/src/utils/keyboard.ts +114 -0
  117. package/src/utils/mammothApi.ts +16 -0
  118. package/src/utils/polyfills.ts +928 -0
  119. package/src/utils/regex.ts +14 -0
  120. package/src/utils/storage.ts +95 -0
  121. package/src/utils/workerBootstrap.ts +87 -0
  122. package/dist/api/index.js +0 -1553
  123. package/dist/contract.js +0 -5
  124. package/dist/index.js +0 -4492
  125. package/dist/types/index.js +0 -49
  126. package/dist/ui/index.css +0 -1876
  127. package/dist/ui/index.js +0 -22245
  128. package/dist/utils/index.js +0 -4296
  129. /package/{dist → src}/styles/ant-modal.css +0 -0
  130. /package/{dist → src}/styles/ant-spin.css +0 -0
  131. /package/{dist → src}/styles/ant-table.css +0 -0
  132. /package/{dist → src}/styles/index.css +0 -0
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Copyright (c) 2017 Copyright brainpoint All Rights Reserved.
3
+ * Author: lipengxiang
4
+ * Desc:
5
+ */
6
+
7
+ "use strict";
8
+
9
+
10
+ /**
11
+ * 从base64数据中创建File对象.
12
+ * @param {*} base64Data
13
+ */
14
+ export function getFileByBase64(base64Data: string, fileName: string) {
15
+ //将base64转换为blob
16
+ let arr = base64Data.split(','),
17
+ mime = arr?.[0]?.match(/:(.*?);/)?.[1],
18
+ bstr = atob(arr[1]),
19
+ n = bstr.length,
20
+ u8arr = new Uint8Array(n);
21
+ while (n--) {
22
+ u8arr[n] = bstr.charCodeAt(n);
23
+ }
24
+ let blob = new Blob([u8arr], { type: mime });
25
+
26
+ //将blob转换为file
27
+ (blob as any).lastModifiedDate = new Date();
28
+ (blob as any).name = fileName || 'image';
29
+ return blob;
30
+ }
31
+
32
+ /**
33
+ * @param file File对象.
34
+ * @param callback (base64Data)=>void
35
+ */
36
+ export function getImageBase64ByFile(file: File, callback: (base64Data: string | null) => void) {
37
+ let reader = new FileReader();
38
+ reader.readAsDataURL(file);
39
+ reader.onload = function () {
40
+ callback(reader.result as string);
41
+ }
42
+ reader.onerror = function () {
43
+ callback(null);
44
+ }
45
+ reader.onabort = function () {
46
+ callback(null);
47
+ }
48
+ }
49
+
50
+ /**
51
+ * @param fileInput 文件的input.
52
+ * @param callback (base64Data, width, height)=>void
53
+ * @param outputFormat ? 图片格式
54
+ */
55
+ export function getImageBase64(fileInput: HTMLInputElement, callback: (base64Data: string | null, width: number, height: number) => void, outputFormat: string) {
56
+ let url;
57
+ if (navigator.userAgent.indexOf("MSIE") >= 1) {
58
+ // IE
59
+ url = fileInput.value;
60
+ } else if (navigator.userAgent.indexOf("Firefox") > 0) {
61
+ // Firefox
62
+ url = window.URL.createObjectURL(fileInput.files?.[0] as Blob);
63
+ } else if (navigator.userAgent.indexOf("Chrome") > 0) {
64
+ // Chrome
65
+ url = window.URL.createObjectURL(fileInput.files?.[0] as Blob);
66
+ } else {
67
+ url = window.URL.createObjectURL(fileInput.files?.[0] as Blob);
68
+ }
69
+
70
+ if (!url) {
71
+ callback(null, 0, 0);
72
+ return;
73
+ }
74
+
75
+ let canvas = document.createElement("canvas"),
76
+ ctx = canvas.getContext("2d"),
77
+ img = new Image();
78
+ img.crossOrigin = "Anonymous";
79
+ img.onload = function() {
80
+ canvas.height = img.height;
81
+ canvas.width = img.width;
82
+ ctx?.drawImage(img, 0, 0);
83
+ let dataURL = canvas.toDataURL(outputFormat || "image/png");
84
+ callback.call(this, dataURL, img.width, img.height);
85
+ canvas = null as any;
86
+ };
87
+ img.src = url;
88
+ }
89
+
90
+ /**
91
+ * 图片压缩
92
+ * @param {*} base64Data
93
+ * @param {*} maxSize 最大的宽或高的尺寸, 0则为1024;
94
+ * @param {*} callback (base64Data, width, height)=>void
95
+ */
96
+ export function compressImage(base64Data: string, maxSize: number, outputFormat: string, callback: (base64Data: string | null, width: number, height: number) => void) {
97
+ maxSize = maxSize > 0 ? maxSize : 1024
98
+ let maxW = maxSize;
99
+ let maxH = maxSize;
100
+ const image = new Image();
101
+ image.addEventListener("load", function(e) {
102
+ let ratio; //图片的压缩比
103
+ let needCompress = false; // 是否需要压缩
104
+ if (maxW < image.naturalWidth) {
105
+ needCompress = true;
106
+ ratio = image.naturalWidth / maxW;
107
+ maxH = image.naturalHeight / ratio;
108
+ }
109
+ //经过处理后,实际图片的尺寸为1024 * 640;
110
+ if (maxH < image.naturalHeight) {
111
+ needCompress = true;
112
+ ratio = image.naturalHeight / maxH;
113
+ maxW = image.naturalWidth / ratio;
114
+ }
115
+ if (!needCompress) {
116
+ maxW = image.naturalWidth;
117
+ maxH = image.naturalHeight;
118
+ callback && callback(base64Data, maxW, maxH);
119
+ return;
120
+ } //如果不需要压缩,需要获取图片的实际尺寸
121
+
122
+ let canvas = document.createElement("canvas");
123
+ canvas.width = maxW;
124
+ canvas.height = maxH;
125
+
126
+ const ctx = canvas.getContext("2d");
127
+ ctx?.clearRect(0, 0, maxW, maxH);
128
+ ctx?.drawImage(image, 0, 0, maxW, maxH);
129
+ // toDataURL的第二个参数表示图片质量,取值为0-1 1为原图
130
+ const compressImage = canvas.toDataURL(outputFormat||"image/png");
131
+ canvas = null as any;
132
+ callback && callback(compressImage, maxW, maxH);
133
+ });
134
+ image.src = base64Data;
135
+ }
@@ -0,0 +1,73 @@
1
+ 'use strict';
2
+
3
+
4
+ /**
5
+ * Copyright (c) 2025 SPACECORE Co.,Ltd. All Rights Reserved.
6
+ * Author: brian.li
7
+ * Date: 2025-07-20 15:53
8
+ * Desc:
9
+ */
10
+
11
+ import { AuthorizationHandler, ResponseCode } from "../../api/_base";
12
+ import { submitForm } from "./submitForm";
13
+ import UIToast from '../UIToast';
14
+
15
+
16
+ /**
17
+ * 文件上传.
18
+ */
19
+ export class FileUploadHandler {
20
+ static readonly UPLOAD_SINGLE_URL: string = import.meta.env.VITE_API_BASE + '/api/fs/upload';
21
+ // static readonly UPLOAD_MULTIPLE_URL: string = import.meta.env.VITE_API_BASE + '/api/upload/multiple';
22
+
23
+ static uploadSingle(file: File, crc32: number,
24
+ progress?: (percent: number) => void,
25
+ error?: (err: any) => void,
26
+ success?: (data: any) => void,
27
+ ) : { abort: () => void } {
28
+ const formData = new FormData();
29
+ formData.append('file', file);
30
+ const form = document.createElement('form');
31
+ form.setAttribute('enctype', 'multipart/form-data');
32
+
33
+ let ctx = { headers: {} };
34
+ (ctx.headers as any).set = (name: string, value: string) => {
35
+ (ctx.headers as any)[name] = value;
36
+ };
37
+ AuthorizationHandler.requestHandleSid(ctx);
38
+ delete (ctx.headers as any).set;
39
+
40
+ return submitForm(form, file, {
41
+ url: FileUploadHandler.UPLOAD_SINGLE_URL + '?size=' + file.size + '&crc32=' + crc32,
42
+ success: ((data: any) => {
43
+ data = JSON.parse(data);
44
+ if (data.code === ResponseCode.SUCCESS) {
45
+ UIToast.success('文件上传成功');
46
+ } else {
47
+ UIToast.error(data.message || '文件上传失败');
48
+ }
49
+ success && success(data);
50
+ }),
51
+ error: ((err: any) => {
52
+ console.error('File upload error:', err);
53
+ UIToast.error('文件上传失败');
54
+ if (error) {
55
+ error(err);
56
+ }
57
+ }),
58
+ progress: progress || ((percent: number) => {
59
+ console.log(`上传进度: ${percent}%`);
60
+ }),
61
+ complete: (status: number) => {
62
+ },
63
+ data: '',
64
+ method: 'POST',
65
+ headers: ctx.headers,
66
+ timeout: Number(import.meta.env.VITE_API_UPLOAD_TIMEOUT)||5000,
67
+ withCredentials: true,
68
+ crossDomain: true,
69
+ sliceOffset: 0,
70
+ sliceLength: -1,
71
+ });
72
+ }
73
+ }
@@ -0,0 +1,106 @@
1
+ 'use strict';
2
+
3
+ import * as febs from 'febs-browser';
4
+
5
+ interface SubmitFileOptions {
6
+ url: string,
7
+ success: (data: any) => void,
8
+ error: (err: any) => void,
9
+ progress: (percent: number) => void,
10
+ complete: (status: number, responseText: any) => void,
11
+ method: 'POST' | 'GET',
12
+ headers: Record<string, string|boolean>,
13
+ timeout?: number,
14
+ withCredentials: boolean,
15
+ crossDomain: boolean,
16
+ sliceOffset: number,
17
+ sliceLength: number,
18
+ }
19
+
20
+ /**
21
+ * @param options: {
22
+ * url: url,
23
+ * success: function() {},
24
+ * error: function(err) {},
25
+ * progress: function(percent) {},
26
+ * method: 'post',
27
+ * headers: {},
28
+ * timeout: 5000,
29
+ * withCredentials: true,
30
+ * crossDomain: true,
31
+ * sliceOffset: this.sliceOffset,
32
+ * sliceLength: this.sliceLength,
33
+ * }
34
+ */
35
+ export function submitFile(fileObj: File, options: SubmitFileOptions)
36
+ : { abort: () => void }
37
+ {
38
+ let default_options = {
39
+ success: options.success,
40
+ method: options.method || 'POST',
41
+ url: options.url,
42
+ mode: (options.crossDomain === false) ? 'same-origin' : 'cors',
43
+ timeout: (options.timeout || 5000)
44
+ };
45
+
46
+ options = { ...default_options, ...options };
47
+
48
+ {
49
+ let formData = new FormData();
50
+
51
+ {
52
+ let blobSlice = (File.prototype as any).mozSlice || (File.prototype as any).webkitSlice || (File.prototype as any).slice;
53
+
54
+ // ToDo: Support Multiple on any input?
55
+ // Just need a loop here..
56
+ let filename = fileObj.name;
57
+ let i_filename = filename.lastIndexOf('/');
58
+ if (i_filename >= 0) {
59
+ filename = filename.substr(i_filename+1);
60
+ }
61
+ i_filename = filename.lastIndexOf('\\');
62
+ if (i_filename >= 0) {
63
+ filename = filename.substr(i_filename+1);
64
+ }
65
+ let blob = blobSlice.call(fileObj, options.sliceOffset, options.sliceOffset+options.sliceLength);
66
+ formData.append('file', blob, filename);
67
+ }
68
+
69
+ (options as any).data = formData;
70
+ }
71
+
72
+ let headers;
73
+ if (options.headers) {
74
+ // delete options.headers['Content-Type'];
75
+ headers = options.headers;
76
+ }
77
+ else {
78
+ headers = {};
79
+ }
80
+
81
+ headers['Content-Type'] = false;
82
+
83
+ return febs.net.ajax({
84
+ url: options.url,
85
+ type: options.method,
86
+ headers: headers,
87
+ processData: false,
88
+ data: (options as any).data,
89
+ timeout: options.timeout,
90
+ withCredentials: options.withCredentials,
91
+ success: function(data: any) {
92
+ if (options.success) {
93
+ options.success(data);
94
+ }
95
+ },
96
+ error: function(xhr: any, statusText: any, err: any) {
97
+ if (options.error) {
98
+ options.error(err);
99
+ }
100
+ },
101
+ complete: function (xhr: any, responseText: any) {
102
+ options.complete(xhr.status, responseText);
103
+ },
104
+ progress: options.progress
105
+ });
106
+ }
@@ -0,0 +1,124 @@
1
+ 'use strict';
2
+
3
+ import * as febs from 'febs-browser';
4
+
5
+ /**
6
+ * @param options: {
7
+ * url: url,
8
+ * success: function() {},
9
+ * error: function(err) {},
10
+ * progress: function(percent) {},
11
+ * method: 'post',
12
+ * headers: {},
13
+ * timeout: 5000,
14
+ * fileIndex: 0,
15
+ * withCredentials: true,
16
+ * crossDomain: true,
17
+ * sliceOffset: this.sliceOffset,
18
+ * sliceLength: this.sliceLength,
19
+ * }
20
+ */
21
+ export function submitForm(formObj: HTMLFormElement, fileObj: File, options: {
22
+ url: string,
23
+ success: (data: any) => void,
24
+ error: (err: any) => void,
25
+ progress: (percent: number) => void,
26
+ complete: (status: number) => void,
27
+ data?: string,
28
+ method?: string,
29
+ headers?: any,
30
+ timeout?: number,
31
+ withCredentials?: boolean,
32
+ crossDomain?: boolean,
33
+ sliceOffset?: number,
34
+ sliceLength?: number,
35
+ }):{ abort: () => void } {
36
+
37
+ options.method = options.method || 'POST';
38
+ options.headers = options.headers || {};
39
+ options.timeout = options.timeout || 5000;
40
+ options.withCredentials = options.withCredentials || true;
41
+ options.crossDomain = options.crossDomain || true;
42
+ options.sliceOffset = options.sliceOffset || 0;
43
+ options.sliceLength = options.sliceLength || -1;
44
+
45
+ let default_options = {
46
+ success: options.success,
47
+ method: options.method || 'POST',
48
+ url: options.url,
49
+ mode: (options.crossDomain != true) ? 'same-origin' : 'cors',
50
+ timeout: (options.timeout || 5000)
51
+ };
52
+
53
+ options = febs.utils.mergeMap(default_options, options || {});
54
+
55
+ let formObj$ = febs.$(formObj);
56
+ if (!!formObj$.attr('enctype') && formObj$.attr('enctype').toLowerCase() === 'multipart/form-data') {
57
+
58
+ let formData = new FormData();
59
+
60
+ // if ('files' in fileObj && (fileObj as any).files.length > 0) {
61
+ let blobSlice = (File.prototype as any).mozSlice || (File.prototype as any).webkitSlice || (File.prototype as any).slice;
62
+
63
+ // ToDo: Support Multiple on any input?
64
+ // Just need a loop here..
65
+ // let filename = (fileObj as any).value;
66
+ let filename = (fileObj.name);
67
+ let i_filename = filename.lastIndexOf('/');
68
+ if (i_filename >= 0) {
69
+ filename = filename.substr(i_filename + 1);
70
+ }
71
+ i_filename = filename.lastIndexOf('\\');
72
+ if (i_filename >= 0) {
73
+ filename = filename.substr(i_filename + 1);
74
+ }
75
+
76
+ let len:number = options.sliceLength||-1;
77
+ if (len == -1) {
78
+ len = fileObj.size - (options.sliceOffset || 0);
79
+ }
80
+
81
+ let blob = blobSlice.call((fileObj),
82
+ options.sliceOffset,
83
+ (options.sliceOffset || 0) + len);
84
+ formData.append('file', blob, filename);
85
+ // }
86
+
87
+ (options as any).data = formData;
88
+ } else {
89
+ throw new Error('only support multipart/form-data');
90
+ }
91
+
92
+ let headers;
93
+ if (options.headers) {
94
+ // delete options.headers['Content-Type'];
95
+ headers = options.headers;
96
+ }
97
+ else {
98
+ headers = {};
99
+ }
100
+
101
+ headers['Content-Type'] = false;
102
+
103
+ return febs.net.ajax({
104
+ url: options.url,
105
+ type: options.method,
106
+ headers: headers,
107
+ processData: false,
108
+ data: (options as any).data,
109
+ timeout: options.timeout,
110
+ withCredentials: options.withCredentials,
111
+ success: function (data: any) {
112
+ if (options.success) {
113
+ options.success(data);
114
+ }
115
+ },
116
+ error: function (xhr: any, statusText: any, err: any) {
117
+ if (options.error) {
118
+ options.error(err);
119
+ }
120
+ },
121
+ complete: options.complete,
122
+ progress: options.progress
123
+ });
124
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Copyright (c) 2017 Copyright brainpoint All Rights Reserved.
3
+ * Author: lipengxiang
4
+ * Desc:
5
+ */
6
+
7
+ "use strict";
8
+
9
+ export enum Err {
10
+ FILE_NOT_FOUND = 'FILE_NOT_FOUND',
11
+ FILE_SIZE_EXCEED = 'FILE_SIZE_EXCEED',
12
+ FILE_HASH_ERROR = 'FILE_HASH_ERROR',
13
+ NET_ERROR = 'NET_ERROR'
14
+ };
15
+ export default Err;
@@ -0,0 +1,250 @@
1
+ /**
2
+ * Copyright (c) 2017 Copyright brainpoint All Rights Reserved.
3
+ * Author: lipengxiang
4
+ * Desc:
5
+ */
6
+
7
+ "use strict";
8
+
9
+ import * as febs from "febs-browser";
10
+ import { Err } from "./upload.err";
11
+ import { submitForm } from "./submitForm";
12
+ import { submitFile } from "./submitFile";
13
+
14
+
15
+ function uploadFile(file: File,
16
+ crc: number,
17
+ sliceOffset: number = 0,
18
+ sliceLength: number = -1,
19
+ checkoutCrc32: boolean = true,
20
+ breakpointResume: (file: File, crc32: number, sliceOffsetCb: (offset: number) => void) => void,
21
+ uploadUrl: string, // 上传文件内容的url. 系统将自动使用 uploadUrl?crc32=&size=的方式来上传.
22
+ data: string = '',
23
+ timeout: number,
24
+ crossDomain: boolean, // 跨域, 默认为true
25
+ headers: Record<string, string|boolean>,
26
+ withCredentials: boolean, // 是否附带cookie, 默认为true,
27
+ beginCB: (fileObj: File, uploader: { abort: () => void }) => void, // 上传开始的回调. function(fieObj, uploader); 调用uploader.abort() 可以停止上传.
28
+ progressCB: (fileObj: File, percent: number) => void, // 上传进度的回调. function(fileObj, percent),
29
+ finishCB: (err: Err | Error | null, fileObj: File, serverData: any) => void, // 上传完成后的回调. function(err, fileObj, serverData, xhr=null)
30
+ ) {
31
+
32
+ try {
33
+ breakpointResume(file, crc, offset => {
34
+ sliceOffset = offset;
35
+ let filesize = file.size;
36
+ if (sliceLength == -1) {
37
+ sliceLength = filesize - sliceOffset;
38
+ }
39
+
40
+ // filesize = filesize - this.sliceOffset;
41
+ // if (this.sliceLength > filesize) {
42
+ // this.sliceLength = filesize;
43
+ // } else {
44
+ // filesize = this.sliceLength;
45
+ // }
46
+
47
+ let urlpath;
48
+ if (checkoutCrc32) {
49
+ urlpath =
50
+ uploadUrl +
51
+ "crc32=" +
52
+ crc +
53
+ "&offset=" +
54
+ sliceOffset +
55
+ "&size=" +
56
+ filesize +
57
+ (data ? "&data=" + data : "");
58
+ } else {
59
+ urlpath = uploadUrl + "size=" + filesize +
60
+ "&offset=" +
61
+ sliceOffset;
62
+ }
63
+
64
+ let per = sliceOffset / file.size;
65
+ let per2 = sliceLength / file.size;
66
+
67
+ try {
68
+ let con = submitFile(file, {
69
+ sliceOffset: sliceOffset,
70
+ sliceLength: sliceLength,
71
+ timeout: timeout,
72
+ method: "POST",
73
+ url: urlpath,
74
+ progress: function (percentComplete) {
75
+ percentComplete = percentComplete ? parseFloat(percentComplete.toFixed(2)) : 0;
76
+ percentComplete = per + per2 * percentComplete;
77
+ percentComplete = parseFloat(percentComplete.toFixed(2));
78
+ if (progressCB)
79
+ progressCB(file, percentComplete);
80
+ },
81
+ error: function () {
82
+ if (finishCB) finishCB(Err.NET_ERROR, file, null);
83
+ // ctx.fileObj[0].value = "";
84
+ },
85
+ success: function (r) {
86
+ try {
87
+ r = JSON.parse(r);
88
+ } catch (e) {
89
+ r = {};
90
+ }
91
+
92
+ if (finishCB) finishCB(null, file, r);
93
+ // ctx.fileObj[0].value = "";
94
+ },
95
+ complete: function (xhr: any, responseText) {
96
+ if (xhr.status != 200) {
97
+ try {
98
+ if (finishCB)
99
+ finishCB(Err.NET_ERROR, file, responseText);
100
+ } catch (e) { ; }
101
+ } else {
102
+ // ctx.fileObj[0].value = "";
103
+ }
104
+ },
105
+ crossDomain: !!crossDomain,
106
+ headers: headers,
107
+ withCredentials: !!withCredentials
108
+ });
109
+ if (beginCB) beginCB(file, con);
110
+ } catch (e: any) {
111
+ if (finishCB) finishCB(e, file, null);
112
+ }
113
+ });
114
+ } catch (e: any) {
115
+ if (finishCB) finishCB(e, file, null);
116
+ console.error(e);
117
+ }
118
+ } // function.
119
+
120
+ /**
121
+ * post方式上传文件
122
+ * 使用 multipart/form-data 方式.
123
+ * @param cfg: object, 其中
124
+ * {
125
+ * data: , // 上传到服务器的任意字符串数据.
126
+ * fileInfo: , // 文件信息; 以下两种
127
+ * 1. formObj: , // 含有enctype="multipart/form-data"的form
128
+ * fileObj: , // form中的file对象
129
+ * fileIndex: , // 选中的file文件的索引; 默认为0;
130
+ * 2. fileObj: , // File 对象.
131
+ * uploadUrl: , // 上传文件内容的url. 系统将自动使用 uploadUrl?crc32=&size=的方式来上传.
132
+ * maxFileSize: , // 允许上传的最大文件.0表示无限制.默认为0
133
+ * beginCB: , // 上传开始的回调. function(fieObj, uploader); 调用uploader.abort() 可以停止上传.
134
+ * finishCB: , // 上传完成后的回调. function(err, fileObj, serverData, xhr=null)
135
+ * // err: - uploadErr.FILE_NOT_FOUND 未选择文件.
136
+ * // - uploadErr.FILE_SIZE_EXCEED 文件大小超出允许的最大值.
137
+ * // - uploadErr.FILE_HASH_ERROR 计算本地文件hash值时错误.
138
+ * // - uploadErr.NET_ERROR ajax上传时出错.
139
+ * // - 其他异常对象
140
+ * // serverData: 服务器返回的数据.
141
+ * progressCB: , // 上传进度的回调. function(fileObj, percent),
142
+ * headers: { // 设置request headers
143
+ * 'customHeader': 'value'
144
+ * },
145
+ * crossDomain: true, // 跨域, 默认为true
146
+ * withCredentials: true, // 是否附带cookie, 默认为true,
147
+ * checkoutCrc32: true, // 是否上传 crc32,size,ajaxmark(防止chrome优化) 三个参数.
148
+ * timeout: 5000, // 默认为 5000
149
+ * breakpointResume: // (file, crc32, (sliceOffset)=>void)=>void; 上传文件前进行确认从哪个偏移地址开始上传
150
+ * sliceOffset: 0, // 上传数据起始偏移地址.
151
+ * sliceLength: -1, // 上传数据段长度 (-1表示到结尾).
152
+ * }
153
+ */
154
+ export function upload(cfg: {
155
+ data?: string, // 上传到服务器的任意字符串数据.
156
+ file: File,
157
+ uploadUrl: string, // 上传文件内容的url. 系统将自动使用 uploadUrl?crc32=&size=的方式来上传.
158
+ maxFileSize?: number, // 允许上传的最大文件.0表示无限制.默认为0
159
+ beginCB: (fileObj: File, uploader: { abort: () => void }) => void, // 上传开始的回调. function(fieObj, uploader); 调用uploader.abort() 可以停止上传.
160
+ finishCB: (err: Err | Error | null, fileObj: File, serverData: any) => void, // 上传完成后的回调. function(err, fileObj, serverData, xhr=null)
161
+ // serverData: 服务器返回的数据.
162
+ progressCB: (fileObj: File, percent: number) => void, // 上传进度的回调. function(fileObj, percent),
163
+ headers?: Record<string, string>,
164
+ crossDomain?: boolean, // 跨域, 默认为true
165
+ withCredentials?: boolean, // 是否附带cookie, 默认为true,
166
+ checkoutCrc32: boolean, // 是否上传 crc32,size,ajaxmark(防止chrome优化) 三个参数.
167
+ timeout?: number, // 默认为 5000
168
+ breakpointResume?: (file: File, crc32: number, sliceOffsetCb: (offset: number) => void) => void, // (file, crc32, (sliceOffset)=>void)=>void; 上传文件前进行确认从哪个偏移地址开始上传
169
+ sliceOffset?: number, // 上传数据起始偏移地址.
170
+ sliceLength?: number, // 上传数据段长度 (-1表示到结尾).
171
+ }) {
172
+ let control_upload_cb = cfg.finishCB;
173
+ let control_upload_progress_cb = cfg.progressCB;
174
+ let control_upload_begin_cb = cfg.beginCB;
175
+ let control_upload_url = cfg.uploadUrl;
176
+ let control_upload_maxFileSize = !cfg.maxFileSize ? Number.MAX_SAFE_INTEGER : cfg.maxFileSize;
177
+ cfg.sliceOffset = cfg.sliceOffset ? cfg.sliceOffset : 0;
178
+ cfg.sliceLength = cfg.sliceLength ? cfg.sliceLength : -1;
179
+ cfg.headers = cfg.headers ? cfg.headers : {};
180
+ cfg.crossDomain = cfg.crossDomain === true ? true : false;
181
+ cfg.withCredentials = cfg.withCredentials === true ? true : false;
182
+ cfg.checkoutCrc32 = cfg.checkoutCrc32 === true ? true : false;
183
+ cfg.timeout = cfg.timeout ? cfg.timeout : 5000;
184
+ cfg.maxFileSize = cfg.maxFileSize ? cfg.maxFileSize : 0;
185
+ cfg.data = cfg.data ? cfg.data : '';
186
+
187
+
188
+ let control_sliceOffset = cfg.sliceOffset || 0;
189
+ let control_sliceLength = cfg.sliceLength || -1;
190
+
191
+ let breakpointResumeFoo = cfg.breakpointResume;
192
+ if (!breakpointResumeFoo) {
193
+ breakpointResumeFoo = (file, crc32, sliceOffsetCb) => {
194
+ sliceOffsetCb(0);
195
+ };
196
+ } // if.
197
+
198
+ let file = cfg.file;
199
+ if (file.size > control_upload_maxFileSize) {
200
+ if (control_upload_cb) control_upload_cb(Err.FILE_SIZE_EXCEED, file, null);
201
+ return;
202
+ }
203
+
204
+ let urlQueryIndex = control_upload_url.indexOf("?");
205
+ if (urlQueryIndex < 0) {
206
+ control_upload_url += "?";
207
+ } else if (urlQueryIndex < control_upload_url.length - 1) {
208
+ control_upload_url += "&";
209
+ }
210
+
211
+ let timeout = cfg.timeout;
212
+
213
+ if (cfg.checkoutCrc32) {
214
+ febs.crypt.crc32_fileSegment(
215
+ file,
216
+ 0,
217
+ -1,
218
+ function (crc: number) {
219
+ if (crc) {
220
+ uploadFile(file, crc, control_sliceOffset, control_sliceLength, cfg.checkoutCrc32,
221
+ breakpointResumeFoo,
222
+ control_upload_url,
223
+ cfg.data,
224
+ timeout,
225
+ !!cfg.crossDomain,
226
+ cfg.headers || {},
227
+ !!cfg.withCredentials,
228
+ control_upload_begin_cb,
229
+ control_upload_progress_cb,
230
+ control_upload_cb);
231
+ } else {
232
+ if (control_upload_cb)
233
+ control_upload_cb(Err.FILE_HASH_ERROR, file, null);
234
+ }
235
+ }
236
+ );
237
+ } else {
238
+ uploadFile(file, 0, control_sliceOffset, control_sliceLength, cfg.checkoutCrc32,
239
+ breakpointResumeFoo,
240
+ control_upload_url,
241
+ cfg.data,
242
+ timeout,
243
+ !!cfg.crossDomain,
244
+ cfg.headers || {},
245
+ !!cfg.withCredentials,
246
+ control_upload_begin_cb,
247
+ control_upload_progress_cb,
248
+ control_upload_cb);
249
+ }
250
+ }