@hzab/utils 1.1.1 → 1.1.3

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.
@@ -6,21 +6,69 @@ import { isBase64Str } from "../base64Str";
6
6
  import { mergeFileName, getFileName, getFileNameExt } from "../file/fileName";
7
7
  import { getFileTypeExt } from "../file/fileType";
8
8
  import { nanoidNumALetters } from "../nanoid";
9
+ import { addQuery, searchToObj } from "../url";
10
+
11
+ /** 预览配置(旧版 getPreviewUrl) */
12
+ export interface IPreviewConfig {
13
+ /** 预览域名或路径前缀 */
14
+ url?: string;
15
+ /** 额外 query(不含 ?) */
16
+ query?: string;
17
+ }
18
+
19
+ /** getFileNameByFileObj 入参文件对象 */
20
+ export interface IFileNameSource {
21
+ name?: string;
22
+ type?: string;
23
+ url?: string;
24
+ createTime?: string | number;
25
+ }
26
+
27
+ /** getFileNameByFileObj 配置项 */
28
+ export interface IGetFileNameByFileObjOpt {
29
+ /** 是否开启 Hash 命名 */
30
+ useHashName?: boolean;
31
+ params?: {
32
+ /** 指定文件名 */
33
+ fileName?: string;
34
+ [key: string]: any;
35
+ };
36
+ }
37
+
38
+ /** 含 previewUrl / url 的文件类对象 */
39
+ export interface IPreviewFileLike {
40
+ previewUrl?: string;
41
+ url?: string;
42
+ [key: string]: any;
43
+ }
44
+
45
+ /** mergePreviewConfig 配置项 */
46
+ export interface IMergePreviewConfig {
47
+ /** 预览域名或路径前缀(有值才拼接) */
48
+ url?: string;
49
+ /** url 别名 */
50
+ baseUrl?: string;
51
+ /** query 对象,优先推荐 */
52
+ query?: Record<string, any>;
53
+ /** `?` 后的原始字符串(不含 ?),会先解析再与 query 合并;同名键以 query 为准 */
54
+ search?: string;
55
+ }
9
56
 
10
57
  /**
11
58
  * 格式化 dir 字符串,必须为非 / 开头, / 结尾。如: test/
12
- * @param {*} dir
59
+ * @param dir
13
60
  * @returns
14
61
  */
