/** * 金额格式化工具 * 后端统一使用整型(分)存储金额,前端在此转换展示 */ /** * 分 → 元,保留2位小数 * @param {number|null} cents 金额(分) * @returns {string|null} "12.50" 或 null */ export function formatPrice(cents) { if (cents == null || cents === undefined) return null return (cents / 100).toFixed(2) } /** * 分 → 元,带 ¥ 前缀 * @param {number|null} cents 金额(分) * @returns {string} "¥12.50" 或 "登录查看" */ export function formatPriceWithSymbol(cents) { if (cents == null || cents === undefined) return '登录查看' return '¥' + (cents / 100).toFixed(2) } /** * 千分比 → 百分比字符串 * @param {number|null} permillage 千分比(235 = 23.5%) * @returns {string} "23.5%" 或 "--" */ export function formatRate(permillage) { if (permillage == null || permillage === undefined) return '--' return (permillage / 10) + '%' } /** * 百分制评分 → 展示值 * @param {number|null} value 百分制(450 = 4.5分) * @param {number} fixed 小数位数 * @returns {string} "4.5" 或 "--" */ export function formatScore(value, fixed) { if (fixed === undefined) fixed = 1 if (value == null || value === undefined) return '--' return (value / 100).toFixed(fixed) } /** * iOS 安全日期解析(微信小程序专用) * * 部分 iOS(JavaScriptCore)的 `new Date(string)` 只支持以下格式: * yyyy/MM/dd、yyyy/MM/dd HH:mm:ss、yyyy-MM-dd、 * yyyy-MM-ddTHH:mm:ss、yyyy-MM-ddTHH:mm:ss+HH:mm * * 后端常见的 "yyyy-MM-dd HH:mm"(空格分隔,MySQL DATETIME 风格) * 以及 Java 的 "Sat Aug 22 14:00:00 CST 2026" 在 iOS 下会解析失败 * 返回 Invalid Date。所有日期字符串解析必须经此函数,禁止直接 new Date(str)。 * * @param {string|number|Date} str 日期字符串、时间戳或 Date 对象 * @returns {Date|null} 解析失败返回 null */ export function parseDate(str) { if (str === null || str === undefined || str === '') return null // 已是 Date 对象或时间戳,直接使用 if (typeof str === 'number') { var dNum = new Date(str) return isNaN(dNum.getTime()) ? null : dNum } if (typeof str === 'object' && str.getTime) return str // Java 格式: "Sat Aug 22 14:00:00 CST 2026" — iOS 不支持,手动归一化 var javaRe = /^\w{3}\s+(\w{3})\s+(\d{1,2})\s+(\d{2}:\d{2}:\d{2})\s+\w{3}\s+(\d{4})$/ var m = String(str).match(javaRe) if (m) { 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'} var jDay = m[2].length === 1 ? '0' + m[2] : m[2] return new Date(m[4] + '/' + (months[m[1]] || '01') + '/' + jDay + ' ' + m[3]) } var s = String(str) // MySQL DATETIME 风格 "yyyy-MM-dd HH:mm[:ss]"(或 "/" 分隔的 "yyyy/MM/dd HH:mm"): // 空格分隔格式 iOS 不支持,统一归一化为 iOS 支持的 "yyyy/MM/dd HH:mm:ss" var spaceDtRe = /^(\d{4})[/-](\d{2})[/-](\d{2}) (\d{2}:\d{2})(:\d{2})?$/ var sm = s.match(spaceDtRe) if (sm) { s = sm[1] + '/' + sm[2] + '/' + sm[3] + ' ' + sm[4] + (sm[5] || ':00') } // ISO 8601 带毫秒+时区偏移:"2026-10-30T13:13:32.000+08:00"(后端 Jackson 默认输出)。 // iOS JavaScriptCore 只支持不带毫秒的 yyyy-MM-ddTHH:mm:ss+HH:mm,带 .SSS 的会 Invalid Date, // 故先去掉毫秒段再交给 new Date()。 var millisIsoRe = /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})\.\d{1,3}(Z|[+-]\d{2}:\d{2})$/ var mm = s.match(millisIsoRe) if (mm) { s = mm[1] + mm[2] } // 其余标准格式原样解析(iOS 支持 yyyy/MM/dd、yyyy-MM-dd、ISO 8601 等) var d = new Date(s) return isNaN(d.getTime()) ? null : d } /** * 日期格式化:仅返回 yyyy-MM-dd * @param {string|number|Date|null} dateStr 日期字符串 * @returns {string} 格式化后的日期,null 返回 '' */ export function formatDate(dateStr) { var d = parseDate(dateStr) if (!d) return '' var y = d.getFullYear() var m = ('0' + (d.getMonth() + 1)).slice(-2) var day = ('0' + d.getDate()).slice(-2) return y + '-' + m + '-' + day } /** * 日期时间格式化:返回 yyyy-MM-dd HH:mm:ss * @param {string|number|Date|null} dateStr 日期字符串 * @returns {string} 格式化后的日期时间,null 返回 '' */ export function formatDateTime(dateStr) { var d = parseDate(dateStr) if (!d) return '' return formatDate(dateStr) + ' ' + ('0' + d.getHours()).slice(-2) + ':' + ('0' + d.getMinutes()).slice(-2) + ':' + ('0' + d.getSeconds()).slice(-2) } /** * 记录的业务日期(yyyy-MM-dd):判定"今日是否打卡"应看业务日期而非 createdAt。 * * 优先级:recordDate(饮食补打)> startTime(运动)> sleepTime(睡眠)> checkinDate(心情/记账)> createdAt。 * createdAt 是记录写入时间(补打时=今天,业务日期=补打日),且后端 Jackson 输出的 * createdAt 形如 "2026-10-30T13:13:32.000+08:00"(带毫秒+偏移),iOS JavaScriptCore 无法解析; * recordDate/checkinDate 为纯日期(yyyy-MM-dd),所有环境均可解析。 * * @param {object|null} record 记录对象(含业务日期字段与 createdAt) * @returns {string} yyyy-MM-dd;无有效日期返回 '' */ export function recordDateOf(record) { if (!record) return '' var keys = ['recordDate', 'startTime', 'sleepTime', 'checkinDate', 'createdAt'] for (var i = 0; i < keys.length; i++) { var v = record[keys[i]] if (v === null || v === undefined || v === '') continue var d = parseDate(v) if (!d) continue var y = d.getFullYear() var mo = ('0' + (d.getMonth() + 1)).slice(-2) var da = ('0' + d.getDate()).slice(-2) return y + '-' + mo + '-' + da } return '' }