feat(app): 初始化喝酒了么小程序基础架构
- 添加项目配置文件(.editorconfig, .gitignore) - 创建App.vue主应用文件,包含启动时Mock数据初始化 - 添加index.html和main.js应用入口文件 - 配置manifest.json应用基本信息和权限设置 - 设置pages.json页面路由和全局样式 - 添加uni.promisify.adaptor.js适配器文件 - 创建uni.scss设计令牌系统,定义琥珀夜光主题色彩 - 新增constants.js常量定义,包含酒类分类和品牌数据
This commit is contained in:
+222
@@ -0,0 +1,222 @@
|
||||
/* 喝酒了么 - 工具函数 */
|
||||
|
||||
/**
|
||||
* 标准杯换算引擎
|
||||
* 核心公式: 标准杯 = (饮用量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 }
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user