import httpRequest from '@/common/httpRequest' // 金额格式化成K格式 export const formatNumberToK = (param) => { // 1. 异常处理:空值(null/undefined)直接返回空字符串 if (param === null || param === undefined) { return ''; } // 2. 统一预处理:无论参数是数字/字符串,都转为字符串并去除首尾空格 let numStr = String(param).trim(); // 3. 空字符串情况直接返回 if (numStr === '') { return ''; } // 4. 将处理后的字符串转为数字 const num = Number(numStr); // 5. 非有效数字的情况,返回原始参数的字符串形式(保持输入原貌) if (isNaN(num)) { return String(param); } // 6. 小于1000的数字,返回整数形式的字符串(无小数) if (num < 1000) { return String(Math.floor(num)); } // 7. 大于等于1000的数字,除以1000取整后拼接K const kNum = Math.floor(num / 1000); return `${kNum}K`; }; // 导出简历文件 let loading = false export const exportResumePdf = (resumesId) => { if (loading) return const baseUrl = httpRequest.config('APIHOST') const url = baseUrl + '/app/resumes/exportPdf' const token = uni.getStorageSync('token') uni.showLoading({ title: '导出中' }) loading = true // #ifdef APP-PLUS // App 端(尤其 iOS)用 downloadFile 最稳定 const downloadUrl = `${url}?resumesId=${encodeURIComponent(resumesId)}` uni.downloadFile({ url: downloadUrl, header: { token }, success: (dRes) => { if (dRes.statusCode !== 200) { uni.showToast({ title: '导出失败:' + dRes.statusCode, icon: 'none' }) return } if (!dRes.tempFilePath) { uni.showToast({ title: '导出失败,未获取到文件', icon: 'none' }) return } uni.openDocument({ filePath: dRes.tempFilePath, showMenu: true, fail: (e) => { console.error('openDocument fail', e) uni.showToast({ title: '无法打开文件', icon: 'none' }) } }) }, fail: (err) => { console.error('downloadFile fail', err) uni.showToast({ title: '下载失败', icon: 'none' }) }, complete: () => { uni.hideLoading() loading = false } }) return // #endif uni.request({ url, method: 'GET', data: { resumesId }, header: { 'content-type': 'application/pdf', token }, responseType: 'arraybuffer', success: (res) => { // 1. 先校验 if (res.statusCode !== 200) { uni.showToast({ title: '导出失败:' + res.statusCode, icon: 'none' }) return } if (!res.data || (res.data.byteLength !== undefined && res.data.byteLength === 0)) { uni.showToast({ title: '导出失败,返回内容为空', icon: 'none' }) return } const mime = 'application/pdf' // #ifdef H5 try { const blob = new Blob([res.data], { type: mime }) const downloadUrl = window.URL.createObjectURL(blob) const link = document.createElement('a') link.href = downloadUrl link.download = '简历.pdf' link.click() window.URL.revokeObjectURL(downloadUrl) uni.showToast({ title: '文件下载成功', icon: 'success' }) } catch (e) { console.error('H5 下载异常', e, res) uni.showToast({ title: 'H5下载失败:' + e.message, icon: 'none' }) } // #endif // #ifdef APP-PLUS // App 端已在上方使用 uni.downloadFile 处理,这里不再执行 // #endif }, fail: (err) => { console.error('exportResumePdf 请求失败', err) uni.showToast({ title: '请求失败:' + err.errMsg, icon: 'none' }) }, complete: () => { uni.hideLoading() loading = false } }) } /** * 防抖:连续触发,只在最后一次操作后 wait 毫秒执行一次 * @param {Function} fn * @param {number} [wait=300] * @returns {Function} 每次 debounce(fn) 都会得到独立计时器,互不干扰 */ export function debounce(fn, wait = 300) { let timer = null return function debounced(...args) { const ctx = this clearTimeout(timer) timer = setTimeout(() => { timer = null fn.apply(ctx, args) }, wait) } } /** * 节流:wait 毫秒内最多执行一次(适合按钮点击、跳转防连点) * @param {Function} fn * @param {number} [wait=500] */ export function throttle(fn, wait = 500) { let last = 0 return function throttled(...args) { const now = Date.now() if (now - last < wait) return last = now fn.apply(this, args) } }