- 添加完整的后端接口文档,包含用户、打卡记录、统计等模块 - 修复应用名称从"喝酒了么"统一更正为"喝了么" - 重构App.vue中的启动逻辑,增加数据迁移和路由守卫功能 - 调整DrinkCalendar组件的UI间距和尺寸参数 - 优化DrinkCard组件的品牌展示文案 - 修改manifest.json中的应用描述和微信小程序配置 - 调整pages.json中的页面路径顺序和tabBar配置 - 更新全局样式文件中的应用名称标识 - 修改mock数据中的用户头像默认值 - 优化卡片导出功能的绘制逻辑和视觉效果
223 lines
5.9 KiB
JavaScript
223 lines
5.9 KiB
JavaScript
/* 喝了么 - 工具函数 */
|
||
|
||
/**
|
||
* 标准杯换算引擎
|
||
* 核心公式: 标准杯 = (饮用量ml × 酒精度数% × 0.8) / 10
|
||
* 1标准杯 = 10g纯酒精
|
||
*/
|
||
export function calcStandardCups(amountMl, degreePercent) {
|
||
if (!amountMl || !degreePercent) return 0
|
||
return Math.round((amountMl * (degreePercent / 100) * 0.8 / 10) * 100) / 100
|
||
}
|
||
|
||
/**
|
||
* 单位换算为ml
|
||
*/
|
||
const UNIT_TO_ML = {
|
||
ml: 1,
|
||
liang: 50, // 1两 = 50ml
|
||
bottle: 500, // 1瓶 = 500ml (啤酒默认)
|
||
cup: 150, // 1杯 = 150ml
|
||
can: 330, // 1听 = 330ml
|
||
shot: 30 // 1shot = 30ml
|
||
}
|
||
|
||
export function unitToMl(amount, unit) {
|
||
const factor = UNIT_TO_ML[unit] || 1
|
||
return amount * factor
|
||
}
|
||
|
||
/**
|
||
* 计算一组饮品的总标准杯
|
||
*/
|
||
export function calcTotalStandardCups(drinks) {
|
||
return drinks.reduce((sum, d) => {
|
||
const ml = unitToMl(d.amount, d.unit)
|
||
return sum + calcStandardCups(ml, d.degree)
|
||
}, 0)
|
||
}
|
||
|
||
/**
|
||
* 日期格式化
|
||
*/
|
||
export function formatDate(date, format = 'YYYY-MM-DD') {
|
||
const d = new Date(date)
|
||
const year = d.getFullYear()
|
||
const month = String(d.getMonth() + 1).padStart(2, '0')
|
||
const day = String(d.getDate()).padStart(2, '0')
|
||
const hours = String(d.getHours()).padStart(2, '0')
|
||
const minutes = String(d.getMinutes()).padStart(2, '0')
|
||
|
||
return format
|
||
.replace('YYYY', year)
|
||
.replace('MM', month)
|
||
.replace('DD', day)
|
||
.replace('HH', hours)
|
||
.replace('mm', minutes)
|
||
}
|
||
|
||
/**
|
||
* 获取指定月份的日历数据
|
||
* @param {number} year
|
||
* @param {number} month 1-12
|
||
* @returns {Array} 日历数组,包含前置空白、日期、后置空白
|
||
*/
|
||
export function getCalendarDays(year, month) {
|
||
const firstDay = new Date(year, month - 1, 1)
|
||
const lastDay = new Date(year, month, 0)
|
||
const daysInMonth = lastDay.getDate()
|
||
// 周一为一周起始 (0=周一, 6=周日)
|
||
let startWeekday = firstDay.getDay() - 1
|
||
if (startWeekday < 0) startWeekday = 6
|
||
|
||
const days = []
|
||
// 前置空白
|
||
for (let i = 0; i < startWeekday; i++) {
|
||
days.push({ day: null, date: null })
|
||
}
|
||
// 日期
|
||
for (let i = 1; i <= daysInMonth; i++) {
|
||
const dateStr = `${year}-${String(month).padStart(2, '0')}-${String(i).padStart(2, '0')}`
|
||
days.push({
|
||
day: i,
|
||
date: dateStr,
|
||
isToday: dateStr === formatDate(new Date(), 'YYYY-MM-DD')
|
||
})
|
||
}
|
||
return days
|
||
}
|
||
|
||
/**
|
||
* 获取本周一的日期
|
||
*/
|
||
export function getWeekStart(date = new Date()) {
|
||
const d = new Date(date)
|
||
const day = d.getDay()
|
||
const diff = d.getDate() - day + (day === 0 ? -6 : 1)
|
||
return new Date(d.setDate(diff))
|
||
}
|
||
|
||
/**
|
||
* 获取时间问候语
|
||
*/
|
||
export function getGreeting() {
|
||
const hour = new Date().getHours()
|
||
if (hour < 6) return '夜深了'
|
||
if (hour < 12) return '早上好'
|
||
if (hour < 14) return '中午好'
|
||
if (hour < 18) return '下午好'
|
||
if (hour < 22) return '晚上好'
|
||
return '夜深了'
|
||
}
|
||
|
||
/**
|
||
* 随机获取一条酒言酒语
|
||
*/
|
||
export function getRandomQuote(quotes) {
|
||
return quotes[Math.floor(Math.random() * quotes.length)]
|
||
}
|
||
|
||
/**
|
||
* 统计数据计算
|
||
*/
|
||
export function calcStats(records) {
|
||
const now = new Date()
|
||
const today = formatDate(now, 'YYYY-MM-DD')
|
||
|
||
// 本周统计
|
||
const weekStart = getWeekStart(now)
|
||
const weekRecords = records.filter(r => new Date(r.date) >= weekStart)
|
||
const weekDrinkCount = weekRecords.filter(r => r.mode === 'drank').length
|
||
const weekCups = weekRecords.reduce((sum, r) => sum + (r.standardCupsTotal || 0), 0)
|
||
|
||
// 本月统计
|
||
const monthStr = formatDate(now, 'YYYY-MM')
|
||
const monthRecords = records.filter(r => r.date && r.date.startsWith(monthStr))
|
||
const monthDrinkCount = monthRecords.filter(r => r.mode === 'drank').length
|
||
const monthCups = monthRecords.reduce((sum, r) => sum + (r.standardCupsTotal || 0), 0)
|
||
|
||
// 连续天数
|
||
let streak = 0
|
||
let streakType = 'drank'
|
||
const d = new Date()
|
||
// 检查今天是否有记录
|
||
const todayRecord = records.find(r => r.date === today)
|
||
if (todayRecord) {
|
||
streakType = todayRecord.mode
|
||
streak = 1
|
||
// 向前追溯
|
||
for (let i = 1; i < 365; i++) {
|
||
const prevDate = new Date(d)
|
||
prevDate.setDate(prevDate.getDate() - i)
|
||
const prevStr = formatDate(prevDate, 'YYYY-MM-DD')
|
||
const prev = records.find(r => r.date === prevStr)
|
||
if (prev && prev.mode === streakType) {
|
||
streak++
|
||
} else {
|
||
break
|
||
}
|
||
}
|
||
}
|
||
|
||
// 总打卡天数
|
||
const totalDays = new Set(records.filter(r => r.mode === 'drank').map(r => r.date)).size
|
||
const totalRecords = records.filter(r => r.mode === 'drank').length
|
||
const totalCups = records.reduce((sum, r) => sum + (r.standardCupsTotal || 0), 0)
|
||
|
||
// 最爱酒类
|
||
const categoryCount = {}
|
||
records.forEach(r => {
|
||
if (r.drinks) {
|
||
r.drinks.forEach(d => {
|
||
categoryCount[d.category] = (categoryCount[d.category] || 0) + 1
|
||
})
|
||
}
|
||
})
|
||
const favCategory = Object.entries(categoryCount).sort((a, b) => b[1] - a[1])[0]
|
||
|
||
return {
|
||
weekDrinkCount,
|
||
weekCups: Math.round(weekCups * 10) / 10,
|
||
monthDrinkCount,
|
||
monthCups: Math.round(monthCups * 10) / 10,
|
||
streak,
|
||
streakType,
|
||
totalDays,
|
||
totalRecords,
|
||
totalCups: Math.round(totalCups * 10) / 10,
|
||
favCategory: favCategory ? favCategory[0] : null,
|
||
categoryVariety: Object.keys(categoryCount).length
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 检查成就解锁
|
||
*/
|
||
export function checkAchievements(stats, achievements) {
|
||
return achievements.map(a => {
|
||
let unlocked = false
|
||
const { type, value, category } = a.condition
|
||
|
||
switch (type) {
|
||
case 'total_records':
|
||
unlocked = stats.totalRecords >= value
|
||
break
|
||
case 'total_standard_cups':
|
||
unlocked = stats.totalCups >= value
|
||
break
|
||
case 'streak_days':
|
||
unlocked = stats.streak >= value
|
||
break
|
||
case 'category_variety':
|
||
unlocked = stats.categoryVariety >= value
|
||
break
|
||
case 'category_records':
|
||
// 需要更细粒度的数据,这里简化处理
|
||
unlocked = false
|
||
break
|
||
}
|
||
|
||
return { ...a, unlocked }
|
||
})
|
||
}
|