tools.js
7.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
/*
* @Date: 2022-04-18 15:59:42
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2026-01-23 15:18:16
* @FilePath: /git/xyxBooking-weapp/src/utils/tools.js
* @Description: 工具函数库
*/
import dayjs from 'dayjs'
import Taro from '@tarojs/taro'
import BASE_URL, { REQUEST_DEFAULT_PARAMS } from './config'
/**
* @description 格式化时间
* @param {string|number|Date} date 时间入参
* @returns {string} 格式化后的时间字符串(YYYY-MM-DD HH:mm)
*/
const formatDate = date => {
return dayjs(date).format('YYYY-MM-DD HH:mm')
}
/**
* @description 判断设备信息
* @returns {Object} 设备信息对象,包含是否为 Android、iOS、是否为平板等属性
*/
const wxInfo = () => {
const info = Taro.getSystemInfoSync()
const isAndroid = info.platform === 'android'
const isiOS = info.platform === 'ios'
// 说明:当前项目只用到 Android/iOS 区分;平板能力按需补充
return {
isAndroid,
isiOS,
isTable: false // 小程序通常不是 tablet 模式,或者可以根据 screenWidth 判断
}
}
/**
* @description 解析 URL 参数
* @param {string} url 完整 URL 或带 query 的路径
* @returns {Object} URL 参数对象(键值对)
*/
const parseQueryString = url => {
if (!url) {
return {}
}
const json = {}
const arr = url.indexOf('?') >= 0 ? url.substr(url.indexOf('?') + 1).split('&') : []
arr.forEach(item => {
const tmp = item.split('=')
json[tmp[0]] = tmp[1]
})
return json
}
/**
* @description 判断字符串是否包含数组中的任意子串
* @param {Array<string>} array 子串数组
* @param {string} str 目标字符串
* @returns {boolean} true=包含任意一个子串,false=都不包含
*/
const strExist = (array, str) => {
if (!str) {
return false
}
const exist = array.filter(arr => {
if (str.indexOf(arr) >= 0) {
return str
}
})
return exist.length > 0
}
/**
* 格式化日期时间字符串,提取开始/结束时间并拼接为「日期 开始时间-结束时间」格式
* @description 处理包含 begin_time/end_time 的数据对象,截取时间戳最后6位(毫秒/时区等冗余部分),
* 最终拼接为 "YYYY-MM-DD HH:mm:ss-HH:mm:ss" 格式的字符串
* @param {Object} data - 包含开始/结束时间的数据源对象
* @param {string} [data.begin_time] - 开始时间字符串(格式示例:2026-01-13T12:30:45.123456+08:00)
* @param {string} [data.end_time] - 结束时间字符串(格式同 begin_time)
* @returns {string} 格式化后的日期时间字符串,格式为「日期 开始时间-结束时间」;若入参无效返回空字符串
* @example
* 输入示例
* const timeData = {
* begin_time: '2026-01-13T10:00:00.987654+08:00',
* end_time: '2026-01-13T18:30:00.123456+08:00'
* };
* 调用函数
* formatDatetime(timeData); // 返回 "2026-01-13 10:00:00-18:30:00"
*
* @example
* 入参为空的情况
* formatDatetime(null); // 返回 ""
* formatDatetime({}); // 返回 ""
*
* @note 1. 入参时间字符串需保证前19位为有效格式(YYYY-MM-DDTHH:mm:ss),否则截取后可能出现异常;
* 2. 若 begin_time/end_time 缺失,拼接后可能出现 "undefined-undefined" 等异常,需保证入参完整性;
* 3. 该函数默认截取时间字符串前19位(slice(0, -6)),需根据实际时间格式调整截取长度
*/
const formatDatetime = data => {
if (!data || !data.begin_time || !data.end_time) {
return ''
}
const normalize = timeStr => {
if (!timeStr) {
return ''
}
let clean = timeStr.split('+')[0]
clean = clean.split('Z')[0]
clean = clean.trim().replace(/\s+/, 'T')
return clean
}
const start = dayjs(normalize(data.begin_time))
const end = dayjs(normalize(data.end_time))
if (!start.isValid() || !end.isValid()) {
return ''
}
const isNextDayMidnight =
end.diff(start, 'day') === 1 && end.hour() === 0 && end.minute() === 0 && end.second() === 0
const endTimeText = isNextDayMidnight ? '24:00' : end.format('HH:mm')
return `${start.format('YYYY-MM-DD')} ${start.format('HH:mm')}-${endTimeText}`
}
/**
* @description 证件号脱敏
* @param {string} id_number 证件号
* @param {Object} [options] 脱敏配置
* @param {number} [options.keep_start] 保留前几位(传了则按“前后保留”模式脱敏)
* @param {number} [options.keep_end] 保留后几位(传了则按“前后保留”模式脱敏)
* @param {number} [options.mask_count=8] 中间替换为 * 的位数(默认 8)
* @returns {string} 脱敏后的证件号
*/
const mask_id_number = (id_number, options = {}) => {
const raw = String(id_number || '')
if (!raw) {
return ''
}
const has_keep_start = Number.isFinite(options.keep_start)
const has_keep_end = Number.isFinite(options.keep_end)
const keep_start = has_keep_start ? options.keep_start : 0
const keep_end = has_keep_end ? options.keep_end : 0
const mask_count = Number.isFinite(options.mask_count) ? options.mask_count : 8
if (has_keep_start && has_keep_end) {
if (raw.length <= keep_start + keep_end) {
return raw
}
const prefix = raw.slice(0, keep_start)
const suffix = raw.slice(raw.length - keep_end)
const middle_len = Math.max(1, raw.length - keep_start - keep_end)
return `${prefix}${'*'.repeat(middle_len)}${suffix}`
}
if (raw.length < 15) {
return raw
}
const safe_mask_count = Math.min(Math.max(1, mask_count), raw.length)
const start = Math.floor((raw.length - safe_mask_count) / 2)
const end = start + safe_mask_count
if (start < 0 || end > raw.length) {
return raw
}
return raw.substring(0, start) + '*'.repeat(safe_mask_count) + raw.substring(end)
}
/**
* @description 二维码状态文案
* @param {string|number} status 状态值
* @returns {string} 状态文案
*/
const get_qrcode_status_text = status => {
const key = String(status || '')
if (key === '1') {
return '未激活'
}
if (key === '3') {
return '待使用'
}
if (key === '5') {
return '被取消'
}
if (key === '7') {
return '已使用'
}
return '未知状态'
}
/**
* @description 订单状态文案
* @param {string|number} status 状态值
* @returns {string} 状态文案
*/
const get_bill_status_text = status => {
const key = String(status || '')
if (key === '3') {
return '预约成功'
}
if (key === '5') {
return '已取消'
}
if (key === '9') {
return '已使用'
}
if (key === '11') {
return '退款中'
}
return '未知状态'
}
/**
* @description 构建 API 请求 URL(带默认公共参数)
* @param {string} action 接口动作名称(例如:openid_wxapp)
* @param {Object} [params={}] 额外 query 参数
* @returns {string} 完整请求 URL(BASE_URL + /srv/?a=...&f=...&client_name=...)
*/
const buildApiUrl = (action, params = {}) => {
const queryParams = new URLSearchParams({
a: action,
f: REQUEST_DEFAULT_PARAMS.f,
client_name: REQUEST_DEFAULT_PARAMS.client_name,
...params
})
return `${BASE_URL}/srv/?${queryParams.toString()}`
}
export {
formatDate,
wxInfo,
parseQueryString,
strExist,
formatDatetime,
mask_id_number,
get_qrcode_status_text,
get_bill_status_text,
buildApiUrl
}