format.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. /**
  2. * 金额格式化工具
  3. * 后端统一使用整型(分)存储金额,前端在此转换展示
  4. */
  5. /**
  6. * 分 → 元,保留2位小数
  7. * @param {number|null} cents 金额(分)
  8. * @returns {string|null} "12.50" 或 null
  9. */
  10. export function formatPrice(cents) {
  11. if (cents == null || cents === undefined) return null
  12. return (cents / 100).toFixed(2)
  13. }
  14. /**
  15. * 分 → 元,带 ¥ 前缀
  16. * @param {number|null} cents 金额(分)
  17. * @returns {string} "¥12.50" 或 "登录查看"
  18. */
  19. export function formatPriceWithSymbol(cents) {
  20. if (cents == null || cents === undefined) return '登录查看'
  21. return '¥' + (cents / 100).toFixed(2)
  22. }
  23. /**
  24. * 千分比 → 百分比字符串
  25. * @param {number|null} permillage 千分比(235 = 23.5%)
  26. * @returns {string} "23.5%" 或 "--"
  27. */
  28. export function formatRate(permillage) {
  29. if (permillage == null || permillage === undefined) return '--'
  30. return (permillage / 10) + '%'
  31. }
  32. /**
  33. * 百分制评分 → 展示值
  34. * @param {number|null} value 百分制(450 = 4.5分)
  35. * @param {number} fixed 小数位数
  36. * @returns {string} "4.5" 或 "--"
  37. */
  38. export function formatScore(value, fixed) {
  39. if (fixed === undefined) fixed = 1
  40. if (value == null || value === undefined) return '--'
  41. return (value / 100).toFixed(fixed)
  42. }
  43. /**
  44. * iOS 安全日期解析(微信小程序专用)
  45. *
  46. * 部分 iOS(JavaScriptCore)的 `new Date(string)` 只支持以下格式:
  47. * yyyy/MM/dd、yyyy/MM/dd HH:mm:ss、yyyy-MM-dd、
  48. * yyyy-MM-ddTHH:mm:ss、yyyy-MM-ddTHH:mm:ss+HH:mm
  49. *
  50. * 后端常见的 "yyyy-MM-dd HH:mm"(空格分隔,MySQL DATETIME 风格)
  51. * 以及 Java 的 "Sat Aug 22 14:00:00 CST 2026" 在 iOS 下会解析失败
  52. * 返回 Invalid Date。所有日期字符串解析必须经此函数,禁止直接 new Date(str)。
  53. *
  54. * @param {string|number|Date} str 日期字符串、时间戳或 Date 对象
  55. * @returns {Date|null} 解析失败返回 null
  56. */
  57. export function parseDate(str) {
  58. if (str === null || str === undefined || str === '') return null
  59. // 已是 Date 对象或时间戳,直接使用
  60. if (typeof str === 'number') {
  61. var dNum = new Date(str)
  62. return isNaN(dNum.getTime()) ? null : dNum
  63. }
  64. if (typeof str === 'object' && str.getTime) return str
  65. // Java 格式: "Sat Aug 22 14:00:00 CST 2026" — iOS 不支持,手动归一化
  66. var javaRe = /^\w{3}\s+(\w{3})\s+(\d{1,2})\s+(\d{2}:\d{2}:\d{2})\s+\w{3}\s+(\d{4})$/
  67. var m = String(str).match(javaRe)
  68. if (m) {
  69. var months = {Jan:'01',Feb:'02',Mar:'03',Apr:'04',May:'05',Jun:'06',Jul:'07',Aug:'08',Sep:'09',Oct:'10',Nov:'11',Dec:'12'}
  70. var jDay = m[2].length === 1 ? '0' + m[2] : m[2]
  71. return new Date(m[4] + '/' + (months[m[1]] || '01') + '/' + jDay + ' ' + m[3])
  72. }
  73. var s = String(str)
  74. // MySQL DATETIME 风格 "yyyy-MM-dd HH:mm[:ss]"(或 "/" 分隔的 "yyyy/MM/dd HH:mm"):
  75. // 空格分隔格式 iOS 不支持,统一归一化为 iOS 支持的 "yyyy/MM/dd HH:mm:ss"
  76. var spaceDtRe = /^(\d{4})[/-](\d{2})[/-](\d{2}) (\d{2}:\d{2})(:\d{2})?$/
  77. var sm = s.match(spaceDtRe)
  78. if (sm) {
  79. s = sm[1] + '/' + sm[2] + '/' + sm[3] + ' ' + sm[4] + (sm[5] || ':00')
  80. }
  81. // ISO 8601 带毫秒+时区偏移:"2026-10-30T13:13:32.000+08:00"(后端 Jackson 默认输出)。
  82. // iOS JavaScriptCore 只支持不带毫秒的 yyyy-MM-ddTHH:mm:ss+HH:mm,带 .SSS 的会 Invalid Date,
  83. // 故先去掉毫秒段再交给 new Date()。
  84. var millisIsoRe = /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})\.\d{1,3}(Z|[+-]\d{2}:\d{2})$/
  85. var mm = s.match(millisIsoRe)
  86. if (mm) {
  87. s = mm[1] + mm[2]
  88. }
  89. // 其余标准格式原样解析(iOS 支持 yyyy/MM/dd、yyyy-MM-dd、ISO 8601 等)
  90. var d = new Date(s)
  91. return isNaN(d.getTime()) ? null : d
  92. }
  93. /**
  94. * 日期格式化:仅返回 yyyy-MM-dd
  95. * @param {string|number|Date|null} dateStr 日期字符串
  96. * @returns {string} 格式化后的日期,null 返回 ''
  97. */
  98. export function formatDate(dateStr) {
  99. var d = parseDate(dateStr)
  100. if (!d) return ''
  101. var y = d.getFullYear()
  102. var m = ('0' + (d.getMonth() + 1)).slice(-2)
  103. var day = ('0' + d.getDate()).slice(-2)
  104. return y + '-' + m + '-' + day
  105. }
  106. /**
  107. * 日期时间格式化:返回 yyyy-MM-dd HH:mm:ss
  108. * @param {string|number|Date|null} dateStr 日期字符串
  109. * @returns {string} 格式化后的日期时间,null 返回 ''
  110. */
  111. export function formatDateTime(dateStr) {
  112. var d = parseDate(dateStr)
  113. if (!d) return ''
  114. return formatDate(dateStr) + ' ' +
  115. ('0' + d.getHours()).slice(-2) + ':' +
  116. ('0' + d.getMinutes()).slice(-2) + ':' +
  117. ('0' + d.getSeconds()).slice(-2)
  118. }
  119. /**
  120. * 记录的业务日期(yyyy-MM-dd):判定"今日是否打卡"应看业务日期而非 createdAt。
  121. *
  122. * 优先级:recordDate(饮食补打)> startTime(运动)> sleepTime(睡眠)> checkinDate(心情/记账)> createdAt。
  123. * createdAt 是记录写入时间(补打时=今天,业务日期=补打日),且后端 Jackson 输出的
  124. * createdAt 形如 "2026-10-30T13:13:32.000+08:00"(带毫秒+偏移),iOS JavaScriptCore 无法解析;
  125. * recordDate/checkinDate 为纯日期(yyyy-MM-dd),所有环境均可解析。
  126. *
  127. * @param {object|null} record 记录对象(含业务日期字段与 createdAt)
  128. * @returns {string} yyyy-MM-dd;无有效日期返回 ''
  129. */
  130. export function recordDateOf(record) {
  131. if (!record) return ''
  132. var keys = ['recordDate', 'startTime', 'sleepTime', 'checkinDate', 'createdAt']
  133. for (var i = 0; i < keys.length; i++) {
  134. var v = record[keys[i]]
  135. if (v === null || v === undefined || v === '') continue
  136. var d = parseDate(v)
  137. if (!d) continue
  138. var y = d.getFullYear()
  139. var mo = ('0' + (d.getMonth() + 1)).slice(-2)
  140. var da = ('0' + d.getDate()).slice(-2)
  141. return y + '-' + mo + '-' + da
  142. }
  143. return ''
  144. }