util.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. /**
  2. * 工具函数库
  3. */
  4. /**
  5. * 格式化日期时间
  6. * @param {String|Date} dateTime 日期时间
  7. * @param {String} format 格式,默认 'YYYY-MM-DD HH:mm:ss'
  8. * @returns {String} 格式化后的字符串
  9. */
  10. export const formatDateTime = (dateTime, format = 'YYYY-MM-DD HH:mm:ss') => {
  11. if (!dateTime) return ''
  12. const date = parseDate(dateTime)
  13. const year = date.getFullYear()
  14. const month = String(date.getMonth() + 1).padStart(2, '0')
  15. const day = String(date.getDate()).padStart(2, '0')
  16. const hour = String(date.getHours()).padStart(2, '0')
  17. const minute = String(date.getMinutes()).padStart(2, '0')
  18. const second = String(date.getSeconds()).padStart(2, '0')
  19. return format
  20. .replace('YYYY', year)
  21. .replace('MM', month)
  22. .replace('DD', day)
  23. .replace('HH', hour)
  24. .replace('mm', minute)
  25. .replace('ss', second)
  26. }
  27. /**
  28. * 格式化时间(月日 时:分)
  29. * @param {String|Date} dateTime 日期时间
  30. * @returns {String} 格式化后的字符串,如 "10月18日 14:00"
  31. */
  32. export const formatTime = (dateTime) => {
  33. if (!dateTime) return ''
  34. const date = parseDate(dateTime)
  35. const month = date.getMonth() + 1
  36. const day = date.getDate()
  37. const hour = date.getHours()
  38. const minute = date.getMinutes()
  39. return `${month}月${day}日 ${hour}:${minute < 10 ? '0' + minute : minute}`
  40. }
  41. /**
  42. * 计算倒计时
  43. * @param {String|Date} endTime 结束时间
  44. * @returns {Object} { days, hours, minutes, seconds, isExpired }
  45. */
  46. export const calculateCountdown = (endTime) => {
  47. if (!endTime) return { isExpired: true }
  48. const end = parseDate(endTime).getTime()
  49. const now = new Date().getTime()
  50. const diff = end - now
  51. if (diff <= 0) {
  52. return { days: 0, hours: 0, minutes: 0, seconds: 0, isExpired: true }
  53. }
  54. const days = Math.floor(diff / (1000 * 60 * 60 * 24))
  55. const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60))
  56. const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60))
  57. const seconds = Math.floor((diff % (1000 * 60)) / 1000)
  58. return { days, hours, minutes, seconds, isExpired: false }
  59. }
  60. /**
  61. * 格式化倒计时文本
  62. * @param {String|Date} endTime 结束时间
  63. * @returns {String} 倒计时文本,如 "2天5小时"、"5小时30分钟"、"30分钟"
  64. */
  65. export const formatCountdown = (endTime) => {
  66. const { days, hours, minutes, isExpired } = calculateCountdown(endTime)
  67. if (isExpired) return '已截止'
  68. if (days > 0) {
  69. return `${days}天${hours}小时`
  70. } else if (hours > 0) {
  71. return `${hours}小时${minutes}分钟`
  72. } else {
  73. return `${minutes}分钟`
  74. }
  75. }
  76. /**
  77. * 格式化价格
  78. * @param {Number} price 价格
  79. * @returns {String} 格式化后的价格,如 "99.00"
  80. */
  81. export const formatPrice = (price) => {
  82. if (price === null || price === undefined) return '0.00'
  83. return Number(price).toFixed(2)
  84. }
  85. /**
  86. * 脱敏手机号
  87. * @param {String} phone 手机号
  88. * @returns {String} 脱敏后的手机号,如 "138****8888"
  89. */
  90. export const maskPhone = (phone) => {
  91. if (!phone) return ''
  92. return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')
  93. }
  94. /**
  95. * 脱敏姓名
  96. * @param {String} name 姓名
  97. * @returns {String} 脱敏后的姓名,如 "张*"、"欧阳**"
  98. */
  99. export const maskName = (name) => {
  100. if (!name) return ''
  101. if (name.length === 2) {
  102. return name[0] + '*'
  103. } else if (name.length > 2) {
  104. return name[0] + '*'.repeat(name.length - 1)
  105. }
  106. return name
  107. }
  108. /**
  109. * 节流函数
  110. * @param {Function} func 要执行的函数
  111. * @param {Number} delay 延迟时间(毫秒)
  112. * @returns {Function} 节流后的函数
  113. */
  114. export const throttle = (func, delay = 500) => {
  115. let timer = null
  116. return function(...args) {
  117. if (!timer) {
  118. timer = setTimeout(() => {
  119. func.apply(this, args)
  120. timer = null
  121. }, delay)
  122. }
  123. }
  124. }
  125. /**
  126. * 防抖函数
  127. * @param {Function} func 要执行的函数
  128. * @param {Number} delay 延迟时间(毫秒)
  129. * @returns {Function} 防抖后的函数
  130. */
  131. export const debounce = (func, delay = 500) => {
  132. let timer = null
  133. return function(...args) {
  134. if (timer) clearTimeout(timer)
  135. timer = setTimeout(() => {
  136. func.apply(this, args)
  137. }, delay)
  138. }
  139. }
  140. /**
  141. * 判断是否登录
  142. * @returns {Boolean} 是否已登录
  143. */
  144. export const isLoggedIn = () => {
  145. const token = uni.getStorageSync('token')
  146. const userInfo = uni.getStorageSync('userInfo')
  147. return !!(token && userInfo)
  148. }
  149. /**
  150. * 跳转登录页
  151. * @param {String} redirectUrl 登录后要跳转的页面
  152. */
  153. export const goToLogin = (redirectUrl = '') => {
  154. let url = '/pages/page3/page3'
  155. if (redirectUrl) {
  156. url += `?redirect=${encodeURIComponent(redirectUrl)}`
  157. }
  158. uni.navigateTo({ url })
  159. }
  160. /**
  161. * 检查登录状态(未登录则跳转登录页)
  162. * @param {String} redirectUrl 登录后要跳转的页面
  163. * @returns {Boolean} 是否已登录
  164. */
  165. export const checkLogin = (redirectUrl = '') => {
  166. if (!isLoggedIn()) {
  167. goToLogin(redirectUrl)
  168. return false
  169. }
  170. return true
  171. }
  172. /**
  173. * 获取年龄
  174. * @param {String} birthday 生日,格式 'YYYY-MM-DD'
  175. * @returns {Number} 年龄
  176. */
  177. export const getAge = (birthday) => {
  178. if (!birthday) return 0
  179. const birthDate = parseDate(birthday)
  180. const today = new Date()
  181. let age = today.getFullYear() - birthDate.getFullYear()
  182. const monthDiff = today.getMonth() - birthDate.getMonth()
  183. if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
  184. age--
  185. }
  186. return age
  187. }
  188. /**
  189. * 获取星座
  190. * @param {String} birthday 生日,格式 'YYYY-MM-DD'
  191. * @returns {String} 星座名称
  192. */
  193. export const getConstellation = (birthday) => {
  194. if (!birthday) return ''
  195. const date = parseDate(birthday)
  196. const month = date.getMonth() + 1
  197. const day = date.getDate()
  198. const constellations = [
  199. { name: '摩羯座', start: [12, 22], end: [1, 19] },
  200. { name: '水瓶座', start: [1, 20], end: [2, 18] },
  201. { name: '双鱼座', start: [2, 19], end: [3, 20] },
  202. { name: '白羊座', start: [3, 21], end: [4, 19] },
  203. { name: '金牛座', start: [4, 20], end: [5, 20] },
  204. { name: '双子座', start: [5, 21], end: [6, 21] },
  205. { name: '巨蟹座', start: [6, 22], end: [7, 22] },
  206. { name: '狮子座', start: [7, 23], end: [8, 22] },
  207. { name: '处女座', start: [8, 23], end: [9, 22] },
  208. { name: '天秤座', start: [9, 23], end: [10, 23] },
  209. { name: '天蝎座', start: [10, 24], end: [11, 22] },
  210. { name: '射手座', start: [11, 23], end: [12, 21] }
  211. ]
  212. for (const constellation of constellations) {
  213. const [startMonth, startDay] = constellation.start
  214. const [endMonth, endDay] = constellation.end
  215. if (startMonth === endMonth) {
  216. if (month === startMonth && day >= startDay && day <= endDay) {
  217. return constellation.name
  218. }
  219. } else {
  220. if ((month === startMonth && day >= startDay) || (month === endMonth && day <= endDay)) {
  221. return constellation.name
  222. }
  223. }
  224. }
  225. return ''
  226. }
  227. /**
  228. * 图片预览
  229. * @param {String|Array} urls 图片地址(单张或数组)
  230. * @param {Number} current 当前显示图片的索引,默认为0
  231. */
  232. export const previewImage = (urls, current = 0) => {
  233. const imageUrls = Array.isArray(urls) ? urls : [urls]
  234. uni.previewImage({
  235. urls: imageUrls,
  236. current: current
  237. })
  238. }
  239. /**
  240. * 分享功能
  241. * @param {Object} options 分享选项 { title, path, imageUrl }
  242. */
  243. export const share = (options = {}) => {
  244. return {
  245. title: options.title || '遇见对的人,从这里开始',
  246. path: options.path || '/pages/index/index',
  247. imageUrl: options.imageUrl || '/static/share-default.jpg'
  248. }
  249. }
  250. /**
  251. * 复制到剪贴板
  252. * @param {String} text 要复制的文本
  253. * @param {String} successMsg 成功提示文本
  254. */
  255. export const copyToClipboard = (text, successMsg = '复制成功') => {
  256. uni.setClipboardData({
  257. data: text,
  258. success: () => {
  259. uni.showToast({
  260. title: successMsg,
  261. icon: 'success'
  262. })
  263. }
  264. })
  265. }
  266. /**
  267. * 拨打电话
  268. * @param {String} phoneNumber 电话号码
  269. */
  270. export const makePhoneCall = (phoneNumber) => {
  271. uni.makePhoneCall({
  272. phoneNumber: phoneNumber
  273. })
  274. }
  275. export default {
  276. formatDateTime,
  277. formatTime,
  278. calculateCountdown,
  279. formatCountdown,
  280. formatPrice,
  281. maskPhone,
  282. maskName,
  283. throttle,
  284. debounce,
  285. isLoggedIn,
  286. goToLogin,
  287. checkLogin,
  288. getAge,
  289. getConstellation,
  290. previewImage,
  291. share,
  292. copyToClipboard,
  293. makePhoneCall
  294. }
  295. /**
  296. * 统一的跨端日期解析(兼容 iOS)
  297. * - 将 'YYYY-MM-DD HH:mm:ss' 或 'YYYY-MM-DD' 转为 'YYYY/MM/DD HH:mm:ss'
  298. * - 直接返回 Date 时不处理
  299. */
  300. export function parseDate(dateTime) {
  301. if (!dateTime) return new Date(NaN)
  302. if (dateTime instanceof Date) return dateTime
  303. let s = String(dateTime).trim()
  304. // 若是纯数字时间戳
  305. if (/^\d{10,13}$/.test(s)) {
  306. const ts = s.length === 10 ? Number(s) * 1000 : Number(s)
  307. return new Date(ts)
  308. }
  309. // iOS 兼容:横杠替换为斜杠
  310. s = s.replace(/-/g, '/').replace('T', ' ').replace(/\.\d{3}Z?$/, '')
  311. return new Date(s)
  312. }