mazey 1.4.38 → 1.4.39
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/LICENSE +21 -21
- package/README.md +334 -316
- package/lib/index.cjs.js +61 -1
- package/lib/index.d.ts +10 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.esm.js +61 -2
- package/package.json +85 -85
- package/src/index.ts +1642 -1619
- package/src/js/data-structure/MSet.js +89 -89
- package/src/js/polyfill.js +63 -63
- package/src/js/service/common.js +4 -4
- package/src/scss/extend/index.scss +148 -148
- package/src/scss/index.scss +4 -4
- package/src/scss/mixin/index.scss +41 -41
- package/src/scss/variate/index.scss +27 -27
- package/src/test.html +33 -33
- package/src/test.ts +52 -26
- package/tsconfig.json +73 -73
- package/typing.d.ts +3 -3
package/src/index.ts
CHANGED
|
@@ -1,1619 +1,1642 @@
|
|
|
1
|
-
/* Deal */
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @method calLongestCommonSubstring
|
|
5
|
-
* @description 计算两个字符串的最长公共子串
|
|
6
|
-
* @param {String} aStr 字符串
|
|
7
|
-
* @param {String} bStr 字符串
|
|
8
|
-
* @return {Number} 长度
|
|
9
|
-
*/
|
|
10
|
-
export function calLongestCommonSubstring(aStr: any, bStr: any) {
|
|
11
|
-
const aLen = aStr.length;
|
|
12
|
-
const bLen = bStr.length;
|
|
13
|
-
// 创建二维数组并且深拷贝
|
|
14
|
-
const arr = deepCopyObject(new Array(aLen).fill(new Array(bLen).fill(0)));
|
|
15
|
-
for (let i = 0; i < aLen; ++i) {
|
|
16
|
-
for (let j = 0; j < bLen; ++j) {
|
|
17
|
-
if (aStr[i] === bStr[j]) {
|
|
18
|
-
let baseNum = 0;
|
|
19
|
-
if (i > 0 && j > 0) {
|
|
20
|
-
baseNum = arr[i - 1][j - 1];
|
|
21
|
-
}
|
|
22
|
-
arr[i][j] = baseNum + 1;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
// 二维数组转一维数组
|
|
27
|
-
const arr1 = Array.prototype.concat.apply([], arr);
|
|
28
|
-
// 获取最长公共子串
|
|
29
|
-
const maxLong = Math.max(...arr1);
|
|
30
|
-
return maxLong;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* @method calLongestCommonSubsequence
|
|
35
|
-
* @description 计算两个字符串的最长公共子序列
|
|
36
|
-
* @param {String} aStr 字符串
|
|
37
|
-
* @param {String} bStr 字符串
|
|
38
|
-
* @return {Number} 长度
|
|
39
|
-
*/
|
|
40
|
-
export function calLongestCommonSubsequence(aStr: any, bStr: any) {
|
|
41
|
-
const aLen = aStr.length;
|
|
42
|
-
const bLen = bStr.length;
|
|
43
|
-
// 创建二维数组并且深拷贝
|
|
44
|
-
const arr = deepCopyObject(new Array(aLen).fill(new Array(bLen).fill(0)));
|
|
45
|
-
for (let i = 0; i < aLen; ++i) {
|
|
46
|
-
for (let j = 0; j < bLen; ++j) {
|
|
47
|
-
if (aStr[i] === bStr[j]) {
|
|
48
|
-
let baseNum = 0;
|
|
49
|
-
if (i > 0 && j > 0) {
|
|
50
|
-
baseNum = arr[i - 1][j - 1];
|
|
51
|
-
}
|
|
52
|
-
arr[i][j] = baseNum + 1;
|
|
53
|
-
} else {
|
|
54
|
-
let [leftValue, topValue] = [0, 0];
|
|
55
|
-
if (j > 0) {
|
|
56
|
-
leftValue = arr[i][j - 1];
|
|
57
|
-
}
|
|
58
|
-
if (i > 0) {
|
|
59
|
-
topValue = arr[i - 1][j];
|
|
60
|
-
}
|
|
61
|
-
arr[i][j] = Math.max(leftValue, topValue);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
// 二维数组转一维数组
|
|
66
|
-
const arr1 = Array.prototype.concat.apply([], arr);
|
|
67
|
-
// 获取最长公共子串
|
|
68
|
-
const maxLong = Math.max(...arr1);
|
|
69
|
-
return maxLong;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* @method join
|
|
74
|
-
* @description 将一系列值连接成固定字符分隔的字符串 123,456 => 123 - 456。
|
|
75
|
-
* @param {String} joinStr 连接值的字符串。
|
|
76
|
-
* @param {Rest} ...rest 需要连接的值 。
|
|
77
|
-
* */
|
|
78
|
-
export function join(joinStr: any, ...rest: any[]) {
|
|
79
|
-
let ret = '';
|
|
80
|
-
const len = joinStr.length;
|
|
81
|
-
for (const v of rest) {
|
|
82
|
-
if (v) {
|
|
83
|
-
ret += `${joinStr}${v}`;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
if (ret) {
|
|
87
|
-
ret = ret.substring(len);
|
|
88
|
-
}
|
|
89
|
-
return ret;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* @method renderTable
|
|
94
|
-
* @description 渲染表格
|
|
95
|
-
* @param {DOM Object} tbID
|
|
96
|
-
* @param {Array} data
|
|
97
|
-
* @param {Array} property
|
|
98
|
-
*/
|
|
99
|
-
export function renderTable(tbID = null, data = [], property = []) {
|
|
100
|
-
const TBODY: any = document.querySelector(`#${tbID} tbody`);
|
|
101
|
-
function mNullToNA(str: any) {
|
|
102
|
-
return str === null ? 'N.A.' : str;
|
|
103
|
-
}
|
|
104
|
-
let content = '';
|
|
105
|
-
TBODY.innerHTML = '';
|
|
106
|
-
// 无数据
|
|
107
|
-
if (!data.length) {
|
|
108
|
-
content = `<tr><td colspan="${property.length}">无数据</td></tr>`;
|
|
109
|
-
} else {
|
|
110
|
-
for (let [i, max] = [0, data.length]; i < max; ++i) {
|
|
111
|
-
const item = data[i];
|
|
112
|
-
content += '<tr>';
|
|
113
|
-
for (let [i, max] = [0, property.length]; i < max; ++i) {
|
|
114
|
-
content += `<td>${mNullToNA(item[property[i]])}</td>`;
|
|
115
|
-
}
|
|
116
|
-
content += '</tr>';
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
TBODY.innerHTML = content;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* @method getHashQueryParam
|
|
124
|
-
* @description 获取地址栏 hash 后面的参数。
|
|
125
|
-
* @param {String} param 获取参数的名字。
|
|
126
|
-
*/
|
|
127
|
-
export function getHashQueryParam(param: string) {
|
|
128
|
-
const hashs = window.location.hash.split('?');
|
|
129
|
-
if (hashs.length === 1) {
|
|
130
|
-
return null;
|
|
131
|
-
}
|
|
132
|
-
const reg = new RegExp(`(^|&)${param}=([^&]*)(&|$)`);
|
|
133
|
-
const ret = hashs[1].match(reg);
|
|
134
|
-
return ret ? ret[2] : null;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* @method camelCaseToKebabCase
|
|
139
|
-
* @description 驼峰转连接线。
|
|
140
|
-
* @param {String} camelCase
|
|
141
|
-
* */
|
|
142
|
-
export function camelCaseToKebabCase(camelCase: string) {
|
|
143
|
-
const kebabCase = camelCase.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
144
|
-
return kebabCase[0] === '-' ? kebabCase.substr(1) : kebabCase;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* @method camelCase2Underscore
|
|
149
|
-
* @description 驼峰转下划线。
|
|
150
|
-
* @param {String} camelCase
|
|
151
|
-
* */
|
|
152
|
-
export function camelCase2Underscore(camelCase: string) {
|
|
153
|
-
const kebabCase = camelCase.replace(/([A-Z])/g, '_$1').toLowerCase();
|
|
154
|
-
return kebabCase[0] === '_' ? kebabCase.substr(1) : kebabCase;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* @method getDomain
|
|
159
|
-
* @description 获取地址中的域名(及其他参数)。
|
|
160
|
-
* @param {String} url
|
|
161
|
-
* @param {Array} rules ['hostname', 'pathname'] = 'km.mazey.net/plugins/servlet/mobile'
|
|
162
|
-
* */
|
|
163
|
-
export function getDomain({ url = '', rules = ['hostname'] } = {}) {
|
|
164
|
-
const aEl: any = document.createElement('a');
|
|
165
|
-
aEl.href = url;
|
|
166
|
-
return rules.reduce((ret, v, index) => {
|
|
167
|
-
ret += aEl[v];
|
|
168
|
-
return ret;
|
|
169
|
-
}, '');
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* @method trim
|
|
174
|
-
* @description 去除左右空格。
|
|
175
|
-
* @param {String} str 需要去除两边空格的字符串。
|
|
176
|
-
* */
|
|
177
|
-
export function trim(str: string) {
|
|
178
|
-
str = str.replace(/^\s+/, ''); // 去除头部空格
|
|
179
|
-
let end = str.length - 1;
|
|
180
|
-
const ws = /\s/;
|
|
181
|
-
while (ws.test(str.charAt(end))) {
|
|
182
|
-
end--; // 最后一个非空格字符的索引
|
|
183
|
-
}
|
|
184
|
-
return str.slice(0, end + 1);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* @method newLine
|
|
189
|
-
* @description html换行。
|
|
190
|
-
* @param {String} str
|
|
191
|
-
* */
|
|
192
|
-
export function newLine(str: string) {
|
|
193
|
-
if (!str) {
|
|
194
|
-
return '';
|
|
195
|
-
}
|
|
196
|
-
const reg = new RegExp('\\n', 'g');
|
|
197
|
-
return str.replace(reg, '<br />');
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* @method deepCopyObject
|
|
202
|
-
* @description 对象深拷贝。
|
|
203
|
-
* @param {Object} obj 被拷贝的对象。
|
|
204
|
-
* @return {Object}
|
|
205
|
-
* */
|
|
206
|
-
export function deepCopyObject(obj: any) {
|
|
207
|
-
return JSON.parse(JSON.stringify(obj));
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* @method generateRndNum
|
|
212
|
-
* @description 生成随机数 mGenerateRndNum(7) => 7658495。
|
|
213
|
-
* @param {Number} n 随机数的长度
|
|
214
|
-
* @return {String}
|
|
215
|
-
* */
|
|
216
|
-
export function generateRndNum(n: number) {
|
|
217
|
-
let ret = '';
|
|
218
|
-
while (n--) {
|
|
219
|
-
ret += Math.floor(Math.random() * 10);
|
|
220
|
-
}
|
|
221
|
-
return ret;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* @method generateUniqueNum
|
|
226
|
-
* @description 根据时间生成唯一标志的数字 mGenerateUniqueNum() => 1538324722364123。
|
|
227
|
-
* @param {Number} n 随机数的长度
|
|
228
|
-
* */
|
|
229
|
-
export function generateUniqueNum(n: number) {
|
|
230
|
-
const [now, rnd] = [mNow(), generateRndNum(n || 3)];
|
|
231
|
-
return now + rnd;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* @method resetForm
|
|
236
|
-
* @description 重置表单输入值为原始(空)状态。
|
|
237
|
-
* @param {String} rest name1,name2,name3...NAME属性,可以多个。
|
|
238
|
-
* */
|
|
239
|
-
export function resetForm(...rest: any[]) {
|
|
240
|
-
for (let i = 0; i < rest.length; i++) {
|
|
241
|
-
const tagMz: any = document.getElementsByName(rest[i])[0]; // tag object
|
|
242
|
-
const tagNameMz = tagMz.tagName.toLowerCase(); // tag name
|
|
243
|
-
if (tagNameMz === 'input') {
|
|
244
|
-
tagMz.value = '';
|
|
245
|
-
} else if (tagNameMz === 'select') {
|
|
246
|
-
tagMz.options[0].selected = true;
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* @method floatToPercent
|
|
253
|
-
* @description 浮点数转为百分比 0.2 => 20%。
|
|
254
|
-
* @param {Number} num 浮点数
|
|
255
|
-
* @param {Number} fixSize 保留几位浮点数
|
|
256
|
-
* */
|
|
257
|
-
export function floatToPercent(num: any, fixSize = 0): string {
|
|
258
|
-
if (fixSize) {
|
|
259
|
-
num = (num * 100).toFixed(fixSize);
|
|
260
|
-
} else {
|
|
261
|
-
num = Math.floor(num * 100);
|
|
262
|
-
}
|
|
263
|
-
return `${num}%`;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* @method floatFixed
|
|
268
|
-
* @description 浮点数保留指定位。
|
|
269
|
-
* */
|
|
270
|
-
export function floatFixed(num: any, size: any) {
|
|
271
|
-
return parseFloat(num).toFixed(size);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
/**
|
|
275
|
-
* @method compatibleExist
|
|
276
|
-
* @description 不存在返回 ——。
|
|
277
|
-
* */
|
|
278
|
-
export function compatibleExist(instance: string, replaceStr: string) {
|
|
279
|
-
let ret = '';
|
|
280
|
-
try {
|
|
281
|
-
ret = instance || replaceStr || '——';
|
|
282
|
-
} catch (e) {
|
|
283
|
-
ret = '——';
|
|
284
|
-
}
|
|
285
|
-
return ret;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
/**
|
|
289
|
-
* @method cancelBubble
|
|
290
|
-
* @description 阻止冒泡。
|
|
291
|
-
* */
|
|
292
|
-
export function cancelBubble(e: any) {
|
|
293
|
-
const ev = e || window.event;
|
|
294
|
-
if (ev.stopPropagation) {
|
|
295
|
-
// W3C
|
|
296
|
-
ev.stopPropagation();
|
|
297
|
-
} else {
|
|
298
|
-
// IE
|
|
299
|
-
ev.cancelBubble = true;
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* @method hasClass
|
|
305
|
-
* */
|
|
306
|
-
export function hasClass(obj: any, cls: string) {
|
|
307
|
-
const oriCls = obj.className; // 获取对象的class值
|
|
308
|
-
const oriClsArr = oriCls.split(/\s+/); // 分隔空格转换成数组
|
|
309
|
-
for (let i = 0; i < oriClsArr.length; i++) {
|
|
310
|
-
if (oriClsArr[i] === cls) {
|
|
311
|
-
return true; // 若匹配到class则返回True
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
return false; // 否则返回False
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
* @method addClass
|
|
319
|
-
* */
|
|
320
|
-
export function addClass(obj: any, cls: string) {
|
|
321
|
-
const oriCls = obj.className;
|
|
322
|
-
let space = '';
|
|
323
|
-
let newCls = ''; // 获取对象的class值
|
|
324
|
-
if (oriCls !== '') {
|
|
325
|
-
space = ' '; // 若原来的class不为空,跟一个空格
|
|
326
|
-
}
|
|
327
|
-
newCls = oriCls + space + cls; // 将新的class加进去
|
|
328
|
-
obj.className = newCls; // 替换新class
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
/**
|
|
332
|
-
* @method removeClass
|
|
333
|
-
* */
|
|
334
|
-
export function removeClass(obj: any, cls: string) {
|
|
335
|
-
const oriCls = obj.className;
|
|
336
|
-
let newCls; // 获取对象的class值
|
|
337
|
-
newCls = ' ' + oriCls + ' '; // 前后加空格
|
|
338
|
-
newCls = newCls.replace(/(\s+)/gi, ' '); // 将多余的空格替换成一个空格
|
|
339
|
-
newCls = newCls.replace(' ' + cls + ' ', ' '); // 将加了前后空格的cls替换成空格' '
|
|
340
|
-
newCls = newCls.replace(/(^\s+)|(\s+$)/g, ''); // 去掉前后空格
|
|
341
|
-
obj.className = newCls;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
/**
|
|
345
|
-
* @method throttle
|
|
346
|
-
* @description 节流。
|
|
347
|
-
* */
|
|
348
|
-
export function throttle(func: any, wait: number, options: any) {
|
|
349
|
-
// timeout: setTimeout Handle
|
|
350
|
-
// previous: 上次时间戳
|
|
351
|
-
let context: any = null;
|
|
352
|
-
let args: any = null;
|
|
353
|
-
let timeout: any = null;
|
|
354
|
-
let [result, previous] = [null, 0];
|
|
355
|
-
if (!options) {
|
|
356
|
-
options = {};
|
|
357
|
-
}
|
|
358
|
-
const later = function () {
|
|
359
|
-
previous = options.leading === false ? 0 : mNow();
|
|
360
|
-
timeout = null;
|
|
361
|
-
result = func.apply(context, args);
|
|
362
|
-
if (!timeout) {
|
|
363
|
-
context = args = null;
|
|
364
|
-
}
|
|
365
|
-
};
|
|
366
|
-
return function (...argRest: Array<any>) {
|
|
367
|
-
const now = mNow();
|
|
368
|
-
if (!previous && options.leading === false) {
|
|
369
|
-
previous = now;
|
|
370
|
-
}
|
|
371
|
-
const remaining = wait - (now - previous);
|
|
372
|
-
context = this;
|
|
373
|
-
args = argRest;
|
|
374
|
-
if (remaining <= 0 || remaining > wait) {
|
|
375
|
-
if (timeout) {
|
|
376
|
-
clearTimeout(timeout);
|
|
377
|
-
timeout = null;
|
|
378
|
-
}
|
|
379
|
-
previous = now;
|
|
380
|
-
result = func.apply(context, args);
|
|
381
|
-
if (!timeout) {
|
|
382
|
-
context = args = null;
|
|
383
|
-
}
|
|
384
|
-
} else if (!timeout && options.trailing !== false) {
|
|
385
|
-
timeout = setTimeout(later, remaining);
|
|
386
|
-
}
|
|
387
|
-
return result;
|
|
388
|
-
};
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
/**
|
|
392
|
-
* @method debounce
|
|
393
|
-
* @description 去抖。
|
|
394
|
-
* */
|
|
395
|
-
export function debounce(func: any, wait: number, immediate: any) {
|
|
396
|
-
let context: any = null;
|
|
397
|
-
let timeout: any = null;
|
|
398
|
-
let timestamp: any = null;
|
|
399
|
-
let args: any = null;
|
|
400
|
-
let [result] = [null];
|
|
401
|
-
const later = function () {
|
|
402
|
-
const last = mNow() - timestamp;
|
|
403
|
-
if (last < wait && last >= 0) {
|
|
404
|
-
timeout = setTimeout(later, wait - last);
|
|
405
|
-
} else {
|
|
406
|
-
timeout = null;
|
|
407
|
-
if (!immediate) {
|
|
408
|
-
result = func.apply(context, args);
|
|
409
|
-
if (!timeout) {
|
|
410
|
-
context = args = null;
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
};
|
|
415
|
-
return function (...argRest: Array<any>) {
|
|
416
|
-
context = this;
|
|
417
|
-
args = argRest;
|
|
418
|
-
timestamp = mNow();
|
|
419
|
-
const callNow = immediate && !timeout;
|
|
420
|
-
if (!timeout) {
|
|
421
|
-
timeout = setTimeout(later, wait);
|
|
422
|
-
}
|
|
423
|
-
if (callNow) {
|
|
424
|
-
result = func.apply(context, args);
|
|
425
|
-
context = args = null;
|
|
426
|
-
}
|
|
427
|
-
return result;
|
|
428
|
-
};
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
/**
|
|
432
|
-
* @method friendlyInterval
|
|
433
|
-
* @description 获取间隔时间。
|
|
434
|
-
* @param {Number/Date} start 开始时间戳 1585325367122
|
|
435
|
-
* @param {Number/Date} end 结束时间戳 1585325367122
|
|
436
|
-
* @type {String} type 返回类型 d: 2(天) text: 2 天 4 时...
|
|
437
|
-
* @return {String/Number} 取决于 type
|
|
438
|
-
* */
|
|
439
|
-
export function friendlyInterval({ start = 0, end = 0, type = 'd' } = {}) {
|
|
440
|
-
if (!isNumber(start)) start = new Date(start).getTime();
|
|
441
|
-
if (!isNumber(end)) end = new Date(end).getTime();
|
|
442
|
-
const t = end - start;
|
|
443
|
-
let ret = null;
|
|
444
|
-
let [d, h, m, s] = new Array(4).fill(0);
|
|
445
|
-
if (t >= 0) {
|
|
446
|
-
d = Math.floor(t / 1000 / 60 / 60 / 24);
|
|
447
|
-
h = Math.floor(t / 1000 / 60 / 60);
|
|
448
|
-
m = Math.floor(t / 1000 / 60);
|
|
449
|
-
s = Math.floor(t / 1000);
|
|
450
|
-
switch (type) {
|
|
451
|
-
case 'd':
|
|
452
|
-
ret = d;
|
|
453
|
-
break;
|
|
454
|
-
case 'text':
|
|
455
|
-
d = Math.floor(t / 1000 / 60 / 60 / 24);
|
|
456
|
-
h = Math.floor((t / 1000 / 60 / 60) % 24);
|
|
457
|
-
m = Math.floor((t / 1000 / 60) % 60);
|
|
458
|
-
s = Math.floor((t / 1000) % 60);
|
|
459
|
-
ret = d + ' 天 ' + h + ' 时 ' + m + ' 分 ' + s + ' 秒';
|
|
460
|
-
break;
|
|
461
|
-
default:
|
|
462
|
-
ret = s;
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
return ret;
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
/**
|
|
469
|
-
* @method isNumber
|
|
470
|
-
* @description 判断是否有效数字
|
|
471
|
-
* @param {Any} num 被判断的值
|
|
472
|
-
* @param {Boolean} isNaNAsNumber 是否 NaN 算数字(默认不算)
|
|
473
|
-
* @param {Boolean} isUnFiniteAsNumber 是否 无限 算数字(默认不算)
|
|
474
|
-
* @return {Boolean} true 是数字
|
|
475
|
-
*/
|
|
476
|
-
export function isNumber(num: any, { isNaNAsNumber = false, isUnFiniteAsNumber = false } = {}): boolean {
|
|
477
|
-
let ret = true;
|
|
478
|
-
// 数字类型
|
|
479
|
-
if (typeof num !== 'number') {
|
|
480
|
-
ret = false;
|
|
481
|
-
}
|
|
482
|
-
// 无限值
|
|
483
|
-
if (isUnFiniteAsNumber === false && !isFinite(num)) {
|
|
484
|
-
// console.log('1333');
|
|
485
|
-
ret = false;
|
|
486
|
-
}
|
|
487
|
-
// NaN
|
|
488
|
-
if (isNaNAsNumber === false && isNaN(num)) {
|
|
489
|
-
// console.log('2333');
|
|
490
|
-
ret = false;
|
|
491
|
-
}
|
|
492
|
-
return ret;
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
/**
|
|
496
|
-
* @method updateQueryStringParameter
|
|
497
|
-
* @description 替换或添加地址栏参数。
|
|
498
|
-
* */
|
|
499
|
-
export function updateQueryStringParameter(uri: string, key: string, value: string) {
|
|
500
|
-
const re = new RegExp('([?&])' + key + '=.*?(&|$)', 'i');
|
|
501
|
-
const separator = uri.indexOf('?') !== -1 ? '&' : '?';
|
|
502
|
-
if (uri.match(re)) {
|
|
503
|
-
return uri.replace(re, '$1' + key + '=' + value + '$2');
|
|
504
|
-
} else {
|
|
505
|
-
return uri + separator + key + '=' + value;
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
/**
|
|
510
|
-
* @method isJsonString
|
|
511
|
-
* @description 判断是否合法 JSON 字符串。
|
|
512
|
-
* */
|
|
513
|
-
export function isJsonString(str: string) {
|
|
514
|
-
try {
|
|
515
|
-
if (typeof JSON.parse(str) === 'object') {
|
|
516
|
-
return true;
|
|
517
|
-
}
|
|
518
|
-
} catch (e) {
|
|
519
|
-
/* pass */
|
|
520
|
-
}
|
|
521
|
-
return false;
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
/**
|
|
525
|
-
* @method getUrlParam
|
|
526
|
-
* @description 链接参数
|
|
527
|
-
* @param {String} sUrl 链接
|
|
528
|
-
* @param {String} sKey 参数
|
|
529
|
-
* */
|
|
530
|
-
export function getUrlParam(sUrl: string, sKey: string) {
|
|
531
|
-
const result: any = {};
|
|
532
|
-
sUrl.replace(/\??(\w+)=([^&]*)&?/g, function (a, k, v): any {
|
|
533
|
-
if (result[k] !== undefined) {
|
|
534
|
-
const t = result[k];
|
|
535
|
-
result[k] = [].concat(t, v);
|
|
536
|
-
} else {
|
|
537
|
-
result[k] = v;
|
|
538
|
-
}
|
|
539
|
-
});
|
|
540
|
-
if (sKey === undefined) {
|
|
541
|
-
return result;
|
|
542
|
-
} else {
|
|
543
|
-
return result[sKey] || '';
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
/**
|
|
548
|
-
* @method getSearchQueryParam
|
|
549
|
-
* @description 地址栏参数。
|
|
550
|
-
* */
|
|
551
|
-
export function getSearchQueryParam(name: string) {
|
|
552
|
-
const reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)');
|
|
553
|
-
const r = window.location.search.substr(1).match(reg);
|
|
554
|
-
if (r != null) {
|
|
555
|
-
return decodeURIComponent(unescape(r[2]));
|
|
556
|
-
}
|
|
557
|
-
return null;
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
/**
|
|
561
|
-
* @method getQueryParam
|
|
562
|
-
* @description 地址栏参数,getSearchQueryParam 的别名。
|
|
563
|
-
* */
|
|
564
|
-
export function getQueryParam(name: string) {
|
|
565
|
-
return getSearchQueryParam(name);
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
/**
|
|
569
|
-
* @method doFn
|
|
570
|
-
* @description 执行有效函数
|
|
571
|
-
* @param {Function} fn 等待被执行的未知是否有效的函数
|
|
572
|
-
* */
|
|
573
|
-
export function doFn(fn: any, ...params: any[]) {
|
|
574
|
-
let ret = null;
|
|
575
|
-
if (fn && typeof fn === 'function') {
|
|
576
|
-
ret = fn(...params);
|
|
577
|
-
}
|
|
578
|
-
return ret;
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
/**
|
|
582
|
-
* @method setSessionStorage
|
|
583
|
-
* @description 存储数据到 sessionStorage
|
|
584
|
-
* @param {String} key 键
|
|
585
|
-
* @param {String} value 值
|
|
586
|
-
* */
|
|
587
|
-
export function setSessionStorage(key: string, value: any = null) {
|
|
588
|
-
if (key) {
|
|
589
|
-
sessionStorage.setItem(key, JSON.stringify(value));
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
/**
|
|
594
|
-
* @method getSessionStorage
|
|
595
|
-
* @description 存储数据到 sessionStorage
|
|
596
|
-
* @param {String} key 键
|
|
597
|
-
* @return {Any} 返回值
|
|
598
|
-
* */
|
|
599
|
-
export function getSessionStorage(key: string) {
|
|
600
|
-
let ret = null;
|
|
601
|
-
if (key) {
|
|
602
|
-
const value = sessionStorage.getItem(key);
|
|
603
|
-
if (value) {
|
|
604
|
-
ret = JSON.parse(value);
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
return ret;
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
/**
|
|
611
|
-
* @method setLocalStorage
|
|
612
|
-
* @description 存储数据到 localStorage
|
|
613
|
-
* @param {String} key 键
|
|
614
|
-
* @param {String} value 值
|
|
615
|
-
* */
|
|
616
|
-
export function setLocalStorage(key: string, value: any = null) {
|
|
617
|
-
if (key) {
|
|
618
|
-
localStorage.setItem(key, JSON.stringify(value));
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
/**
|
|
623
|
-
* @method getLocalStorage
|
|
624
|
-
* @description 存储数据到 localStorage
|
|
625
|
-
* @param {String} key 键
|
|
626
|
-
* @return {Any} 返回值
|
|
627
|
-
* */
|
|
628
|
-
export function getLocalStorage(key: string) {
|
|
629
|
-
let ret = null;
|
|
630
|
-
if (key) {
|
|
631
|
-
const value = localStorage.getItem(key);
|
|
632
|
-
if (value) {
|
|
633
|
-
ret = JSON.parse(value);
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
return ret;
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
/**
|
|
640
|
-
* @method loadCSS
|
|
641
|
-
* @description 动态加载css文件
|
|
642
|
-
* @param {String} url -- css资源路径
|
|
643
|
-
* @param {String} id -- link标签id
|
|
644
|
-
* @return {Promise<Boolean>} true -- 加载成功
|
|
645
|
-
*/
|
|
646
|
-
export function loadCSS({ url = '', id = '' } = {}) {
|
|
647
|
-
let success: any = null;
|
|
648
|
-
let fail: any = null;
|
|
649
|
-
const status = new Promise((resolve, reject) => {
|
|
650
|
-
([success, fail] = [resolve, reject]);
|
|
651
|
-
});
|
|
652
|
-
// const tempCB = (typeof callback === 'function' ? callback : function () { });
|
|
653
|
-
const callback = function () {
|
|
654
|
-
doFn(success, true);
|
|
655
|
-
};
|
|
656
|
-
let node: any = document.createElement('link');
|
|
657
|
-
const supportOnload = 'onload' in node;
|
|
658
|
-
const isOldWebKit = +navigator.userAgent.replace(/.*(?:AppleWebKit|AndroidWebKit)\/?(\d+).*/i, '$1') < 536; // webkit旧内核做特殊处理
|
|
659
|
-
const protectNum = 300000; // 阈值10分钟,一秒钟执行pollCss 500次
|
|
660
|
-
|
|
661
|
-
node.rel = 'stylesheet';
|
|
662
|
-
node.type = 'text/css';
|
|
663
|
-
node.href = url;
|
|
664
|
-
if (typeof id !== 'undefined') {
|
|
665
|
-
node.id = id;
|
|
666
|
-
}
|
|
667
|
-
document.getElementsByTagName('head')[0].appendChild(node);
|
|
668
|
-
|
|
669
|
-
// for Old WebKit and Old Firefox
|
|
670
|
-
if (isOldWebKit || !supportOnload) {
|
|
671
|
-
// Begin after node insertion
|
|
672
|
-
setTimeout(function () {
|
|
673
|
-
pollCss(node, callback, 0);
|
|
674
|
-
}, 1);
|
|
675
|
-
return status;
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
if (supportOnload) {
|
|
679
|
-
node.onload = onload;
|
|
680
|
-
node.onerror = function () {
|
|
681
|
-
// 加载失败(404)
|
|
682
|
-
onload();
|
|
683
|
-
};
|
|
684
|
-
} else {
|
|
685
|
-
// todo: 和 !supportOnload 重复
|
|
686
|
-
node.onreadystatechange = function () {
|
|
687
|
-
if (/loaded|complete/.test(node.readyState)) {
|
|
688
|
-
onload();
|
|
689
|
-
}
|
|
690
|
-
};
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
function onload() {
|
|
694
|
-
// 确保只跑一次下载操作
|
|
695
|
-
node.onload = node.onerror = node.onreadystatechange = null;
|
|
696
|
-
|
|
697
|
-
// 清空node引用,在低版本IE,不清除会造成内存泄露
|
|
698
|
-
node = null;
|
|
699
|
-
|
|
700
|
-
callback();
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
// 循环判断css是否已加载成功
|
|
704
|
-
/*
|
|
705
|
-
* @param node -- link节点
|
|
706
|
-
* @param callback -- 回调函数
|
|
707
|
-
* @param step -- 计步器,避免无限循环
|
|
708
|
-
*/
|
|
709
|
-
function pollCss(node: any, callback: any, step: number) {
|
|
710
|
-
const sheet = node.sheet;
|
|
711
|
-
let isLoaded: any;
|
|
712
|
-
|
|
713
|
-
step += 1;
|
|
714
|
-
|
|
715
|
-
// 保护,大于10分钟,则不再轮询
|
|
716
|
-
if (step > protectNum) {
|
|
717
|
-
isLoaded = true;
|
|
718
|
-
|
|
719
|
-
// 清空node引用
|
|
720
|
-
node = null;
|
|
721
|
-
|
|
722
|
-
callback();
|
|
723
|
-
return;
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
if (isOldWebKit) {
|
|
727
|
-
// for WebKit < 536
|
|
728
|
-
if (sheet) {
|
|
729
|
-
isLoaded = true;
|
|
730
|
-
}
|
|
731
|
-
} else if (sheet) {
|
|
732
|
-
// for Firefox < 9.0
|
|
733
|
-
try {
|
|
734
|
-
if (sheet.cssRules) {
|
|
735
|
-
isLoaded = true;
|
|
736
|
-
}
|
|
737
|
-
} catch (ex) {
|
|
738
|
-
// 火狐特殊版本,通过特定值获知是否下载成功
|
|
739
|
-
// The value of `ex.name` is changed from "NS_ERROR_DOM_SECURITY_ERR"
|
|
740
|
-
// to "SecurityError" since Firefox 13.0. But Firefox is less than 9.0
|
|
741
|
-
// in here, So it is ok to just rely on "NS_ERROR_DOM_SECURITY_ERR"
|
|
742
|
-
if (ex.name === 'NS_ERROR_DOM_SECURITY_ERR') {
|
|
743
|
-
isLoaded = true;
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
setTimeout(function () {
|
|
749
|
-
if (isLoaded) {
|
|
750
|
-
// 延迟20ms是为了给下载的样式留够渲染的时间
|
|
751
|
-
callback();
|
|
752
|
-
} else {
|
|
753
|
-
pollCss(node, callback, step);
|
|
754
|
-
}
|
|
755
|
-
}, 20);
|
|
756
|
-
}
|
|
757
|
-
return status;
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
/**
|
|
761
|
-
* @method loadScript
|
|
762
|
-
* @description 动态加载js文件
|
|
763
|
-
* @param {String} url -- js资源路径
|
|
764
|
-
* @param {String} id -- DOM ID
|
|
765
|
-
* @param {Function} callback -- 加载后回调函数
|
|
766
|
-
* @param {Number} timeout -- 超时时长
|
|
767
|
-
* @param {Boolean} isDefer -- 是否添加 defer 标签
|
|
768
|
-
* @return {Promise<Boolean>} -- true 成功
|
|
769
|
-
*/
|
|
770
|
-
export function loadScript({ url = '', id = '', callback = function () { /* pass */ }, timeout = 5000, isDefer = false } = {}): any {
|
|
771
|
-
let success: any = null;
|
|
772
|
-
let fail: any = null;
|
|
773
|
-
const script: any = document.createElement('script');
|
|
774
|
-
// 如果没有 script 标签,那么代码就不会运行。可以利用这一事实,在页面的第一个 script 标签上使用 insertBefore()。
|
|
775
|
-
const firstScript: any = document.getElementsByTagName('script')[0];
|
|
776
|
-
script.type = 'text/javascript';
|
|
777
|
-
if (isDefer) {
|
|
778
|
-
script.defer = 'defer';
|
|
779
|
-
}
|
|
780
|
-
if (id) {
|
|
781
|
-
script.id = id;
|
|
782
|
-
}
|
|
783
|
-
if (script.readyState) {
|
|
784
|
-
// IE
|
|
785
|
-
script.onreadystatechange = function () {
|
|
786
|
-
if (script.readyState === 'loaded' || script.readyState === 'complete') {
|
|
787
|
-
script.onreadystatechange = null;
|
|
788
|
-
doFn(callback);
|
|
789
|
-
doFn(success, true);
|
|
790
|
-
}
|
|
791
|
-
};
|
|
792
|
-
} else {
|
|
793
|
-
// Others
|
|
794
|
-
script.onload = function () {
|
|
795
|
-
doFn(callback);
|
|
796
|
-
doFn(success, true);
|
|
797
|
-
};
|
|
798
|
-
}
|
|
799
|
-
script.src = url;
|
|
800
|
-
firstScript && firstScript.parentNode.insertBefore(script, firstScript);
|
|
801
|
-
return new Promise((resolve, reject) => {
|
|
802
|
-
([success, fail] = [resolve, reject]);
|
|
803
|
-
if (timeout) {
|
|
804
|
-
setTimeout(fail.bind(null, 'timeout'), timeout);
|
|
805
|
-
}
|
|
806
|
-
});
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
/**
|
|
810
|
-
* @method mNow
|
|
811
|
-
* @description 获取时间戳
|
|
812
|
-
*/
|
|
813
|
-
export function mNow() {
|
|
814
|
-
let ret = 0;
|
|
815
|
-
if (Date.now) {
|
|
816
|
-
ret = Date.now();
|
|
817
|
-
} else {
|
|
818
|
-
ret = new Date().getTime();
|
|
819
|
-
}
|
|
820
|
-
return ret;
|
|
821
|
-
}
|
|
822
|
-
|
|
823
|
-
/**
|
|
824
|
-
* @method setCookie
|
|
825
|
-
* @description 设置 Cookie
|
|
826
|
-
*/
|
|
827
|
-
export function setCookie(name: string, value: string, days: number, domain: string) {
|
|
828
|
-
let domainParts, expires;
|
|
829
|
-
let date: any;
|
|
830
|
-
if (days) {
|
|
831
|
-
date = new Date();
|
|
832
|
-
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
|
833
|
-
expires = "; expires=" + date.toGMTString();
|
|
834
|
-
} else {
|
|
835
|
-
expires = "";
|
|
836
|
-
}
|
|
837
|
-
const host = location.host;
|
|
838
|
-
if (host.split('.').length === 1) {
|
|
839
|
-
// no "." in a domain - it's localhost or something similar
|
|
840
|
-
document.cookie = name + "=" + value + expires + "; path=/";
|
|
841
|
-
} else {
|
|
842
|
-
// Remember the cookie on all subdomains.
|
|
843
|
-
//
|
|
844
|
-
// Start with trying to set cookie to the top domain.
|
|
845
|
-
// (example: if user is on foo.com, try to set
|
|
846
|
-
// cookie to domain ".com")
|
|
847
|
-
//
|
|
848
|
-
// If the cookie will not be set, it means ".com"
|
|
849
|
-
// is a top level domain and we need to
|
|
850
|
-
// set the cookie to ".foo.com"
|
|
851
|
-
domainParts = host.split('.');
|
|
852
|
-
domainParts.shift();
|
|
853
|
-
domain = domain || ('.' + domainParts.join('.'));
|
|
854
|
-
document.cookie = name + "=" + value + expires + "; path=/; domain=" + domain;
|
|
855
|
-
// check if cookie was successfuly set to the given domain
|
|
856
|
-
// (otherwise it was a Top-Level Domain)
|
|
857
|
-
if (getCookie(name) === null || getCookie(name) !== value) {
|
|
858
|
-
// append "." to current domain
|
|
859
|
-
domain = domain || ('.' + host);
|
|
860
|
-
document.cookie = name + "=" + value + expires + "; path=/; domain=" + domain;
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
/**
|
|
866
|
-
* @method getCookie
|
|
867
|
-
* @description 获取 Cookie
|
|
868
|
-
*/
|
|
869
|
-
export function getCookie(name: string) {
|
|
870
|
-
const nameEQ = name + "=";
|
|
871
|
-
const ca = document.cookie.split(';');
|
|
872
|
-
for (let i = 0; i < ca.length; i++) {
|
|
873
|
-
let c = ca[i];
|
|
874
|
-
while (c.charAt(0) == ' ') {
|
|
875
|
-
c = c.substring(1, c.length);
|
|
876
|
-
}
|
|
877
|
-
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
|
|
878
|
-
}
|
|
879
|
-
return null;
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
/**
|
|
883
|
-
* @method getPerformance
|
|
884
|
-
* @description 获取页面加载相关的各项数据
|
|
885
|
-
* @param {Boolean} camelCase -- true(默认) 以驼峰形式返回数据 false 以下划线形式返回数据
|
|
886
|
-
* @return {Promise<Object>} 加载数据
|
|
887
|
-
*/
|
|
888
|
-
export function getPerformance({ camelCase = true } = {}) {
|
|
889
|
-
let success: any;
|
|
890
|
-
let fail: any;
|
|
891
|
-
const status = new Promise((resolve, reject) => {
|
|
892
|
-
([success, fail] = [resolve, reject]);
|
|
893
|
-
});
|
|
894
|
-
const timing = window.performance.timing;
|
|
895
|
-
const startTime = timing.navigationStart || timing.fetchStart;
|
|
896
|
-
let firstPaintTime: any;
|
|
897
|
-
let firstContentfulPaintTime: any;
|
|
898
|
-
// 是否已经形成数据(页面加载完成之后)
|
|
899
|
-
if (window.performance && window.performance.timing && window.performance.timing.loadEventEnd > 0) {
|
|
900
|
-
// console.log('created')
|
|
901
|
-
getTiming();
|
|
902
|
-
} else {
|
|
903
|
-
// console.log('creating')
|
|
904
|
-
window.addEventListener("load", function() {
|
|
905
|
-
//不能影响最后的时间计算
|
|
906
|
-
window.setTimeout(function() {
|
|
907
|
-
getTiming();
|
|
908
|
-
}, 0);
|
|
909
|
-
});
|
|
910
|
-
}
|
|
911
|
-
// performance
|
|
912
|
-
function getTiming() {
|
|
913
|
-
// 获取首次渲染时间
|
|
914
|
-
try {
|
|
915
|
-
if (window.performance && Boolean(window.performance.getEntriesByType)) {
|
|
916
|
-
const performance = window.performance;
|
|
917
|
-
const performanceEntries = performance.getEntriesByType('paint');
|
|
918
|
-
performanceEntries.forEach((performanceEntry, i, entries) => {
|
|
919
|
-
const startTime = Math.round(performanceEntry.startTime);
|
|
920
|
-
if (performanceEntry.name === 'first-paint')
|
|
921
|
-
firstPaintTime = startTime;
|
|
922
|
-
else if (performanceEntry.name === 'first-contentful-paint')
|
|
923
|
-
firstContentfulPaintTime = startTime;
|
|
924
|
-
});
|
|
925
|
-
} else {
|
|
926
|
-
console.error('paint');
|
|
927
|
-
}
|
|
928
|
-
} catch (e) {
|
|
929
|
-
console.error(e.message);
|
|
930
|
-
}
|
|
931
|
-
// 获取加载时间
|
|
932
|
-
if (
|
|
933
|
-
window.performance &&
|
|
934
|
-
typeof window.performance.getEntries === 'function'
|
|
935
|
-
) {
|
|
936
|
-
const performanceNavigationTiming: any =
|
|
937
|
-
(window.performance.getEntries() || [])[0] || {};
|
|
938
|
-
const data: any = {
|
|
939
|
-
// url: encodeURI(location.href),
|
|
940
|
-
// ua: navigator.userAgent,
|
|
941
|
-
os: getOS(),
|
|
942
|
-
osVersion: getOSVersion(),
|
|
943
|
-
deviceType: getDeviceType(),
|
|
944
|
-
network: getNetWork(),
|
|
945
|
-
screenDirection: getOrientationStatu(),
|
|
946
|
-
unloadTime: timing.unloadEventEnd - timing.unloadEventStart, //上个文档的卸载时间
|
|
947
|
-
redirectTime: timing.redirectEnd - timing.redirectStart, //*重定向时间
|
|
948
|
-
dnsTime: timing.domainLookupEnd - timing.domainLookupStart, //*DNS查询时间
|
|
949
|
-
tcpTime: timing.connectEnd - timing.connectStart, //*服务器连接时间
|
|
950
|
-
sslTime: getSSLTime(timing.connectEnd, timing.secureConnectionStart), //*ssl连接时间
|
|
951
|
-
responseTime: timing.responseStart - timing.requestStart, //*服务器响应时间
|
|
952
|
-
downloadTime: timing.responseEnd - timing.responseStart, //*网页下载时间
|
|
953
|
-
firstPaintTime: firstPaintTime, //*首次渲染时间
|
|
954
|
-
firstContentfulPaintTime: firstContentfulPaintTime, //*首次渲染内容时间
|
|
955
|
-
domreadyTime: timing.domContentLoadedEventStart - startTime || 0, //*dom ready时间(总和)
|
|
956
|
-
onloadTime: timing.loadEventStart - startTime || 0, //*onload时间(总和)
|
|
957
|
-
whiteTime: timing.responseStart - startTime, //*白屏时间
|
|
958
|
-
renderTime: timing.loadEventEnd - startTime || 0, //整个过程的时间之和
|
|
959
|
-
decodedBodySize: performanceNavigationTiming.decodedBodySize || '', //页面压缩前大小
|
|
960
|
-
encodedBodySize: performanceNavigationTiming.encodedBodySize || '', //页面压缩后大小
|
|
961
|
-
};
|
|
962
|
-
// 过滤异常数据
|
|
963
|
-
Object.keys(data).forEach(k => {
|
|
964
|
-
// 过滤掉 <0 的数据
|
|
965
|
-
if (isNumber(data[k]) && data[k] < 0) {
|
|
966
|
-
data[k] = 0;
|
|
967
|
-
}
|
|
968
|
-
});
|
|
969
|
-
// 过滤掉白屏时间 > onload 的数据
|
|
970
|
-
if (isNumber(data.whiteTime) && data.whiteTime > data.onloadTime) {
|
|
971
|
-
data.whiteTime = 0;
|
|
972
|
-
}
|
|
973
|
-
if (startTime > 0) {
|
|
974
|
-
let Underscore: any;
|
|
975
|
-
if (!camelCase) {
|
|
976
|
-
Object.keys(data).forEach(k => {
|
|
977
|
-
if (!Underscore) Underscore = {};
|
|
978
|
-
// console.log('camelCase2Underscore', k, data, )
|
|
979
|
-
Underscore[camelCase2Underscore(k)] = data[k];
|
|
980
|
-
});
|
|
981
|
-
}
|
|
982
|
-
success(Underscore || data);
|
|
983
|
-
} else {
|
|
984
|
-
fail('startTime');
|
|
985
|
-
}
|
|
986
|
-
} else {
|
|
987
|
-
fail('getEntries');
|
|
988
|
-
}
|
|
989
|
-
}
|
|
990
|
-
//获取当前操作系统
|
|
991
|
-
function getOS() {
|
|
992
|
-
let os;
|
|
993
|
-
if (navigator.userAgent.indexOf('Android') > -1 || navigator.userAgent.indexOf('Linux') > -1) {
|
|
994
|
-
os = 'android';
|
|
995
|
-
} else if (navigator.userAgent.indexOf('iPhone') > -1) {
|
|
996
|
-
os = 'ios';
|
|
997
|
-
} else if (navigator.userAgent.indexOf('Windows Phone') > -1) {
|
|
998
|
-
os = 'wp';
|
|
999
|
-
} else {
|
|
1000
|
-
os = 'others';
|
|
1001
|
-
}
|
|
1002
|
-
return os;
|
|
1003
|
-
}
|
|
1004
|
-
// 获取操作系统版本
|
|
1005
|
-
function getOSVersion() {
|
|
1006
|
-
let OSVision: any;
|
|
1007
|
-
const u = navigator.userAgent;
|
|
1008
|
-
const isAndroid = u.indexOf('Android') > -1 || u.indexOf('Linux') > -1; //Android
|
|
1009
|
-
const isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios终端
|
|
1010
|
-
if (isAndroid) {
|
|
1011
|
-
OSVision = (navigator.userAgent.split(';')[1].match(/\d+\.\d+/g) || [])[0];
|
|
1012
|
-
}
|
|
1013
|
-
if (isIOS) {
|
|
1014
|
-
OSVision = (navigator.userAgent.split(';')[1].match(/(\d+)_(\d+)_?(\d+)?/) || [])[0];
|
|
1015
|
-
}
|
|
1016
|
-
return OSVision;
|
|
1017
|
-
}
|
|
1018
|
-
//获取设备类型
|
|
1019
|
-
function getDeviceType() {
|
|
1020
|
-
let deviceType;
|
|
1021
|
-
const sUserAgent = navigator.userAgent.toLowerCase();
|
|
1022
|
-
const bIsIpad = sUserAgent.match(/(ipad)/i) && 'ipad';
|
|
1023
|
-
const bIsIphoneOs = sUserAgent.match(/iphone os/i) && 'iphone os';
|
|
1024
|
-
const bIsMidp = sUserAgent.match(/midp/i) && 'midp';
|
|
1025
|
-
const bIsUc7 = sUserAgent.match(/rv:1.2.3.4/i) && 'rv:1.2.3.4';
|
|
1026
|
-
const bIsUc = sUserAgent.match(/ucweb/i) && 'ucweb';
|
|
1027
|
-
const bIsAndroid = sUserAgent.match(/android/i) && 'android';
|
|
1028
|
-
const bIsCE = sUserAgent.match(/windows ce/i) && 'windows ce';
|
|
1029
|
-
const bIsWM = sUserAgent.match(/windows mobile/i) && 'windows mobile';
|
|
1030
|
-
if (!(bIsIpad || bIsIphoneOs || bIsMidp || bIsUc7 || bIsUc || bIsAndroid || bIsCE || bIsWM)) {
|
|
1031
|
-
deviceType = 'pc'; //pc
|
|
1032
|
-
} else if (bIsIphoneOs || bIsMidp || bIsUc7 || bIsUc || bIsAndroid || bIsCE || bIsWM) {
|
|
1033
|
-
deviceType = 'phone'; //phone
|
|
1034
|
-
} else if (bIsIpad) {
|
|
1035
|
-
deviceType = 'ipad'; //ipad
|
|
1036
|
-
} else {
|
|
1037
|
-
deviceType = undefined;
|
|
1038
|
-
}
|
|
1039
|
-
return deviceType;
|
|
1040
|
-
}
|
|
1041
|
-
// 获取网络状态
|
|
1042
|
-
function getNetWork() {
|
|
1043
|
-
let netWork: any;
|
|
1044
|
-
if ((navigator as any).connection && (navigator as any).connection.effectiveType) {
|
|
1045
|
-
switch ((navigator as any).connection.effectiveType) {
|
|
1046
|
-
case 'wifi':
|
|
1047
|
-
netWork = 'wifi'; // wifi
|
|
1048
|
-
break;
|
|
1049
|
-
case '4g':
|
|
1050
|
-
netWork = '4g'; // 4g
|
|
1051
|
-
break;
|
|
1052
|
-
case '2g':
|
|
1053
|
-
netWork = '2g'; // 2g
|
|
1054
|
-
break;
|
|
1055
|
-
case '3g':
|
|
1056
|
-
netWork = '3g'; // 3g
|
|
1057
|
-
break;
|
|
1058
|
-
case 'ethernet':
|
|
1059
|
-
netWork = 'ethernet'; // ethernet
|
|
1060
|
-
break;
|
|
1061
|
-
case 'default':
|
|
1062
|
-
netWork = undefined; // 未知
|
|
1063
|
-
break;
|
|
1064
|
-
}
|
|
1065
|
-
}
|
|
1066
|
-
return netWork;
|
|
1067
|
-
}
|
|
1068
|
-
// 获取横竖屏状态
|
|
1069
|
-
function getOrientationStatu() {
|
|
1070
|
-
let orientationStatu: any;
|
|
1071
|
-
if (window.screen && window.screen.orientation && window.screen.orientation.angle) {
|
|
1072
|
-
if (window.screen.orientation.angle === 180 || window.screen.orientation.angle === 0) { // 竖屏
|
|
1073
|
-
orientationStatu = '|';
|
|
1074
|
-
}
|
|
1075
|
-
if (window.screen.orientation.angle === 90 || window.screen.orientation.angle === -90) { // 横屏
|
|
1076
|
-
orientationStatu = '-';
|
|
1077
|
-
}
|
|
1078
|
-
}
|
|
1079
|
-
return orientationStatu;
|
|
1080
|
-
}
|
|
1081
|
-
// 获取ssl连接时间
|
|
1082
|
-
function getSSLTime(connectEnd: any, secureConnectionStart: any) {
|
|
1083
|
-
let ssl_time: any;
|
|
1084
|
-
if (secureConnectionStart) {
|
|
1085
|
-
ssl_time = connectEnd - secureConnectionStart;
|
|
1086
|
-
}
|
|
1087
|
-
return ssl_time;
|
|
1088
|
-
}
|
|
1089
|
-
return status;
|
|
1090
|
-
}
|
|
1091
|
-
|
|
1092
|
-
/**
|
|
1093
|
-
* @method inRate
|
|
1094
|
-
* @description 百分位概率
|
|
1095
|
-
* @param {Number} rate -- 0.1 ~ 1 => 1% ~ 100%
|
|
1096
|
-
* @return {Boolean} true 命中
|
|
1097
|
-
*/
|
|
1098
|
-
export function inRate(rate: number): boolean {
|
|
1099
|
-
if(Math.random() < rate) {
|
|
1100
|
-
return true;
|
|
1101
|
-
}
|
|
1102
|
-
return false;
|
|
1103
|
-
}
|
|
1104
|
-
|
|
1105
|
-
/**
|
|
1106
|
-
* @method isSafePWAEnv
|
|
1107
|
-
* @description 判断是否是安全的 PWA 环境
|
|
1108
|
-
* @return {Boolean} true 是
|
|
1109
|
-
*/
|
|
1110
|
-
export function isSafePWAEnv(): boolean {
|
|
1111
|
-
// 判断是否支持 async await
|
|
1112
|
-
function isSupportAsyncAwait() {
|
|
1113
|
-
let isSupportAsyncAwaitFunc;
|
|
1114
|
-
try {
|
|
1115
|
-
// eval("func = async function(){};");
|
|
1116
|
-
const fn = new Function("return async function(){};");
|
|
1117
|
-
isSupportAsyncAwaitFunc = fn();
|
|
1118
|
-
// console.log('isSupportAsyncAwaitFunc', isSupportAsyncAwaitFunc);
|
|
1119
|
-
// 由于async函数的构造器不是全局对象,所以我们需要由下面代码来获取async函数的构造器
|
|
1120
|
-
// 具体可以查看以下MDN上有关于AsyncFunction的说明,
|
|
1121
|
-
// 地址:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncFunction
|
|
1122
|
-
return Object.getPrototypeOf(isSupportAsyncAwaitFunc).constructor != null;
|
|
1123
|
-
} catch (e) {
|
|
1124
|
-
return false;
|
|
1125
|
-
}
|
|
1126
|
-
}
|
|
1127
|
-
// 判断是否支持 Promise
|
|
1128
|
-
function isSupportPromise() {
|
|
1129
|
-
if(typeof Promise !== "undefined" && Promise.toString().indexOf("[native code]") !== -1){
|
|
1130
|
-
return true;
|
|
1131
|
-
}
|
|
1132
|
-
return false;
|
|
1133
|
-
}
|
|
1134
|
-
// 浏览器信息
|
|
1135
|
-
const BrowserType = getBrowserType();
|
|
1136
|
-
if (
|
|
1137
|
-
('serviceWorker' in navigator) &&
|
|
1138
|
-
isSupportAsyncAwait() &&
|
|
1139
|
-
isSupportPromise() &&
|
|
1140
|
-
Boolean(window.fetch) &&
|
|
1141
|
-
Boolean(window.indexedDB) &&
|
|
1142
|
-
Boolean(window.caches) &&
|
|
1143
|
-
!BrowserType['shell']
|
|
1144
|
-
) {
|
|
1145
|
-
return true;
|
|
1146
|
-
}
|
|
1147
|
-
return false;
|
|
1148
|
-
}
|
|
1149
|
-
|
|
1150
|
-
/**
|
|
1151
|
-
* @method getBrowserType
|
|
1152
|
-
* @description 返回浏览器信息 https://github.com/JowayYoung/juejin-code/blob/master/browser-type.js
|
|
1153
|
-
* @return {Object} 浏览器信息
|
|
1154
|
-
*/
|
|
1155
|
-
export function getBrowserType(): any {
|
|
1156
|
-
try {
|
|
1157
|
-
// 权重:系统 + 系统版本 > 平台 > 内核 + 载体 + 内核版本 + 载体版本 > 外壳 + 外壳版本
|
|
1158
|
-
const ua: any = navigator.userAgent.toLowerCase();
|
|
1159
|
-
const testUa: (regexp: any) => any = regexp => regexp.test(ua);
|
|
1160
|
-
const testVs: (regexp: any) => any = regexp => ua.match(regexp)
|
|
1161
|
-
.toString()
|
|
1162
|
-
.replace(/[^0-9|_.]/g, "")
|
|
1163
|
-
.replace(/_/g, ".");
|
|
1164
|
-
// 系统
|
|
1165
|
-
let system = "unknow";
|
|
1166
|
-
if (testUa(/windows|win32|win64|wow32|wow64/g)) {
|
|
1167
|
-
system = "windows"; // windows系统
|
|
1168
|
-
} else if (testUa(/macintosh|macintel/g)) {
|
|
1169
|
-
system = "macos"; // macos系统
|
|
1170
|
-
} else if (testUa(/x11/g)) {
|
|
1171
|
-
system = "linux"; // linux系统
|
|
1172
|
-
} else if (testUa(/android|adr/g)) {
|
|
1173
|
-
system = "android"; // android系统
|
|
1174
|
-
} else if (testUa(/ios|iphone|ipad|ipod|iwatch/g)) {
|
|
1175
|
-
system = "ios"; // ios系统
|
|
1176
|
-
}
|
|
1177
|
-
// 系统版本
|
|
1178
|
-
let systemVs = "unknow";
|
|
1179
|
-
if (system === "windows") {
|
|
1180
|
-
if (testUa(/windows nt 5.0|windows 2000/g)) {
|
|
1181
|
-
systemVs = "2000";
|
|
1182
|
-
} else if (testUa(/windows nt 5.1|windows xp/g)) {
|
|
1183
|
-
systemVs = "xp";
|
|
1184
|
-
} else if (testUa(/windows nt 5.2|windows 2003/g)) {
|
|
1185
|
-
systemVs = "2003";
|
|
1186
|
-
} else if (testUa(/windows nt 6.0|windows vista/g)) {
|
|
1187
|
-
systemVs = "vista";
|
|
1188
|
-
} else if (testUa(/windows nt 6.1|windows 7/g)) {
|
|
1189
|
-
systemVs = "7";
|
|
1190
|
-
} else if (testUa(/windows nt 6.2|windows 8/g)) {
|
|
1191
|
-
systemVs = "8";
|
|
1192
|
-
} else if (testUa(/windows nt 6.3|windows 8.1/g)) {
|
|
1193
|
-
systemVs = "8.1";
|
|
1194
|
-
} else if (testUa(/windows nt 10.0|windows 10/g)) {
|
|
1195
|
-
systemVs = "10";
|
|
1196
|
-
}
|
|
1197
|
-
} else if (system === "macos") {
|
|
1198
|
-
systemVs = testVs(/os x [\d._]+/g);
|
|
1199
|
-
} else if (system === "android") {
|
|
1200
|
-
systemVs = testVs(/android [\d._]+/g);
|
|
1201
|
-
} else if (system === "ios") {
|
|
1202
|
-
systemVs = testVs(/os [\d._]+/g);
|
|
1203
|
-
}
|
|
1204
|
-
// 平台
|
|
1205
|
-
let platform = "unknow";
|
|
1206
|
-
if (system === "windows" || system === "macos" || system === "linux") {
|
|
1207
|
-
platform = "desktop"; // 桌面端
|
|
1208
|
-
} else if (system === "android" || system === "ios" || testUa(/mobile/g)) {
|
|
1209
|
-
platform = "mobile"; // 移动端
|
|
1210
|
-
}
|
|
1211
|
-
// 内核和载体
|
|
1212
|
-
let engine = "unknow";
|
|
1213
|
-
let supporter = "unknow";
|
|
1214
|
-
if (testUa(/applewebkit/g)) {
|
|
1215
|
-
engine = "webkit"; // webkit内核
|
|
1216
|
-
if (testUa(/edge/g)) {
|
|
1217
|
-
supporter = "edge"; // edge浏览器
|
|
1218
|
-
} else if (testUa(/opr/g)) {
|
|
1219
|
-
supporter = "opera"; // opera浏览器
|
|
1220
|
-
} else if (testUa(/chrome/g)) {
|
|
1221
|
-
supporter = "chrome"; // chrome浏览器
|
|
1222
|
-
} else if (testUa(/safari/g)) {
|
|
1223
|
-
supporter = "safari"; // safari浏览器
|
|
1224
|
-
}
|
|
1225
|
-
} else if (testUa(/gecko/g) && testUa(/firefox/g)) {
|
|
1226
|
-
engine = "gecko"; // gecko内核
|
|
1227
|
-
supporter = "firefox"; // firefox浏览器
|
|
1228
|
-
} else if (testUa(/presto/g)) {
|
|
1229
|
-
engine = "presto"; // presto内核
|
|
1230
|
-
supporter = "opera"; // opera浏览器
|
|
1231
|
-
} else if (testUa(/trident|compatible|msie/g)) {
|
|
1232
|
-
engine = "trident"; // trident内核
|
|
1233
|
-
supporter = "iexplore"; // iexplore浏览器
|
|
1234
|
-
}
|
|
1235
|
-
// 内核版本
|
|
1236
|
-
let engineVs = "unknow";
|
|
1237
|
-
if (engine === "webkit") {
|
|
1238
|
-
engineVs = testVs(/applewebkit\/[\d._]+/g);
|
|
1239
|
-
} else if (engine === "gecko") {
|
|
1240
|
-
engineVs = testVs(/gecko\/[\d._]+/g);
|
|
1241
|
-
} else if (engine === "presto") {
|
|
1242
|
-
engineVs = testVs(/presto\/[\d._]+/g);
|
|
1243
|
-
} else if (engine === "trident") {
|
|
1244
|
-
engineVs = testVs(/trident\/[\d._]+/g);
|
|
1245
|
-
}
|
|
1246
|
-
// 载体版本
|
|
1247
|
-
let supporterVs = "unknow";
|
|
1248
|
-
if (supporter === "chrome") {
|
|
1249
|
-
supporterVs = testVs(/chrome\/[\d._]+/g);
|
|
1250
|
-
} else if (supporter === "safari") {
|
|
1251
|
-
supporterVs = testVs(/version\/[\d._]+/g);
|
|
1252
|
-
} else if (supporter === "firefox") {
|
|
1253
|
-
supporterVs = testVs(/firefox\/[\d._]+/g);
|
|
1254
|
-
} else if (supporter === "opera") {
|
|
1255
|
-
supporterVs = testVs(/opr\/[\d._]+/g);
|
|
1256
|
-
} else if (supporter === "iexplore") {
|
|
1257
|
-
supporterVs = testVs(/(msie [\d._]+)|(rv:[\d._]+)/g);
|
|
1258
|
-
} else if (supporter === "edge") {
|
|
1259
|
-
supporterVs = testVs(/edge\/[\d._]+/g);
|
|
1260
|
-
}
|
|
1261
|
-
// 外壳和外壳版本
|
|
1262
|
-
let shell = "none";
|
|
1263
|
-
let shellVs = "unknow";
|
|
1264
|
-
if (testUa(/micromessenger/g)) {
|
|
1265
|
-
shell = "wechat"; // 微信浏览器
|
|
1266
|
-
shellVs = testVs(/micromessenger\/[\d._]+/g);
|
|
1267
|
-
} else if (testUa(/qqbrowser/g)) {
|
|
1268
|
-
shell = "qq"; // QQ浏览器
|
|
1269
|
-
shellVs = testVs(/qqbrowser\/[\d._]+/g);
|
|
1270
|
-
} else if (testUa(/ucbrowser/g)) {
|
|
1271
|
-
shell = "uc"; // UC浏览器
|
|
1272
|
-
shellVs = testVs(/ucbrowser\/[\d._]+/g);
|
|
1273
|
-
} else if (testUa(/qihu 360se/g)) {
|
|
1274
|
-
shell = "360"; // 360浏览器(无版本)
|
|
1275
|
-
} else if (testUa(/2345explorer/g)) {
|
|
1276
|
-
shell = "2345"; // 2345浏览器
|
|
1277
|
-
shellVs = testVs(/2345explorer\/[\d._]+/g);
|
|
1278
|
-
} else if (testUa(/metasr/g)) {
|
|
1279
|
-
shell = "sougou"; // 搜狗浏览器(无版本)
|
|
1280
|
-
} else if (testUa(/lbbrowser/g)) {
|
|
1281
|
-
shell = "liebao"; // 猎豹浏览器(无版本)
|
|
1282
|
-
} else if (testUa(/maxthon/g)) {
|
|
1283
|
-
shell = "maxthon"; // 遨游浏览器
|
|
1284
|
-
shellVs = testVs(/maxthon\/[\d._]+/g);
|
|
1285
|
-
}
|
|
1286
|
-
return Object.assign({
|
|
1287
|
-
engine, // webkit gecko presto trident
|
|
1288
|
-
engineVs,
|
|
1289
|
-
platform, // desktop mobile
|
|
1290
|
-
supporter, // chrome safari firefox opera iexplore edge
|
|
1291
|
-
supporterVs,
|
|
1292
|
-
system, // windows macos linux android ios
|
|
1293
|
-
systemVs
|
|
1294
|
-
}, shell === "none" ? {} : {
|
|
1295
|
-
shell, // wechat qq uc 360 2345 sougou liebao maxthon
|
|
1296
|
-
shellVs
|
|
1297
|
-
});
|
|
1298
|
-
} catch (err) {
|
|
1299
|
-
return {};
|
|
1300
|
-
}
|
|
1301
|
-
}
|
|
1302
|
-
|
|
1303
|
-
/**
|
|
1304
|
-
* @method clearHtml
|
|
1305
|
-
* @description 去除HTML标签
|
|
1306
|
-
* @param {String} string 带html标签的字符串
|
|
1307
|
-
* @return {String} 字符串
|
|
1308
|
-
*/
|
|
1309
|
-
export function clearHtml (string = ''): string {
|
|
1310
|
-
return string.replace(/<\/?.+?>/g, '').replace(/[\r\n]/g, '');
|
|
1311
|
-
}
|
|
1312
|
-
|
|
1313
|
-
/**
|
|
1314
|
-
* @method cutCHSString
|
|
1315
|
-
* @description 截取字符串,中文算2个字节
|
|
1316
|
-
* @param {String} str 要截取的字符串
|
|
1317
|
-
* @param {Number} len
|
|
1318
|
-
* @param {Boolean} hasDot
|
|
1319
|
-
* @returns {String} 返回截取后的字符串
|
|
1320
|
-
*/
|
|
1321
|
-
export function cutCHSString(str = '', len = str.length, hasDot = false): string {
|
|
1322
|
-
if (str == '' || !str) {
|
|
1323
|
-
return '';
|
|
1324
|
-
} else {
|
|
1325
|
-
let newLength = 0;
|
|
1326
|
-
let newStr = '';
|
|
1327
|
-
// eslint-disable-next-line no-control-regex
|
|
1328
|
-
const chineseRegex = /[^\x00-\xff]/g;
|
|
1329
|
-
let singleChar = '';
|
|
1330
|
-
const strLength = str.replace(chineseRegex, '**').length;
|
|
1331
|
-
for (let i = 0; i < strLength; i++) {
|
|
1332
|
-
singleChar = str.charAt(i).toString();
|
|
1333
|
-
if (singleChar.match(chineseRegex) != null) {
|
|
1334
|
-
newLength += 2;
|
|
1335
|
-
} else {
|
|
1336
|
-
newLength++;
|
|
1337
|
-
}
|
|
1338
|
-
if (newLength > len) {
|
|
1339
|
-
break;
|
|
1340
|
-
}
|
|
1341
|
-
newStr += singleChar;
|
|
1342
|
-
}
|
|
1343
|
-
|
|
1344
|
-
if (hasDot && strLength > len) {
|
|
1345
|
-
newStr += '...';
|
|
1346
|
-
}
|
|
1347
|
-
return newStr;
|
|
1348
|
-
}
|
|
1349
|
-
}
|
|
1350
|
-
|
|
1351
|
-
/**
|
|
1352
|
-
* @method windowLoaded
|
|
1353
|
-
* @description 页面加载完成
|
|
1354
|
-
* @param {Number} timeout 超时时间 / 单位:秒
|
|
1355
|
-
* @return {Promise<string>} document is loaded? 'complete' 'load' / 'timeout'
|
|
1356
|
-
*/
|
|
1357
|
-
export function windowLoaded({ timeout = 90 } = {}): Promise<string> {
|
|
1358
|
-
let loaded: (value: string) => void = () => undefined;
|
|
1359
|
-
let loadFail: (value: string) => void = () => undefined;
|
|
1360
|
-
const status = new Promise((resolve: (value: string) => void, reject: (value: string) => void) => {
|
|
1361
|
-
loaded = resolve;
|
|
1362
|
-
loadFail = reject;
|
|
1363
|
-
});
|
|
1364
|
-
if (document.readyState === 'complete') {
|
|
1365
|
-
loaded('complete');
|
|
1366
|
-
} else {
|
|
1367
|
-
window.addEventListener('load', () => loaded('load'));
|
|
1368
|
-
}
|
|
1369
|
-
// 超过 timeout 秒后加载失败
|
|
1370
|
-
setTimeout(() => loadFail('timeout'), timeout * 1000);
|
|
1371
|
-
return status;
|
|
1372
|
-
}
|
|
1373
|
-
|
|
1374
|
-
/**
|
|
1375
|
-
* @method addInlineStyle
|
|
1376
|
-
* @description 添加内联样式
|
|
1377
|
-
* @param {String} inlineStyle 内联样式字符串
|
|
1378
|
-
* @param {String} id <style> 标签的 ID
|
|
1379
|
-
* @return {Boolean} 添加成功/失败
|
|
1380
|
-
*/
|
|
1381
|
-
export function addInlineStyle({ inlineStyle = '', id = '' } = {}): boolean {
|
|
1382
|
-
if (!inlineStyle) {
|
|
1383
|
-
return false;
|
|
1384
|
-
}
|
|
1385
|
-
// 创建 style 文档碎片
|
|
1386
|
-
const styleFrag = document.createDocumentFragment();
|
|
1387
|
-
let idDom = null;
|
|
1388
|
-
let domId = '';
|
|
1389
|
-
// Custom Style
|
|
1390
|
-
const customStyle = document.createElement('style');
|
|
1391
|
-
// 如果需要 ID
|
|
1392
|
-
if (id) {
|
|
1393
|
-
domId = `${id}`;
|
|
1394
|
-
idDom = document.getElementById(domId);
|
|
1395
|
-
// 如果 Dom 不存在,插入 style
|
|
1396
|
-
if (!idDom) {
|
|
1397
|
-
customStyle.setAttribute('id', id);
|
|
1398
|
-
customStyle.innerHTML = inlineStyle;
|
|
1399
|
-
styleFrag.appendChild(customStyle);
|
|
1400
|
-
document.head.appendChild(styleFrag);
|
|
1401
|
-
} else { // 如果 Dom 存在,直接更新
|
|
1402
|
-
idDom.innerHTML = inlineStyle;
|
|
1403
|
-
}
|
|
1404
|
-
} else { // 不需要 ID,直接添加新标签
|
|
1405
|
-
customStyle.innerHTML = inlineStyle;
|
|
1406
|
-
styleFrag.appendChild(customStyle);
|
|
1407
|
-
document.head.appendChild(styleFrag);
|
|
1408
|
-
}
|
|
1409
|
-
return true;
|
|
1410
|
-
}
|
|
1411
|
-
|
|
1412
|
-
/**
|
|
1413
|
-
* @method customScrollBarForTransformEle
|
|
1414
|
-
* @description 为进行变换(transform)的元素设置滚动条
|
|
1415
|
-
* @param {String} containerClassName 进行变换(transform)的元素↓的容器(Father)的类名
|
|
1416
|
-
* @param {String} imgBoxClassName 图片的容器(transform It's YOU!)的类名
|
|
1417
|
-
* @param {String} imgBoxDom 图片的容器 Dom 对象
|
|
1418
|
-
* @param {String} imgClassName 图片的类名
|
|
1419
|
-
* @param {Object} imgDom 图片 Dom 对象
|
|
1420
|
-
* @param {String} action 操作类型 hide 隐藏指定 `containerClassName` 滚动条
|
|
1421
|
-
* @param {String} customStyle 滚动条的定制样式,会覆盖默认样式
|
|
1422
|
-
* @return {Boolean} 设置成功
|
|
1423
|
-
*/
|
|
1424
|
-
export function customScrollBarForTransformEle({ containerClassName = '', imgBoxClassName = '', imgBoxDom = null, imgClassName = '', imgDom = null, action = '', customStyle = '' } = {}): boolean {
|
|
1425
|
-
// 变量
|
|
1426
|
-
const barIsHideId = 'bar-is-hide';
|
|
1427
|
-
const barTopId = 'bar-top';
|
|
1428
|
-
const barBottomId = 'bar-bottom';
|
|
1429
|
-
if (action === 'hide') {
|
|
1430
|
-
addInlineStyle({
|
|
1431
|
-
inlineStyle: `
|
|
1432
|
-
.${containerClassName}::after {
|
|
1433
|
-
display: none;
|
|
1434
|
-
}
|
|
1435
|
-
`,
|
|
1436
|
-
id: barIsHideId,
|
|
1437
|
-
});
|
|
1438
|
-
return true;
|
|
1439
|
-
}
|
|
1440
|
-
setTimeout(() => {
|
|
1441
|
-
// 样式初始化
|
|
1442
|
-
addInlineStyle({
|
|
1443
|
-
inlineStyle: `
|
|
1444
|
-
.${containerClassName}::after {
|
|
1445
|
-
content: ' ';
|
|
1446
|
-
position: fixed;
|
|
1447
|
-
top: 0;
|
|
1448
|
-
right: 2px;
|
|
1449
|
-
min-height: 0vh;
|
|
1450
|
-
max-height: 100vh;
|
|
1451
|
-
width: 6px;
|
|
1452
|
-
border-radius: 3px;
|
|
1453
|
-
box-shadow: inset 0 0 6px rgba(0,0,0,.3);
|
|
1454
|
-
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3);
|
|
1455
|
-
background-color: rgb(109, 108, 109);
|
|
1456
|
-
${customStyle}
|
|
1457
|
-
}
|
|
1458
|
-
`,
|
|
1459
|
-
id: 'bar-init'
|
|
1460
|
-
});
|
|
1461
|
-
const imgInstance = imgDom || document.querySelector(`.${imgClassName}`);
|
|
1462
|
-
if (!imgInstance) {
|
|
1463
|
-
console.warn('Need element\'s Dom instance.');
|
|
1464
|
-
return false;
|
|
1465
|
-
}
|
|
1466
|
-
const imgBoxInstance = imgBoxDom || document.querySelector(`.${imgBoxClassName}`);
|
|
1467
|
-
if (!imgBoxInstance) {
|
|
1468
|
-
console.warn('Need element box\'s Dom instance.');
|
|
1469
|
-
return false;
|
|
1470
|
-
}
|
|
1471
|
-
const Box2ToRes = getBox2To();
|
|
1472
|
-
// console.log('获取图片距离顶部的距离', Box2ToRes);
|
|
1473
|
-
const ImgHeightRes = getImgHeight();
|
|
1474
|
-
// console.log('获取图片的高度', ImgHeightRes);
|
|
1475
|
-
const WindowHeightRes = getWindowHeight();
|
|
1476
|
-
// console.log('获取窗口的高度', WindowHeightRes);
|
|
1477
|
-
const TopOverflowPercentRes = getTopOverflowPercent();
|
|
1478
|
-
// console.log('获取顶部溢出的百分比', TopOverflowPercentRes);
|
|
1479
|
-
const BottomOverflowPercentRes = getBottomOverflowPercent();
|
|
1480
|
-
// console.log('获取顶部溢出的百分比', BottomOverflowPercentRes);
|
|
1481
|
-
const hideBarRes = hideBar();
|
|
1482
|
-
// console.log('如果都是 0 ,隐藏滚动条', hideBarRes);
|
|
1483
|
-
if (!hideBarRes) {
|
|
1484
|
-
setBarTop();
|
|
1485
|
-
// const setBarTopRes = setBarTop();
|
|
1486
|
-
// console.log('设置顶部状态栏', setBarTopRes);
|
|
1487
|
-
setBarBottom();
|
|
1488
|
-
// const setBarBottomRes = setBarBottom();
|
|
1489
|
-
// console.log('设置底部状态栏', setBarBottomRes);
|
|
1490
|
-
}
|
|
1491
|
-
// 获取图片距离顶部的距离
|
|
1492
|
-
function getBox2To () {
|
|
1493
|
-
const translates = window.getComputedStyle(imgBoxInstance as any, null).transform;
|
|
1494
|
-
const tanslateY = parseFloat(translates.substring(6).split(',')[5]);
|
|
1495
|
-
return tanslateY;
|
|
1496
|
-
}
|
|
1497
|
-
// 获取图片的高度
|
|
1498
|
-
function getImgHeight () {
|
|
1499
|
-
return (imgInstance)?.clientHeight;
|
|
1500
|
-
}
|
|
1501
|
-
// 获取窗口的高度
|
|
1502
|
-
function getWindowHeight () {
|
|
1503
|
-
return (document.querySelector(`.${containerClassName}`) as any).clientHeight; // window.outerHeight;
|
|
1504
|
-
}
|
|
1505
|
-
// 获取顶部溢出的百分比
|
|
1506
|
-
function getTopOverflowPercent () {
|
|
1507
|
-
let box2To = Box2ToRes;
|
|
1508
|
-
if (box2To >= 0) {
|
|
1509
|
-
box2To = 0;
|
|
1510
|
-
}
|
|
1511
|
-
box2To = Math.abs(box2To);
|
|
1512
|
-
// 图片高度
|
|
1513
|
-
const imgHeight = ImgHeightRes;
|
|
1514
|
-
const per = floatToPercent(box2To/(imgHeight as any), 2);
|
|
1515
|
-
return {
|
|
1516
|
-
box2To,
|
|
1517
|
-
per,
|
|
1518
|
-
};
|
|
1519
|
-
}
|
|
1520
|
-
// 获取底部溢出的百分比
|
|
1521
|
-
function getBottomOverflowPercent () {
|
|
1522
|
-
// 窗口高度
|
|
1523
|
-
const windowHeight = WindowHeightRes;
|
|
1524
|
-
// 图片高度
|
|
1525
|
-
const imgHeight = ImgHeightRes;
|
|
1526
|
-
// 上面溢出的高度
|
|
1527
|
-
const box2To = Box2ToRes;
|
|
1528
|
-
// 底部
|
|
1529
|
-
const remindImageHeight = windowHeight - box2To;
|
|
1530
|
-
let box2ToBottom = (imgHeight as any) - remindImageHeight;
|
|
1531
|
-
if (box2ToBottom <= 0) {
|
|
1532
|
-
box2ToBottom = 0;
|
|
1533
|
-
}
|
|
1534
|
-
const per = floatToPercent(box2ToBottom/(imgHeight as any), 2);
|
|
1535
|
-
return {
|
|
1536
|
-
box2ToBottom,
|
|
1537
|
-
per,
|
|
1538
|
-
};
|
|
1539
|
-
}
|
|
1540
|
-
// 如果都是 0 ,隐藏滚动条
|
|
1541
|
-
function hideBar () {
|
|
1542
|
-
const { box2To } = TopOverflowPercentRes;
|
|
1543
|
-
const { box2ToBottom } = BottomOverflowPercentRes;
|
|
1544
|
-
if (!box2To && !box2ToBottom) {
|
|
1545
|
-
return addInlineStyle({
|
|
1546
|
-
inlineStyle: `
|
|
1547
|
-
.${containerClassName}::after {
|
|
1548
|
-
display: none;
|
|
1549
|
-
}
|
|
1550
|
-
`,
|
|
1551
|
-
id: barIsHideId
|
|
1552
|
-
});
|
|
1553
|
-
} else {
|
|
1554
|
-
addInlineStyle({
|
|
1555
|
-
inlineStyle: `
|
|
1556
|
-
.${containerClassName}::after {
|
|
1557
|
-
display: block;
|
|
1558
|
-
}
|
|
1559
|
-
`,
|
|
1560
|
-
id: barIsHideId
|
|
1561
|
-
});
|
|
1562
|
-
}
|
|
1563
|
-
return false;
|
|
1564
|
-
}
|
|
1565
|
-
// 设置顶部状态栏
|
|
1566
|
-
function setBarTop () {
|
|
1567
|
-
const { per } = TopOverflowPercentRes;
|
|
1568
|
-
return addInlineStyle({
|
|
1569
|
-
inlineStyle: `
|
|
1570
|
-
.${containerClassName}::after {
|
|
1571
|
-
top: ${per};
|
|
1572
|
-
}
|
|
1573
|
-
`,
|
|
1574
|
-
id: barTopId
|
|
1575
|
-
});
|
|
1576
|
-
}
|
|
1577
|
-
// 设置底部状态栏
|
|
1578
|
-
function setBarBottom () {
|
|
1579
|
-
const { per } = BottomOverflowPercentRes;
|
|
1580
|
-
return addInlineStyle({
|
|
1581
|
-
inlineStyle: `
|
|
1582
|
-
.${containerClassName}::after {
|
|
1583
|
-
bottom: ${per};
|
|
1584
|
-
}
|
|
1585
|
-
`,
|
|
1586
|
-
id: barBottomId
|
|
1587
|
-
});
|
|
1588
|
-
}
|
|
1589
|
-
}, 25);
|
|
1590
|
-
return true;
|
|
1591
|
-
}
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
/**
|
|
1595
|
-
* @method calcContainImageSizeAndPosition
|
|
1596
|
-
* @description 计算适配容器宽度的图片的尺寸、距离顶部的距离。如果高度不足以占满容器,使其垂直居中;如果高度比容器长,由上向下铺开。
|
|
1597
|
-
* @param {Number} oriImageWidth 图片原始宽度
|
|
1598
|
-
* @param {Number} oriImageHeight 图片原始高度
|
|
1599
|
-
* @param {Number} viewportWidth 可视窗口宽度
|
|
1600
|
-
* @param {Number} viewportHeight 可视窗口高度
|
|
1601
|
-
* @return {Object} 结果 {"targetImageWidth":375,"targetImageHeight":375,"top":218.5,"wPer":1.25}
|
|
1602
|
-
*/
|
|
1603
|
-
export function calcContainImageSizeAndPosition({ oriImageWidth = 0, oriImageHeight = 0, viewportWidth = 0, viewportHeight = 0 } = {}): any {
|
|
1604
|
-
const targetImageWidth = viewportWidth;
|
|
1605
|
-
let [ targetImageHeight, top, wPer ] = [ 0, 0, 0 ];
|
|
1606
|
-
// 比例
|
|
1607
|
-
wPer = viewportWidth / oriImageWidth; // 1.+ 1.1 1.5
|
|
1608
|
-
targetImageHeight = oriImageHeight * wPer;
|
|
1609
|
-
if (targetImageHeight < viewportHeight) {
|
|
1610
|
-
const doubleGap = viewportHeight - targetImageHeight;
|
|
1611
|
-
top = doubleGap / 2;
|
|
1612
|
-
}
|
|
1613
|
-
return {
|
|
1614
|
-
targetImageWidth,
|
|
1615
|
-
targetImageHeight,
|
|
1616
|
-
top,
|
|
1617
|
-
wPer,
|
|
1618
|
-
};
|
|
1619
|
-
}
|
|
1
|
+
/* Deal */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @method calLongestCommonSubstring
|
|
5
|
+
* @description 计算两个字符串的最长公共子串
|
|
6
|
+
* @param {String} aStr 字符串
|
|
7
|
+
* @param {String} bStr 字符串
|
|
8
|
+
* @return {Number} 长度
|
|
9
|
+
*/
|
|
10
|
+
export function calLongestCommonSubstring(aStr: any, bStr: any) {
|
|
11
|
+
const aLen = aStr.length;
|
|
12
|
+
const bLen = bStr.length;
|
|
13
|
+
// 创建二维数组并且深拷贝
|
|
14
|
+
const arr = deepCopyObject(new Array(aLen).fill(new Array(bLen).fill(0)));
|
|
15
|
+
for (let i = 0; i < aLen; ++i) {
|
|
16
|
+
for (let j = 0; j < bLen; ++j) {
|
|
17
|
+
if (aStr[i] === bStr[j]) {
|
|
18
|
+
let baseNum = 0;
|
|
19
|
+
if (i > 0 && j > 0) {
|
|
20
|
+
baseNum = arr[i - 1][j - 1];
|
|
21
|
+
}
|
|
22
|
+
arr[i][j] = baseNum + 1;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// 二维数组转一维数组
|
|
27
|
+
const arr1 = Array.prototype.concat.apply([], arr);
|
|
28
|
+
// 获取最长公共子串
|
|
29
|
+
const maxLong = Math.max(...arr1);
|
|
30
|
+
return maxLong;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @method calLongestCommonSubsequence
|
|
35
|
+
* @description 计算两个字符串的最长公共子序列
|
|
36
|
+
* @param {String} aStr 字符串
|
|
37
|
+
* @param {String} bStr 字符串
|
|
38
|
+
* @return {Number} 长度
|
|
39
|
+
*/
|
|
40
|
+
export function calLongestCommonSubsequence(aStr: any, bStr: any) {
|
|
41
|
+
const aLen = aStr.length;
|
|
42
|
+
const bLen = bStr.length;
|
|
43
|
+
// 创建二维数组并且深拷贝
|
|
44
|
+
const arr = deepCopyObject(new Array(aLen).fill(new Array(bLen).fill(0)));
|
|
45
|
+
for (let i = 0; i < aLen; ++i) {
|
|
46
|
+
for (let j = 0; j < bLen; ++j) {
|
|
47
|
+
if (aStr[i] === bStr[j]) {
|
|
48
|
+
let baseNum = 0;
|
|
49
|
+
if (i > 0 && j > 0) {
|
|
50
|
+
baseNum = arr[i - 1][j - 1];
|
|
51
|
+
}
|
|
52
|
+
arr[i][j] = baseNum + 1;
|
|
53
|
+
} else {
|
|
54
|
+
let [leftValue, topValue] = [0, 0];
|
|
55
|
+
if (j > 0) {
|
|
56
|
+
leftValue = arr[i][j - 1];
|
|
57
|
+
}
|
|
58
|
+
if (i > 0) {
|
|
59
|
+
topValue = arr[i - 1][j];
|
|
60
|
+
}
|
|
61
|
+
arr[i][j] = Math.max(leftValue, topValue);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// 二维数组转一维数组
|
|
66
|
+
const arr1 = Array.prototype.concat.apply([], arr);
|
|
67
|
+
// 获取最长公共子串
|
|
68
|
+
const maxLong = Math.max(...arr1);
|
|
69
|
+
return maxLong;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @method join
|
|
74
|
+
* @description 将一系列值连接成固定字符分隔的字符串 123,456 => 123 - 456。
|
|
75
|
+
* @param {String} joinStr 连接值的字符串。
|
|
76
|
+
* @param {Rest} ...rest 需要连接的值 。
|
|
77
|
+
* */
|
|
78
|
+
export function join(joinStr: any, ...rest: any[]) {
|
|
79
|
+
let ret = '';
|
|
80
|
+
const len = joinStr.length;
|
|
81
|
+
for (const v of rest) {
|
|
82
|
+
if (v) {
|
|
83
|
+
ret += `${joinStr}${v}`;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (ret) {
|
|
87
|
+
ret = ret.substring(len);
|
|
88
|
+
}
|
|
89
|
+
return ret;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* @method renderTable
|
|
94
|
+
* @description 渲染表格
|
|
95
|
+
* @param {DOM Object} tbID
|
|
96
|
+
* @param {Array} data
|
|
97
|
+
* @param {Array} property
|
|
98
|
+
*/
|
|
99
|
+
export function renderTable(tbID = null, data = [], property = []) {
|
|
100
|
+
const TBODY: any = document.querySelector(`#${tbID} tbody`);
|
|
101
|
+
function mNullToNA(str: any) {
|
|
102
|
+
return str === null ? 'N.A.' : str;
|
|
103
|
+
}
|
|
104
|
+
let content = '';
|
|
105
|
+
TBODY.innerHTML = '';
|
|
106
|
+
// 无数据
|
|
107
|
+
if (!data.length) {
|
|
108
|
+
content = `<tr><td colspan="${property.length}">无数据</td></tr>`;
|
|
109
|
+
} else {
|
|
110
|
+
for (let [i, max] = [0, data.length]; i < max; ++i) {
|
|
111
|
+
const item = data[i];
|
|
112
|
+
content += '<tr>';
|
|
113
|
+
for (let [i, max] = [0, property.length]; i < max; ++i) {
|
|
114
|
+
content += `<td>${mNullToNA(item[property[i]])}</td>`;
|
|
115
|
+
}
|
|
116
|
+
content += '</tr>';
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
TBODY.innerHTML = content;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* @method getHashQueryParam
|
|
124
|
+
* @description 获取地址栏 hash 后面的参数。
|
|
125
|
+
* @param {String} param 获取参数的名字。
|
|
126
|
+
*/
|
|
127
|
+
export function getHashQueryParam(param: string) {
|
|
128
|
+
const hashs = window.location.hash.split('?');
|
|
129
|
+
if (hashs.length === 1) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
const reg = new RegExp(`(^|&)${param}=([^&]*)(&|$)`);
|
|
133
|
+
const ret = hashs[1].match(reg);
|
|
134
|
+
return ret ? ret[2] : null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* @method camelCaseToKebabCase
|
|
139
|
+
* @description 驼峰转连接线。
|
|
140
|
+
* @param {String} camelCase
|
|
141
|
+
* */
|
|
142
|
+
export function camelCaseToKebabCase(camelCase: string) {
|
|
143
|
+
const kebabCase = camelCase.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
144
|
+
return kebabCase[0] === '-' ? kebabCase.substr(1) : kebabCase;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* @method camelCase2Underscore
|
|
149
|
+
* @description 驼峰转下划线。
|
|
150
|
+
* @param {String} camelCase
|
|
151
|
+
* */
|
|
152
|
+
export function camelCase2Underscore(camelCase: string) {
|
|
153
|
+
const kebabCase = camelCase.replace(/([A-Z])/g, '_$1').toLowerCase();
|
|
154
|
+
return kebabCase[0] === '_' ? kebabCase.substr(1) : kebabCase;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* @method getDomain
|
|
159
|
+
* @description 获取地址中的域名(及其他参数)。
|
|
160
|
+
* @param {String} url
|
|
161
|
+
* @param {Array} rules ['hostname', 'pathname'] = 'km.mazey.net/plugins/servlet/mobile'
|
|
162
|
+
* */
|
|
163
|
+
export function getDomain({ url = '', rules = ['hostname'] } = {}) {
|
|
164
|
+
const aEl: any = document.createElement('a');
|
|
165
|
+
aEl.href = url;
|
|
166
|
+
return rules.reduce((ret, v, index) => {
|
|
167
|
+
ret += aEl[v];
|
|
168
|
+
return ret;
|
|
169
|
+
}, '');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* @method trim
|
|
174
|
+
* @description 去除左右空格。
|
|
175
|
+
* @param {String} str 需要去除两边空格的字符串。
|
|
176
|
+
* */
|
|
177
|
+
export function trim(str: string) {
|
|
178
|
+
str = str.replace(/^\s+/, ''); // 去除头部空格
|
|
179
|
+
let end = str.length - 1;
|
|
180
|
+
const ws = /\s/;
|
|
181
|
+
while (ws.test(str.charAt(end))) {
|
|
182
|
+
end--; // 最后一个非空格字符的索引
|
|
183
|
+
}
|
|
184
|
+
return str.slice(0, end + 1);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* @method newLine
|
|
189
|
+
* @description html换行。
|
|
190
|
+
* @param {String} str
|
|
191
|
+
* */
|
|
192
|
+
export function newLine(str: string) {
|
|
193
|
+
if (!str) {
|
|
194
|
+
return '';
|
|
195
|
+
}
|
|
196
|
+
const reg = new RegExp('\\n', 'g');
|
|
197
|
+
return str.replace(reg, '<br />');
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* @method deepCopyObject
|
|
202
|
+
* @description 对象深拷贝。
|
|
203
|
+
* @param {Object} obj 被拷贝的对象。
|
|
204
|
+
* @return {Object}
|
|
205
|
+
* */
|
|
206
|
+
export function deepCopyObject(obj: any) {
|
|
207
|
+
return JSON.parse(JSON.stringify(obj));
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* @method generateRndNum
|
|
212
|
+
* @description 生成随机数 mGenerateRndNum(7) => 7658495。
|
|
213
|
+
* @param {Number} n 随机数的长度
|
|
214
|
+
* @return {String}
|
|
215
|
+
* */
|
|
216
|
+
export function generateRndNum(n: number) {
|
|
217
|
+
let ret = '';
|
|
218
|
+
while (n--) {
|
|
219
|
+
ret += Math.floor(Math.random() * 10);
|
|
220
|
+
}
|
|
221
|
+
return ret;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* @method generateUniqueNum
|
|
226
|
+
* @description 根据时间生成唯一标志的数字 mGenerateUniqueNum() => 1538324722364123。
|
|
227
|
+
* @param {Number} n 随机数的长度
|
|
228
|
+
* */
|
|
229
|
+
export function generateUniqueNum(n: number) {
|
|
230
|
+
const [now, rnd] = [mNow(), generateRndNum(n || 3)];
|
|
231
|
+
return now + rnd;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* @method resetForm
|
|
236
|
+
* @description 重置表单输入值为原始(空)状态。
|
|
237
|
+
* @param {String} rest name1,name2,name3...NAME属性,可以多个。
|
|
238
|
+
* */
|
|
239
|
+
export function resetForm(...rest: any[]) {
|
|
240
|
+
for (let i = 0; i < rest.length; i++) {
|
|
241
|
+
const tagMz: any = document.getElementsByName(rest[i])[0]; // tag object
|
|
242
|
+
const tagNameMz = tagMz.tagName.toLowerCase(); // tag name
|
|
243
|
+
if (tagNameMz === 'input') {
|
|
244
|
+
tagMz.value = '';
|
|
245
|
+
} else if (tagNameMz === 'select') {
|
|
246
|
+
tagMz.options[0].selected = true;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* @method floatToPercent
|
|
253
|
+
* @description 浮点数转为百分比 0.2 => 20%。
|
|
254
|
+
* @param {Number} num 浮点数
|
|
255
|
+
* @param {Number} fixSize 保留几位浮点数
|
|
256
|
+
* */
|
|
257
|
+
export function floatToPercent(num: any, fixSize = 0): string {
|
|
258
|
+
if (fixSize) {
|
|
259
|
+
num = (num * 100).toFixed(fixSize);
|
|
260
|
+
} else {
|
|
261
|
+
num = Math.floor(num * 100);
|
|
262
|
+
}
|
|
263
|
+
return `${num}%`;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* @method floatFixed
|
|
268
|
+
* @description 浮点数保留指定位。
|
|
269
|
+
* */
|
|
270
|
+
export function floatFixed(num: any, size: any) {
|
|
271
|
+
return parseFloat(num).toFixed(size);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* @method compatibleExist
|
|
276
|
+
* @description 不存在返回 ——。
|
|
277
|
+
* */
|
|
278
|
+
export function compatibleExist(instance: string, replaceStr: string) {
|
|
279
|
+
let ret = '';
|
|
280
|
+
try {
|
|
281
|
+
ret = instance || replaceStr || '——';
|
|
282
|
+
} catch (e) {
|
|
283
|
+
ret = '——';
|
|
284
|
+
}
|
|
285
|
+
return ret;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* @method cancelBubble
|
|
290
|
+
* @description 阻止冒泡。
|
|
291
|
+
* */
|
|
292
|
+
export function cancelBubble(e: any) {
|
|
293
|
+
const ev = e || window.event;
|
|
294
|
+
if (ev.stopPropagation) {
|
|
295
|
+
// W3C
|
|
296
|
+
ev.stopPropagation();
|
|
297
|
+
} else {
|
|
298
|
+
// IE
|
|
299
|
+
ev.cancelBubble = true;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* @method hasClass
|
|
305
|
+
* */
|
|
306
|
+
export function hasClass(obj: any, cls: string) {
|
|
307
|
+
const oriCls = obj.className; // 获取对象的class值
|
|
308
|
+
const oriClsArr = oriCls.split(/\s+/); // 分隔空格转换成数组
|
|
309
|
+
for (let i = 0; i < oriClsArr.length; i++) {
|
|
310
|
+
if (oriClsArr[i] === cls) {
|
|
311
|
+
return true; // 若匹配到class则返回True
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return false; // 否则返回False
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* @method addClass
|
|
319
|
+
* */
|
|
320
|
+
export function addClass(obj: any, cls: string) {
|
|
321
|
+
const oriCls = obj.className;
|
|
322
|
+
let space = '';
|
|
323
|
+
let newCls = ''; // 获取对象的class值
|
|
324
|
+
if (oriCls !== '') {
|
|
325
|
+
space = ' '; // 若原来的class不为空,跟一个空格
|
|
326
|
+
}
|
|
327
|
+
newCls = oriCls + space + cls; // 将新的class加进去
|
|
328
|
+
obj.className = newCls; // 替换新class
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* @method removeClass
|
|
333
|
+
* */
|
|
334
|
+
export function removeClass(obj: any, cls: string) {
|
|
335
|
+
const oriCls = obj.className;
|
|
336
|
+
let newCls; // 获取对象的class值
|
|
337
|
+
newCls = ' ' + oriCls + ' '; // 前后加空格
|
|
338
|
+
newCls = newCls.replace(/(\s+)/gi, ' '); // 将多余的空格替换成一个空格
|
|
339
|
+
newCls = newCls.replace(' ' + cls + ' ', ' '); // 将加了前后空格的cls替换成空格' '
|
|
340
|
+
newCls = newCls.replace(/(^\s+)|(\s+$)/g, ''); // 去掉前后空格
|
|
341
|
+
obj.className = newCls;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* @method throttle
|
|
346
|
+
* @description 节流。
|
|
347
|
+
* */
|
|
348
|
+
export function throttle(func: any, wait: number, options: any) {
|
|
349
|
+
// timeout: setTimeout Handle
|
|
350
|
+
// previous: 上次时间戳
|
|
351
|
+
let context: any = null;
|
|
352
|
+
let args: any = null;
|
|
353
|
+
let timeout: any = null;
|
|
354
|
+
let [result, previous] = [null, 0];
|
|
355
|
+
if (!options) {
|
|
356
|
+
options = {};
|
|
357
|
+
}
|
|
358
|
+
const later = function () {
|
|
359
|
+
previous = options.leading === false ? 0 : mNow();
|
|
360
|
+
timeout = null;
|
|
361
|
+
result = func.apply(context, args);
|
|
362
|
+
if (!timeout) {
|
|
363
|
+
context = args = null;
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
return function (...argRest: Array<any>) {
|
|
367
|
+
const now = mNow();
|
|
368
|
+
if (!previous && options.leading === false) {
|
|
369
|
+
previous = now;
|
|
370
|
+
}
|
|
371
|
+
const remaining = wait - (now - previous);
|
|
372
|
+
context = this;
|
|
373
|
+
args = argRest;
|
|
374
|
+
if (remaining <= 0 || remaining > wait) {
|
|
375
|
+
if (timeout) {
|
|
376
|
+
clearTimeout(timeout);
|
|
377
|
+
timeout = null;
|
|
378
|
+
}
|
|
379
|
+
previous = now;
|
|
380
|
+
result = func.apply(context, args);
|
|
381
|
+
if (!timeout) {
|
|
382
|
+
context = args = null;
|
|
383
|
+
}
|
|
384
|
+
} else if (!timeout && options.trailing !== false) {
|
|
385
|
+
timeout = setTimeout(later, remaining);
|
|
386
|
+
}
|
|
387
|
+
return result;
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* @method debounce
|
|
393
|
+
* @description 去抖。
|
|
394
|
+
* */
|
|
395
|
+
export function debounce(func: any, wait: number, immediate: any) {
|
|
396
|
+
let context: any = null;
|
|
397
|
+
let timeout: any = null;
|
|
398
|
+
let timestamp: any = null;
|
|
399
|
+
let args: any = null;
|
|
400
|
+
let [result] = [null];
|
|
401
|
+
const later = function () {
|
|
402
|
+
const last = mNow() - timestamp;
|
|
403
|
+
if (last < wait && last >= 0) {
|
|
404
|
+
timeout = setTimeout(later, wait - last);
|
|
405
|
+
} else {
|
|
406
|
+
timeout = null;
|
|
407
|
+
if (!immediate) {
|
|
408
|
+
result = func.apply(context, args);
|
|
409
|
+
if (!timeout) {
|
|
410
|
+
context = args = null;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
return function (...argRest: Array<any>) {
|
|
416
|
+
context = this;
|
|
417
|
+
args = argRest;
|
|
418
|
+
timestamp = mNow();
|
|
419
|
+
const callNow = immediate && !timeout;
|
|
420
|
+
if (!timeout) {
|
|
421
|
+
timeout = setTimeout(later, wait);
|
|
422
|
+
}
|
|
423
|
+
if (callNow) {
|
|
424
|
+
result = func.apply(context, args);
|
|
425
|
+
context = args = null;
|
|
426
|
+
}
|
|
427
|
+
return result;
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* @method friendlyInterval
|
|
433
|
+
* @description 获取间隔时间。
|
|
434
|
+
* @param {Number/Date} start 开始时间戳 1585325367122
|
|
435
|
+
* @param {Number/Date} end 结束时间戳 1585325367122
|
|
436
|
+
* @type {String} type 返回类型 d: 2(天) text: 2 天 4 时...
|
|
437
|
+
* @return {String/Number} 取决于 type
|
|
438
|
+
* */
|
|
439
|
+
export function friendlyInterval({ start = 0, end = 0, type = 'd' } = {}) {
|
|
440
|
+
if (!isNumber(start)) start = new Date(start).getTime();
|
|
441
|
+
if (!isNumber(end)) end = new Date(end).getTime();
|
|
442
|
+
const t = end - start;
|
|
443
|
+
let ret = null;
|
|
444
|
+
let [d, h, m, s] = new Array(4).fill(0);
|
|
445
|
+
if (t >= 0) {
|
|
446
|
+
d = Math.floor(t / 1000 / 60 / 60 / 24);
|
|
447
|
+
h = Math.floor(t / 1000 / 60 / 60);
|
|
448
|
+
m = Math.floor(t / 1000 / 60);
|
|
449
|
+
s = Math.floor(t / 1000);
|
|
450
|
+
switch (type) {
|
|
451
|
+
case 'd':
|
|
452
|
+
ret = d;
|
|
453
|
+
break;
|
|
454
|
+
case 'text':
|
|
455
|
+
d = Math.floor(t / 1000 / 60 / 60 / 24);
|
|
456
|
+
h = Math.floor((t / 1000 / 60 / 60) % 24);
|
|
457
|
+
m = Math.floor((t / 1000 / 60) % 60);
|
|
458
|
+
s = Math.floor((t / 1000) % 60);
|
|
459
|
+
ret = d + ' 天 ' + h + ' 时 ' + m + ' 分 ' + s + ' 秒';
|
|
460
|
+
break;
|
|
461
|
+
default:
|
|
462
|
+
ret = s;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
return ret;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* @method isNumber
|
|
470
|
+
* @description 判断是否有效数字
|
|
471
|
+
* @param {Any} num 被判断的值
|
|
472
|
+
* @param {Boolean} isNaNAsNumber 是否 NaN 算数字(默认不算)
|
|
473
|
+
* @param {Boolean} isUnFiniteAsNumber 是否 无限 算数字(默认不算)
|
|
474
|
+
* @return {Boolean} true 是数字
|
|
475
|
+
*/
|
|
476
|
+
export function isNumber(num: any, { isNaNAsNumber = false, isUnFiniteAsNumber = false } = {}): boolean {
|
|
477
|
+
let ret = true;
|
|
478
|
+
// 数字类型
|
|
479
|
+
if (typeof num !== 'number') {
|
|
480
|
+
ret = false;
|
|
481
|
+
}
|
|
482
|
+
// 无限值
|
|
483
|
+
if (isUnFiniteAsNumber === false && !isFinite(num)) {
|
|
484
|
+
// console.log('1333');
|
|
485
|
+
ret = false;
|
|
486
|
+
}
|
|
487
|
+
// NaN
|
|
488
|
+
if (isNaNAsNumber === false && isNaN(num)) {
|
|
489
|
+
// console.log('2333');
|
|
490
|
+
ret = false;
|
|
491
|
+
}
|
|
492
|
+
return ret;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* @method updateQueryStringParameter
|
|
497
|
+
* @description 替换或添加地址栏参数。
|
|
498
|
+
* */
|
|
499
|
+
export function updateQueryStringParameter(uri: string, key: string, value: string) {
|
|
500
|
+
const re = new RegExp('([?&])' + key + '=.*?(&|$)', 'i');
|
|
501
|
+
const separator = uri.indexOf('?') !== -1 ? '&' : '?';
|
|
502
|
+
if (uri.match(re)) {
|
|
503
|
+
return uri.replace(re, '$1' + key + '=' + value + '$2');
|
|
504
|
+
} else {
|
|
505
|
+
return uri + separator + key + '=' + value;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* @method isJsonString
|
|
511
|
+
* @description 判断是否合法 JSON 字符串。
|
|
512
|
+
* */
|
|
513
|
+
export function isJsonString(str: string) {
|
|
514
|
+
try {
|
|
515
|
+
if (typeof JSON.parse(str) === 'object') {
|
|
516
|
+
return true;
|
|
517
|
+
}
|
|
518
|
+
} catch (e) {
|
|
519
|
+
/* pass */
|
|
520
|
+
}
|
|
521
|
+
return false;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* @method getUrlParam
|
|
526
|
+
* @description 链接参数
|
|
527
|
+
* @param {String} sUrl 链接
|
|
528
|
+
* @param {String} sKey 参数
|
|
529
|
+
* */
|
|
530
|
+
export function getUrlParam(sUrl: string, sKey: string) {
|
|
531
|
+
const result: any = {};
|
|
532
|
+
sUrl.replace(/\??(\w+)=([^&]*)&?/g, function (a, k, v): any {
|
|
533
|
+
if (result[k] !== undefined) {
|
|
534
|
+
const t = result[k];
|
|
535
|
+
result[k] = [].concat(t, v);
|
|
536
|
+
} else {
|
|
537
|
+
result[k] = v;
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
if (sKey === undefined) {
|
|
541
|
+
return result;
|
|
542
|
+
} else {
|
|
543
|
+
return result[sKey] || '';
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* @method getSearchQueryParam
|
|
549
|
+
* @description 地址栏参数。
|
|
550
|
+
* */
|
|
551
|
+
export function getSearchQueryParam(name: string) {
|
|
552
|
+
const reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)');
|
|
553
|
+
const r = window.location.search.substr(1).match(reg);
|
|
554
|
+
if (r != null) {
|
|
555
|
+
return decodeURIComponent(unescape(r[2]));
|
|
556
|
+
}
|
|
557
|
+
return null;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* @method getQueryParam
|
|
562
|
+
* @description 地址栏参数,getSearchQueryParam 的别名。
|
|
563
|
+
* */
|
|
564
|
+
export function getQueryParam(name: string) {
|
|
565
|
+
return getSearchQueryParam(name);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* @method doFn
|
|
570
|
+
* @description 执行有效函数
|
|
571
|
+
* @param {Function} fn 等待被执行的未知是否有效的函数
|
|
572
|
+
* */
|
|
573
|
+
export function doFn(fn: any, ...params: any[]) {
|
|
574
|
+
let ret = null;
|
|
575
|
+
if (fn && typeof fn === 'function') {
|
|
576
|
+
ret = fn(...params);
|
|
577
|
+
}
|
|
578
|
+
return ret;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/**
|
|
582
|
+
* @method setSessionStorage
|
|
583
|
+
* @description 存储数据到 sessionStorage
|
|
584
|
+
* @param {String} key 键
|
|
585
|
+
* @param {String} value 值
|
|
586
|
+
* */
|
|
587
|
+
export function setSessionStorage(key: string, value: any = null) {
|
|
588
|
+
if (key) {
|
|
589
|
+
sessionStorage.setItem(key, JSON.stringify(value));
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* @method getSessionStorage
|
|
595
|
+
* @description 存储数据到 sessionStorage
|
|
596
|
+
* @param {String} key 键
|
|
597
|
+
* @return {Any} 返回值
|
|
598
|
+
* */
|
|
599
|
+
export function getSessionStorage(key: string) {
|
|
600
|
+
let ret = null;
|
|
601
|
+
if (key) {
|
|
602
|
+
const value = sessionStorage.getItem(key);
|
|
603
|
+
if (value) {
|
|
604
|
+
ret = JSON.parse(value);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
return ret;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* @method setLocalStorage
|
|
612
|
+
* @description 存储数据到 localStorage
|
|
613
|
+
* @param {String} key 键
|
|
614
|
+
* @param {String} value 值
|
|
615
|
+
* */
|
|
616
|
+
export function setLocalStorage(key: string, value: any = null) {
|
|
617
|
+
if (key) {
|
|
618
|
+
localStorage.setItem(key, JSON.stringify(value));
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* @method getLocalStorage
|
|
624
|
+
* @description 存储数据到 localStorage
|
|
625
|
+
* @param {String} key 键
|
|
626
|
+
* @return {Any} 返回值
|
|
627
|
+
* */
|
|
628
|
+
export function getLocalStorage(key: string) {
|
|
629
|
+
let ret = null;
|
|
630
|
+
if (key) {
|
|
631
|
+
const value = localStorage.getItem(key);
|
|
632
|
+
if (value) {
|
|
633
|
+
ret = JSON.parse(value);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
return ret;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* @method loadCSS
|
|
641
|
+
* @description 动态加载css文件
|
|
642
|
+
* @param {String} url -- css资源路径
|
|
643
|
+
* @param {String} id -- link标签id
|
|
644
|
+
* @return {Promise<Boolean>} true -- 加载成功
|
|
645
|
+
*/
|
|
646
|
+
export function loadCSS({ url = '', id = '' } = {}) {
|
|
647
|
+
let success: any = null;
|
|
648
|
+
let fail: any = null;
|
|
649
|
+
const status = new Promise((resolve, reject) => {
|
|
650
|
+
([success, fail] = [resolve, reject]);
|
|
651
|
+
});
|
|
652
|
+
// const tempCB = (typeof callback === 'function' ? callback : function () { });
|
|
653
|
+
const callback = function () {
|
|
654
|
+
doFn(success, true);
|
|
655
|
+
};
|
|
656
|
+
let node: any = document.createElement('link');
|
|
657
|
+
const supportOnload = 'onload' in node;
|
|
658
|
+
const isOldWebKit = +navigator.userAgent.replace(/.*(?:AppleWebKit|AndroidWebKit)\/?(\d+).*/i, '$1') < 536; // webkit旧内核做特殊处理
|
|
659
|
+
const protectNum = 300000; // 阈值10分钟,一秒钟执行pollCss 500次
|
|
660
|
+
|
|
661
|
+
node.rel = 'stylesheet';
|
|
662
|
+
node.type = 'text/css';
|
|
663
|
+
node.href = url;
|
|
664
|
+
if (typeof id !== 'undefined') {
|
|
665
|
+
node.id = id;
|
|
666
|
+
}
|
|
667
|
+
document.getElementsByTagName('head')[0].appendChild(node);
|
|
668
|
+
|
|
669
|
+
// for Old WebKit and Old Firefox
|
|
670
|
+
if (isOldWebKit || !supportOnload) {
|
|
671
|
+
// Begin after node insertion
|
|
672
|
+
setTimeout(function () {
|
|
673
|
+
pollCss(node, callback, 0);
|
|
674
|
+
}, 1);
|
|
675
|
+
return status;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
if (supportOnload) {
|
|
679
|
+
node.onload = onload;
|
|
680
|
+
node.onerror = function () {
|
|
681
|
+
// 加载失败(404)
|
|
682
|
+
onload();
|
|
683
|
+
};
|
|
684
|
+
} else {
|
|
685
|
+
// todo: 和 !supportOnload 重复
|
|
686
|
+
node.onreadystatechange = function () {
|
|
687
|
+
if (/loaded|complete/.test(node.readyState)) {
|
|
688
|
+
onload();
|
|
689
|
+
}
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
function onload() {
|
|
694
|
+
// 确保只跑一次下载操作
|
|
695
|
+
node.onload = node.onerror = node.onreadystatechange = null;
|
|
696
|
+
|
|
697
|
+
// 清空node引用,在低版本IE,不清除会造成内存泄露
|
|
698
|
+
node = null;
|
|
699
|
+
|
|
700
|
+
callback();
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// 循环判断css是否已加载成功
|
|
704
|
+
/*
|
|
705
|
+
* @param node -- link节点
|
|
706
|
+
* @param callback -- 回调函数
|
|
707
|
+
* @param step -- 计步器,避免无限循环
|
|
708
|
+
*/
|
|
709
|
+
function pollCss(node: any, callback: any, step: number) {
|
|
710
|
+
const sheet = node.sheet;
|
|
711
|
+
let isLoaded: any;
|
|
712
|
+
|
|
713
|
+
step += 1;
|
|
714
|
+
|
|
715
|
+
// 保护,大于10分钟,则不再轮询
|
|
716
|
+
if (step > protectNum) {
|
|
717
|
+
isLoaded = true;
|
|
718
|
+
|
|
719
|
+
// 清空node引用
|
|
720
|
+
node = null;
|
|
721
|
+
|
|
722
|
+
callback();
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
if (isOldWebKit) {
|
|
727
|
+
// for WebKit < 536
|
|
728
|
+
if (sheet) {
|
|
729
|
+
isLoaded = true;
|
|
730
|
+
}
|
|
731
|
+
} else if (sheet) {
|
|
732
|
+
// for Firefox < 9.0
|
|
733
|
+
try {
|
|
734
|
+
if (sheet.cssRules) {
|
|
735
|
+
isLoaded = true;
|
|
736
|
+
}
|
|
737
|
+
} catch (ex) {
|
|
738
|
+
// 火狐特殊版本,通过特定值获知是否下载成功
|
|
739
|
+
// The value of `ex.name` is changed from "NS_ERROR_DOM_SECURITY_ERR"
|
|
740
|
+
// to "SecurityError" since Firefox 13.0. But Firefox is less than 9.0
|
|
741
|
+
// in here, So it is ok to just rely on "NS_ERROR_DOM_SECURITY_ERR"
|
|
742
|
+
if (ex.name === 'NS_ERROR_DOM_SECURITY_ERR') {
|
|
743
|
+
isLoaded = true;
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
setTimeout(function () {
|
|
749
|
+
if (isLoaded) {
|
|
750
|
+
// 延迟20ms是为了给下载的样式留够渲染的时间
|
|
751
|
+
callback();
|
|
752
|
+
} else {
|
|
753
|
+
pollCss(node, callback, step);
|
|
754
|
+
}
|
|
755
|
+
}, 20);
|
|
756
|
+
}
|
|
757
|
+
return status;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
/**
|
|
761
|
+
* @method loadScript
|
|
762
|
+
* @description 动态加载js文件
|
|
763
|
+
* @param {String} url -- js资源路径
|
|
764
|
+
* @param {String} id -- DOM ID
|
|
765
|
+
* @param {Function} callback -- 加载后回调函数
|
|
766
|
+
* @param {Number} timeout -- 超时时长
|
|
767
|
+
* @param {Boolean} isDefer -- 是否添加 defer 标签
|
|
768
|
+
* @return {Promise<Boolean>} -- true 成功
|
|
769
|
+
*/
|
|
770
|
+
export function loadScript({ url = '', id = '', callback = function () { /* pass */ }, timeout = 5000, isDefer = false } = {}): any {
|
|
771
|
+
let success: any = null;
|
|
772
|
+
let fail: any = null;
|
|
773
|
+
const script: any = document.createElement('script');
|
|
774
|
+
// 如果没有 script 标签,那么代码就不会运行。可以利用这一事实,在页面的第一个 script 标签上使用 insertBefore()。
|
|
775
|
+
const firstScript: any = document.getElementsByTagName('script')[0];
|
|
776
|
+
script.type = 'text/javascript';
|
|
777
|
+
if (isDefer) {
|
|
778
|
+
script.defer = 'defer';
|
|
779
|
+
}
|
|
780
|
+
if (id) {
|
|
781
|
+
script.id = id;
|
|
782
|
+
}
|
|
783
|
+
if (script.readyState) {
|
|
784
|
+
// IE
|
|
785
|
+
script.onreadystatechange = function () {
|
|
786
|
+
if (script.readyState === 'loaded' || script.readyState === 'complete') {
|
|
787
|
+
script.onreadystatechange = null;
|
|
788
|
+
doFn(callback);
|
|
789
|
+
doFn(success, true);
|
|
790
|
+
}
|
|
791
|
+
};
|
|
792
|
+
} else {
|
|
793
|
+
// Others
|
|
794
|
+
script.onload = function () {
|
|
795
|
+
doFn(callback);
|
|
796
|
+
doFn(success, true);
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
script.src = url;
|
|
800
|
+
firstScript && firstScript.parentNode.insertBefore(script, firstScript);
|
|
801
|
+
return new Promise((resolve, reject) => {
|
|
802
|
+
([success, fail] = [resolve, reject]);
|
|
803
|
+
if (timeout) {
|
|
804
|
+
setTimeout(fail.bind(null, 'timeout'), timeout);
|
|
805
|
+
}
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
/**
|
|
810
|
+
* @method mNow
|
|
811
|
+
* @description 获取时间戳
|
|
812
|
+
*/
|
|
813
|
+
export function mNow() {
|
|
814
|
+
let ret = 0;
|
|
815
|
+
if (Date.now) {
|
|
816
|
+
ret = Date.now();
|
|
817
|
+
} else {
|
|
818
|
+
ret = new Date().getTime();
|
|
819
|
+
}
|
|
820
|
+
return ret;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
/**
|
|
824
|
+
* @method setCookie
|
|
825
|
+
* @description 设置 Cookie
|
|
826
|
+
*/
|
|
827
|
+
export function setCookie(name: string, value: string, days: number, domain: string) {
|
|
828
|
+
let domainParts, expires;
|
|
829
|
+
let date: any;
|
|
830
|
+
if (days) {
|
|
831
|
+
date = new Date();
|
|
832
|
+
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
|
833
|
+
expires = "; expires=" + date.toGMTString();
|
|
834
|
+
} else {
|
|
835
|
+
expires = "";
|
|
836
|
+
}
|
|
837
|
+
const host = location.host;
|
|
838
|
+
if (host.split('.').length === 1) {
|
|
839
|
+
// no "." in a domain - it's localhost or something similar
|
|
840
|
+
document.cookie = name + "=" + value + expires + "; path=/";
|
|
841
|
+
} else {
|
|
842
|
+
// Remember the cookie on all subdomains.
|
|
843
|
+
//
|
|
844
|
+
// Start with trying to set cookie to the top domain.
|
|
845
|
+
// (example: if user is on foo.com, try to set
|
|
846
|
+
// cookie to domain ".com")
|
|
847
|
+
//
|
|
848
|
+
// If the cookie will not be set, it means ".com"
|
|
849
|
+
// is a top level domain and we need to
|
|
850
|
+
// set the cookie to ".foo.com"
|
|
851
|
+
domainParts = host.split('.');
|
|
852
|
+
domainParts.shift();
|
|
853
|
+
domain = domain || ('.' + domainParts.join('.'));
|
|
854
|
+
document.cookie = name + "=" + value + expires + "; path=/; domain=" + domain;
|
|
855
|
+
// check if cookie was successfuly set to the given domain
|
|
856
|
+
// (otherwise it was a Top-Level Domain)
|
|
857
|
+
if (getCookie(name) === null || getCookie(name) !== value) {
|
|
858
|
+
// append "." to current domain
|
|
859
|
+
domain = domain || ('.' + host);
|
|
860
|
+
document.cookie = name + "=" + value + expires + "; path=/; domain=" + domain;
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
/**
|
|
866
|
+
* @method getCookie
|
|
867
|
+
* @description 获取 Cookie
|
|
868
|
+
*/
|
|
869
|
+
export function getCookie(name: string) {
|
|
870
|
+
const nameEQ = name + "=";
|
|
871
|
+
const ca = document.cookie.split(';');
|
|
872
|
+
for (let i = 0; i < ca.length; i++) {
|
|
873
|
+
let c = ca[i];
|
|
874
|
+
while (c.charAt(0) == ' ') {
|
|
875
|
+
c = c.substring(1, c.length);
|
|
876
|
+
}
|
|
877
|
+
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
|
|
878
|
+
}
|
|
879
|
+
return null;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
/**
|
|
883
|
+
* @method getPerformance
|
|
884
|
+
* @description 获取页面加载相关的各项数据
|
|
885
|
+
* @param {Boolean} camelCase -- true(默认) 以驼峰形式返回数据 false 以下划线形式返回数据
|
|
886
|
+
* @return {Promise<Object>} 加载数据
|
|
887
|
+
*/
|
|
888
|
+
export function getPerformance({ camelCase = true } = {}) {
|
|
889
|
+
let success: any;
|
|
890
|
+
let fail: any;
|
|
891
|
+
const status = new Promise((resolve, reject) => {
|
|
892
|
+
([success, fail] = [resolve, reject]);
|
|
893
|
+
});
|
|
894
|
+
const timing = window.performance.timing;
|
|
895
|
+
const startTime = timing.navigationStart || timing.fetchStart;
|
|
896
|
+
let firstPaintTime: any;
|
|
897
|
+
let firstContentfulPaintTime: any;
|
|
898
|
+
// 是否已经形成数据(页面加载完成之后)
|
|
899
|
+
if (window.performance && window.performance.timing && window.performance.timing.loadEventEnd > 0) {
|
|
900
|
+
// console.log('created')
|
|
901
|
+
getTiming();
|
|
902
|
+
} else {
|
|
903
|
+
// console.log('creating')
|
|
904
|
+
window.addEventListener("load", function() {
|
|
905
|
+
//不能影响最后的时间计算
|
|
906
|
+
window.setTimeout(function() {
|
|
907
|
+
getTiming();
|
|
908
|
+
}, 0);
|
|
909
|
+
});
|
|
910
|
+
}
|
|
911
|
+
// performance
|
|
912
|
+
function getTiming() {
|
|
913
|
+
// 获取首次渲染时间
|
|
914
|
+
try {
|
|
915
|
+
if (window.performance && Boolean(window.performance.getEntriesByType)) {
|
|
916
|
+
const performance = window.performance;
|
|
917
|
+
const performanceEntries = performance.getEntriesByType('paint');
|
|
918
|
+
performanceEntries.forEach((performanceEntry, i, entries) => {
|
|
919
|
+
const startTime = Math.round(performanceEntry.startTime);
|
|
920
|
+
if (performanceEntry.name === 'first-paint')
|
|
921
|
+
firstPaintTime = startTime;
|
|
922
|
+
else if (performanceEntry.name === 'first-contentful-paint')
|
|
923
|
+
firstContentfulPaintTime = startTime;
|
|
924
|
+
});
|
|
925
|
+
} else {
|
|
926
|
+
console.error('paint');
|
|
927
|
+
}
|
|
928
|
+
} catch (e) {
|
|
929
|
+
console.error(e.message);
|
|
930
|
+
}
|
|
931
|
+
// 获取加载时间
|
|
932
|
+
if (
|
|
933
|
+
window.performance &&
|
|
934
|
+
typeof window.performance.getEntries === 'function'
|
|
935
|
+
) {
|
|
936
|
+
const performanceNavigationTiming: any =
|
|
937
|
+
(window.performance.getEntries() || [])[0] || {};
|
|
938
|
+
const data: any = {
|
|
939
|
+
// url: encodeURI(location.href),
|
|
940
|
+
// ua: navigator.userAgent,
|
|
941
|
+
os: getOS(),
|
|
942
|
+
osVersion: getOSVersion(),
|
|
943
|
+
deviceType: getDeviceType(),
|
|
944
|
+
network: getNetWork(),
|
|
945
|
+
screenDirection: getOrientationStatu(),
|
|
946
|
+
unloadTime: timing.unloadEventEnd - timing.unloadEventStart, //上个文档的卸载时间
|
|
947
|
+
redirectTime: timing.redirectEnd - timing.redirectStart, //*重定向时间
|
|
948
|
+
dnsTime: timing.domainLookupEnd - timing.domainLookupStart, //*DNS查询时间
|
|
949
|
+
tcpTime: timing.connectEnd - timing.connectStart, //*服务器连接时间
|
|
950
|
+
sslTime: getSSLTime(timing.connectEnd, timing.secureConnectionStart), //*ssl连接时间
|
|
951
|
+
responseTime: timing.responseStart - timing.requestStart, //*服务器响应时间
|
|
952
|
+
downloadTime: timing.responseEnd - timing.responseStart, //*网页下载时间
|
|
953
|
+
firstPaintTime: firstPaintTime, //*首次渲染时间
|
|
954
|
+
firstContentfulPaintTime: firstContentfulPaintTime, //*首次渲染内容时间
|
|
955
|
+
domreadyTime: timing.domContentLoadedEventStart - startTime || 0, //*dom ready时间(总和)
|
|
956
|
+
onloadTime: timing.loadEventStart - startTime || 0, //*onload时间(总和)
|
|
957
|
+
whiteTime: timing.responseStart - startTime, //*白屏时间
|
|
958
|
+
renderTime: timing.loadEventEnd - startTime || 0, //整个过程的时间之和
|
|
959
|
+
decodedBodySize: performanceNavigationTiming.decodedBodySize || '', //页面压缩前大小
|
|
960
|
+
encodedBodySize: performanceNavigationTiming.encodedBodySize || '', //页面压缩后大小
|
|
961
|
+
};
|
|
962
|
+
// 过滤异常数据
|
|
963
|
+
Object.keys(data).forEach(k => {
|
|
964
|
+
// 过滤掉 <0 的数据
|
|
965
|
+
if (isNumber(data[k]) && data[k] < 0) {
|
|
966
|
+
data[k] = 0;
|
|
967
|
+
}
|
|
968
|
+
});
|
|
969
|
+
// 过滤掉白屏时间 > onload 的数据
|
|
970
|
+
if (isNumber(data.whiteTime) && data.whiteTime > data.onloadTime) {
|
|
971
|
+
data.whiteTime = 0;
|
|
972
|
+
}
|
|
973
|
+
if (startTime > 0) {
|
|
974
|
+
let Underscore: any;
|
|
975
|
+
if (!camelCase) {
|
|
976
|
+
Object.keys(data).forEach(k => {
|
|
977
|
+
if (!Underscore) Underscore = {};
|
|
978
|
+
// console.log('camelCase2Underscore', k, data, )
|
|
979
|
+
Underscore[camelCase2Underscore(k)] = data[k];
|
|
980
|
+
});
|
|
981
|
+
}
|
|
982
|
+
success(Underscore || data);
|
|
983
|
+
} else {
|
|
984
|
+
fail('startTime');
|
|
985
|
+
}
|
|
986
|
+
} else {
|
|
987
|
+
fail('getEntries');
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
//获取当前操作系统
|
|
991
|
+
function getOS() {
|
|
992
|
+
let os;
|
|
993
|
+
if (navigator.userAgent.indexOf('Android') > -1 || navigator.userAgent.indexOf('Linux') > -1) {
|
|
994
|
+
os = 'android';
|
|
995
|
+
} else if (navigator.userAgent.indexOf('iPhone') > -1) {
|
|
996
|
+
os = 'ios';
|
|
997
|
+
} else if (navigator.userAgent.indexOf('Windows Phone') > -1) {
|
|
998
|
+
os = 'wp';
|
|
999
|
+
} else {
|
|
1000
|
+
os = 'others';
|
|
1001
|
+
}
|
|
1002
|
+
return os;
|
|
1003
|
+
}
|
|
1004
|
+
// 获取操作系统版本
|
|
1005
|
+
function getOSVersion() {
|
|
1006
|
+
let OSVision: any;
|
|
1007
|
+
const u = navigator.userAgent;
|
|
1008
|
+
const isAndroid = u.indexOf('Android') > -1 || u.indexOf('Linux') > -1; //Android
|
|
1009
|
+
const isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios终端
|
|
1010
|
+
if (isAndroid) {
|
|
1011
|
+
OSVision = (navigator.userAgent.split(';')[1].match(/\d+\.\d+/g) || [])[0];
|
|
1012
|
+
}
|
|
1013
|
+
if (isIOS) {
|
|
1014
|
+
OSVision = (navigator.userAgent.split(';')[1].match(/(\d+)_(\d+)_?(\d+)?/) || [])[0];
|
|
1015
|
+
}
|
|
1016
|
+
return OSVision;
|
|
1017
|
+
}
|
|
1018
|
+
//获取设备类型
|
|
1019
|
+
function getDeviceType() {
|
|
1020
|
+
let deviceType;
|
|
1021
|
+
const sUserAgent = navigator.userAgent.toLowerCase();
|
|
1022
|
+
const bIsIpad = sUserAgent.match(/(ipad)/i) && 'ipad';
|
|
1023
|
+
const bIsIphoneOs = sUserAgent.match(/iphone os/i) && 'iphone os';
|
|
1024
|
+
const bIsMidp = sUserAgent.match(/midp/i) && 'midp';
|
|
1025
|
+
const bIsUc7 = sUserAgent.match(/rv:1.2.3.4/i) && 'rv:1.2.3.4';
|
|
1026
|
+
const bIsUc = sUserAgent.match(/ucweb/i) && 'ucweb';
|
|
1027
|
+
const bIsAndroid = sUserAgent.match(/android/i) && 'android';
|
|
1028
|
+
const bIsCE = sUserAgent.match(/windows ce/i) && 'windows ce';
|
|
1029
|
+
const bIsWM = sUserAgent.match(/windows mobile/i) && 'windows mobile';
|
|
1030
|
+
if (!(bIsIpad || bIsIphoneOs || bIsMidp || bIsUc7 || bIsUc || bIsAndroid || bIsCE || bIsWM)) {
|
|
1031
|
+
deviceType = 'pc'; //pc
|
|
1032
|
+
} else if (bIsIphoneOs || bIsMidp || bIsUc7 || bIsUc || bIsAndroid || bIsCE || bIsWM) {
|
|
1033
|
+
deviceType = 'phone'; //phone
|
|
1034
|
+
} else if (bIsIpad) {
|
|
1035
|
+
deviceType = 'ipad'; //ipad
|
|
1036
|
+
} else {
|
|
1037
|
+
deviceType = undefined;
|
|
1038
|
+
}
|
|
1039
|
+
return deviceType;
|
|
1040
|
+
}
|
|
1041
|
+
// 获取网络状态
|
|
1042
|
+
function getNetWork() {
|
|
1043
|
+
let netWork: any;
|
|
1044
|
+
if ((navigator as any).connection && (navigator as any).connection.effectiveType) {
|
|
1045
|
+
switch ((navigator as any).connection.effectiveType) {
|
|
1046
|
+
case 'wifi':
|
|
1047
|
+
netWork = 'wifi'; // wifi
|
|
1048
|
+
break;
|
|
1049
|
+
case '4g':
|
|
1050
|
+
netWork = '4g'; // 4g
|
|
1051
|
+
break;
|
|
1052
|
+
case '2g':
|
|
1053
|
+
netWork = '2g'; // 2g
|
|
1054
|
+
break;
|
|
1055
|
+
case '3g':
|
|
1056
|
+
netWork = '3g'; // 3g
|
|
1057
|
+
break;
|
|
1058
|
+
case 'ethernet':
|
|
1059
|
+
netWork = 'ethernet'; // ethernet
|
|
1060
|
+
break;
|
|
1061
|
+
case 'default':
|
|
1062
|
+
netWork = undefined; // 未知
|
|
1063
|
+
break;
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
return netWork;
|
|
1067
|
+
}
|
|
1068
|
+
// 获取横竖屏状态
|
|
1069
|
+
function getOrientationStatu() {
|
|
1070
|
+
let orientationStatu: any;
|
|
1071
|
+
if (window.screen && window.screen.orientation && window.screen.orientation.angle) {
|
|
1072
|
+
if (window.screen.orientation.angle === 180 || window.screen.orientation.angle === 0) { // 竖屏
|
|
1073
|
+
orientationStatu = '|';
|
|
1074
|
+
}
|
|
1075
|
+
if (window.screen.orientation.angle === 90 || window.screen.orientation.angle === -90) { // 横屏
|
|
1076
|
+
orientationStatu = '-';
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
return orientationStatu;
|
|
1080
|
+
}
|
|
1081
|
+
// 获取ssl连接时间
|
|
1082
|
+
function getSSLTime(connectEnd: any, secureConnectionStart: any) {
|
|
1083
|
+
let ssl_time: any;
|
|
1084
|
+
if (secureConnectionStart) {
|
|
1085
|
+
ssl_time = connectEnd - secureConnectionStart;
|
|
1086
|
+
}
|
|
1087
|
+
return ssl_time;
|
|
1088
|
+
}
|
|
1089
|
+
return status;
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
/**
|
|
1093
|
+
* @method inRate
|
|
1094
|
+
* @description 百分位概率
|
|
1095
|
+
* @param {Number} rate -- 0.1 ~ 1 => 1% ~ 100%
|
|
1096
|
+
* @return {Boolean} true 命中
|
|
1097
|
+
*/
|
|
1098
|
+
export function inRate(rate: number): boolean {
|
|
1099
|
+
if(Math.random() < rate) {
|
|
1100
|
+
return true;
|
|
1101
|
+
}
|
|
1102
|
+
return false;
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
/**
|
|
1106
|
+
* @method isSafePWAEnv
|
|
1107
|
+
* @description 判断是否是安全的 PWA 环境
|
|
1108
|
+
* @return {Boolean} true 是
|
|
1109
|
+
*/
|
|
1110
|
+
export function isSafePWAEnv(): boolean {
|
|
1111
|
+
// 判断是否支持 async await
|
|
1112
|
+
function isSupportAsyncAwait() {
|
|
1113
|
+
let isSupportAsyncAwaitFunc;
|
|
1114
|
+
try {
|
|
1115
|
+
// eval("func = async function(){};");
|
|
1116
|
+
const fn = new Function("return async function(){};");
|
|
1117
|
+
isSupportAsyncAwaitFunc = fn();
|
|
1118
|
+
// console.log('isSupportAsyncAwaitFunc', isSupportAsyncAwaitFunc);
|
|
1119
|
+
// 由于async函数的构造器不是全局对象,所以我们需要由下面代码来获取async函数的构造器
|
|
1120
|
+
// 具体可以查看以下MDN上有关于AsyncFunction的说明,
|
|
1121
|
+
// 地址:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncFunction
|
|
1122
|
+
return Object.getPrototypeOf(isSupportAsyncAwaitFunc).constructor != null;
|
|
1123
|
+
} catch (e) {
|
|
1124
|
+
return false;
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
// 判断是否支持 Promise
|
|
1128
|
+
function isSupportPromise() {
|
|
1129
|
+
if(typeof Promise !== "undefined" && Promise.toString().indexOf("[native code]") !== -1){
|
|
1130
|
+
return true;
|
|
1131
|
+
}
|
|
1132
|
+
return false;
|
|
1133
|
+
}
|
|
1134
|
+
// 浏览器信息
|
|
1135
|
+
const BrowserType = getBrowserType();
|
|
1136
|
+
if (
|
|
1137
|
+
('serviceWorker' in navigator) &&
|
|
1138
|
+
isSupportAsyncAwait() &&
|
|
1139
|
+
isSupportPromise() &&
|
|
1140
|
+
Boolean(window.fetch) &&
|
|
1141
|
+
Boolean(window.indexedDB) &&
|
|
1142
|
+
Boolean(window.caches) &&
|
|
1143
|
+
!BrowserType['shell']
|
|
1144
|
+
) {
|
|
1145
|
+
return true;
|
|
1146
|
+
}
|
|
1147
|
+
return false;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
/**
|
|
1151
|
+
* @method getBrowserType
|
|
1152
|
+
* @description 返回浏览器信息 https://github.com/JowayYoung/juejin-code/blob/master/browser-type.js
|
|
1153
|
+
* @return {Object} 浏览器信息
|
|
1154
|
+
*/
|
|
1155
|
+
export function getBrowserType(): any {
|
|
1156
|
+
try {
|
|
1157
|
+
// 权重:系统 + 系统版本 > 平台 > 内核 + 载体 + 内核版本 + 载体版本 > 外壳 + 外壳版本
|
|
1158
|
+
const ua: any = navigator.userAgent.toLowerCase();
|
|
1159
|
+
const testUa: (regexp: any) => any = regexp => regexp.test(ua);
|
|
1160
|
+
const testVs: (regexp: any) => any = regexp => ua.match(regexp)
|
|
1161
|
+
.toString()
|
|
1162
|
+
.replace(/[^0-9|_.]/g, "")
|
|
1163
|
+
.replace(/_/g, ".");
|
|
1164
|
+
// 系统
|
|
1165
|
+
let system = "unknow";
|
|
1166
|
+
if (testUa(/windows|win32|win64|wow32|wow64/g)) {
|
|
1167
|
+
system = "windows"; // windows系统
|
|
1168
|
+
} else if (testUa(/macintosh|macintel/g)) {
|
|
1169
|
+
system = "macos"; // macos系统
|
|
1170
|
+
} else if (testUa(/x11/g)) {
|
|
1171
|
+
system = "linux"; // linux系统
|
|
1172
|
+
} else if (testUa(/android|adr/g)) {
|
|
1173
|
+
system = "android"; // android系统
|
|
1174
|
+
} else if (testUa(/ios|iphone|ipad|ipod|iwatch/g)) {
|
|
1175
|
+
system = "ios"; // ios系统
|
|
1176
|
+
}
|
|
1177
|
+
// 系统版本
|
|
1178
|
+
let systemVs = "unknow";
|
|
1179
|
+
if (system === "windows") {
|
|
1180
|
+
if (testUa(/windows nt 5.0|windows 2000/g)) {
|
|
1181
|
+
systemVs = "2000";
|
|
1182
|
+
} else if (testUa(/windows nt 5.1|windows xp/g)) {
|
|
1183
|
+
systemVs = "xp";
|
|
1184
|
+
} else if (testUa(/windows nt 5.2|windows 2003/g)) {
|
|
1185
|
+
systemVs = "2003";
|
|
1186
|
+
} else if (testUa(/windows nt 6.0|windows vista/g)) {
|
|
1187
|
+
systemVs = "vista";
|
|
1188
|
+
} else if (testUa(/windows nt 6.1|windows 7/g)) {
|
|
1189
|
+
systemVs = "7";
|
|
1190
|
+
} else if (testUa(/windows nt 6.2|windows 8/g)) {
|
|
1191
|
+
systemVs = "8";
|
|
1192
|
+
} else if (testUa(/windows nt 6.3|windows 8.1/g)) {
|
|
1193
|
+
systemVs = "8.1";
|
|
1194
|
+
} else if (testUa(/windows nt 10.0|windows 10/g)) {
|
|
1195
|
+
systemVs = "10";
|
|
1196
|
+
}
|
|
1197
|
+
} else if (system === "macos") {
|
|
1198
|
+
systemVs = testVs(/os x [\d._]+/g);
|
|
1199
|
+
} else if (system === "android") {
|
|
1200
|
+
systemVs = testVs(/android [\d._]+/g);
|
|
1201
|
+
} else if (system === "ios") {
|
|
1202
|
+
systemVs = testVs(/os [\d._]+/g);
|
|
1203
|
+
}
|
|
1204
|
+
// 平台
|
|
1205
|
+
let platform = "unknow";
|
|
1206
|
+
if (system === "windows" || system === "macos" || system === "linux") {
|
|
1207
|
+
platform = "desktop"; // 桌面端
|
|
1208
|
+
} else if (system === "android" || system === "ios" || testUa(/mobile/g)) {
|
|
1209
|
+
platform = "mobile"; // 移动端
|
|
1210
|
+
}
|
|
1211
|
+
// 内核和载体
|
|
1212
|
+
let engine = "unknow";
|
|
1213
|
+
let supporter = "unknow";
|
|
1214
|
+
if (testUa(/applewebkit/g)) {
|
|
1215
|
+
engine = "webkit"; // webkit内核
|
|
1216
|
+
if (testUa(/edge/g)) {
|
|
1217
|
+
supporter = "edge"; // edge浏览器
|
|
1218
|
+
} else if (testUa(/opr/g)) {
|
|
1219
|
+
supporter = "opera"; // opera浏览器
|
|
1220
|
+
} else if (testUa(/chrome/g)) {
|
|
1221
|
+
supporter = "chrome"; // chrome浏览器
|
|
1222
|
+
} else if (testUa(/safari/g)) {
|
|
1223
|
+
supporter = "safari"; // safari浏览器
|
|
1224
|
+
}
|
|
1225
|
+
} else if (testUa(/gecko/g) && testUa(/firefox/g)) {
|
|
1226
|
+
engine = "gecko"; // gecko内核
|
|
1227
|
+
supporter = "firefox"; // firefox浏览器
|
|
1228
|
+
} else if (testUa(/presto/g)) {
|
|
1229
|
+
engine = "presto"; // presto内核
|
|
1230
|
+
supporter = "opera"; // opera浏览器
|
|
1231
|
+
} else if (testUa(/trident|compatible|msie/g)) {
|
|
1232
|
+
engine = "trident"; // trident内核
|
|
1233
|
+
supporter = "iexplore"; // iexplore浏览器
|
|
1234
|
+
}
|
|
1235
|
+
// 内核版本
|
|
1236
|
+
let engineVs = "unknow";
|
|
1237
|
+
if (engine === "webkit") {
|
|
1238
|
+
engineVs = testVs(/applewebkit\/[\d._]+/g);
|
|
1239
|
+
} else if (engine === "gecko") {
|
|
1240
|
+
engineVs = testVs(/gecko\/[\d._]+/g);
|
|
1241
|
+
} else if (engine === "presto") {
|
|
1242
|
+
engineVs = testVs(/presto\/[\d._]+/g);
|
|
1243
|
+
} else if (engine === "trident") {
|
|
1244
|
+
engineVs = testVs(/trident\/[\d._]+/g);
|
|
1245
|
+
}
|
|
1246
|
+
// 载体版本
|
|
1247
|
+
let supporterVs = "unknow";
|
|
1248
|
+
if (supporter === "chrome") {
|
|
1249
|
+
supporterVs = testVs(/chrome\/[\d._]+/g);
|
|
1250
|
+
} else if (supporter === "safari") {
|
|
1251
|
+
supporterVs = testVs(/version\/[\d._]+/g);
|
|
1252
|
+
} else if (supporter === "firefox") {
|
|
1253
|
+
supporterVs = testVs(/firefox\/[\d._]+/g);
|
|
1254
|
+
} else if (supporter === "opera") {
|
|
1255
|
+
supporterVs = testVs(/opr\/[\d._]+/g);
|
|
1256
|
+
} else if (supporter === "iexplore") {
|
|
1257
|
+
supporterVs = testVs(/(msie [\d._]+)|(rv:[\d._]+)/g);
|
|
1258
|
+
} else if (supporter === "edge") {
|
|
1259
|
+
supporterVs = testVs(/edge\/[\d._]+/g);
|
|
1260
|
+
}
|
|
1261
|
+
// 外壳和外壳版本
|
|
1262
|
+
let shell = "none";
|
|
1263
|
+
let shellVs = "unknow";
|
|
1264
|
+
if (testUa(/micromessenger/g)) {
|
|
1265
|
+
shell = "wechat"; // 微信浏览器
|
|
1266
|
+
shellVs = testVs(/micromessenger\/[\d._]+/g);
|
|
1267
|
+
} else if (testUa(/qqbrowser/g)) {
|
|
1268
|
+
shell = "qq"; // QQ浏览器
|
|
1269
|
+
shellVs = testVs(/qqbrowser\/[\d._]+/g);
|
|
1270
|
+
} else if (testUa(/ucbrowser/g)) {
|
|
1271
|
+
shell = "uc"; // UC浏览器
|
|
1272
|
+
shellVs = testVs(/ucbrowser\/[\d._]+/g);
|
|
1273
|
+
} else if (testUa(/qihu 360se/g)) {
|
|
1274
|
+
shell = "360"; // 360浏览器(无版本)
|
|
1275
|
+
} else if (testUa(/2345explorer/g)) {
|
|
1276
|
+
shell = "2345"; // 2345浏览器
|
|
1277
|
+
shellVs = testVs(/2345explorer\/[\d._]+/g);
|
|
1278
|
+
} else if (testUa(/metasr/g)) {
|
|
1279
|
+
shell = "sougou"; // 搜狗浏览器(无版本)
|
|
1280
|
+
} else if (testUa(/lbbrowser/g)) {
|
|
1281
|
+
shell = "liebao"; // 猎豹浏览器(无版本)
|
|
1282
|
+
} else if (testUa(/maxthon/g)) {
|
|
1283
|
+
shell = "maxthon"; // 遨游浏览器
|
|
1284
|
+
shellVs = testVs(/maxthon\/[\d._]+/g);
|
|
1285
|
+
}
|
|
1286
|
+
return Object.assign({
|
|
1287
|
+
engine, // webkit gecko presto trident
|
|
1288
|
+
engineVs,
|
|
1289
|
+
platform, // desktop mobile
|
|
1290
|
+
supporter, // chrome safari firefox opera iexplore edge
|
|
1291
|
+
supporterVs,
|
|
1292
|
+
system, // windows macos linux android ios
|
|
1293
|
+
systemVs
|
|
1294
|
+
}, shell === "none" ? {} : {
|
|
1295
|
+
shell, // wechat qq uc 360 2345 sougou liebao maxthon
|
|
1296
|
+
shellVs
|
|
1297
|
+
});
|
|
1298
|
+
} catch (err) {
|
|
1299
|
+
return {};
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
/**
|
|
1304
|
+
* @method clearHtml
|
|
1305
|
+
* @description 去除HTML标签
|
|
1306
|
+
* @param {String} string 带html标签的字符串
|
|
1307
|
+
* @return {String} 字符串
|
|
1308
|
+
*/
|
|
1309
|
+
export function clearHtml (string = ''): string {
|
|
1310
|
+
return string.replace(/<\/?.+?>/g, '').replace(/[\r\n]/g, '');
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
/**
|
|
1314
|
+
* @method cutCHSString
|
|
1315
|
+
* @description 截取字符串,中文算2个字节
|
|
1316
|
+
* @param {String} str 要截取的字符串
|
|
1317
|
+
* @param {Number} len
|
|
1318
|
+
* @param {Boolean} hasDot
|
|
1319
|
+
* @returns {String} 返回截取后的字符串
|
|
1320
|
+
*/
|
|
1321
|
+
export function cutCHSString(str = '', len = str.length, hasDot = false): string {
|
|
1322
|
+
if (str == '' || !str) {
|
|
1323
|
+
return '';
|
|
1324
|
+
} else {
|
|
1325
|
+
let newLength = 0;
|
|
1326
|
+
let newStr = '';
|
|
1327
|
+
// eslint-disable-next-line no-control-regex
|
|
1328
|
+
const chineseRegex = /[^\x00-\xff]/g;
|
|
1329
|
+
let singleChar = '';
|
|
1330
|
+
const strLength = str.replace(chineseRegex, '**').length;
|
|
1331
|
+
for (let i = 0; i < strLength; i++) {
|
|
1332
|
+
singleChar = str.charAt(i).toString();
|
|
1333
|
+
if (singleChar.match(chineseRegex) != null) {
|
|
1334
|
+
newLength += 2;
|
|
1335
|
+
} else {
|
|
1336
|
+
newLength++;
|
|
1337
|
+
}
|
|
1338
|
+
if (newLength > len) {
|
|
1339
|
+
break;
|
|
1340
|
+
}
|
|
1341
|
+
newStr += singleChar;
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
if (hasDot && strLength > len) {
|
|
1345
|
+
newStr += '...';
|
|
1346
|
+
}
|
|
1347
|
+
return newStr;
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
/**
|
|
1352
|
+
* @method windowLoaded
|
|
1353
|
+
* @description 页面加载完成
|
|
1354
|
+
* @param {Number} timeout 超时时间 / 单位:秒
|
|
1355
|
+
* @return {Promise<string>} document is loaded? 'complete' 'load' / 'timeout'
|
|
1356
|
+
*/
|
|
1357
|
+
export function windowLoaded({ timeout = 90 } = {}): Promise<string> {
|
|
1358
|
+
let loaded: (value: string) => void = () => undefined;
|
|
1359
|
+
let loadFail: (value: string) => void = () => undefined;
|
|
1360
|
+
const status = new Promise((resolve: (value: string) => void, reject: (value: string) => void) => {
|
|
1361
|
+
loaded = resolve;
|
|
1362
|
+
loadFail = reject;
|
|
1363
|
+
});
|
|
1364
|
+
if (document.readyState === 'complete') {
|
|
1365
|
+
loaded('complete');
|
|
1366
|
+
} else {
|
|
1367
|
+
window.addEventListener('load', () => loaded('load'));
|
|
1368
|
+
}
|
|
1369
|
+
// 超过 timeout 秒后加载失败
|
|
1370
|
+
setTimeout(() => loadFail('timeout'), timeout * 1000);
|
|
1371
|
+
return status;
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
/**
|
|
1375
|
+
* @method addInlineStyle
|
|
1376
|
+
* @description 添加内联样式
|
|
1377
|
+
* @param {String} inlineStyle 内联样式字符串
|
|
1378
|
+
* @param {String} id <style> 标签的 ID
|
|
1379
|
+
* @return {Boolean} 添加成功/失败
|
|
1380
|
+
*/
|
|
1381
|
+
export function addInlineStyle({ inlineStyle = '', id = '' } = {}): boolean {
|
|
1382
|
+
if (!inlineStyle) {
|
|
1383
|
+
return false;
|
|
1384
|
+
}
|
|
1385
|
+
// 创建 style 文档碎片
|
|
1386
|
+
const styleFrag = document.createDocumentFragment();
|
|
1387
|
+
let idDom = null;
|
|
1388
|
+
let domId = '';
|
|
1389
|
+
// Custom Style
|
|
1390
|
+
const customStyle = document.createElement('style');
|
|
1391
|
+
// 如果需要 ID
|
|
1392
|
+
if (id) {
|
|
1393
|
+
domId = `${id}`;
|
|
1394
|
+
idDom = document.getElementById(domId);
|
|
1395
|
+
// 如果 Dom 不存在,插入 style
|
|
1396
|
+
if (!idDom) {
|
|
1397
|
+
customStyle.setAttribute('id', id);
|
|
1398
|
+
customStyle.innerHTML = inlineStyle;
|
|
1399
|
+
styleFrag.appendChild(customStyle);
|
|
1400
|
+
document.head.appendChild(styleFrag);
|
|
1401
|
+
} else { // 如果 Dom 存在,直接更新
|
|
1402
|
+
idDom.innerHTML = inlineStyle;
|
|
1403
|
+
}
|
|
1404
|
+
} else { // 不需要 ID,直接添加新标签
|
|
1405
|
+
customStyle.innerHTML = inlineStyle;
|
|
1406
|
+
styleFrag.appendChild(customStyle);
|
|
1407
|
+
document.head.appendChild(styleFrag);
|
|
1408
|
+
}
|
|
1409
|
+
return true;
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
/**
|
|
1413
|
+
* @method customScrollBarForTransformEle
|
|
1414
|
+
* @description 为进行变换(transform)的元素设置滚动条
|
|
1415
|
+
* @param {String} containerClassName 进行变换(transform)的元素↓的容器(Father)的类名
|
|
1416
|
+
* @param {String} imgBoxClassName 图片的容器(transform It's YOU!)的类名
|
|
1417
|
+
* @param {String} imgBoxDom 图片的容器 Dom 对象
|
|
1418
|
+
* @param {String} imgClassName 图片的类名
|
|
1419
|
+
* @param {Object} imgDom 图片 Dom 对象
|
|
1420
|
+
* @param {String} action 操作类型 hide 隐藏指定 `containerClassName` 滚动条
|
|
1421
|
+
* @param {String} customStyle 滚动条的定制样式,会覆盖默认样式
|
|
1422
|
+
* @return {Boolean} 设置成功
|
|
1423
|
+
*/
|
|
1424
|
+
export function customScrollBarForTransformEle({ containerClassName = '', imgBoxClassName = '', imgBoxDom = null, imgClassName = '', imgDom = null, action = '', customStyle = '' } = {}): boolean {
|
|
1425
|
+
// 变量
|
|
1426
|
+
const barIsHideId = 'bar-is-hide';
|
|
1427
|
+
const barTopId = 'bar-top';
|
|
1428
|
+
const barBottomId = 'bar-bottom';
|
|
1429
|
+
if (action === 'hide') {
|
|
1430
|
+
addInlineStyle({
|
|
1431
|
+
inlineStyle: `
|
|
1432
|
+
.${containerClassName}::after {
|
|
1433
|
+
display: none;
|
|
1434
|
+
}
|
|
1435
|
+
`,
|
|
1436
|
+
id: barIsHideId,
|
|
1437
|
+
});
|
|
1438
|
+
return true;
|
|
1439
|
+
}
|
|
1440
|
+
setTimeout(() => {
|
|
1441
|
+
// 样式初始化
|
|
1442
|
+
addInlineStyle({
|
|
1443
|
+
inlineStyle: `
|
|
1444
|
+
.${containerClassName}::after {
|
|
1445
|
+
content: ' ';
|
|
1446
|
+
position: fixed;
|
|
1447
|
+
top: 0;
|
|
1448
|
+
right: 2px;
|
|
1449
|
+
min-height: 0vh;
|
|
1450
|
+
max-height: 100vh;
|
|
1451
|
+
width: 6px;
|
|
1452
|
+
border-radius: 3px;
|
|
1453
|
+
box-shadow: inset 0 0 6px rgba(0,0,0,.3);
|
|
1454
|
+
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3);
|
|
1455
|
+
background-color: rgb(109, 108, 109);
|
|
1456
|
+
${customStyle}
|
|
1457
|
+
}
|
|
1458
|
+
`,
|
|
1459
|
+
id: 'bar-init'
|
|
1460
|
+
});
|
|
1461
|
+
const imgInstance = imgDom || document.querySelector(`.${imgClassName}`);
|
|
1462
|
+
if (!imgInstance) {
|
|
1463
|
+
console.warn('Need element\'s Dom instance.');
|
|
1464
|
+
return false;
|
|
1465
|
+
}
|
|
1466
|
+
const imgBoxInstance = imgBoxDom || document.querySelector(`.${imgBoxClassName}`);
|
|
1467
|
+
if (!imgBoxInstance) {
|
|
1468
|
+
console.warn('Need element box\'s Dom instance.');
|
|
1469
|
+
return false;
|
|
1470
|
+
}
|
|
1471
|
+
const Box2ToRes = getBox2To();
|
|
1472
|
+
// console.log('获取图片距离顶部的距离', Box2ToRes);
|
|
1473
|
+
const ImgHeightRes = getImgHeight();
|
|
1474
|
+
// console.log('获取图片的高度', ImgHeightRes);
|
|
1475
|
+
const WindowHeightRes = getWindowHeight();
|
|
1476
|
+
// console.log('获取窗口的高度', WindowHeightRes);
|
|
1477
|
+
const TopOverflowPercentRes = getTopOverflowPercent();
|
|
1478
|
+
// console.log('获取顶部溢出的百分比', TopOverflowPercentRes);
|
|
1479
|
+
const BottomOverflowPercentRes = getBottomOverflowPercent();
|
|
1480
|
+
// console.log('获取顶部溢出的百分比', BottomOverflowPercentRes);
|
|
1481
|
+
const hideBarRes = hideBar();
|
|
1482
|
+
// console.log('如果都是 0 ,隐藏滚动条', hideBarRes);
|
|
1483
|
+
if (!hideBarRes) {
|
|
1484
|
+
setBarTop();
|
|
1485
|
+
// const setBarTopRes = setBarTop();
|
|
1486
|
+
// console.log('设置顶部状态栏', setBarTopRes);
|
|
1487
|
+
setBarBottom();
|
|
1488
|
+
// const setBarBottomRes = setBarBottom();
|
|
1489
|
+
// console.log('设置底部状态栏', setBarBottomRes);
|
|
1490
|
+
}
|
|
1491
|
+
// 获取图片距离顶部的距离
|
|
1492
|
+
function getBox2To () {
|
|
1493
|
+
const translates = window.getComputedStyle(imgBoxInstance as any, null).transform;
|
|
1494
|
+
const tanslateY = parseFloat(translates.substring(6).split(',')[5]);
|
|
1495
|
+
return tanslateY;
|
|
1496
|
+
}
|
|
1497
|
+
// 获取图片的高度
|
|
1498
|
+
function getImgHeight () {
|
|
1499
|
+
return (imgInstance)?.clientHeight;
|
|
1500
|
+
}
|
|
1501
|
+
// 获取窗口的高度
|
|
1502
|
+
function getWindowHeight () {
|
|
1503
|
+
return (document.querySelector(`.${containerClassName}`) as any).clientHeight; // window.outerHeight;
|
|
1504
|
+
}
|
|
1505
|
+
// 获取顶部溢出的百分比
|
|
1506
|
+
function getTopOverflowPercent () {
|
|
1507
|
+
let box2To = Box2ToRes;
|
|
1508
|
+
if (box2To >= 0) {
|
|
1509
|
+
box2To = 0;
|
|
1510
|
+
}
|
|
1511
|
+
box2To = Math.abs(box2To);
|
|
1512
|
+
// 图片高度
|
|
1513
|
+
const imgHeight = ImgHeightRes;
|
|
1514
|
+
const per = floatToPercent(box2To/(imgHeight as any), 2);
|
|
1515
|
+
return {
|
|
1516
|
+
box2To,
|
|
1517
|
+
per,
|
|
1518
|
+
};
|
|
1519
|
+
}
|
|
1520
|
+
// 获取底部溢出的百分比
|
|
1521
|
+
function getBottomOverflowPercent () {
|
|
1522
|
+
// 窗口高度
|
|
1523
|
+
const windowHeight = WindowHeightRes;
|
|
1524
|
+
// 图片高度
|
|
1525
|
+
const imgHeight = ImgHeightRes;
|
|
1526
|
+
// 上面溢出的高度
|
|
1527
|
+
const box2To = Box2ToRes;
|
|
1528
|
+
// 底部
|
|
1529
|
+
const remindImageHeight = windowHeight - box2To;
|
|
1530
|
+
let box2ToBottom = (imgHeight as any) - remindImageHeight;
|
|
1531
|
+
if (box2ToBottom <= 0) {
|
|
1532
|
+
box2ToBottom = 0;
|
|
1533
|
+
}
|
|
1534
|
+
const per = floatToPercent(box2ToBottom/(imgHeight as any), 2);
|
|
1535
|
+
return {
|
|
1536
|
+
box2ToBottom,
|
|
1537
|
+
per,
|
|
1538
|
+
};
|
|
1539
|
+
}
|
|
1540
|
+
// 如果都是 0 ,隐藏滚动条
|
|
1541
|
+
function hideBar () {
|
|
1542
|
+
const { box2To } = TopOverflowPercentRes;
|
|
1543
|
+
const { box2ToBottom } = BottomOverflowPercentRes;
|
|
1544
|
+
if (!box2To && !box2ToBottom) {
|
|
1545
|
+
return addInlineStyle({
|
|
1546
|
+
inlineStyle: `
|
|
1547
|
+
.${containerClassName}::after {
|
|
1548
|
+
display: none;
|
|
1549
|
+
}
|
|
1550
|
+
`,
|
|
1551
|
+
id: barIsHideId
|
|
1552
|
+
});
|
|
1553
|
+
} else {
|
|
1554
|
+
addInlineStyle({
|
|
1555
|
+
inlineStyle: `
|
|
1556
|
+
.${containerClassName}::after {
|
|
1557
|
+
display: block;
|
|
1558
|
+
}
|
|
1559
|
+
`,
|
|
1560
|
+
id: barIsHideId
|
|
1561
|
+
});
|
|
1562
|
+
}
|
|
1563
|
+
return false;
|
|
1564
|
+
}
|
|
1565
|
+
// 设置顶部状态栏
|
|
1566
|
+
function setBarTop () {
|
|
1567
|
+
const { per } = TopOverflowPercentRes;
|
|
1568
|
+
return addInlineStyle({
|
|
1569
|
+
inlineStyle: `
|
|
1570
|
+
.${containerClassName}::after {
|
|
1571
|
+
top: ${per};
|
|
1572
|
+
}
|
|
1573
|
+
`,
|
|
1574
|
+
id: barTopId
|
|
1575
|
+
});
|
|
1576
|
+
}
|
|
1577
|
+
// 设置底部状态栏
|
|
1578
|
+
function setBarBottom () {
|
|
1579
|
+
const { per } = BottomOverflowPercentRes;
|
|
1580
|
+
return addInlineStyle({
|
|
1581
|
+
inlineStyle: `
|
|
1582
|
+
.${containerClassName}::after {
|
|
1583
|
+
bottom: ${per};
|
|
1584
|
+
}
|
|
1585
|
+
`,
|
|
1586
|
+
id: barBottomId
|
|
1587
|
+
});
|
|
1588
|
+
}
|
|
1589
|
+
}, 25);
|
|
1590
|
+
return true;
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
|
|
1594
|
+
/**
|
|
1595
|
+
* @method calcContainImageSizeAndPosition
|
|
1596
|
+
* @description 计算适配容器宽度的图片的尺寸、距离顶部的距离。如果高度不足以占满容器,使其垂直居中;如果高度比容器长,由上向下铺开。
|
|
1597
|
+
* @param {Number} oriImageWidth 图片原始宽度
|
|
1598
|
+
* @param {Number} oriImageHeight 图片原始高度
|
|
1599
|
+
* @param {Number} viewportWidth 可视窗口宽度
|
|
1600
|
+
* @param {Number} viewportHeight 可视窗口高度
|
|
1601
|
+
* @return {Object} 结果 {"targetImageWidth":375,"targetImageHeight":375,"top":218.5,"wPer":1.25}
|
|
1602
|
+
*/
|
|
1603
|
+
export function calcContainImageSizeAndPosition({ oriImageWidth = 0, oriImageHeight = 0, viewportWidth = 0, viewportHeight = 0 } = {}): any {
|
|
1604
|
+
const targetImageWidth = viewportWidth;
|
|
1605
|
+
let [ targetImageHeight, top, wPer ] = [ 0, 0, 0 ];
|
|
1606
|
+
// 比例
|
|
1607
|
+
wPer = viewportWidth / oriImageWidth; // 1.+ 1.1 1.5
|
|
1608
|
+
targetImageHeight = oriImageHeight * wPer;
|
|
1609
|
+
if (targetImageHeight < viewportHeight) {
|
|
1610
|
+
const doubleGap = viewportHeight - targetImageHeight;
|
|
1611
|
+
top = doubleGap / 2;
|
|
1612
|
+
}
|
|
1613
|
+
return {
|
|
1614
|
+
targetImageWidth,
|
|
1615
|
+
targetImageHeight,
|
|
1616
|
+
top,
|
|
1617
|
+
wPer,
|
|
1618
|
+
};
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
/**
|
|
1622
|
+
* @method genCustomConsole
|
|
1623
|
+
* @description 生成自定义控制台打印
|
|
1624
|
+
* @param {String} prefix 前缀
|
|
1625
|
+
* @param {Function} allowFn 允许打印的判断函数
|
|
1626
|
+
* @return {Object} 新实例
|
|
1627
|
+
*/
|
|
1628
|
+
export function genCustomConsole({ prefix = '' } = {}): any {
|
|
1629
|
+
// ...
|
|
1630
|
+
const methods = ['log', 'info', 'warn', 'error'];
|
|
1631
|
+
const newConsole = Object.create(null);
|
|
1632
|
+
methods.forEach(method => {
|
|
1633
|
+
newConsole[method] = function (...argu: any) {
|
|
1634
|
+
if (prefix) {
|
|
1635
|
+
(console as any)[method](prefix, ...argu);
|
|
1636
|
+
} else {
|
|
1637
|
+
(console as any)[method](...argu);
|
|
1638
|
+
}
|
|
1639
|
+
};
|
|
1640
|
+
});
|
|
1641
|
+
return newConsole;
|
|
1642
|
+
}
|