|
@@ -63,13 +63,41 @@
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
|
|
|
+ <!-- 打卡功能区域 -->
|
|
|
|
|
+ <view class="checkin-card" v-if="checkinTasks.length > 0">
|
|
|
|
|
+ <view class="card-header">
|
|
|
|
|
+ <text class="card-title">今日打卡</text>
|
|
|
|
|
+ <text class="card-subtitle">{{ currentDate }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="card-content">
|
|
|
|
|
+ <view
|
|
|
|
|
+ class="checkin-item"
|
|
|
|
|
+ v-for="(task, index) in checkinTasks"
|
|
|
|
|
+ :key="index"
|
|
|
|
|
+ @click="toggleCheckin(task)"
|
|
|
|
|
+ >
|
|
|
|
|
+ <view class="checkin-info">
|
|
|
|
|
+ <text class="checkin-time">{{ task.time }}</text>
|
|
|
|
|
+ <text class="checkin-type">{{ task.type }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="checkin-status" :class="{ checked: task.checked }">
|
|
|
|
|
+ <uni-icons
|
|
|
|
|
+ :type="task.checked ? 'checkbox-filled' : 'circle'"
|
|
|
|
|
+ :color="task.checked ? '#07C160' : '#ddd'"
|
|
|
|
|
+ size="24"
|
|
|
|
|
+ />
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
<TabBar />
|
|
<TabBar />
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
<script setup lang="ts">
|
|
|
-import { ref, computed } from 'vue'
|
|
|
|
|
|
|
+import { ref, computed, onMounted } from 'vue'
|
|
|
import { onShow } from '@dcloudio/uni-app'
|
|
import { onShow } from '@dcloudio/uni-app'
|
|
|
import CustomNav from '@/components/custom-nav.vue'
|
|
import CustomNav from '@/components/custom-nav.vue'
|
|
|
import TabBar from '@/components/tab-bar.vue'
|
|
import TabBar from '@/components/tab-bar.vue'
|
|
@@ -96,6 +124,19 @@ const avatarSrc = computed(() => {
|
|
|
return defaultAvatarUrl
|
|
return defaultAvatarUrl
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
|
|
+// 打卡任务相关数据
|
|
|
|
|
+const checkinTasks = ref<Array<{
|
|
|
|
|
+ time: string
|
|
|
|
|
+ type: string
|
|
|
|
|
+ checked: boolean
|
|
|
|
|
+}>>([])
|
|
|
|
|
+
|
|
|
|
|
+// 当前日期
|
|
|
|
|
+const currentDate = computed(() => {
|
|
|
|
|
+ const now = new Date()
|
|
|
|
|
+ return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
const loadUser = () => {
|
|
const loadUser = () => {
|
|
|
try {
|
|
try {
|
|
|
const u = uni.getStorageSync('user_info')
|
|
const u = uni.getStorageSync('user_info')
|
|
@@ -158,6 +199,137 @@ const fetchUserInfo = async () => {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// 生成打卡任务
|
|
|
|
|
+const generateCheckinTasks = () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 获取本地存储的提醒设置
|
|
|
|
|
+ const patientReminders = uni.getStorageSync('patientReminders')
|
|
|
|
|
+ if (!patientReminders) {
|
|
|
|
|
+ console.log('未找到本地提醒设置')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 获取今天的打卡记录
|
|
|
|
|
+ const today = currentDate.value
|
|
|
|
|
+ const checkinRecords = uni.getStorageSync('checkinRecords') || {}
|
|
|
|
|
+ const todayRecords = checkinRecords[today] || {}
|
|
|
|
|
+
|
|
|
|
|
+ const tasks: Array<{ time: string; type: string; checked: boolean }> = []
|
|
|
|
|
+
|
|
|
|
|
+ // 处理血压测量任务
|
|
|
|
|
+ if (patientReminders.bloodPressureReminder?.enabled) {
|
|
|
|
|
+ patientReminders.bloodPressureReminder.times.forEach((time: string) => {
|
|
|
|
|
+ tasks.push({
|
|
|
|
|
+ time,
|
|
|
|
|
+ type: '测量血压',
|
|
|
|
|
+ checked: !!todayRecords[`bloodPressure_${time}`]
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 处理血糖测量任务
|
|
|
|
|
+ if (patientReminders.bloodSugarReminder?.enabled) {
|
|
|
|
|
+ patientReminders.bloodSugarReminder.times.forEach((time: string) => {
|
|
|
|
|
+ tasks.push({
|
|
|
|
|
+ time,
|
|
|
|
|
+ type: '测量血糖',
|
|
|
|
|
+ checked: !!todayRecords[`bloodSugar_${time}`]
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 处理心率测量任务
|
|
|
|
|
+ if (patientReminders.heartRateReminder?.enabled) {
|
|
|
|
|
+ patientReminders.heartRateReminder.times.forEach((time: string) => {
|
|
|
|
|
+ tasks.push({
|
|
|
|
|
+ time,
|
|
|
|
|
+ type: '测量心率',
|
|
|
|
|
+ checked: !!todayRecords[`heartRate_${time}`]
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 处理用药提醒任务
|
|
|
|
|
+ if (patientReminders.medicationReminder?.enabled) {
|
|
|
|
|
+ patientReminders.medicationReminder.times.forEach((time: string) => {
|
|
|
|
|
+ tasks.push({
|
|
|
|
|
+ time,
|
|
|
|
|
+ type: '用药提醒',
|
|
|
|
|
+ checked: !!todayRecords[`medication_${time}`]
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 按时间排序
|
|
|
|
|
+ tasks.sort((a, b) => a.time.localeCompare(b.time))
|
|
|
|
|
+
|
|
|
|
|
+ checkinTasks.value = tasks
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error('生成打卡任务失败:', e)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 切换打卡状态
|
|
|
|
|
+const toggleCheckin = (task: { time: string; type: string }) => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const today = currentDate.value
|
|
|
|
|
+ const checkinRecords = uni.getStorageSync('checkinRecords') || {}
|
|
|
|
|
+
|
|
|
|
|
+ // 初始化今天的记录
|
|
|
|
|
+ if (!checkinRecords[today]) {
|
|
|
|
|
+ checkinRecords[today] = {}
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 生成任务键名
|
|
|
|
|
+ let taskKey = ''
|
|
|
|
|
+ if (task.type === '测量血压') {
|
|
|
|
|
+ taskKey = `bloodPressure_${task.time}`
|
|
|
|
|
+ } else if (task.type === '测量血糖') {
|
|
|
|
|
+ taskKey = `bloodSugar_${task.time}`
|
|
|
|
|
+ } else if (task.type === '测量心率') {
|
|
|
|
|
+ taskKey = `heartRate_${task.time}`
|
|
|
|
|
+ } else if (task.type === '用药提醒') {
|
|
|
|
|
+ taskKey = `medication_${task.time}`
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 切换打卡状态
|
|
|
|
|
+ checkinRecords[today][taskKey] = !checkinRecords[today][taskKey]
|
|
|
|
|
+
|
|
|
|
|
+ // 保存到本地存储
|
|
|
|
|
+ uni.setStorageSync('checkinRecords', checkinRecords)
|
|
|
|
|
+
|
|
|
|
|
+ // 更新界面显示
|
|
|
|
|
+ generateCheckinTasks()
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error('切换打卡状态失败:', e)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 清理过期的打卡记录
|
|
|
|
|
+const cleanExpiredCheckinRecords = () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const checkinRecords = uni.getStorageSync('checkinRecords') || {}
|
|
|
|
|
+ const today = currentDate.value
|
|
|
|
|
+ const keys = Object.keys(checkinRecords)
|
|
|
|
|
+
|
|
|
|
|
+ // 只保留最近7天的记录
|
|
|
|
|
+ const sevenDaysAgo = new Date()
|
|
|
|
|
+ sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7)
|
|
|
|
|
+
|
|
|
|
|
+ const cleanedRecords: Record<string, any> = {}
|
|
|
|
|
+ keys.forEach(key => {
|
|
|
|
|
+ // 简单的日期比较(格式:YYYY-MM-DD)
|
|
|
|
|
+ if (key >= sevenDaysAgo.toISOString().split('T')[0] || key === today) {
|
|
|
|
|
+ cleanedRecords[key] = checkinRecords[key]
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ uni.setStorageSync('checkinRecords', cleanedRecords)
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error('清理过期打卡记录失败:', e)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// 如果在微信小程序端且未登录,自动跳转到登录页
|
|
// 如果在微信小程序端且未登录,自动跳转到登录页
|
|
|
onShow(() => {
|
|
onShow(() => {
|
|
|
const token = uni.getStorageSync('token')
|
|
const token = uni.getStorageSync('token')
|
|
@@ -166,9 +338,27 @@ onShow(() => {
|
|
|
uni.reLaunch({ url: '/pages/public/login/index' })
|
|
uni.reLaunch({ url: '/pages/public/login/index' })
|
|
|
} else {
|
|
} else {
|
|
|
fetchUserInfo()
|
|
fetchUserInfo()
|
|
|
|
|
+ // 生成打卡任务
|
|
|
|
|
+ generateCheckinTasks()
|
|
|
|
|
+ // 清理过期记录
|
|
|
|
|
+ cleanExpiredCheckinRecords()
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
|
|
+// 监听提醒设置更新事件
|
|
|
|
|
+onMounted(() => {
|
|
|
|
|
+ uni.$on('reminderSettingsUpdated', () => {
|
|
|
|
|
+ // 延迟一点时间确保本地存储已更新
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ generateCheckinTasks()
|
|
|
|
|
+ }, 100)
|
|
|
|
|
+ })
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+// 在组件卸载前移除事件监听
|
|
|
|
|
+// 注意:在实际的uni-app项目中,可能需要根据具体生命周期进行调整
|
|
|
|
|
+// 如果存在onUnload钩子,应该在那里移除事件监听器
|
|
|
|
|
+
|
|
|
function handleScan(res: any) {
|
|
function handleScan(res: any) {
|
|
|
return handleQrScanResult(res)
|
|
return handleQrScanResult(res)
|
|
|
}
|
|
}
|
|
@@ -358,4 +548,78 @@ function onQrClick() {
|
|
|
font-size: 28rpx;
|
|
font-size: 28rpx;
|
|
|
color: #666;
|
|
color: #666;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+.checkin-card {
|
|
|
|
|
+ background-color: #fff;
|
|
|
|
|
+ border-radius: 20rpx;
|
|
|
|
|
+ margin: 20rpx;
|
|
|
|
|
+ box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.card-header {
|
|
|
|
|
+ padding: 20rpx;
|
|
|
|
|
+ border-bottom: 1rpx solid #eee;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.card-title {
|
|
|
|
|
+ font-size: 36rpx;
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ color: #333;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.card-subtitle {
|
|
|
|
|
+ font-size: 28rpx;
|
|
|
|
|
+ color: #666;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.card-content {
|
|
|
|
|
+ padding: 20rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.checkin-item {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ padding: 20rpx;
|
|
|
|
|
+ border-radius: 10rpx;
|
|
|
|
|
+ background-color: #f9f9f9;
|
|
|
|
|
+ margin-bottom: 20rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.checkin-item:last-child {
|
|
|
|
|
+ margin-bottom: 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.checkin-info {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.checkin-time {
|
|
|
|
|
+ font-size: 32rpx;
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ color: #333;
|
|
|
|
|
+ margin-bottom: 10rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.checkin-type {
|
|
|
|
|
+ font-size: 28rpx;
|
|
|
|
|
+ color: #666;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.checkin-status {
|
|
|
|
|
+ width: 40rpx;
|
|
|
|
|
+ height: 40rpx;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.checkin-status.checked {
|
|
|
|
|
+ color: #07C160;
|
|
|
|
|
+}
|
|
|
</style>
|
|
</style>
|