15
- export const formatDirStr = function (dir) {
62
+ export const formatDirStr = function (dir?: string | null): string | undefined {
16
63
  return dir?.replace(/^\/(.*)\/*$/, "$1").replace(/(.?=*)\/*$/, "$1/");
17
64
  };
18
65
 
19
66
  /**
20
67
  * 合并 dir 字符串
21
- * @param {*} dir
68
+ * @param dir1
69
+ * @param dir2
22
70
  */
23
- export const mergeDirStr = function (dir1, dir2) {
71
+ export const mergeDirStr = function (dir1?: string | null, dir2?: string | null): string {
24
72
  return `${formatDirStr(dir1 || "")}${formatDirStr(dir2 || "")}`;
25
73
  };
26
74
 
@@ -30,8 +78,8 @@ export const mergeDirStr = function (dir1, dir2) {
30
78
  * @param maxCount
31
79
  * @returns
32
80
  */
33
- export function handleMaxCount(fileList, maxCount) {
34
- let list = getArr(fileList);
81
+ export function handleMaxCount<T>(fileList: T | T[] | null | undefined, maxCount?: number): T[] {
82
+ let list = getArr(fileList) as T[];
35
83
  if (maxCount > 0 && list.length > maxCount) {
36
84
  list = list.slice(0, maxCount);
37
85
  }
@@ -40,11 +88,11 @@ export function handleMaxCount(fileList, maxCount) {
40
88
 
41
89
  /**
42
90
  * 获取预览 url
43
- * @param {*} uri
44
- * @param {*} previewConfig
91
+ * @param uri
92
+ * @param previewConfig
45
93
  * @returns
46
94
  */
47
- export function getPreviewUrl(uri, previewConfig) {
95
+ export function getPreviewUrl(uri: string, previewConfig?: IPreviewConfig): string {
48
96
  const isUriPattern = /http[s]?:\/\/[^\s]+/.test(uri);
49
97
  const query = previewConfig?.query ? "?" + previewConfig?.query : "";
50
98
 
@@ -60,8 +108,9 @@ export function getPreviewUrl(uri, previewConfig) {
60
108
  * 数据存储格式 ~k[key]-[data]~ 如: ~ktime-1743649562530~
61
109
  * key、data 中的 ~ 转为 _
62
110
  * @param file
111
+ * @param opt
63
112
  */
64
- export const getFileNameByFileObj = (file, opt: any) => {
113
+ export const getFileNameByFileObj = (file: IFileNameSource, opt?: IGetFileNameByFileObjOpt): string => {
65
114
  const {
66
115
  useHashName = true,
67
116
  params: { fileName = "" } = {}, // 给 params 加默认空对象
@@ -75,8 +124,6 @@ export const getFileNameByFileObj = (file, opt: any) => {
75
124
  const _fileName = mainPart || _fullFileName || getFileName(file.url) || "file";
76
125
  const _ext = ext || getFileTypeExt(file.type);
77
126
 
78
-
79
-
80
127
  const hashStr = useHashName ? nanoidNumALetters() : "";
81
128
  const nm = mergeFileName(_fileName + "." + _ext, (_fileName.indexOf("~kid-") < 0 ? `~kid-${hashStr}-${file.createTime || dayjs().valueOf()}-${ext}` : ""));
82
129
 
@@ -84,13 +131,13 @@ export const getFileNameByFileObj = (file, opt: any) => {
84
131
  };
85
132
 
86
133
  /**
87
- * 解析
134
+ * 解析文件名中的 ~k[key]-[data]~ 键值对
88
135
  * @param url
89
136
  * @returns
90
137
  */
91
- export const getFileNameObj = function (url): any {
138
+ export const getFileNameObj = function (url: string): Record<string, string> {
92
139
  const list = url.match(/~k([^-~]+)-([^~]*)~/g);
93
- const res = {};
140
+ const res: Record<string, string> = {};
94
141
  list?.forEach((it) => {
95
142
  const arr = it?.match(/~k([^-~]+)-([^~]*)~/);
96
143
  if (arr?.length >= 3) {
@@ -101,11 +148,11 @@ export const getFileNameObj = function (url): any {
101
148
  };
102
149
 
103
150
  /**
104
- * 解析
105
- * @param url
151
+ * 将对象序列化为 ~k[key]-[data]~ 字符串
152
+ * @param obj
106
153
  * @returns
107
154
  */
108
- export function setFileNameObj(obj) {
155
+ export function setFileNameObj(obj: Record<string, any>): string {
109
156
  return Object.keys(obj)
110
157
  .map((key) => {
111
158
  return `~k${key?.replace(/~/g, "_")}-${obj[key]?.toString()?.replace(/~/g, "_")}~`;
@@ -119,44 +166,67 @@ export function setFileNameObj(obj) {
119
166
  * @param key
120
167
  * @returns
121
168
  */
122
- export const getValByKey = function (url, key) {
169
+ export const getValByKey = function (url: string, key: string): string | undefined {
123
170
  const reg = new RegExp(`~k${key?.replace(/~/g, "_")}-\([^~]*\)~`);
124
171
  return url.match(reg)?.[1];
125
172
  };
126
173
 
127
174
  /**
128
- * 预览 url 添加前缀
129
- * @param {*} uri
130
- * @param {*} conf
175
+ * 是否为无需再拼前缀的绝对/特殊地址
176
+ * @param uri
177
+ */
178
+ function isAbsoluteOrSpecialUrl(uri?: string | null): boolean {
179
+ if (!uri) {
180
+ return false;
181
+ }
182
+ if (isBase64Str(uri)) {
183
+ return true;
184
+ }
185
+ // http(s)://、协议相对 //、data:、blob:
186
+ if (/^(https?:)?\/\//i.test(uri) || /^(data|blob):/i.test(uri)) {
187
+ return true;
188
+ }
189
+ return false;
190
+ }
191
+
192
+ /**
193
+ * 预览 url 添加前缀(仅用于预览展示,不应用于表单提交值)
194
+ * @param uri 相对路径、完整 url,或含 previewUrl/url 的文件对象
195
+ * @param conf
196
+ * - url / baseUrl: 预览域名或路径前缀(有值才拼接)
197
+ * - query: query 对象,优先推荐
198
+ * - search: `?` 后的原始字符串(不含 ?),会先解析再与 query 合并;同名键以 query 为准
131
199
  * @returns
132
200
  */
133
- export function mergePreviewConfig(uri, conf) {
134
- let _uri = uri;
135
- let basePath = "";
136
- if (typeof _uri === "object") {
137
- basePath = _uri.basePath;
138
- _uri = _uri.previewUrl || _uri.url;
201
+ export function mergePreviewConfig(
202
+ uri: string | IPreviewFileLike | null | undefined,
203
+ conf?: IMergePreviewConfig,
204
+ ): string | undefined {
205
+ let _uri: string | undefined = typeof uri === "string" ? uri : undefined;
206
+ if (typeof uri === "object" && uri) {
207
+ _uri = uri.previewUrl || uri.url;
139
208
  }
140
209
  if (typeof _uri !== "string") {
141
210
  return _uri;
142
211
  }
143
- _uri = _uri?.trim();
144
- // base64 直接返回
145
- if (isBase64Str(_uri)) {
212
+ _uri = _uri.trim();
213
+ if (!_uri) {
146
214
  return _uri;
147
215
  }
148
- const isUriPattern = /http[s]?:\/\/[^\s]+/.test(_uri);
149
- const query = conf?.query?.trim() ? "?" + conf?.query?.trim() : "";
150
-
151
- if (isUriPattern) {
152
- // 源地址为 http 地址,直接拼接 query 参数
153
- return `${_uri}${query}`;
154
- }
155
216
 
156
- if (conf) {
157
- // 拼接前缀
158
- return `${conf?.url?.trim()?.replace(/\/$/, "")}/${_uri?.trim()?.replace(/^\//, "")}${query}`?.trim();
217
+ const baseUrl = (conf?.url ?? conf?.baseUrl)?.trim?.() || "";
218
+ const searchStr = conf?.search?.trim() || "";
219
+ const searchParams = searchStr ? searchToObj(`?${searchStr}`) || {} : {};
220
+ const query = conf?.query && typeof conf.query === "object" ? conf.query : {};
221
+ const queryParams = { ...searchParams, ...query };
222
+ const hasQuery = Object.keys(queryParams).length > 0;
223
+
224
+ let result = _uri;
225
+ // 已是完整/特殊地址:不二次拼前缀,仅按需挂 query
226
+ if (!isAbsoluteOrSpecialUrl(_uri) && baseUrl) {
227
+ // 仅当配置了有效前缀时才拼接,避免 conf 为空对象时拼出 "undefined/xxx"
228
+ result = `${baseUrl.replace(/\/$/, "")}/${_uri.replace(/^\//, "")}`.trim();
159
229
  }
160
230
 
161
- return _uri;
231
+ return hasQuery ? addQuery(result, queryParams) : result;
162
232
  }