提交 | 用户 | 时间
|
820397
|
1 |
import { toNumber } from 'lodash-es' |
H |
2 |
|
|
3 |
/** |
|
4 |
* |
|
5 |
* @param component 需要注册的组件 |
|
6 |
* @param alias 组件别名 |
|
7 |
* @returns any |
|
8 |
*/ |
|
9 |
export const withInstall = <T>(component: T, alias?: string) => { |
|
10 |
const comp = component as any |
|
11 |
comp.install = (app: any) => { |
|
12 |
app.component(comp.name || comp.displayName, component) |
|
13 |
if (alias) { |
|
14 |
app.config.globalProperties[alias] = component |
|
15 |
} |
|
16 |
} |
|
17 |
return component as T & Plugin |
|
18 |
} |
|
19 |
|
|
20 |
/** |
|
21 |
* @param str 需要转下划线的驼峰字符串 |
|
22 |
* @returns 字符串下划线 |
|
23 |
*/ |
|
24 |
export const humpToUnderline = (str: string): string => { |
|
25 |
return str.replace(/([A-Z])/g, '-$1').toLowerCase() |
|
26 |
} |
|
27 |
|
|
28 |
/** |
|
29 |
* @param str 需要转驼峰的下划线字符串 |
|
30 |
* @returns 字符串驼峰 |
|
31 |
*/ |
|
32 |
export const underlineToHump = (str: string): string => { |
|
33 |
if (!str) return '' |
|
34 |
return str.replace(/\-(\w)/g, (_, letter: string) => { |
|
35 |
return letter.toUpperCase() |
|
36 |
}) |
|
37 |
} |
|
38 |
|
|
39 |
/** |
|
40 |
* 驼峰转横杠 |
|
41 |
*/ |
|
42 |
export const humpToDash = (str: string): string => { |
|
43 |
return str.replace(/([A-Z])/g, '-$1').toLowerCase() |
|
44 |
} |
|
45 |
|
|
46 |
export const setCssVar = (prop: string, val: any, dom = document.documentElement) => { |
|
47 |
dom.style.setProperty(prop, val) |
|
48 |
} |
|
49 |
|
|
50 |
/** |
|
51 |
* 查找数组对象的某个下标 |
|
52 |
* @param {Array} ary 查找的数组 |
|
53 |
* @param {Functon} fn 判断的方法 |
|
54 |
*/ |
|
55 |
// eslint-disable-next-line |
|
56 |
export const findIndex = <T = Recordable>(ary: Array<T>, fn: Fn): number => { |
|
57 |
if (ary.findIndex) { |
|
58 |
return ary.findIndex(fn) |
|
59 |
} |
|
60 |
let index = -1 |
|
61 |
ary.some((item: T, i: number, ary: Array<T>) => { |
|
62 |
const ret: T = fn(item, i, ary) |
|
63 |
if (ret) { |
|
64 |
index = i |
|
65 |
return ret |
|
66 |
} |
|
67 |
}) |
|
68 |
return index |
|
69 |
} |
|
70 |
|
|
71 |
export const trim = (str: string) => { |
|
72 |
return str.replace(/(^\s*)|(\s*$)/g, '') |
|
73 |
} |
|
74 |
|
|
75 |
/** |
|
76 |
* @param {Date | number | string} time 需要转换的时间 |
|
77 |
* @param {String} fmt 需要转换的格式 如 yyyy-MM-dd、yyyy-MM-dd HH:mm:ss |
|
78 |
*/ |
|
79 |
export function formatTime(time: Date | number | string, fmt: string) { |
|
80 |
if (!time) return '' |
|
81 |
else { |
|
82 |
const date = new Date(time) |
|
83 |
const o = { |
|
84 |
'M+': date.getMonth() + 1, |
|
85 |
'd+': date.getDate(), |
|
86 |
'H+': date.getHours(), |
|
87 |
'm+': date.getMinutes(), |
|
88 |
's+': date.getSeconds(), |
|
89 |
'q+': Math.floor((date.getMonth() + 3) / 3), |
|
90 |
S: date.getMilliseconds() |
|
91 |
} |
|
92 |
if (/(y+)/.test(fmt)) { |
|
93 |
fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length)) |
|
94 |
} |
|
95 |
for (const k in o) { |
|
96 |
if (new RegExp('(' + k + ')').test(fmt)) { |
|
97 |
fmt = fmt.replace( |
|
98 |
RegExp.$1, |
|
99 |
RegExp.$1.length === 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length) |
|
100 |
) |
|
101 |
} |
|
102 |
} |
|
103 |
return fmt |
|
104 |
} |
|
105 |
} |
|
106 |
|
|
107 |
/** |
|
108 |
* 生成随机字符串 |
|
109 |
*/ |
|
110 |
export function toAnyString() { |
|
111 |
const str: string = 'xxxxx-xxxxx-4xxxx-yxxxx-xxxxx'.replace(/[xy]/g, (c: string) => { |
|
112 |
const r: number = (Math.random() * 16) | 0 |
|
113 |
const v: number = c === 'x' ? r : (r & 0x3) | 0x8 |
|
114 |
return v.toString() |
|
115 |
}) |
|
116 |
return str |
|
117 |
} |
|
118 |
|
|
119 |
/** |
|
120 |
* 首字母大写 |
|
121 |
*/ |
|
122 |
export function firstUpperCase(str: string) { |
|
123 |
return str.toLowerCase().replace(/( |^)[a-z]/g, (L) => L.toUpperCase()) |
|
124 |
} |
|
125 |
|
|
126 |
export const generateUUID = () => { |
|
127 |
if (typeof crypto === 'object') { |
|
128 |
if (typeof crypto.randomUUID === 'function') { |
|
129 |
return crypto.randomUUID() |
|
130 |
} |
|
131 |
if (typeof crypto.getRandomValues === 'function' && typeof Uint8Array === 'function') { |
|
132 |
const callback = (c: any) => { |
|
133 |
const num = Number(c) |
|
134 |
return (num ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (num / 4)))).toString( |
|
135 |
16 |
|
136 |
) |
|
137 |
} |
|
138 |
return '10000000-1000-4000-8000-100000000000'.replace(/[018]/g, callback) |
|
139 |
} |
|
140 |
} |
|
141 |
let timestamp = new Date().getTime() |
|
142 |
let performanceNow = |
|
143 |
(typeof performance !== 'undefined' && performance.now && performance.now() * 1000) || 0 |
|
144 |
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { |
|
145 |
let random = Math.random() * 16 |
|
146 |
if (timestamp > 0) { |
|
147 |
random = (timestamp + random) % 16 | 0 |
|
148 |
timestamp = Math.floor(timestamp / 16) |
|
149 |
} else { |
|
150 |
random = (performanceNow + random) % 16 | 0 |
|
151 |
performanceNow = Math.floor(performanceNow / 16) |
|
152 |
} |
|
153 |
return (c === 'x' ? random : (random & 0x3) | 0x8).toString(16) |
|
154 |
}) |
|
155 |
} |
|
156 |
|
|
157 |
/** |
|
158 |
* element plus 的文件大小 Formatter 实现 |
|
159 |
* |
|
160 |
* @param row 行数据 |
|
161 |
* @param column 字段 |
|
162 |
* @param cellValue 字段值 |
|
163 |
*/ |
|
164 |
// @ts-ignore |
|
165 |
export const fileSizeFormatter = (row, column, cellValue) => { |
|
166 |
const fileSize = cellValue |
|
167 |
const unitArr = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] |
|
168 |
const srcSize = parseFloat(fileSize) |
|
169 |
const index = Math.floor(Math.log(srcSize) / Math.log(1024)) |
|
170 |
const size = srcSize / Math.pow(1024, index) |
|
171 |
const sizeStr = size.toFixed(2) //保留的小数位数 |
|
172 |
return sizeStr + ' ' + unitArr[index] |
|
173 |
} |
|
174 |
|
|
175 |
/** |
|
176 |
* 将值复制到目标对象,且以目标对象属性为准,例:target: {a:1} source:{a:2,b:3} 结果为:{a:2} |
|
177 |
* @param target 目标对象 |
|
178 |
* @param source 源对象 |
|
179 |
*/ |
|
180 |
export const copyValueToTarget = (target: any, source: any) => { |
|
181 |
const newObj = Object.assign({}, target, source) |
|
182 |
// 删除多余属性 |
|
183 |
Object.keys(newObj).forEach((key) => { |
|
184 |
// 如果不是target中的属性则删除 |
|
185 |
if (Object.keys(target).indexOf(key) === -1) { |
|
186 |
delete newObj[key] |
|
187 |
} |
|
188 |
}) |
|
189 |
// 更新目标对象值 |
|
190 |
Object.assign(target, newObj) |
|
191 |
} |
|
192 |
|
|
193 |
/** |
|
194 |
* 获取链接的参数值 |
|
195 |
* @param key 参数键名 |
|
196 |
* @param urlStr 链接地址,默认为当前浏览器的地址 |
|
197 |
*/ |
|
198 |
export const getUrlValue = (key: string, urlStr: string = location.href): string => { |
|
199 |
if (!urlStr || !key) return '' |
|
200 |
const url = new URL(decodeURIComponent(urlStr)) |
|
201 |
return url.searchParams.get(key) ?? '' |
|
202 |
} |
|
203 |
|
|
204 |
/** |
|
205 |
* 获取链接的参数值(值类型) |
|
206 |
* @param key 参数键名 |
|
207 |
* @param urlStr 链接地址,默认为当前浏览器的地址 |
|
208 |
*/ |
|
209 |
export const getUrlNumberValue = (key: string, urlStr: string = location.href): number => { |
|
210 |
return toNumber(getUrlValue(key, urlStr)) |
|
211 |
} |
|
212 |
|
|
213 |
/** |
|
214 |
* 构建排序字段 |
|
215 |
* @param prop 字段名称 |
|
216 |
* @param order 顺序 |
|
217 |
*/ |
|
218 |
export const buildSortingField = ({ prop, order }) => { |
|
219 |
return { field: prop, order: order === 'ascending' ? 'asc' : 'desc' } |
|
220 |
} |
|
221 |
|
|
222 |
// ========== NumberUtils 数字方法 ========== |
|
223 |
|
|
224 |
/** |
|
225 |
* 数组求和 |
|
226 |
* |
|
227 |
* @param values 数字数组 |
|
228 |
* @return 求和结果,默认为 0 |
|
229 |
*/ |
|
230 |
export const getSumValue = (values: number[]): number => { |
|
231 |
return values.reduce((prev, curr) => { |
|
232 |
const value = Number(curr) |
|
233 |
if (!Number.isNaN(value)) { |
|
234 |
return prev + curr |
|
235 |
} else { |
|
236 |
return prev |
|
237 |
} |
|
238 |
}, 0) |
|
239 |
} |
|
240 |
|
|
241 |
// ========== 通用金额方法 ========== |
|
242 |
|
|
243 |
/** |
|
244 |
* 将一个整数转换为分数保留两位小数 |
|
245 |
* @param num |
|
246 |
*/ |
|
247 |
export const formatToFraction = (num: number | string | undefined): string => { |
|
248 |
if (typeof num === 'undefined') return '0.00' |
|
249 |
const parsedNumber = typeof num === 'string' ? parseFloat(num) : num |
|
250 |
return (parsedNumber / 100.0).toFixed(2) |
|
251 |
} |
|
252 |
|
|
253 |
/** |
|
254 |
* 将一个数转换为 1.00 这样 |
|
255 |
* 数据呈现的时候使用 |
|
256 |
* |
|
257 |
* @param num 整数 |
|
258 |
*/ |
|
259 |
// TODO @芋艿:看看怎么融合掉 |
|
260 |
export const floatToFixed2 = (num: number | string | undefined): string => { |
|
261 |
let str = '0.00' |
|
262 |
if (typeof num === 'undefined') { |
|
263 |
return str |
|
264 |
} |
|
265 |
const f = formatToFraction(num) |
|
266 |
const decimalPart = f.toString().split('.')[1] |
|
267 |
const len = decimalPart ? decimalPart.length : 0 |
|
268 |
switch (len) { |
|
269 |
case 0: |
|
270 |
str = f.toString() + '.00' |
|
271 |
break |
|
272 |
case 1: |
|
273 |
str = f.toString() + '0' |
|
274 |
break |
|
275 |
case 2: |
|
276 |
str = f.toString() |
|
277 |
break |
|
278 |
} |
|
279 |
return str |
|
280 |
} |
|
281 |
|
|
282 |
/** |
|
283 |
* 将一个分数转换为整数 |
|
284 |
* @param num |
|
285 |
*/ |
|
286 |
// TODO @芋艿:看看怎么融合掉 |
|
287 |
export const convertToInteger = (num: number | string | undefined): number => { |
|
288 |
if (typeof num === 'undefined') return 0 |
|
289 |
const parsedNumber = typeof num === 'string' ? parseFloat(num) : num |
|
290 |
// TODO 分转元后还有小数则四舍五入 |
|
291 |
return Math.round(parsedNumber * 100) |
|
292 |
} |
|
293 |
|
|
294 |
/** |
|
295 |
* 元转分 |
|
296 |
*/ |
|
297 |
export const yuanToFen = (amount: string | number): number => { |
|
298 |
return convertToInteger(amount) |
|
299 |
} |
|
300 |
|
|
301 |
/** |
|
302 |
* 分转元 |
|
303 |
*/ |
|
304 |
export const fenToYuan = (price: string | number): string => { |
|
305 |
return formatToFraction(price) |
|
306 |
} |
|
307 |
|
|
308 |
/** |
|
309 |
* 计算环比 |
|
310 |
* |
|
311 |
* @param value 当前数值 |
|
312 |
* @param reference 对比数值 |
|
313 |
*/ |
|
314 |
export const calculateRelativeRate = (value?: number, reference?: number) => { |
|
315 |
// 防止除0 |
|
316 |
if (!reference || reference == 0) return 0 |
|
317 |
|
|
318 |
return ((100 * ((value || 0) - reference)) / reference).toFixed(0) |
|
319 |
} |
|
320 |
|
|
321 |
// ========== ERP 专属方法 ========== |
|
322 |
|
|
323 |
const ERP_COUNT_DIGIT = 3 |
|
324 |
const ERP_PRICE_DIGIT = 2 |
|
325 |
|
|
326 |
/** |
|
327 |
* 【ERP】格式化 Input 数字 |
|
328 |
* |
|
329 |
* 例如说:库存数量 |
|
330 |
* |
|
331 |
* @param num 数量 |
|
332 |
* @package digit 保留的小数位数 |
|
333 |
* @return 格式化后的数量 |
|
334 |
*/ |
|
335 |
export const erpNumberFormatter = (num: number | string | undefined, digit: number) => { |
|
336 |
if (num == null) { |
|
337 |
return '' |
|
338 |
} |
|
339 |
if (typeof num === 'string') { |
|
340 |
num = parseFloat(num) |
|
341 |
} |
|
342 |
// 如果非 number,则直接返回空串 |
|
343 |
if (isNaN(num)) { |
|
344 |
return '' |
|
345 |
} |
|
346 |
return num.toFixed(digit) |
|
347 |
} |
|
348 |
|
|
349 |
/** |
|
350 |
* 【ERP】格式化数量,保留三位小数 |
|
351 |
* |
|
352 |
* 例如说:库存数量 |
|
353 |
* |
|
354 |
* @param num 数量 |
|
355 |
* @return 格式化后的数量 |
|
356 |
*/ |
|
357 |
export const erpCountInputFormatter = (num: number | string | undefined) => { |
|
358 |
return erpNumberFormatter(num, ERP_COUNT_DIGIT) |
|
359 |
} |
|
360 |
|
|
361 |
// noinspection JSCommentMatchesSignature |
|
362 |
/** |
|
363 |
* 【ERP】格式化数量,保留三位小数 |
|
364 |
* |
|
365 |
* @param cellValue 数量 |
|
366 |
* @return 格式化后的数量 |
|
367 |
*/ |
|
368 |
export const erpCountTableColumnFormatter = (_, __, cellValue: any, ___) => { |
|
369 |
return erpNumberFormatter(cellValue, ERP_COUNT_DIGIT) |
|
370 |
} |
|
371 |
|
|
372 |
/** |
|
373 |
* 【ERP】格式化金额,保留二位小数 |
|
374 |
* |
|
375 |
* 例如说:库存数量 |
|
376 |
* |
|
377 |
* @param num 数量 |
|
378 |
* @return 格式化后的数量 |
|
379 |
*/ |
|
380 |
export const erpPriceInputFormatter = (num: number | string | undefined) => { |
|
381 |
return erpNumberFormatter(num, ERP_PRICE_DIGIT) |
|
382 |
} |
|
383 |
|
|
384 |
// noinspection JSCommentMatchesSignature |
|
385 |
/** |
|
386 |
* 【ERP】格式化金额,保留二位小数 |
|
387 |
* |
|
388 |
* @param cellValue 数量 |
|
389 |
* @return 格式化后的数量 |
|
390 |
*/ |
|
391 |
export const erpPriceTableColumnFormatter = (_, __, cellValue: any, ___) => { |
|
392 |
return erpNumberFormatter(cellValue, ERP_PRICE_DIGIT) |
|
393 |
} |
|
394 |
|
|
395 |
/** |
|
396 |
* 【ERP】价格计算,四舍五入保留两位小数 |
|
397 |
* |
|
398 |
* @param price 价格 |
|
399 |
* @param count 数量 |
|
400 |
* @return 总价格。如果有任一为空,则返回 undefined |
|
401 |
*/ |
|
402 |
export const erpPriceMultiply = (price: number, count: number) => { |
|
403 |
if (price == null || count == null) { |
|
404 |
return undefined |
|
405 |
} |
|
406 |
return parseFloat((price * count).toFixed(ERP_PRICE_DIGIT)) |
|
407 |
} |
|
408 |
|
|
409 |
/** |
|
410 |
* 【ERP】百分比计算,四舍五入保留两位小数 |
|
411 |
* |
|
412 |
* 如果 total 为 0,则返回 0 |
|
413 |
* |
|
414 |
* @param value 当前值 |
|
415 |
* @param total 总值 |
|
416 |
*/ |
|
417 |
export const erpCalculatePercentage = (value: number, total: number) => { |
|
418 |
if (total === 0) return 0 |
|
419 |
return ((value / total) * 100).toFixed(2) |
|
420 |
} |
|
421 |
|
|
422 |
/** |
|
423 |
* 适配 echarts map 的地名 |
|
424 |
* |
|
425 |
* @param areaName 地区名称 |
|
426 |
*/ |
|
427 |
export const areaReplace = (areaName: string) => { |
|
428 |
if (!areaName) { |
|
429 |
return areaName |
|
430 |
} |
|
431 |
return areaName |
|
432 |
.replace('维吾尔自治区', '') |
|
433 |
.replace('壮族自治区', '') |
|
434 |
.replace('回族自治区', '') |
|
435 |
.replace('自治区', '') |
|
436 |
.replace('省', '') |
|
437 |
} |
|
438 |
|
|
439 |
/** |
|
440 |
* 解析 JSON 字符串 |
|
441 |
* |
|
442 |
* @param str |
|
443 |
*/ |
|
444 |
export function jsonParse(str: string) { |
|
445 |
try { |
|
446 |
return JSON.parse(str) |
|
447 |
} catch (e) { |
|
448 |
console.error(`str[${str}] 不是一个 JSON 字符串`) |
|
449 |
return '' |
|
450 |
} |
|
451 |
} |