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 }} · 记录每一次改变
|
{{ today }} · 记录每一次改变
|
||||||
</text>
|
</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<wd-button type="info" size="mini" :disabled="hasSignedToday" @click="handleSign">
|
||||||
|
{{ hasSignedToday ? '已签到' : '签到'}}
|
||||||
|
</wd-button>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<wd-card title="📊 今日概览">
|
<wd-card title="📊 今日概览">
|
||||||
@@ -95,6 +99,7 @@ import { onShow } from "@dcloudio/uni-app";
|
|||||||
import { getOverview, getPlan, getProfile } from "@/utils/profile";
|
import { getOverview, getPlan, getProfile } from "@/utils/profile";
|
||||||
import appLogo from '@/static/logo.png'
|
import appLogo from '@/static/logo.png'
|
||||||
import type { IOverview, IPlan } from "@/type/profile";
|
import type { IOverview, IPlan } from "@/type/profile";
|
||||||
|
import { addSignInPoint } from "@/utils/points";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'Home',
|
name: 'Home',
|
||||||
@@ -105,6 +110,7 @@ const imgURL = appLogo
|
|||||||
const recordStore = useRecordStore()
|
const recordStore = useRecordStore()
|
||||||
|
|
||||||
const todayRecords = ref<IHealthRecord[]>([])
|
const todayRecords = ref<IHealthRecord[]>([])
|
||||||
|
const hasSignedToday = ref(checkSignedToday())
|
||||||
|
|
||||||
const now = new Date()
|
const now = new Date()
|
||||||
const hour = now.getHours()
|
const hour = now.getHours()
|
||||||
@@ -119,6 +125,15 @@ const greeting = computed(() => {
|
|||||||
return '晚上好 ✨'
|
return '晚上好 ✨'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function checkSignedToday() {
|
||||||
|
return wx.getStorageSync('lastSignDate') === getDateStr(Date.now())
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSign() {
|
||||||
|
addSignInPoint()
|
||||||
|
hasSignedToday.value = checkSignedToday()
|
||||||
|
}
|
||||||
|
|
||||||
const today = computed(() => {
|
const today = computed(() => {
|
||||||
const m = now.getMonth() + 1
|
const m = now.getMonth() + 1
|
||||||
const d = now.getDate()
|
const d = now.getDate()
|
||||||
@@ -128,6 +143,7 @@ const today = computed(() => {
|
|||||||
|
|
||||||
onShow(() => {
|
onShow(() => {
|
||||||
todayRecords.value = getDayRecords(getDateStr(Date.now()))
|
todayRecords.value = getDayRecords(getDateStr(Date.now()))
|
||||||
|
hasSignedToday.value = checkSignedToday()
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(() => recordStore.isRecord, () => {
|
watch(() => recordStore.isRecord, () => {
|
||||||
|
|||||||
@@ -113,7 +113,13 @@ const chartOpts = {
|
|||||||
enableScroll: false,
|
enableScroll: false,
|
||||||
legend: { show: true },
|
legend: { show: true },
|
||||||
xAxis: { disableGrid: true },
|
xAxis: { disableGrid: true },
|
||||||
yAxis: { gridType: 'dash', dashLength: 2 },
|
yAxis: {
|
||||||
|
gridType: 'dash',
|
||||||
|
dashLength: 2,
|
||||||
|
data: [
|
||||||
|
{ min: 0, format: (val: number) => val.toFixed(1) }
|
||||||
|
]
|
||||||
|
},
|
||||||
extra: {
|
extra: {
|
||||||
line: { type: 'straight', width: 2, activeType: 'hollow'},
|
line: { type: 'straight', width: 2, activeType: 'hollow'},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -20,6 +20,24 @@
|
|||||||
</view>
|
</view>
|
||||||
</wd-card>
|
</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-group>
|
||||||
<wd-cell size="large" title="记录默认值" is-link @click="openDefaultPopup"/>
|
<wd-cell size="large" title="记录默认值" is-link @click="openDefaultPopup"/>
|
||||||
<wd-cell size="large" title="今日概览" is-link @click="openOverviewPopup"/>
|
<wd-cell size="large" title="今日概览" is-link @click="openOverviewPopup"/>
|
||||||
@@ -38,6 +56,8 @@
|
|||||||
<plan-popup />
|
<plan-popup />
|
||||||
|
|
||||||
<default-popup />
|
<default-popup />
|
||||||
|
|
||||||
|
<point-popup />
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -53,6 +73,9 @@ import { useAppStore } from "@/store/app";
|
|||||||
import type { IProfile } from "@/type/profile";
|
import type { IProfile } from "@/type/profile";
|
||||||
import { countRecordsByMonth, getMaxRecord, getTotalCount } from "@/utils/record";
|
import { countRecordsByMonth, getMaxRecord, getTotalCount } from "@/utils/record";
|
||||||
import DefaultPopup from "@/components/Profile/DefaultPopup.vue";
|
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 profileStore = useProfileStore()
|
||||||
const appStore = useAppStore()
|
const appStore = useAppStore()
|
||||||
@@ -68,6 +91,12 @@ const genderText = computed(() => {
|
|||||||
return '性别未填'
|
return '性别未填'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
onShow(() => {
|
||||||
|
appStore.points = getPoints()
|
||||||
|
appStore.recordCount = getRecordCount()
|
||||||
|
appStore.achievement = getAchievement()
|
||||||
|
})
|
||||||
|
|
||||||
watch(() => profileStore.isRefresh, () => {
|
watch(() => profileStore.isRefresh, () => {
|
||||||
profile.value = getProfile()
|
profile.value = getProfile()
|
||||||
})
|
})
|
||||||
@@ -99,4 +128,8 @@ function openStoragePopup() {
|
|||||||
profileStore.storageMax = getMaxRecord()
|
profileStore.storageMax = getMaxRecord()
|
||||||
profileStore.showStoragePopup = true
|
profileStore.showStoragePopup = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openPointPopup() {
|
||||||
|
profileStore.showPointPopup = true
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ export const useAppStore = defineStore('app', {
|
|||||||
state: () => ({
|
state: () => ({
|
||||||
version: '1.1.0',
|
version: '1.1.0',
|
||||||
viewRecordType: 'calendar',
|
viewRecordType: 'calendar',
|
||||||
|
points: 0,
|
||||||
|
recordCount: 0,
|
||||||
|
achievement: ''
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ export const useProfileStore = defineStore('profile', {
|
|||||||
showOverviewPopup: false,
|
showOverviewPopup: false,
|
||||||
showPlanPopup: false,
|
showPlanPopup: false,
|
||||||
showDefaultRecordValuePopup: false,
|
showDefaultRecordValuePopup: false,
|
||||||
|
showPointPopup: false,
|
||||||
profileForm: {
|
profileForm: {
|
||||||
gender: '',
|
gender: '',
|
||||||
height: 0,
|
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 { getProfile } from '@/utils/profile'
|
||||||
import type { IGroupHealthRecord, IHealthRecord, IRecordMeta, IRecordStats } from "@/type/record";
|
import type { IGroupHealthRecord, IHealthRecord, IRecordMeta, IRecordStats } from "@/type/record";
|
||||||
import type { IStats } from "@/type";
|
import type { IStats } from "@/type";
|
||||||
|
import { addRecordPoint } from "@/utils/points";
|
||||||
|
|
||||||
const MAX_RECORD = 1000;
|
const MAX_RECORD = 1000;
|
||||||
const RECORD_PREFIX = 'health_'
|
const RECORD_PREFIX = 'health_'
|
||||||
@@ -72,6 +73,8 @@ function addRecord(type: string, data: Record<string, any>, timestamp: number) {
|
|||||||
wx.setStorageSync(key, all)
|
wx.setStorageSync(key, all)
|
||||||
|
|
||||||
appendRecentRecord(type, record)
|
appendRecentRecord(type, record)
|
||||||
|
|
||||||
|
addRecordPoint()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user