feat: 增加积分模块
This commit is contained in:
44
src/components/Profile/PointPopup.vue
Normal file
44
src/components/Profile/PointPopup.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<wd-popup
|
||||
v-model="profileStore.showPointPopup"
|
||||
position="bottom"
|
||||
:safe-area-inset-bottom="true"
|
||||
custom-style="border-radius: 32rpx 32rpx 0 0; padding: 40rpx 32rpx"
|
||||
>
|
||||
<view class="p-4">
|
||||
<view class="text-center text-base font-semibold mb-4">
|
||||
积分与成就说明
|
||||
</view>
|
||||
|
||||
<view class="space-y-3 text-sm text-gray-700">
|
||||
<view>
|
||||
<text class="font-medium">✨ 积分规则</text>
|
||||
|
||||
<view class="mt-2 space-y-1 text-xs text-gray-600">
|
||||
<view> 每日签到:+5 积分</view>
|
||||
<view> 记录健康数据:+10 积分(每日上限5次)</view>
|
||||
<view> 咨询AI助手:-100 积分</view>
|
||||
<view> 删除数据不影响已获得的积分</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view>
|
||||
<text class="font-medium">🏆 成就说明</text>
|
||||
|
||||
<view class="mt-2 space-y-1 text-xs text-gray-600">
|
||||
<view>🌱 健康新手:累计记录 10 次</view>
|
||||
<view>🌿 记录达人:累计记录 50 次</view>
|
||||
<view>🌳 健康先锋:累计记录 100 次</view>
|
||||
<view>🏆 数据大师:累计记录 500 次</view>
|
||||
<view>💎 传奇健康家:累计记录 1000 次</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</wd-popup>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useProfileStore } from "@/store/profile";
|
||||
const profileStore = useProfileStore();
|
||||
</script>
|
||||
@@ -11,6 +11,10 @@
|
||||
{{ today }} · 记录每一次改变
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<wd-button type="info" size="mini" :disabled="hasSignedToday" @click="handleSign">
|
||||
{{ hasSignedToday ? '已签到' : '签到'}}
|
||||
</wd-button>
|
||||
</view>
|
||||
|
||||
<wd-card title="📊 今日概览">
|
||||
@@ -95,6 +99,7 @@ import { onShow } from "@dcloudio/uni-app";
|
||||
import { getOverview, getPlan, getProfile } from "@/utils/profile";
|
||||
import appLogo from '@/static/logo.png'
|
||||
import type { IOverview, IPlan } from "@/type/profile";
|
||||
import { addSignInPoint } from "@/utils/points";
|
||||
|
||||
defineOptions({
|
||||
name: 'Home',
|
||||
@@ -105,6 +110,7 @@ const imgURL = appLogo
|
||||
const recordStore = useRecordStore()
|
||||
|
||||
const todayRecords = ref<IHealthRecord[]>([])
|
||||
const hasSignedToday = ref(checkSignedToday())
|
||||
|
||||
const now = new Date()
|
||||
const hour = now.getHours()
|
||||
@@ -119,6 +125,15 @@ const greeting = computed(() => {
|
||||
return '晚上好 ✨'
|
||||
})
|
||||
|
||||
function checkSignedToday() {
|
||||
return wx.getStorageSync('lastSignDate') === getDateStr(Date.now())
|
||||
}
|
||||
|
||||
function handleSign() {
|
||||
addSignInPoint()
|
||||
hasSignedToday.value = checkSignedToday()
|
||||
}
|
||||
|
||||
const today = computed(() => {
|
||||
const m = now.getMonth() + 1
|
||||
const d = now.getDate()
|
||||
@@ -128,6 +143,7 @@ const today = computed(() => {
|
||||
|
||||
onShow(() => {
|
||||
todayRecords.value = getDayRecords(getDateStr(Date.now()))
|
||||
hasSignedToday.value = checkSignedToday()
|
||||
})
|
||||
|
||||
watch(() => recordStore.isRecord, () => {
|
||||
|
||||
@@ -113,7 +113,13 @@ const chartOpts = {
|
||||
enableScroll: false,
|
||||
legend: { show: true },
|
||||
xAxis: { disableGrid: true },
|
||||
yAxis: { gridType: 'dash', dashLength: 2 },
|
||||
yAxis: {
|
||||
gridType: 'dash',
|
||||
dashLength: 2,
|
||||
data: [
|
||||
{ min: 0, format: (val: number) => val.toFixed(1) }
|
||||
]
|
||||
},
|
||||
extra: {
|
||||
line: { type: 'straight', width: 2, activeType: 'hollow'},
|
||||
},
|
||||
|
||||
@@ -20,6 +20,24 @@
|
||||
</view>
|
||||
</wd-card>
|
||||
|
||||
<wd-card title="积分成就" type="rectangle">
|
||||
<view class="flex items-center justify-between">
|
||||
<view class="mt-2 space-y-1 text-gray-600">
|
||||
<view>✨ 当前积分:{{ appStore.points }}分</view>
|
||||
<view>📊 记录次数:{{ appStore.recordCount }}次</view>
|
||||
<view>🏆 当前成就:{{ appStore.achievement }}</view>
|
||||
</view>
|
||||
|
||||
<wd-button size="small" plain type="primary" @click="openPointPopup">
|
||||
查看说明
|
||||
</wd-button>
|
||||
</view>
|
||||
|
||||
<view class="mt-2 text-xs text-gray-400">
|
||||
坚持记录健康数据,解锁更多成就
|
||||
</view>
|
||||
</wd-card>
|
||||
|
||||
<wd-cell-group>
|
||||
<wd-cell size="large" title="记录默认值" is-link @click="openDefaultPopup"/>
|
||||
<wd-cell size="large" title="今日概览" is-link @click="openOverviewPopup"/>
|
||||
@@ -38,6 +56,8 @@
|
||||
<plan-popup />
|
||||
|
||||
<default-popup />
|
||||
|
||||
<point-popup />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -53,6 +73,9 @@ import { useAppStore } from "@/store/app";
|
||||
import type { IProfile } from "@/type/profile";
|
||||
import { countRecordsByMonth, getMaxRecord, getTotalCount } from "@/utils/record";
|
||||
import DefaultPopup from "@/components/Profile/DefaultPopup.vue";
|
||||
import { getAchievement, getPoints, getRecordCount } from "@/utils/points";
|
||||
import { onShow } from "@dcloudio/uni-app";
|
||||
import PointPopup from "@/components/Profile/PointPopup.vue";
|
||||
|
||||
const profileStore = useProfileStore()
|
||||
const appStore = useAppStore()
|
||||
@@ -68,6 +91,12 @@ const genderText = computed(() => {
|
||||
return '性别未填'
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
appStore.points = getPoints()
|
||||
appStore.recordCount = getRecordCount()
|
||||
appStore.achievement = getAchievement()
|
||||
})
|
||||
|
||||
watch(() => profileStore.isRefresh, () => {
|
||||
profile.value = getProfile()
|
||||
})
|
||||
@@ -99,4 +128,8 @@ function openStoragePopup() {
|
||||
profileStore.storageMax = getMaxRecord()
|
||||
profileStore.showStoragePopup = true
|
||||
}
|
||||
|
||||
function openPointPopup() {
|
||||
profileStore.showPointPopup = true
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -4,6 +4,9 @@ export const useAppStore = defineStore('app', {
|
||||
state: () => ({
|
||||
version: '1.1.0',
|
||||
viewRecordType: 'calendar',
|
||||
points: 0,
|
||||
recordCount: 0,
|
||||
achievement: ''
|
||||
}),
|
||||
actions: {
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ export const useProfileStore = defineStore('profile', {
|
||||
showOverviewPopup: false,
|
||||
showPlanPopup: false,
|
||||
showDefaultRecordValuePopup: false,
|
||||
showPointPopup: false,
|
||||
profileForm: {
|
||||
gender: '',
|
||||
height: 0,
|
||||
|
||||
84
src/utils/points.ts
Normal file
84
src/utils/points.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import { getDateStr } from "@/utils/record";
|
||||
|
||||
const POINTS = {
|
||||
SIGN_IN: 5, // 签到积分
|
||||
RECORD: 10, // 记录一次积分
|
||||
AI_COST: 100 // AI问答消耗积分
|
||||
}
|
||||
|
||||
// 获取当前积分
|
||||
export function getPoints(): number {
|
||||
return wx.getStorageSync('points') || 0
|
||||
}
|
||||
|
||||
// 签到
|
||||
export function addSignInPoint() {
|
||||
const lastSignDate = wx.getStorageSync('lastSignDate')
|
||||
const today = getDateStr(Date.now())
|
||||
|
||||
if (lastSignDate === today) {
|
||||
uni.showToast({ title: '今天已经签到过了', icon: 'success' })
|
||||
}
|
||||
|
||||
const currentPoints = getPoints() + POINTS.SIGN_IN
|
||||
wx.setStorageSync('points', currentPoints)
|
||||
wx.setStorageSync('lastSignDate', today)
|
||||
|
||||
uni.showToast({ title: '今日签到成功', icon: 'success' })
|
||||
}
|
||||
|
||||
// 记录一次健康数据
|
||||
export function addRecordPoint() {
|
||||
// 累计记录次数
|
||||
const recordCount = (wx.getStorageSync('recordCount') || 0) + 1
|
||||
wx.setStorageSync('recordCount', recordCount)
|
||||
|
||||
// 累计积分
|
||||
const today = getDateStr(Date.now())
|
||||
const lastRecordDate = wx.getStorageSync('lastRecordDate')
|
||||
const todayRecordCount = lastRecordDate === today ? wx.getStorageSync('todayRecordCount') || 0 : 0
|
||||
|
||||
if (todayRecordCount >= 5) {
|
||||
return
|
||||
}
|
||||
|
||||
wx.setStorageSync('lastRecordDate', today)
|
||||
wx.setStorageSync('todayRecordCount', todayRecordCount + 1)
|
||||
|
||||
const currentPoints = getPoints() + POINTS.RECORD
|
||||
wx.setStorageSync('points', currentPoints)
|
||||
}
|
||||
|
||||
// 兑换AI问答
|
||||
function exchangeAIPoint() {
|
||||
const currentPoints = getPoints()
|
||||
|
||||
if (currentPoints < POINTS.AI_COST) {
|
||||
return {
|
||||
success: false,
|
||||
msg: `积分不足,还需要${POINTS.AI_COST - currentPoints}分`
|
||||
}
|
||||
}
|
||||
|
||||
wx.setStorageSync('points', currentPoints - POINTS.AI_COST)
|
||||
|
||||
// 累计兑换次数
|
||||
const aiCount = (wx.getStorageSync('aiCount') || 0) + 1
|
||||
wx.setStorageSync('aiCount', aiCount)
|
||||
}
|
||||
|
||||
export function getRecordCount() :number {
|
||||
return uni.getStorageSync('recordCount') || 0
|
||||
}
|
||||
|
||||
export function getAchievement() :string {
|
||||
const recordCount = uni.getStorageSync('recordCount') || 0
|
||||
|
||||
if (recordCount >= 1000) return '💎 传奇健康家'
|
||||
if (recordCount >= 500) return '🏆 数据大师'
|
||||
if (recordCount >= 100) return '🌳 健康先锋'
|
||||
if (recordCount >= 50) return '🌿 记录达人'
|
||||
if (recordCount >= 10) return '🌱 健康新手'
|
||||
|
||||
return '暂无成就'
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { getProfile } from '@/utils/profile'
|
||||
import type { IGroupHealthRecord, IHealthRecord, IRecordMeta, IRecordStats } from "@/type/record";
|
||||
import type { IStats } from "@/type";
|
||||
import { addRecordPoint } from "@/utils/points";
|
||||
|
||||
const MAX_RECORD = 1000;
|
||||
const RECORD_PREFIX = 'health_'
|
||||
@@ -72,6 +73,8 @@ function addRecord(type: string, data: Record<string, any>, timestamp: number) {
|
||||
wx.setStorageSync(key, all)
|
||||
|
||||
appendRecentRecord(type, record)
|
||||
|
||||
addRecordPoint()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user