refactor(logs): update date filtering logic and improve UI for log entries

This commit is contained in:
nepiedg
2026-04-27 00:31:52 +08:00
parent a39f1371a3
commit 4d68477a13
2 changed files with 53 additions and 58 deletions
+51 -57
View File
@@ -1,18 +1,16 @@
<template> <template>
<view class="page"> <view class="page">
<view class="status-bar" :style="{ height: navBarHeight + 'px' }"></view>
<view class="filters-sticky"> <view class="filters-sticky">
<view class="filters" :style="{ top: navBarHeight + 'px' }"> <view class="filters">
<view class="tabs"> <view class="date-filters">
<view <view
v-for="tab in tabs" v-for="option in dateFilters"
:key="tab.value" :key="option.value"
class="tab" class="date-filter"
:class="{ 'tab-active': currentTab === tab.value }" :class="{ 'date-filter-active': currentDateFilter === option.value }"
@tap="currentTab = tab.value" @tap="currentDateFilter = option.value"
> >
{{ tab.label }} {{ option.label }}
</view> </view>
</view> </view>
</view> </view>
@@ -45,7 +43,7 @@
<view class="section-head"> <view class="section-head">
<text class="section-label">时间记录</text> <text class="section-label">时间记录</text>
<text class="section-note">{{ currentTab === 'smoke' ? '仅看抽烟记录' : '按日期倒序' }}</text> <text class="section-note">{{ currentDateFilterLabel }} · 按日期倒序</text>
</view> </view>
<view v-if="logsStore.loading && logsStore.logs.length === 0" class="skeleton"> <view v-if="logsStore.loading && logsStore.logs.length === 0" class="skeleton">
@@ -162,14 +160,15 @@ import { useLogin } from '@/hooks/useLogin'
const { waitForLogin } = useLogin() const { waitForLogin } = useLogin()
const logsStore = useLogsStore() const logsStore = useLogsStore()
const navBarHeight = ref(0)
const tabs = [ const dateFilters = [
{ label: '全部', value: 'all' }, { label: '全部', value: 'all' },
{ label: '抽烟记录', value: 'smoke' } { label: '今天', value: 'today' },
{ label: '近7天', value: 'week' },
{ label: '本月', value: 'month' }
] ]
const currentTab = ref('all') const currentDateFilter = ref('all')
const showEditDialog = ref(false) const showEditDialog = ref(false)
const editType = ref('smoke') const editType = ref('smoke')
const editData = ref(null) const editData = ref(null)
@@ -177,11 +176,12 @@ const editingLogId = ref(null)
// 筛选后的记录 // 筛选后的记录
const filteredLogs = computed(() => { const filteredLogs = computed(() => {
const logs = logsStore.formattedLogs const logs = logsStore.formattedLogs.filter(log => log.type === 'smoke')
if (currentTab.value === 'all') { return logs.filter(log => isInDateFilter(log.displayDate, currentDateFilter.value))
return logs })
}
return logs.filter(log => log.type === currentTab.value) const currentDateFilterLabel = computed(() => {
return dateFilters.find(item => item.value === currentDateFilter.value)?.label || '全部'
}) })
// 按日期分组 // 按日期分组
@@ -218,6 +218,25 @@ function localDateStr(d) {
return `${y}-${m}-${day}` return `${y}-${m}-${day}`
} }
function isInDateFilter(dateStr, filter) {
if (!dateStr || filter === 'all') return true
const today = new Date()
const target = new Date(`${dateStr}T00:00:00`)
if (Number.isNaN(target.getTime())) return false
const todayText = localDateStr(today)
if (filter === 'today') return dateStr === todayText
if (filter === 'week') {
const start = new Date(today)
start.setDate(today.getDate() - 6)
start.setHours(0, 0, 0, 0)
return target >= start && dateStr <= todayText
}
if (filter === 'month') {
return target.getFullYear() === today.getFullYear() && target.getMonth() === today.getMonth()
}
return true
}
// 格式化分组标题 // 格式化分组标题
function formatGroupTitle(dateStr) { function formatGroupTitle(dateStr) {
if (!dateStr) return '' if (!dateStr) return ''
@@ -241,7 +260,7 @@ function formatGroupTitle(dateStr) {
// 下拉刷新 // 下拉刷新
async function onRefresh() { async function onRefresh() {
await logsStore.fetchLogs(true, currentTab.value) await logsStore.fetchLogs(true, 'smoke')
} }
// 上拉加载 // 上拉加载
@@ -300,25 +319,13 @@ function handleDelete(log) {
async function initPage() { async function initPage() {
try { try {
await waitForLogin() await waitForLogin()
await logsStore.fetchLogs(true, currentTab.value) await logsStore.fetchLogs(true, 'smoke')
} catch (e) { } catch (e) {
console.error('initPage error:', e) console.error('initPage error:', e)
} }
} }
function setupNavBar() {
const systemInfo = uni.getSystemInfoSync()
const statusBarH = systemInfo.statusBarHeight || 0
try {
const menuBtn = uni.getMenuButtonBoundingClientRect()
navBarHeight.value = menuBtn.bottom + (menuBtn.top - statusBarH)
} catch (e) {
navBarHeight.value = statusBarH + 44
}
}
onMounted(() => { onMounted(() => {
setupNavBar()
initPage() initPage()
}) })
@@ -342,10 +349,6 @@ function levelLabel(level) {
return '极强' return '极强'
} }
watch(currentTab, async (value) => {
await logsStore.fetchLogs(true, value)
})
onShareAppMessage(() => { onShareAppMessage(() => {
return { return {
title: '戒烟助手 - 我的戒烟记录', title: '戒烟助手 - 我的戒烟记录',
@@ -365,32 +368,23 @@ onShareAppMessage(() => {
box-sizing: border-box; box-sizing: border-box;
} }
.status-bar {
background: transparent;
}
.filters-sticky { .filters-sticky {
position: relative; position: relative;
height: 96rpx; height: 88rpx;
flex-shrink: 0; flex-shrink: 0;
z-index: 20; z-index: 20;
} }
.filters { .filters {
position: fixed; position: relative;
left: 32rpx; z-index: 1;
right: 32rpx; padding: 10rpx 0 12rpx;
z-index: 50;
padding-top: 8rpx;
padding-bottom: 14rpx;
background: rgba(246, 248, 246, 0.9);
box-sizing: border-box; box-sizing: border-box;
-webkit-backdrop-filter: blur(14px);
backdrop-filter: blur(14px);
} }
.tabs { .date-filters {
display: flex; display: flex;
gap: 8rpx;
background: rgba(255, 255, 255, 0.82); background: rgba(255, 255, 255, 0.82);
border: 1rpx solid rgba(226, 232, 240, 0.82); border: 1rpx solid rgba(226, 232, 240, 0.82);
border-radius: 22rpx; border-radius: 22rpx;
@@ -399,17 +393,17 @@ onShareAppMessage(() => {
backdrop-filter: blur(12px); backdrop-filter: blur(12px);
} }
.tab { .date-filter {
flex: 1; flex: 1;
text-align: center; text-align: center;
padding: 15rpx 0; padding: 14rpx 0;
border-radius: 18rpx; border-radius: 18rpx;
font-size: 25rpx; font-size: 24rpx;
font-weight: 900; font-weight: 900;
color: #64748B; color: #64748B;
} }
.tab-active { .date-filter-active {
background: linear-gradient(135deg, #10B981, #06B6D4); background: linear-gradient(135deg, #10B981, #06B6D4);
color: #FFFFFF; color: #FFFFFF;
box-shadow: 0 8rpx 18rpx rgba(16, 185, 129, 0.16); box-shadow: 0 8rpx 18rpx rgba(16, 185, 129, 0.16);
+2 -1
View File
@@ -142,6 +142,7 @@ const profileStore = useProfileStore()
const userStore = useUserStore() const userStore = useUserStore()
const { waitForLogin } = useLogin() const { waitForLogin } = useLogin()
const { proxy } = getCurrentInstance() const { proxy } = getCurrentInstance()
const MEDALLION_IMAGE = '../../static/achievements/theme-medallion.png'
const navBarHeight = ref(0) const navBarHeight = ref(0)
const latestNSTIResult = ref(null) const latestNSTIResult = ref(null)
@@ -529,7 +530,7 @@ async function handleSaveAchievementPoster() {
await fetchPosterData() await fetchPosterData()
const [qrPath, medallionInfo] = await Promise.all([ const [qrPath, medallionInfo] = await Promise.all([
downloadMiniProgramTestCode({ path: 'pages/index/index', width: 240 }).catch(() => ''), downloadMiniProgramTestCode({ path: 'pages/index/index', width: 240 }).catch(() => ''),
getImageInfo('/static/achievements/theme-medallion.png') getImageInfo(MEDALLION_IMAGE)
]) ])
const posterPath = await drawAchievementPoster(qrPath, medallionInfo) const posterPath = await drawAchievementPoster(qrPath, medallionInfo)
await savePosterToAlbum(posterPath) await savePosterToAlbum(posterPath)