@base-web-kits/base-tools-ts 0.9.4 → 0.9.6
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/base-tools-ts.umd.global.js +2 -1
- package/dist/base-tools-ts.umd.global.js +2 -1
- package/dist/base-tools-ts.umd.global.js.map +1 -0
- package/dist/index.cjs +1 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -0
- package/package.json +1 -1
- package/src/ts/array/index.ts +13 -0
- package/src/ts/async/index.ts +20 -0
- package/src/ts/bean/EventBus.ts +61 -0
- package/src/ts/bean/index.ts +1 -0
- package/src/ts/day/index.ts +184 -0
- package/src/ts/index.ts +14 -0
- package/src/ts/lodash/index.ts +7 -0
- package/src/ts/number/big.ts +192 -0
- package/src/ts/number/format.ts +253 -0
- package/src/ts/number/index.ts +3 -0
- package/src/ts/number/random.ts +65 -0
- package/src/ts/object/index.ts +12 -0
- package/src/ts/string/format.ts +52 -0
- package/src/ts/string/index.ts +3 -0
- package/src/ts/string/other.ts +33 -0
- package/src/ts/string/random.ts +42 -0
- package/src/ts/typing/index.ts +161 -0
- package/src/ts/url/file/index.ts +57 -0
- package/src/ts/url/index.ts +4 -0
- package/src/ts/url/oss/index.d.ts +120 -0
- package/src/ts/url/oss/index.ts +168 -0
- package/src/ts/url/param/index.ts +119 -0
- package/src/ts/url/qn/index.d.ts +103 -0
- package/src/ts/url/qn/index.ts +237 -0
- package/src/ts/validator/index.ts +601 -0
- package/index.cjs +0 -2
- package/index.d.ts +0 -1
- package/index.js +0 -1
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// 兼容oss后期添加的参数
|
|
2
|
+
type OtherOption = Record<string, string | number | boolean | undefined>;
|
|
3
|
+
|
|
4
|
+
export type OSSGravity = 'nw' | 'north' | 'ne' | 'west' | 'center' | 'east' | 'sw' | 'south' | 'se';
|
|
5
|
+
|
|
6
|
+
export type OSSFormat = 'png' | 'jpg' | 'jpeg' | 'webp' | 'bmp' | 'gif' | 'tiff' | 'heic' | 'avif';
|
|
7
|
+
|
|
8
|
+
export type OSSResizeOption = {
|
|
9
|
+
p?: number;
|
|
10
|
+
w?: number;
|
|
11
|
+
h?: number;
|
|
12
|
+
m?: 'lfit' | 'mfit' | 'fill' | 'pad' | 'fixed';
|
|
13
|
+
l?: number;
|
|
14
|
+
s?: number;
|
|
15
|
+
limit?: 1 | 0;
|
|
16
|
+
color?: string;
|
|
17
|
+
} & OtherOption;
|
|
18
|
+
|
|
19
|
+
export type OSSCropOption = {
|
|
20
|
+
w?: number;
|
|
21
|
+
h?: number;
|
|
22
|
+
x?: number;
|
|
23
|
+
y?: number;
|
|
24
|
+
g?: OSSGravity;
|
|
25
|
+
p?: number;
|
|
26
|
+
} & OtherOption;
|
|
27
|
+
|
|
28
|
+
export type OSSIndexCropOption = {
|
|
29
|
+
x?: number;
|
|
30
|
+
y?: number;
|
|
31
|
+
i?: number;
|
|
32
|
+
} & OtherOption;
|
|
33
|
+
|
|
34
|
+
export type OSSQualityOption = {
|
|
35
|
+
q?: number;
|
|
36
|
+
Q?: number;
|
|
37
|
+
} & OtherOption;
|
|
38
|
+
|
|
39
|
+
export type OSSWatermarkOption = {
|
|
40
|
+
type?: string;
|
|
41
|
+
text?: string;
|
|
42
|
+
size?: number;
|
|
43
|
+
color?: string;
|
|
44
|
+
shadow?: number;
|
|
45
|
+
t?: number;
|
|
46
|
+
g?: OSSGravity;
|
|
47
|
+
x?: number;
|
|
48
|
+
y?: number;
|
|
49
|
+
voffset?: number;
|
|
50
|
+
fill?: 0 | 1;
|
|
51
|
+
padx?: number;
|
|
52
|
+
pady?: number;
|
|
53
|
+
image?: string;
|
|
54
|
+
P?: number;
|
|
55
|
+
} & OtherOption;
|
|
56
|
+
|
|
57
|
+
export type OSSBlurOption = {
|
|
58
|
+
r: number;
|
|
59
|
+
s: number;
|
|
60
|
+
g?: 'face' | 'faces';
|
|
61
|
+
p?: number;
|
|
62
|
+
} & OtherOption;
|
|
63
|
+
|
|
64
|
+
export type OSSRoundedCornersOption = {
|
|
65
|
+
r: number;
|
|
66
|
+
} & OtherOption;
|
|
67
|
+
|
|
68
|
+
export type OSSCircleOption = {
|
|
69
|
+
r: number;
|
|
70
|
+
} & OtherOption;
|
|
71
|
+
|
|
72
|
+
export type OSSImgOption = {
|
|
73
|
+
resize?: OSSResizeOption;
|
|
74
|
+
watermark?: OSSWatermarkOption;
|
|
75
|
+
flip?: 0 | 1 | 2;
|
|
76
|
+
crop?: OSSCropOption;
|
|
77
|
+
quality?: OSSQualityOption;
|
|
78
|
+
format?: OSSFormat;
|
|
79
|
+
info?: boolean;
|
|
80
|
+
'auto-orient'?: 0 | 1;
|
|
81
|
+
circle?: OSSCircleOption;
|
|
82
|
+
indexcrop?: OSSIndexCropOption;
|
|
83
|
+
'rounded-corners'?: OSSRoundedCornersOption;
|
|
84
|
+
blur?: OSSBlurOption;
|
|
85
|
+
rotate?: number;
|
|
86
|
+
interlace?: 0 | 1;
|
|
87
|
+
'average-hue'?: boolean;
|
|
88
|
+
bright?: number;
|
|
89
|
+
sharpen?: number;
|
|
90
|
+
contrast?: number;
|
|
91
|
+
} & {
|
|
92
|
+
[action: string]: number | string | boolean | Record<string, unknown> | undefined;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export type OSSVideoOption = {
|
|
96
|
+
convert?: Record<string, unknown>;
|
|
97
|
+
animation?: Record<string, unknown>;
|
|
98
|
+
sprite?: Record<string, unknown>;
|
|
99
|
+
snapshots?: Record<string, unknown>;
|
|
100
|
+
concat?: Record<string, unknown>;
|
|
101
|
+
info?: boolean;
|
|
102
|
+
} & {
|
|
103
|
+
[action: string]: number | string | boolean | Record<string, unknown> | undefined;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export type OSSAudioOption = {
|
|
107
|
+
convert?: Record<string, unknown>;
|
|
108
|
+
concat?: Record<string, unknown>;
|
|
109
|
+
info?: boolean;
|
|
110
|
+
} & {
|
|
111
|
+
[action: string]: number | string | boolean | Record<string, unknown> | undefined;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export type OSSHlsOption = {
|
|
115
|
+
m3u8?: boolean | Record<string, unknown>;
|
|
116
|
+
} & {
|
|
117
|
+
[action: string]: number | string | boolean | Record<string, unknown> | undefined;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
export type OSSOption = OSSImgOption | OSSVideoOption | OSSAudioOption | OSSHlsOption;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
OSSOption,
|
|
3
|
+
OSSAudioOption,
|
|
4
|
+
OSSHlsOption,
|
|
5
|
+
OSSImgOption,
|
|
6
|
+
OSSVideoOption,
|
|
7
|
+
OSSWatermarkOption,
|
|
8
|
+
} from './index.d';
|
|
9
|
+
|
|
10
|
+
export * from './index.d';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* oss图片处理
|
|
14
|
+
* 参考官方文档: https://help.aliyun.com/zh/oss/user-guide/img-parameters/?spm=a2c4g.11186623.help-menu-31815.d_0_11_2_1.572824a1a1W5Pf&scm=20140722.H_144582._.OR_help-T_cn~zh-V_1
|
|
15
|
+
* @param src 原始图片URL
|
|
16
|
+
* @param option 图片处理选项
|
|
17
|
+
* @returns 处理后的图片URL(格式: `{src}?x-oss-process=image/xx`)
|
|
18
|
+
* @example
|
|
19
|
+
* 缩放: getOSSImg('xx.jpg', { resize: { w: 100, h: 100 } })
|
|
20
|
+
* 水印: getOSSImg('xx.jpg', { watermark: { text: '水印' } });
|
|
21
|
+
* 翻转: getOSSImg('xx.jpg', { flip: 1 });
|
|
22
|
+
* 裁剪: getOSSImg('xx.jpg', { crop: { w: 100, h: 100 } });
|
|
23
|
+
* 质量: getOSSImg('xx.jpg', { quality: { q: 80 } });
|
|
24
|
+
* 格式转换: getOSSImg('xx.jpg', { format: 'jpg' });
|
|
25
|
+
* 获取信息: getOSSImg('xx.jpg', { info: true });
|
|
26
|
+
* 自适应方向: getOSSImg('xx.jpg', { 'auto-orient': 1 });
|
|
27
|
+
* 内切圆: getOSSImg('xx.jpg', { circle: { r: 100 } });
|
|
28
|
+
* 索引切割: getOSSImg('xx.jpg', { indexcrop: { x: 100 } });
|
|
29
|
+
* 圆角: getOSSImg('xx.jpg', { 'rounded-corners': { r: 10 } });
|
|
30
|
+
* 模糊: getOSSImg('xx.jpg', { blur: { r: 10, s: 10 } });
|
|
31
|
+
* 旋转: getOSSImg('xx.jpg', { rotate: 90 });
|
|
32
|
+
* 渐进显示: getOSSImg('xx.jpg', { interlace: 1 });
|
|
33
|
+
* 主色调: getOSSImg('xx.jpg', { 'average-hue': true });
|
|
34
|
+
* 亮度: getOSSImg('xx.jpg', { bright: 10 });
|
|
35
|
+
* 锐化: getOSSImg('xx.jpg', { sharpen: 100 });
|
|
36
|
+
* 对比度: getOSSImg('xx.jpg', { contrast: 100 });
|
|
37
|
+
*/
|
|
38
|
+
export function getOSSImg(src: string, option: OSSImgOption) {
|
|
39
|
+
return buildOSSUrl(src, 'image', option);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* oss视频处理
|
|
44
|
+
* 参考官方文档: https://help.aliyun.com/zh/oss/user-guide/audio-and-video-processing/
|
|
45
|
+
* @param src 原始视频URL
|
|
46
|
+
* @param option 视频处理选项
|
|
47
|
+
* @returns 处理后的URL(格式: `{src}?x-oss-process=video/xx`)
|
|
48
|
+
* @example
|
|
49
|
+
* 视频转码: getOSSVideo('xx.mp4', { convert: { format: 'mp4' } })
|
|
50
|
+
* 转为动图: getOSSVideo('xx.mp4', { animation: { format: 'gif' } })
|
|
51
|
+
* 雪碧图: getOSSVideo('xx.mp4', { sprite: { format: 'png' } })
|
|
52
|
+
* 多帧截取: getOSSVideo('xx.mp4', { snapshots: { count: 3 } })
|
|
53
|
+
* 视频拼接: getOSSVideo('xx.mp4', { concat: { list: 'a.mp4,b.mp4' } })
|
|
54
|
+
* 信息查询: getOSSVideo('xx.mp4', { info: true })
|
|
55
|
+
* 组合操作: getOSSVideo('xx.mp4', { convert: { format: 'mp4' }, snapshots: { count: 3 } })
|
|
56
|
+
*/
|
|
57
|
+
export function getOSSVideo(src: string, option: OSSVideoOption) {
|
|
58
|
+
return buildOSSUrl(src, 'video', option);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* oss音频处理
|
|
63
|
+
* 参考官方文档: https://help.aliyun.com/zh/oss/user-guide/audio-and-video-processing/
|
|
64
|
+
* @param src 原始音频URL
|
|
65
|
+
* @param option 音频处理选项
|
|
66
|
+
* @returns 处理后的URL(格式: `{src}?x-oss-process=audio/xx`)
|
|
67
|
+
* @example
|
|
68
|
+
* 音频转码: getOSSAudio('xx.mp3', { 'convert': { format: 'mp3' } })
|
|
69
|
+
* 音频拼接: getOSSAudio('xx.mp3', { 'concat': { list: 'a.mp3,b.mp3' } })
|
|
70
|
+
* 信息查询: getOSSAudio('xx.mp3', { 'info': true })
|
|
71
|
+
*/
|
|
72
|
+
export function getOSSAudio(src: string, option: OSSAudioOption) {
|
|
73
|
+
return buildOSSUrl(src, 'audio', option);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* oss直播处理(边转边播 HLS)
|
|
78
|
+
* 参考官方文档: https://help.aliyun.com/zh/oss/user-guide/audio-and-video-processing/
|
|
79
|
+
* @param src 原始视频URL
|
|
80
|
+
* @param option HLS 选项(或布尔)
|
|
81
|
+
* @returns 处理后的URL(格式: `{src}?x-oss-process=hls/xx`)
|
|
82
|
+
* @example
|
|
83
|
+
* 生成播放列表: getOSSHls('xx.mp4', { 'm3u8': true })
|
|
84
|
+
* 配置参数: getOSSHls('xx.mp4', { 'm3u8': { playlist: 1, segtime: 6 } })
|
|
85
|
+
*/
|
|
86
|
+
export function getOSSHls(src: string, option: OSSHlsOption) {
|
|
87
|
+
return buildOSSUrl(src, 'hls', option);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* 构造oss处理地址
|
|
92
|
+
* @param src 原始地址
|
|
93
|
+
* @param type oss处理类型,如`image`, `audio`, `video`, `hls`
|
|
94
|
+
* @param option oss处理选项
|
|
95
|
+
* @returns 处理后的URL(格式: `{src}?x-oss-process={type}/{segs.join('/')}`)
|
|
96
|
+
*/
|
|
97
|
+
export function buildOSSUrl(src: string, type: string, option: OSSOption) {
|
|
98
|
+
if (!src || !option) return src;
|
|
99
|
+
if (src.startsWith('blob:')) return src;
|
|
100
|
+
if (src.includes('.svg')) return src;
|
|
101
|
+
|
|
102
|
+
const segs: string[] = [];
|
|
103
|
+
|
|
104
|
+
// 遍历选项,构造处理参数
|
|
105
|
+
for (const [k, v] of Object.entries(option)) {
|
|
106
|
+
const seg = k === 'watermark' ? getWatermark(v as OSSWatermarkOption) : getOSSSegs(k, v);
|
|
107
|
+
if (seg) segs.push(seg);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!segs.length) return src;
|
|
111
|
+
|
|
112
|
+
// 拼接处理参数(先移除查询参数,避免重复拼接)
|
|
113
|
+
const base = src.split('?')[0];
|
|
114
|
+
return `${base}?x-oss-process=${type}/${segs.join('/')}`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* 构造图片处理参数
|
|
119
|
+
* @param type 图片处理类型,如`resize`, `flip`, `format`, `info`
|
|
120
|
+
* @param option 图片处理选项
|
|
121
|
+
* @returns `object`返回格式为`resize,w_100,h_100`
|
|
122
|
+
* @returns `number`返回格式为`flip,1`
|
|
123
|
+
* @returns `string`返回格式为`format,jpg`
|
|
124
|
+
* @returns `true`返回格式为`info`, `false`返回空字符串
|
|
125
|
+
*/
|
|
126
|
+
function getOSSSegs(type: string, option?: Record<string, unknown> | number | string | boolean) {
|
|
127
|
+
if (!option && option !== 0) return '';
|
|
128
|
+
|
|
129
|
+
if (option === true) return type;
|
|
130
|
+
|
|
131
|
+
if (typeof option === 'number' || typeof option === 'string') return `${type},${option}`;
|
|
132
|
+
|
|
133
|
+
const segs = Object.entries(option)
|
|
134
|
+
.map(([k, v]) => `${k}_${v}`)
|
|
135
|
+
.join(',');
|
|
136
|
+
|
|
137
|
+
return segs ? `${type},${segs}` : '';
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* 图片水印 (文本和图片已Base64编码)
|
|
142
|
+
* @returns 格式: `watermark,text_xxx`
|
|
143
|
+
*/
|
|
144
|
+
function getWatermark(w?: OSSWatermarkOption) {
|
|
145
|
+
if (!w) return '';
|
|
146
|
+
if (w.image) w.image = toBase64Url(w.image);
|
|
147
|
+
if (w.text) w.text = toBase64Url(w.text);
|
|
148
|
+
if (w.type) w.type = toBase64Url(w.type);
|
|
149
|
+
return getOSSSegs('watermark', w);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Base64编码
|
|
154
|
+
*/
|
|
155
|
+
function toBase64Url(s: string) {
|
|
156
|
+
let b64 = '';
|
|
157
|
+
if (typeof Buffer !== 'undefined') {
|
|
158
|
+
const buf = Buffer.from(s, 'utf-8');
|
|
159
|
+
b64 = buf.toString('base64');
|
|
160
|
+
} else {
|
|
161
|
+
try {
|
|
162
|
+
b64 = btoa(unescape(encodeURIComponent(s)));
|
|
163
|
+
} catch {
|
|
164
|
+
b64 = '';
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return b64.replace(/=+$/g, '').replace(/\+/g, '-').replace(/\//g, '_');
|
|
168
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 获取url的查询参数值
|
|
3
|
+
* - 采用纯JS解析,因为小程序不支持URLSearchParams
|
|
4
|
+
* @param key 参数名
|
|
5
|
+
* @param url 完整 URL 或仅查询串(如 "a=1&b=2")
|
|
6
|
+
* @returns 解码后的参数值 (若不存在|"null"|"undefined",则返回 null)
|
|
7
|
+
* @example
|
|
8
|
+
* const q = getUrlParam('q', 'https://a.com/?q=%E6%B5%8B%E8%AF%95'); // "测试"
|
|
9
|
+
* const a = getUrlParam('a', 'a=1'); // "1"
|
|
10
|
+
* const list = getUrlParam('list', 'list=[1,2]'); // "[1,2]"
|
|
11
|
+
* const list = getUrlParam('list', 'list=null'); // null
|
|
12
|
+
* const list = getUrlParam('list', 'list=undefined'); // null
|
|
13
|
+
*/
|
|
14
|
+
export function getUrlParam(key: string, url: string) {
|
|
15
|
+
const raw = url.includes('?') ? url.slice(url.indexOf('?') + 1) : url.includes('=') ? url : '';
|
|
16
|
+
const qs = raw.split('#')[0];
|
|
17
|
+
if (!qs) return null;
|
|
18
|
+
const pairs = qs.split('&').filter(Boolean);
|
|
19
|
+
const decode = (s: string) => {
|
|
20
|
+
try {
|
|
21
|
+
return decodeURIComponent(s.replace(/\+/g, ' '));
|
|
22
|
+
} catch {
|
|
23
|
+
return s;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
for (const pair of pairs) {
|
|
27
|
+
const i = pair.indexOf('=');
|
|
28
|
+
const k = i >= 0 ? pair.slice(0, i) : pair;
|
|
29
|
+
if (decode(k) === key) {
|
|
30
|
+
const v = i >= 0 ? decode(pair.slice(i + 1)) : '';
|
|
31
|
+
return v !== 'null' && v !== 'undefined' ? v : null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 获取url的查询参数值,并转为number类型
|
|
39
|
+
* @param key 参数名
|
|
40
|
+
* @param url 完整 URL 或仅查询串(如 "a=1&b=2")
|
|
41
|
+
* @returns 解码后的参数值 (若不存在|"非数字字符串",则返回 null)
|
|
42
|
+
* @example
|
|
43
|
+
* const a = getUrlNumber('a', 'https://a.com/?a=1'); // 1
|
|
44
|
+
* const a = getUrlNumber('a', 'a=1'); // 1
|
|
45
|
+
* const a = getUrlNumber('a', 'a=1.2'); // 1.2
|
|
46
|
+
* const a = getUrlNumber('a', 'a=abc'); // null
|
|
47
|
+
*/
|
|
48
|
+
export function getUrlNumber(key: string, url: string) {
|
|
49
|
+
const str = getUrlParam(key, url);
|
|
50
|
+
if (!str) return null;
|
|
51
|
+
|
|
52
|
+
const num = Number(str);
|
|
53
|
+
return isNaN(num) ? null : num;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 获取url的所有查询参数值
|
|
58
|
+
* - 采用纯JS解析,因为小程序不支持URLSearchParams
|
|
59
|
+
* @param url 完整 URL 或仅查询串(如 "a=1&b=2")
|
|
60
|
+
* @returns 解码后的键值对象(无参数返回空对象; "null"|"undefined"的参数会被忽略)
|
|
61
|
+
* @example
|
|
62
|
+
* const params = getUrlParamAll('a=1&b=2'); // { a: "1", b: "2" }
|
|
63
|
+
* const params = getUrlParamAll('https://a.com/?a=1&b=2'); // { a: "1", b: "2" }
|
|
64
|
+
* const params = getUrlParamAll('a=1&b=null'); // { a: "1" }
|
|
65
|
+
* const params = getUrlParamAll('a=1&b=undefined'); // { a: "1" }
|
|
66
|
+
*/
|
|
67
|
+
export function getUrlParamAll(url: string) {
|
|
68
|
+
const raw = url.includes('?') ? url.slice(url.indexOf('?') + 1) : url.includes('=') ? url : '';
|
|
69
|
+
const qs = raw.split('#')[0];
|
|
70
|
+
const result: Record<string, string> = {};
|
|
71
|
+
if (!qs) return result;
|
|
72
|
+
const decode = (s: string) => {
|
|
73
|
+
try {
|
|
74
|
+
return decodeURIComponent(s.replace(/\+/g, ' '));
|
|
75
|
+
} catch {
|
|
76
|
+
return s;
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
for (const seg of qs.split('&')) {
|
|
80
|
+
if (!seg) continue;
|
|
81
|
+
const i = seg.indexOf('=');
|
|
82
|
+
const k = i >= 0 ? seg.slice(0, i) : seg;
|
|
83
|
+
const v = i >= 0 ? seg.slice(i + 1) : '';
|
|
84
|
+
const dv = decode(v);
|
|
85
|
+
if (dv !== 'null' && dv !== 'undefined') {
|
|
86
|
+
result[decode(k)] = dv;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* 将对象参数拼接到 URL
|
|
94
|
+
* - 采用纯JS拼接,因为小程序不支持URLSearchParams
|
|
95
|
+
* @param url 基础地址
|
|
96
|
+
* @param param 将要追加的参数对象;`null/undefined` 值会被忽略,Object 会使用 `JSON.stringify`
|
|
97
|
+
* @returns 拼接后的完整 URL(保留原有哈希片段)
|
|
98
|
+
* @example
|
|
99
|
+
* const url = appendUrlParam('https://a.com', { q: '测试', list: [1, 2], a: null, b: undefined }); // 'https://a.com/?q=%E6%B5%8B%E8%AF%95&list=[1,2]'
|
|
100
|
+
*/
|
|
101
|
+
export function appendUrlParam(url: string, param: Record<string, unknown>) {
|
|
102
|
+
if (!param || typeof param !== 'object') return url;
|
|
103
|
+
|
|
104
|
+
const hashIndex = url.indexOf('#');
|
|
105
|
+
const baseWithoutHash = hashIndex >= 0 ? url.slice(0, hashIndex) : url;
|
|
106
|
+
const hash = hashIndex >= 0 ? url.slice(hashIndex) : '';
|
|
107
|
+
|
|
108
|
+
const [base, existingQs] = baseWithoutHash.split('?');
|
|
109
|
+
const parts: string[] = [];
|
|
110
|
+
if (existingQs) parts.push(existingQs);
|
|
111
|
+
for (const key in param) {
|
|
112
|
+
const rawVal = param[key];
|
|
113
|
+
if (rawVal === null || rawVal === undefined) continue;
|
|
114
|
+
const val = typeof rawVal === 'object' ? JSON.stringify(rawVal) : String(rawVal);
|
|
115
|
+
parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(val)}`);
|
|
116
|
+
}
|
|
117
|
+
const qs = parts.filter(Boolean).join('&');
|
|
118
|
+
return base + (qs ? `?${qs}` : '') + hash;
|
|
119
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
type QnOtherOption = Record<string, string | number | boolean | undefined>;
|
|
2
|
+
|
|
3
|
+
export type QnImageView2Option = {
|
|
4
|
+
mode?: 0 | 1 | 2 | 3 | 4 | 5;
|
|
5
|
+
w?: number;
|
|
6
|
+
h?: number;
|
|
7
|
+
l?: number;
|
|
8
|
+
limit?: 0 | 1;
|
|
9
|
+
q?: number;
|
|
10
|
+
format?: string;
|
|
11
|
+
} & QnOtherOption;
|
|
12
|
+
|
|
13
|
+
export type QnMogr2Option = {
|
|
14
|
+
thumbnail?: string;
|
|
15
|
+
crop?: string;
|
|
16
|
+
rotate?: number;
|
|
17
|
+
'auto-orient'?: boolean;
|
|
18
|
+
format?: string;
|
|
19
|
+
interlace?: 0 | 1;
|
|
20
|
+
background?: string;
|
|
21
|
+
q?: number;
|
|
22
|
+
blur?: string | { r: number; s: number };
|
|
23
|
+
colors?: number;
|
|
24
|
+
} & QnOtherOption;
|
|
25
|
+
|
|
26
|
+
export type QnWatermarkOption = {
|
|
27
|
+
type?: 'image' | 'text' | number;
|
|
28
|
+
image?: string;
|
|
29
|
+
text?: string;
|
|
30
|
+
font?: string;
|
|
31
|
+
fontsize?: number;
|
|
32
|
+
fill?: string;
|
|
33
|
+
gravity?: string;
|
|
34
|
+
dx?: number;
|
|
35
|
+
dy?: number;
|
|
36
|
+
dissolve?: number;
|
|
37
|
+
} & QnOtherOption;
|
|
38
|
+
|
|
39
|
+
export type QnImgOption = {
|
|
40
|
+
imageView2?: QnImageView2Option;
|
|
41
|
+
imageMogr2?: QnMogr2Option;
|
|
42
|
+
watermark?: QnWatermarkOption;
|
|
43
|
+
imageslim?: boolean;
|
|
44
|
+
imageInfo?: boolean;
|
|
45
|
+
} & {
|
|
46
|
+
thumbnail?: string;
|
|
47
|
+
crop?: string;
|
|
48
|
+
rotate?: number;
|
|
49
|
+
'auto-orient'?: boolean;
|
|
50
|
+
format?: string;
|
|
51
|
+
interlace?: 0 | 1;
|
|
52
|
+
background?: string;
|
|
53
|
+
q?: number;
|
|
54
|
+
blur?: string | { r: number; s: number };
|
|
55
|
+
colors?: number;
|
|
56
|
+
[action: string]: number | string | boolean | Record<string, unknown> | undefined;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export type QnAvthumbOption = {
|
|
60
|
+
format?: string;
|
|
61
|
+
s?: string;
|
|
62
|
+
vcodec?: 'libx264' | 'libx265' | 'copy' | string;
|
|
63
|
+
vb?: string; // e.g. '1.25m', '128k', support '!'
|
|
64
|
+
r?: number; // frame rate
|
|
65
|
+
acodec?: 'libmp3lame' | 'libfdk_aac' | 'copy' | string;
|
|
66
|
+
ab?: string; // e.g. '128k', support '!'
|
|
67
|
+
ar?: number; // sampling rate
|
|
68
|
+
} & QnOtherOption;
|
|
69
|
+
|
|
70
|
+
export type QnVframeOption = {
|
|
71
|
+
format?: 'jpg' | 'png';
|
|
72
|
+
offset?: number; // seconds, support decimals
|
|
73
|
+
w?: number;
|
|
74
|
+
h?: number;
|
|
75
|
+
} & QnOtherOption;
|
|
76
|
+
|
|
77
|
+
export type QnVideoOption = {
|
|
78
|
+
avthumb?: QnAvthumbOption;
|
|
79
|
+
vframe?: QnVframeOption;
|
|
80
|
+
} & {
|
|
81
|
+
[action: string]: number | string | boolean | Record<string, unknown> | undefined;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export type QnAudioOption = {
|
|
85
|
+
avthumb?: QnAvthumbOption;
|
|
86
|
+
} & {
|
|
87
|
+
[action: string]: number | string | boolean | Record<string, unknown> | undefined;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export type QnHlsOption = {
|
|
91
|
+
level?: number; // e.g. 3 for 720p preset
|
|
92
|
+
format?: 'm3u8';
|
|
93
|
+
segtime?: number; // 2-10 seconds
|
|
94
|
+
t?: string; // duration like '1.500s'
|
|
95
|
+
vcodec?: 'libx264' | 'libx265' | 'copy' | string;
|
|
96
|
+
vb?: string; // bitrate e.g. '1.25m', '128k', support '!'
|
|
97
|
+
r?: number; // frame rate [1,60]
|
|
98
|
+
s?: string; // resolution '1280x720'
|
|
99
|
+
acodec?: 'libmp3lame' | 'libfdk_aac' | 'copy' | string;
|
|
100
|
+
ab?: string; // audio bitrate
|
|
101
|
+
ar?: number; // audio sampling rate
|
|
102
|
+
output?: string; // m3u8 filename (url-safe base64)
|
|
103
|
+
} & QnOtherOption;
|