tools.js
9.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
/*
* @Date: 2022-04-18 15:59:42
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2026-01-21 13:33:36
* @FilePath: /mlaj/src/utils/tools.js
* @Description: 文件描述
*/
import dayjs from 'dayjs';
// 格式化时间
const formatDate = (date) => {
return dayjs(date).format('YYYY-MM-DD HH:mm');
};
/**
* @description 判断浏览器属于平台
* @returns
*/
const wxInfo = () => {
let u = navigator.userAgent;
let isAndroid = u.indexOf('Android') > -1 || u.indexOf('Linux') > -1; //android终端或者uc浏览器
let isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios终端
let isMobile = u.indexOf('Android') > -1 || u.indexOf('iPhone') > -1 || u.indexOf('iPad') > -1; // 移动端平台
let isIpad = u.indexOf('iPad') > -1; // iPad平台
let uAgent = navigator.userAgent.toLowerCase();
let isWeiXin = (uAgent.match(/MicroMessenger/i) == 'micromessenger') ? true : false;
let isWeiXinDesktop = isWeiXin && uAgent.indexOf('wechat') > -1 ? true : false;
let isPC = (uAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone|micromessenger)/i)) ? false : true;
let isIOS = /iphone|ipad|ipod/.test(uAgent); // iOS设备
let isWeChatBrowser = /micromessenger/.test(uAgent); // 微信浏览器
let isIOSWeChat = isIOS && isWeChatBrowser;
return {
isAndroid,
isiOS,
isWeiXin,
isMobile,
isIpad,
isPC,
isWeiXinDesktop,
isIOSWeChat
};
};
/**
* @description 判断多行省略文本
* @param {*} id 目标dom标签
* @returns
*/
const hasEllipsis = (id) => {
let oDiv = document.getElementById(id);
let flag = false;
if (oDiv.scrollHeight > oDiv.clientHeight) {
flag = true
}
return flag
}
/**
* @description 解析URL参数
* @param {*} url
* @returns
*/
const parseQueryString = url => {
var json = {};
var arr = url.indexOf('?') >= 0 ? url.substr(url.indexOf('?') + 1).split('&') : [];
arr.forEach(item => {
var tmp = item.split('=');
json[tmp[0]] = decodeURIComponent(tmp[1]);
});
return json;
}
/**
* 字符串包含字符数组中字符的状态
* @param {*} array 字符数组
* @param {*} str 字符串
* @returns 包含状态
*/
const strExist = (array, str) => {
const exist = array.filter(arr => {
if (str.indexOf(arr) >= 0) return str;
})
return exist.length > 0
}
// 获取参数key/value值对
const getUrlParams = (url) => {
// 没有参数处理
if (url.split('?').length === 1) return false;
let arr = url.split('?');
let res = arr[1].split('&');
let items = {};
for (let i = 0; i < res.length; i++) {
let [key, value] = res[i].split('=');
items[key] = value;
}
return items
}
// 格式化URL参数为字符串
const stringifyQuery = (params) => {
const queryString = [];
Object.keys(params || {}).forEach((k) => {
queryString.push(k + '=' + params[k]);
});
return '?' + queryString.join('&');
};
// 格式化时长(秒转换为可读格式)
const formatDuration = (seconds) => {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const remainingSeconds = seconds % 60;
let result = '';
if (hours > 0) {
result += `${hours}小时`;
}
if (minutes > 0) {
result += `${minutes}分钟`;
}
if (remainingSeconds > 0 || result === '') {
result += `${remainingSeconds}秒`;
}
return result;
};
/**
* @description 归一化“打卡任务列表”字段,避免各页面散落 map 导致漏改。
* @param {Array<any>} list 原始任务列表(通常为接口返回 task_list/timeout_task_list)
* @returns {Array<{id: number|string, name: string, task_type: string, is_gray: boolean, is_finish: boolean, checkin_subtask_id: number|string|undefined}>}
*/
const normalizeCheckinTaskItems = (list) => {
const raw_list = Array.isArray(list) ? list : [];
return raw_list.map((item) => {
const safe_item = item || {};
const id = safe_item.id;
const name = safe_item.title || safe_item.name || '';
const task_type = safe_item.task_type || safe_item.taskType || '';
const is_gray = !!safe_item.is_gray;
const is_finish = !!safe_item.is_finish;
const checkin_subtask_id = safe_item.checkin_subtask_id || safe_item.subtask_id;
return {
id,
name,
task_type,
is_gray,
is_finish,
checkin_subtask_id
};
});
};
/**
* @description 归一化作业提交类型配置(兼容多种后端格式),并提取各类型的上传大小限制(MB)。
* @param {any} attachment_type 后端返回的 attachment_type 字段,可能是多种结构。
* @returns {{options: Array<{key: string, value: string}>, upload_size_limit_mb_map: (null|{image?: number, video?: number, audio?: number})}}
*/
const normalizeAttachmentTypeConfig = (attachment_type) => {
const type_map = {
text: '文本',
image: '图片',
audio: '音频',
video: '视频'
};
// 支持的类型 key(用于过滤未知字段,避免误把 max_size / 其他字段当成类型)
const known_type_keys = ['text', 'image', 'audio', 'video'];
const upload_size_limit_mb_map = {};
let options = [];
if (Array.isArray(attachment_type)) {
const first = attachment_type[0];
if (first && typeof first === 'object' && !Array.isArray(first)) {
// 情况1:新结构(数组对象风格),例如:
// [{ type: 'image', max_size: 500 }, { type: 'video', max_size: 1000 }]
const has_type_style = attachment_type.some(
(item) => item && typeof item === 'object' && !Array.isArray(item) && ('type' in item || 'max_size' in item)
);
if (has_type_style) {
// 生成“可选类型”列表
options = attachment_type
.map((item) => {
const key = item?.type || item?.key || item?.name || '';
return {
key,
value: type_map[key] || key
};
})
.filter((item) => !!item.key);
// 提取上传大小限制(只处理 image/video/audio)
attachment_type.forEach((item) => {
const key = item?.type || item?.key || item?.name;
const size = Number(item?.max_size);
if ((key === 'image' || key === 'video' || key === 'audio') && Number.isFinite(size) && size > 0) {
upload_size_limit_mb_map[key] = size;
}
});
} else {
// 情况2:映射结构(数组里装对象映射),例如:
// [{ image: 500, video: 500 }]
// 这里先合并为一个对象,再统一处理
const merged = {};
attachment_type.forEach((item) => {
if (item && typeof item === 'object' && !Array.isArray(item)) {
Object.assign(merged, item);
}
});
// 映射结构里 key 就是类型,value 就是大小(MB)
const keys = Object.keys(merged).filter((k) => known_type_keys.includes(k));
options = keys.map((key) => ({
key,
value: type_map[key] || key
}));
['image', 'video', 'audio'].forEach((key) => {
const size = Number(merged[key]);
if (Number.isFinite(size) && size > 0) {
upload_size_limit_mb_map[key] = size;
}
});
}
} else {
// 情况3:旧结构(字符串数组),例如:['image', 'video']
options = attachment_type.map((key) => ({
key,
value: type_map[key] || key
}));
}
} else if (attachment_type && typeof attachment_type === 'object') {
// 情况4:对象结构(可能是类型中文映射,也可能是类型->大小)
const keys = Object.keys(attachment_type);
const has_size_mapping = keys.some((k) => (k === 'image' || k === 'video' || k === 'audio') && Number.isFinite(Number(attachment_type[k])));
if (has_size_mapping) {
// 类型 -> 大小(MB),例如:{ image: 500, video: 1000 }
options = keys
.filter((k) => known_type_keys.includes(k))
.map((key) => ({
key,
value: type_map[key] || key
}));
['image', 'video', 'audio'].forEach((key) => {
const size = Number(attachment_type[key]);
if (Number.isFinite(size) && size > 0) {
upload_size_limit_mb_map[key] = size;
}
});
} else {
// 类型 -> 中文显示,或其他自定义结构,例如:{ image: '图片', video: '视频' }
options = Object.entries(attachment_type).map(([key, value]) => ({
key,
value
}));
}
} else {
options = [];
}
if (options.length === 0) {
// 兜底:后端未配置时,默认给出四种类型
options = [
{ key: 'text', value: '文本' },
{ key: 'image', value: '图片' },
{ key: 'audio', value: '音频' },
{ key: 'video', value: '视频' }
];
}
return {
options,
// 没解析到任何大小限制时,返回 null,避免覆盖 useCheckin 内部默认值
upload_size_limit_mb_map: Object.keys(upload_size_limit_mb_map).length ? upload_size_limit_mb_map : null
};
};
export {
formatDate,
wxInfo,
hasEllipsis,
parseQueryString,
strExist,
getUrlParams,
stringifyQuery,
formatDuration,
normalizeCheckinTaskItems,
normalizeAttachmentTypeConfig,
};