@hzab/utils 0.0.1 → 1.0.0-beta

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hzab/utils",
3
- "version": "0.0.1",
3
+ "version": "1.0.0-beta",
4
4
  "description": "utils",
5
5
  "main": "src",
6
6
  "scripts": {
@@ -0,0 +1,49 @@
1
+ /**
2
+ * 是否是 base64 字符串
3
+ * @param {*} str
4
+ * @returns
5
+ */
6
+ export const isBase64Str = function (str) {
7
+ if (typeof str !== "string") {
8
+ return false;
9
+ }
10
+ return /^data:\w+\/\w+;base64/i.test(str);
11
+ };
12
+
13
+ /**
14
+ * 从 base64 字符串获取文件类型
15
+ * @param base64Str
16
+ * @returns
17
+ */
18
+ export function getFileTypeFromBase64(base64Str) {
19
+ if (!isBase64Str(base64Str)) {
20
+ return base64Str;
21
+ }
22
+ // 移除Base64前缀(如果存在)
23
+ const base64Data = base64Str.replace(/^data:.*;base64,/, "");
24
+
25
+ // 键值对调换:Base64前缀作为键,文件类型信息作为值
26
+ const prefixMap = {
27
+ JVBERi0: { type: "pdf", mimeType: "application/pdf" },
28
+ R0lGODdh: { type: "gif", mimeType: "image/gif" },
29
+ R0lGODlh: { type: "gif", mimeType: "image/gif" },
30
+ iVBORw0KGgo: { type: "png", mimeType: "image/png" },
31
+ "/9j/": { type: "jpeg", mimeType: "image/jpeg" },
32
+ SUQzB: { type: "mp4", mimeType: "video/mp4" },
33
+ AAAAGGZ0eXBpc29tAA: { type: "mpeg", mimeType: "audio/mpeg" },
34
+ UklGR: { type: "webp", mimeType: "image/webp" },
35
+ PD94bWwgdmVyc2lvbj0: { type: "xml", mimeType: "text/xml" },
36
+ PHN2ZyB4bWxucz0: { type: "svg", mimeType: "image/svg+xml" },
37
+ dGV4dCB4bWxucz0: { type: "txt", mimeType: "text/plain" },
38
+ };
39
+
40
+ // 使用Object.keys遍历所有前缀,一次循环完成检测
41
+ for (const prefix of Object.keys(prefixMap)) {
42
+ if (base64Data.startsWith(prefix)) {
43
+ return prefixMap[prefix];
44
+ }
45
+ }
46
+
47
+ // 未匹配到已知类型
48
+ return { type: "unknown", mimeType: "application/octet-stream" };
49
+ }
package/src/blob.ts ADDED
@@ -0,0 +1,42 @@
1
+ /**
2
+ * 通过 Blob 的文件签名判断文件类型
3
+ * @param {Blob} blob - 待检测的 Blob 对象
4
+ * @returns {Promise<string>} - 匹配的 MIME 类型或 'unknown'
5
+ */
6
+ export async function getFileTypeFromBlob(blob: Blob): Promise<string> {
7
+ // 预设文件签名表(键:十六进制前缀,值:MIME类型)
8
+ const fileSignatures = {
9
+ "89504E47": "image/png",
10
+ FFD8FF: "image/jpeg",
11
+ "47494638": "image/gif",
12
+ "25504446": "application/pdf",
13
+ "504B0304": "application/zip", // ZIP 或 DOCX/XLSX 等基于 ZIP 的格式
14
+ "3C3F786D6C": "text/xml", // XML
15
+ "7B22": "application/json", // JSON(开头可能为 { 或 {")
16
+ "25215053": "application/postscript", // PS
17
+ "494433": "audio/mpeg", // MP3
18
+ "0000001866747970": "video/mp4", // MP4(部分情况)
19
+ };
20
+
21
+ // 读取 Blob 的前 8 字节(足够覆盖大部分签名)
22
+ const buffer = await blob.slice(0, 8).arrayBuffer();
23
+ const uint8Array = new Uint8Array(buffer);
24
+
25
+ // 转换为十六进制字符串(大写)
26
+ let hex = "";
27
+ for (let i = 0; i < uint8Array.length; i++) {
28
+ const byte = uint8Array[i].toString(16).padStart(2, "0");
29
+ hex += byte.toUpperCase();
30
+ }
31
+
32
+ // 对比签名表(优先匹配长前缀)
33
+ const signatures = Object.keys(fileSignatures).sort((a, b) => b.length - a.length);
34
+ for (const signature of signatures) {
35
+ if (hex.startsWith(signature)) {
36
+ return fileSignatures[signature];
37
+ }
38
+ }
39
+
40
+ // 未匹配到已知类型
41
+ return "unknown";
42
+ }
@@ -0,0 +1,40 @@
1
+ export interface ICheckEmptyOpt {
2
+ /** 【废弃】空值占位符 */
3
+ emptyStr?: string;
4
+ /** 空值占位符 */
5
+ emptySymbol?: string;
6
+ checkEmptyArr?: boolean;
7
+ }
8
+ export interface IHandleEmptyValOpt extends ICheckEmptyOpt {}
9
+
10
+ /**
11
+ * 空数据判断
12
+ * @param {*} val
13
+ * @param {*} opt
14
+ * @returns
15
+ */
16
+ export function checkEmpty(val, opt: ICheckEmptyOpt = {}) {
17
+ const { checkEmptyArr = true } = opt || {};
18
+ if (val === "" || val === null || val === undefined) {
19
+ return true;
20
+ }
21
+ if (checkEmptyArr && Array.isArray(val) && Array.length === 0) {
22
+ return true;
23
+ }
24
+ return false;
25
+ }
26
+
27
+ /**
28
+ * 处理空数据显示占位符
29
+ * @param val
30
+ * @param opt
31
+ * @returns
32
+ */
33
+ export const handleEmptyVal = (val, opt: IHandleEmptyValOpt = {}) => {
34
+ const { emptyStr = "--" } = opt || {};
35
+ const emptySymbol = opt?.emptySymbol ?? emptyStr;
36
+ if (checkEmpty(val, opt)) {
37
+ return emptySymbol;
38
+ }
39
+ return val;
40
+ };
@@ -1,3 +1,4 @@
1
+ import dayjs from "dayjs";
1
2
  import { nanoidNumALetters } from "../nanoid";
2
3
 
3
4
  /**
@@ -62,6 +63,7 @@ export const mergeFileName = function (fileName, str = "") {
62
63
  }
63
64
  const name = fileName?.match(/([^\/]+?)\.[^\/]+$/)?.[1] || "";
64
65
  const suffix = fileName?.match(/[^\/]+?\.([^\/]+)$/)?.[1] || "";
66
+
65
67
  return `${name}${str ?? ""}.${suffix}`;
66
68
  };
67
69
 
@@ -71,5 +73,5 @@ export const mergeFileName = function (fileName, str = "") {
71
73
  * @returns
72
74
  */
73
75
  export const getUFileName = function (fileName) {
74
- return mergeFileName(getFileName(fileName) || "rc-upload", "-" + `~kid-${nanoidNumALetters()}~` + `${Date.now()}`);
76
+ return mergeFileName(getFileName(fileName) || "up", "-" + `~kid-${nanoidNumALetters()}~`);
75
77
  };
package/src/string.ts CHANGED
@@ -1,12 +1,3 @@
1
- /**
2
- * 是否是 base64 字符串
3
- * @param {*} str
4
- * @returns
5
- */
6
- export const isBase64Str = function (str) {
7
- return /^data:\w+\/\w+;base64/i.test(str);
8
- };
9
-
10
1
  /**
11
2
  * 字符串中划线转为小驼峰
12
3
  * @param s
@@ -17,3 +8,24 @@ export const convertToCamelCase = function (s) {
17
8
  return letter.toUpperCase();
18
9
  });
19
10
  };
11
+
12
+ /**
13
+ * 对象字符串转为对象
14
+ * @param objStr
15
+ */
16
+ export const objStrToObj = function (objStr) {
17
+ let _objStr = objStr;
18
+ if (typeof _objStr === "object") {
19
+ return _objStr;
20
+ }
21
+ // 对象字符串 转为 对象
22
+ if (typeof _objStr === "string") {
23
+ _objStr = _objStr.trim();
24
+ if (/^\[.*\]$|^\{.*\}$/.test(_objStr)) {
25
+ try {
26
+ _objStr = JSON.parse(_objStr);
27
+ } catch (error) {}
28
+ }
29
+ }
30
+ return _objStr;
31
+ };
@@ -1,3 +1,5 @@
1
+ import dayjs from "dayjs";
2
+
1
3
  import { axios } from "@hzab/data-model";
2
4
 
3
5
  import { formatDirStr, mergeDirStr, getFileNameByFileObj } from "./uploadUtils";
@@ -102,7 +104,7 @@ export function getSignature(opt: IGetSignatureOpt = {}) {
102
104
  window._$offlineSignatureRes &&
103
105
  window._$offlineSignatureRes.data &&
104
106
  serverUrl + JSON.stringify(params) === window._$offlineSignatureRes.serverUrlParams &&
105
- Date.now() - window._$offlineSignatureRes.__saveTime < window._$offlineSignatureRes.expireTimeMilles - 10000
107
+ dayjs().valueOf() - window._$offlineSignatureRes.__saveTime < window._$offlineSignatureRes.expireTimeMilles - 10000
106
108
  ) {
107
109
  return Promise.resolve(window._$offlineSignatureRes.data);
108
110
  }
@@ -128,7 +130,7 @@ export function getSignature(opt: IGetSignatureOpt = {}) {
128
130
  data: res?.data?.data,
129
131
  };
130
132
  if (window._$offlineSignatureRes) {
131
- window._$offlineSignatureRes.__saveTime = Date.now();
133
+ window._$offlineSignatureRes.__saveTime = dayjs().valueOf();
132
134
  window._$offlineSignatureRes.serverUrlParams = serverUrl + JSON.stringify(params);
133
135
  }
134
136
  return window._$offlineSignatureRes.data;
@@ -0,0 +1,281 @@
1
+ /**
2
+ * 文件数据格式归一化
3
+ */
4
+ import dayjs from "dayjs";
5
+
6
+ import { nanoidNumALetters } from "../nanoid";
7
+ import { objStrToObj } from "../string";
8
+ import { isBase64Str, getFileTypeFromBase64 } from "../base64Str";
9
+ import { getFileURL } from "../file/file";
10
+ import { getFileType } from "../file/fileType";
11
+ import { getUFileName, getFileName, getFullFileName, getFileExt } from "../file/fileName";
12
+ import { getFileNameObj, mergePreviewConfig } from "./uploadUtils";
13
+
14
+ /**
15
+ * 文件数据格式
16
+ */
17
+ export interface IFile {
18
+ /** 唯一 id */
19
+ uid?: string;
20
+ /** 唯一 id */
21
+ id: string;
22
+ /** 文件地址 */
23
+ url: string;
24
+ /** 存储的文件地址 */
25
+ storeUrl: string;
26
+ /** 存储的文件名称 */
27
+ name?: string;
28
+ /** 存储的文件名称 */
29
+ filename?: string;
30
+ /** 上传的文件名称 */
31
+ originalFilename: string;
32
+ /** 基础路径 */
33
+ basePath?: string;
34
+ /** 存储路径 */
35
+ path?: string;
36
+ /** 文件后缀 */
37
+ ext?: string;
38
+ /** 文件类型 */
39
+ contentType: string;
40
+ /** 上传平台 */
41
+ platform: string;
42
+ /** 文件预览地址 */
43
+ previewUrl?: string;
44
+ /** 文件缩略图地址 */
45
+ thumbnailUrl?: string;
46
+ /** 文件类型 */
47
+ type: string;
48
+ /** 文件大小 */
49
+ size: number;
50
+ /** 原始 file 文件 */
51
+ originFileObj?: File;
52
+ /** 文件上传时间 */
53
+ createTime?: string;
54
+ /** 最后更新时间戳 */
55
+ lastModified?: number;
56
+ }
57
+
58
+ /**
59
+ * 数据处理配置项
60
+ */
61
+ export interface INormalizationOpt {
62
+ /** 列表数据模式:数组 | 字符串 */
63
+ listMode: "array" | "jsonStr" | "splitStr";
64
+ /** 字符串列表数据模式下的分隔符 */
65
+ split: string;
66
+ /** 子项数据模式: 格式化对象 | file 文件格式 | url 地址 | JSON 序列化字符串 */
67
+ itemMode: "object" | "file" | "url" | "jsonStr";
68
+ /** 预览配置参数 */
69
+ previewConfig?: Object;
70
+ /** 设置预览 URL 的回调 */
71
+ getPreviewUrl?: (file, opt) => string;
72
+ /** 缩略图配置参数 */
73
+ thumbnailUrlConf?: Object;
74
+ /** 设置缩略图 URL 的回调 */
75
+ getThumbnailUrl?: (file, opt) => string;
76
+ }
77
+
78
+ /** 内部文件格式 */
79
+ const fileTpl: IFile = {
80
+ uid: "",
81
+ id: "",
82
+ url: "",
83
+ storeUrl: "",
84
+ name: "",
85
+ filename: "",
86
+ originalFilename: "",
87
+ basePath: "",
88
+ path: "",
89
+ ext: "",
90
+ type: "",
91
+ contentType: "",
92
+ previewUrl: undefined,
93
+ size: undefined,
94
+ platform: undefined,
95
+ lastModified: undefined,
96
+ createTime: undefined,
97
+ originFileObj: undefined,
98
+ };
99
+
100
+ /**
101
+ * 文件数据格式归一化
102
+ */
103
+ export const normalizationFileObj = (data: IFile, opt: INormalizationOpt) => {
104
+ if (!data) {
105
+ console.warn("Upload handleInputData 请传入正确的数据");
106
+ return data;
107
+ }
108
+ let file = {
109
+ ...fileTpl,
110
+ };
111
+ // 对象字符串 转为 对象
112
+ const _data = objStrToObj(data);
113
+ if (isBase64Str(_data)) {
114
+ // base64 格式
115
+ file.id = nanoidNumALetters();
116
+ file.uid = file.id;
117
+ file.url = _data;
118
+ file.storeUrl = _data;
119
+ file.name = file.id;
120
+ file.filename = file.name;
121
+ file.originalFilename = file.name;
122
+ // 根据 base64 数据获取文件类型
123
+ const typeObj = getFileTypeFromBase64(_data);
124
+ file.type = typeObj.mimeType;
125
+ file.ext = typeObj.type;
126
+ } else if (typeof _data === "string") {
127
+ // url 格式
128
+ const obj = getFileNameObj(_data);
129
+ file.id = obj?.id;
130
+ file.uid = file.id;
131
+ file.url = _data;
132
+ file.storeUrl = _data;
133
+ file.name = getFullFileName(file.url);
134
+ file.filename = file.name;
135
+ file.originalFilename = getFullFileName(file.url);
136
+ file.type = getFileType(file.url);
137
+ file.ext = getFileExt(file.url);
138
+ } else if (typeof _data === "object") {
139
+ const sourceData = _data.originFileObj || _data;
140
+ file.id = _data.id || _data.uid;
141
+ file.url = sourceData.url || sourceData.ossUrl;
142
+ file.storeUrl = file.url;
143
+ file.originalFilename = _data.originalFilename || getFullFileName(file.url) || _data.name;
144
+ file.name = _data.filename || _data.name || getFullFileName(file.url);
145
+ file.filename = file.name;
146
+ file.originFileObj = data instanceof File ? data : undefined;
147
+ file.lastModified = _data.lastModified;
148
+ file.basePath = _data.basePath;
149
+ file.path = _data.path;
150
+ file.ext = getFileExt(file.url);
151
+ file.type = _data.contentType || _data.type;
152
+ file.contentType = file.type;
153
+ file.size = _data.size;
154
+ file.platform = _data.platform;
155
+ file.createTime = _data.createTime;
156
+ }
157
+
158
+ if (!file.id) {
159
+ file.id =
160
+ file.uid ||
161
+ `${
162
+ getFileName(file.url || file.name || file.filename) || "rc-upload"
163
+ }-${dayjs().valueOf()}-${nanoidNumALetters()}`;
164
+ }
165
+ if (!file.name) {
166
+ file.name = file.id;
167
+ }
168
+ if (!file.filename) {
169
+ file.filename = file.name;
170
+ }
171
+ if (!file.storeUrl) {
172
+ file.storeUrl = file.url;
173
+ }
174
+
175
+ // 预览地址
176
+ if (!file.previewUrl) {
177
+ const { getPreviewUrl, previewConfig } = opt || {};
178
+ file.previewUrl = getPreviewUrl ? getPreviewUrl(file, opt) : mergePreviewConfig(file, previewConfig);
179
+ }
180
+ // 缩略图地址
181
+ if (!file.thumbnailUrl) {
182
+ const { getThumbnailUrl, thumbnailUrlConf } = opt || {};
183
+ file.thumbnailUrl = getThumbnailUrl ? getThumbnailUrl(file, opt) : mergePreviewConfig(file, thumbnailUrlConf);
184
+ }
185
+
186
+ return file;
187
+ };
188
+
189
+ /**
190
+ * 解析传入的文件列表数据
191
+ */
192
+ export const parseFileList = (fileList, opt: INormalizationOpt) => {
193
+ if (!fileList) {
194
+ return [];
195
+ }
196
+
197
+ let _fileList = fileList;
198
+ const { listMode, split } = opt || {};
199
+
200
+ // splitStr jsonStr 模式下处理字符串
201
+ const isStr = typeof _fileList === "string";
202
+ if (isStr && listMode === "splitStr") {
203
+ // @ts-ignore
204
+ _fileList = _fileList?.split(split ?? ", ");
205
+ }
206
+
207
+ if (isStr && listMode === "jsonStr") {
208
+ _fileList = objStrToObj(_fileList);
209
+ }
210
+
211
+ const isArr = Array.isArray(_fileList);
212
+ // 统一成数组格式
213
+ if (_fileList !== null && _fileList !== undefined && !isArr) {
214
+ _fileList = [_fileList];
215
+ }
216
+
217
+ if (isArr) {
218
+ const arrRes = [];
219
+ _fileList.forEach((it) => {
220
+ const itRes = normalizationFileObj(it, opt);
221
+ itRes && arrRes.push(itRes);
222
+ });
223
+ return arrRes;
224
+ }
225
+ return _fileList;
226
+ };
227
+
228
+ /**
229
+ * 出参子项归一化
230
+ * 根据配置,将 对象 处理成所需格式
231
+ */
232
+ export const formatFile = function (file, opt: INormalizationOpt) {
233
+ const { itemMode = "object" } = opt || {};
234
+ if (itemMode === "file") {
235
+ return file?.originFileObj || file;
236
+ }
237
+ if (itemMode === "object") {
238
+ return normalizationFileObj(file, opt);
239
+ }
240
+ if (itemMode === "jsonStr" && typeof file !== "string") {
241
+ const { originFileObj, ...jFile } = normalizationFileObj(file, opt);
242
+ return JSON.stringify({ ...jFile });
243
+ }
244
+ if (itemMode === "url") {
245
+ return typeof file === "string" ? file : file.originFileObj?.url || file?.url;
246
+ }
247
+ return file;
248
+ };
249
+
250
+ /**
251
+ * 出参 fileList 归一化
252
+ * 根据配置,将 对象 处理成所需格式
253
+ */
254
+ export const formatFileList = function (fileList, opt: INormalizationOpt) {
255
+ if (!fileList) {
256
+ return undefined;
257
+ }
258
+
259
+ const { listMode, split } = opt || {};
260
+
261
+ let _fileList = fileList;
262
+ if (_fileList && !Array.isArray(_fileList)) {
263
+ _fileList = [fileList];
264
+ }
265
+
266
+ // 数组
267
+ if (listMode === "array") {
268
+ return _fileList?.map((it) => formatFile(it, opt));
269
+ }
270
+ // 序列化后的字符串
271
+ if (listMode === "jsonStr") {
272
+ return JSON.stringify(_fileList?.map((it) => formatFile(it, opt)));
273
+ }
274
+ // join 后的字符串
275
+ if (listMode === "splitStr") {
276
+ return _fileList
277
+ ?.map((it) => formatFile(it, opt))
278
+ .filter((it) => it)
279
+ .join(split ?? ", ");
280
+ }
281
+ };
@@ -1,3 +1,5 @@
1
+ import dayjs from "dayjs";
2
+
1
3
  import { axios } from "@hzab/data-model";
2
4
  import { formatDirStr, mergeDirStr, getFileNameByFileObj } from "./uploadUtils";
3
5
 
@@ -65,7 +67,7 @@ export function getSignature(opt: IGetSignatureOpt = {}) {
65
67
  if (
66
68
  window._$ossSignatureRes &&
67
69
  serverUrl === window._$ossSignatureRes.serverUrl &&
68
- Date.now() - window._$ossSignatureRes.__saveTime < window._$ossSignatureRes.expireTimeMilles - 10000
70
+ dayjs().valueOf() - window._$ossSignatureRes.__saveTime < window._$ossSignatureRes.expireTimeMilles - 10000
69
71
  ) {
70
72
  return Promise.resolve(window._$ossSignatureRes);
71
73
  }
@@ -84,7 +86,7 @@ export function getSignature(opt: IGetSignatureOpt = {}) {
84
86
  .then((res) => {
85
87
  window._$ossSignatureRes = res?.data?.data ?? res?.data ?? res;
86
88
  if (window._$ossSignatureRes) {
87
- window._$ossSignatureRes.__saveTime = Date.now();
89
+ window._$ossSignatureRes.__saveTime = dayjs().valueOf();
88
90
  window._$ossSignatureRes.serverUrl = serverUrl;
89
91
  }
90
92
  return window._$ossSignatureRes;
@@ -1,7 +1,10 @@
1
+ import dayjs from "dayjs";
2
+
1
3
  import { getArr } from "../array";
4
+ import { isBase64Str } from "../base64Str";
2
5
 
6
+ import { mergeFileName, getFileName } from "../file/fileName";
3
7
  import { nanoidNumALetters } from "../nanoid";
4
- import { mergeFileName, getFullFileName } from "../file/fileName";
5
8
 
6
9
  /**
7
10
  * 格式化 dir 字符串,必须为非 / 开头, / 结尾。如: test/
@@ -51,6 +54,30 @@ export function getPreviewUrl(uri, previewConfig) {
51
54
  return uri;
52
55
  }
53
56
 
57
+
58
+ export const getFileNameExt = function (fileName,) {
59
+ const regex = /^(.*)\.([^.]+)$/;
60
+ const result = fileName.match(regex);
61
+ if (result) {
62
+ const [, mainPart, ext] = result;
63
+ return {
64
+ mainPart,
65
+ ext
66
+ }
67
+ }
68
+ return {
69
+ mainPart: fileName,
70
+ ext: ""
71
+ }
72
+ };
73
+
74
+ export const getFileTypeExt = function (type,) {
75
+ if (type.indexOf("/") >= 0) {
76
+ return type?.match(/[^\/]*\/([^\/]+)$/)?.[1]
77
+ };
78
+ return type;
79
+ };
80
+
54
81
  /**
55
82
  * 根据 file 对象获取文件名称,文件名称包含:初始名称、类型
56
83
  * 数据存储格式 ~k[key]-[data]~ 如: ~ktime-1743649562530~
@@ -58,22 +85,31 @@ export function getPreviewUrl(uri, previewConfig) {
58
85
  * @param file
59
86
  */
60
87
  export const getFileNameByFileObj = (file, opt: any) => {
61
- const { useHashName = true } = opt || {};
62
- const _fileName = file.name || getFullFileName(file.url);
63
- if (_fileName.indexOf("~kid-") >= 0 || !useHashName) {
88
+
89
+ const {
90
+ useHashName = true,
91
+ params: { fileName = "" } = {} // 给 params 加默认空对象
92
+ } = opt || {};
93
+
94
+ const { mainPart,
95
+ ext } = getFileNameExt(file.name || "");
96
+
97
+ const type = ext || getFileTypeExt(file.type || file.contentType);
98
+
99
+ const _fileName = fileName || mainPart || getFileName(file.url) || "oss";
100
+
101
+ if (_fileName.indexOf("~kid-") >= 0) {
64
102
  return _fileName;
65
103
  }
66
- // id、时间、文件类型
67
- const id = file.id || file.uid || nanoidNumALetters();
68
- return mergeFileName(
69
- _fileName,
70
- "-" +
71
- setFileNameObj({
72
- id,
73
- time: file.createTime ?? Date.now(),
74
- type: (file.type || file.contentType)?.replace("/", "_"),
75
- }),
76
- );
104
+
105
+ const objStr = setFileNameObj({
106
+ t: file.createTime || dayjs().valueOf(),
107
+ ext: file.ext || (file.type || file.contentType)?.replace("/", "_"),
108
+ });
109
+ const hashStr = useHashName ? `${nanoidNumALetters()}-` : "";
110
+ const nm = mergeFileName(_fileName + "." + type, `~kid-${hashStr}${objStr}`);
111
+
112
+ return nm
77
113
  };
78
114
 
79
115
  /**
@@ -116,3 +152,40 @@ export const getValByKey = function (url, key) {
116
152
  const reg = new RegExp(`~k${key?.replace(/~/g, "_")}-\([^~]*\)~`);
117
153
  return url.match(reg)?.[1];
118
154
  };
155
+
156
+ /**
157
+ * 预览 url 添加前缀
158
+ * @param {*} uri
159
+ * @param {*} conf
160
+ * @returns
161
+ */
162
+ export function mergePreviewConfig(uri, conf) {
163
+ let _uri = uri;
164
+ let basePath = "";
165
+ if (typeof _uri === "object") {
166
+ basePath = _uri.basePath;
167
+ _uri = _uri.previewUrl || _uri.url;
168
+ }
169
+ if (typeof _uri !== "string") {
170
+ return _uri;
171
+ }
172
+ _uri = _uri?.trim();
173
+ // base64 直接返回
174
+ if (isBase64Str(_uri)) {
175
+ return _uri;
176
+ }
177
+ const isUriPattern = /http[s]?:\/\/[^\s]+/.test(_uri);
178
+ const query = conf?.query?.trim() ? "?" + conf?.query?.trim() : "";
179
+
180
+ if (isUriPattern) {
181
+ // 源地址为 http 地址,直接拼接 query 参数
182
+ return `${_uri}${query}`;
183
+ }
184
+
185
+ if (conf) {
186
+ // 拼接前缀
187
+ return `${conf?.url?.trim()?.replace(/\/$/, "")}/${_uri?.trim()?.replace(/^\//, "")}${query}`?.trim();
188
+ }
189
+
190
+ return _uri;
191
+ }