util.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. import httpRequest from '@/common/httpRequest'
  2. // 金额格式化成K格式
  3. export const formatNumberToK = (param) => {
  4. // 1. 异常处理:空值(null/undefined)直接返回空字符串
  5. if (param === null || param === undefined) {
  6. return '';
  7. }
  8. // 2. 统一预处理:无论参数是数字/字符串,都转为字符串并去除首尾空格
  9. let numStr = String(param).trim();
  10. // 3. 空字符串情况直接返回
  11. if (numStr === '') {
  12. return '';
  13. }
  14. // 4. 将处理后的字符串转为数字
  15. const num = Number(numStr);
  16. // 5. 非有效数字的情况,返回原始参数的字符串形式(保持输入原貌)
  17. if (isNaN(num)) {
  18. return String(param);
  19. }
  20. // 6. 小于1000的数字,返回整数形式的字符串(无小数)
  21. if (num < 1000) {
  22. return String(Math.floor(num));
  23. }
  24. // 7. 大于等于1000的数字,除以1000取整后拼接K
  25. const kNum = Math.floor(num / 1000);
  26. return `${kNum}K`;
  27. };
  28. // 导出简历文件
  29. let loading = false
  30. export const exportResumePdf = (resumesId) => {
  31. if (loading) return
  32. const baseUrl = httpRequest.config('APIHOST')
  33. const url = baseUrl + '/app/resumes/exportPdf'
  34. const token = uni.getStorageSync('token')
  35. uni.showLoading({ title: '导出中' })
  36. loading = true
  37. // #ifdef APP-PLUS
  38. // App 端(尤其 iOS)用 downloadFile 最稳定
  39. const downloadUrl = `${url}?resumesId=${encodeURIComponent(resumesId)}`
  40. uni.downloadFile({
  41. url: downloadUrl,
  42. header: { token },
  43. success: (dRes) => {
  44. if (dRes.statusCode !== 200) {
  45. uni.showToast({ title: '导出失败:' + dRes.statusCode, icon: 'none' })
  46. return
  47. }
  48. if (!dRes.tempFilePath) {
  49. uni.showToast({ title: '导出失败,未获取到文件', icon: 'none' })
  50. return
  51. }
  52. uni.openDocument({
  53. filePath: dRes.tempFilePath,
  54. showMenu: true,
  55. fail: (e) => {
  56. console.error('openDocument fail', e)
  57. uni.showToast({ title: '无法打开文件', icon: 'none' })
  58. }
  59. })
  60. },
  61. fail: (err) => {
  62. console.error('downloadFile fail', err)
  63. uni.showToast({ title: '下载失败', icon: 'none' })
  64. },
  65. complete: () => {
  66. uni.hideLoading()
  67. loading = false
  68. }
  69. })
  70. return
  71. // #endif
  72. uni.request({
  73. url,
  74. method: 'GET',
  75. data: {
  76. resumesId
  77. },
  78. header: {
  79. 'content-type': 'application/pdf',
  80. token
  81. },
  82. responseType: 'arraybuffer',
  83. success: (res) => {
  84. // 1. 先校验
  85. if (res.statusCode !== 200) {
  86. uni.showToast({
  87. title: '导出失败:' + res.statusCode,
  88. icon: 'none'
  89. })
  90. return
  91. }
  92. if (!res.data || (res.data.byteLength !== undefined && res.data.byteLength === 0)) {
  93. uni.showToast({
  94. title: '导出失败,返回内容为空',
  95. icon: 'none'
  96. })
  97. return
  98. }
  99. const mime = 'application/pdf'
  100. // #ifdef H5
  101. try {
  102. const blob = new Blob([res.data], {
  103. type: mime
  104. })
  105. const downloadUrl = window.URL.createObjectURL(blob)
  106. const link = document.createElement('a')
  107. link.href = downloadUrl
  108. link.download = '简历.pdf'
  109. link.click()
  110. window.URL.revokeObjectURL(downloadUrl)
  111. uni.showToast({
  112. title: '文件下载成功',
  113. icon: 'success'
  114. })
  115. } catch (e) {
  116. console.error('H5 下载异常', e, res)
  117. uni.showToast({
  118. title: 'H5下载失败:' + e.message,
  119. icon: 'none'
  120. })
  121. }
  122. // #endif
  123. // #ifdef APP-PLUS
  124. // App 端已在上方使用 uni.downloadFile 处理,这里不再执行
  125. // #endif
  126. },
  127. fail: (err) => {
  128. console.error('exportResumePdf 请求失败', err)
  129. uni.showToast({
  130. title: '请求失败:' + err.errMsg,
  131. icon: 'none'
  132. })
  133. },
  134. complete: () => {
  135. uni.hideLoading()
  136. loading = false
  137. }
  138. })
  139. }
  140. /**
  141. * 防抖:连续触发,只在最后一次操作后 wait 毫秒执行一次
  142. * @param {Function} fn
  143. * @param {number} [wait=300]
  144. * @returns {Function} 每次 debounce(fn) 都会得到独立计时器,互不干扰
  145. */
  146. export function debounce(fn, wait = 300) {
  147. let timer = null
  148. return function debounced(...args) {
  149. const ctx = this
  150. clearTimeout(timer)
  151. timer = setTimeout(() => {
  152. timer = null
  153. fn.apply(ctx, args)
  154. }, wait)
  155. }
  156. }
  157. /**
  158. * 节流:wait 毫秒内最多执行一次(适合按钮点击、跳转防连点)
  159. * @param {Function} fn
  160. * @param {number} [wait=500]
  161. */
  162. export function throttle(fn, wait = 500) {
  163. let last = 0
  164. return function throttled(...args) {
  165. const now = Date.now()
  166. if (now - last < wait) return
  167. last = now
  168. fn.apply(this, args)
  169. }
  170. }