313 lines
5.9 KiB
Vue
313 lines
5.9 KiB
Vue
<template>
|
|
<view class="page container">
|
|
<view class="tabs">
|
|
<view
|
|
v-for="tab in tabs"
|
|
:key="tab.value"
|
|
class="tab"
|
|
:class="{ 'tab-active': currentTab === tab.value }"
|
|
@tap="currentTab = tab.value"
|
|
>
|
|
{{ tab.label }}
|
|
</view>
|
|
</view>
|
|
|
|
<view class="timeline">
|
|
<view v-for="(group, date) in groupedLogs" :key="date" class="timeline-group">
|
|
<view class="timeline-date">
|
|
<text class="timeline-date-badge">{{ formatDate(date) }}</text>
|
|
</view>
|
|
|
|
<view class="timeline-items">
|
|
<view
|
|
v-for="log in group"
|
|
:key="log.id"
|
|
class="timeline-item"
|
|
@touchstart="onTouchStart"
|
|
@touchmove="onTouchMove"
|
|
@touchend="onTouchEnd"
|
|
>
|
|
<view class="timeline-line"></view>
|
|
<view class="timeline-dot" :class="log.type === 'resisted' ? 'dot-green' : 'dot-smoke'">
|
|
<text v-if="log.type === 'resisted'">🛡</text>
|
|
<text v-else>🚬</text>
|
|
</view>
|
|
<view class="timeline-content card">
|
|
<view class="log-header">
|
|
<text class="log-type">{{ log.type === 'resisted' ? '已忍住' : '已抽烟' }}</text>
|
|
<text v-if="log.badge" class="log-badge" :class="log.badge.class">{{ log.badge.text }}</text>
|
|
</view>
|
|
<text class="log-time">{{ log.time }}</text>
|
|
<view v-if="log.reason" class="log-reason">
|
|
<text class="reason-icon">😫</text>
|
|
<text class="reason-text">{{ log.reason }}</text>
|
|
</view>
|
|
<text v-if="log.interval" class="log-interval">间隔 {{ log.interval }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="fab" @tap="addLog">
|
|
<text class="fab-icon">+</text>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed } from 'vue'
|
|
|
|
const tabs = [
|
|
{ label: '全部', value: 'all' },
|
|
{ label: '已抽烟', value: 'smoke' },
|
|
{ label: '已忍住', value: 'resisted' }
|
|
]
|
|
|
|
const currentTab = ref('all')
|
|
|
|
const logs = ref([
|
|
{
|
|
id: 1,
|
|
date: '2026-01-25',
|
|
time: '4:20 PM',
|
|
type: 'resisted',
|
|
reason: '压力大',
|
|
badge: { text: '成功', class: 'badge-success' }
|
|
},
|
|
{
|
|
id: 2,
|
|
date: '2026-01-25',
|
|
time: '1:15 PM',
|
|
type: 'smoke',
|
|
reason: '无聊',
|
|
interval: '1小时30分'
|
|
},
|
|
{
|
|
id: 3,
|
|
date: '2026-01-25',
|
|
time: '11:45 AM',
|
|
type: 'smoke',
|
|
reason: '晨间习惯',
|
|
badge: { text: '今日第一支', class: 'badge-info' }
|
|
},
|
|
{
|
|
id: 4,
|
|
date: '2026-01-24',
|
|
time: '10:30 PM',
|
|
type: 'smoke',
|
|
reason: '压力大',
|
|
interval: '4小时12分'
|
|
}
|
|
])
|
|
|
|
const groupedLogs = computed(() => {
|
|
const filtered = currentTab.value === 'all'
|
|
? logs.value
|
|
: logs.value.filter(l => l.type === currentTab.value)
|
|
|
|
return filtered.reduce((groups, log) => {
|
|
if (!groups[log.date]) {
|
|
groups[log.date] = []
|
|
}
|
|
groups[log.date].push(log)
|
|
return groups
|
|
}, {})
|
|
})
|
|
|
|
function formatDate(dateStr) {
|
|
const date = new Date(dateStr)
|
|
const today = new Date()
|
|
const yesterday = new Date(today)
|
|
yesterday.setDate(yesterday.getDate() - 1)
|
|
|
|
if (dateStr === today.toISOString().split('T')[0]) {
|
|
return `今天, ${date.getMonth() + 1}月${date.getDate()}日`
|
|
}
|
|
if (dateStr === yesterday.toISOString().split('T')[0]) {
|
|
return `昨天, ${date.getMonth() + 1}月${date.getDate()}日`
|
|
}
|
|
return `${date.getMonth() + 1}月${date.getDate()}日`
|
|
}
|
|
|
|
function addLog() {
|
|
uni.switchTab({ url: '/pages/index/index' })
|
|
}
|
|
|
|
function onTouchStart() {}
|
|
function onTouchMove() {}
|
|
function onTouchEnd() {}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.page {
|
|
padding-bottom: 180rpx;
|
|
}
|
|
|
|
.tabs {
|
|
display: flex;
|
|
gap: 16rpx;
|
|
margin-bottom: 32rpx;
|
|
}
|
|
|
|
.tab {
|
|
padding: 16rpx 32rpx;
|
|
border-radius: 32rpx;
|
|
font-size: 28rpx;
|
|
color: var(--color-text-secondary);
|
|
background-color: var(--color-bg-card);
|
|
}
|
|
|
|
.tab-active {
|
|
background-color: var(--color-primary);
|
|
color: var(--color-bg);
|
|
font-weight: 600;
|
|
}
|
|
|
|
.timeline-group {
|
|
margin-bottom: 32rpx;
|
|
}
|
|
|
|
.timeline-date {
|
|
margin-bottom: 16rpx;
|
|
}
|
|
|
|
.timeline-date-badge {
|
|
font-size: 24rpx;
|
|
color: var(--color-primary);
|
|
background-color: rgba(74, 222, 128, 0.1);
|
|
padding: 8rpx 20rpx;
|
|
border-radius: 16rpx;
|
|
}
|
|
|
|
.timeline-items {
|
|
position: relative;
|
|
padding-left: 60rpx;
|
|
}
|
|
|
|
.timeline-item {
|
|
position: relative;
|
|
margin-bottom: 24rpx;
|
|
}
|
|
|
|
.timeline-line {
|
|
position: absolute;
|
|
left: -36rpx;
|
|
top: 48rpx;
|
|
bottom: -24rpx;
|
|
width: 4rpx;
|
|
background-color: var(--color-border);
|
|
}
|
|
|
|
.timeline-item:last-child .timeline-line {
|
|
display: none;
|
|
}
|
|
|
|
.timeline-dot {
|
|
position: absolute;
|
|
left: -48rpx;
|
|
top: 16rpx;
|
|
width: 48rpx;
|
|
height: 48rpx;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 24rpx;
|
|
z-index: 1;
|
|
}
|
|
|
|
.dot-green {
|
|
background-color: var(--color-primary);
|
|
}
|
|
|
|
.dot-smoke {
|
|
background-color: var(--color-bg-card-light);
|
|
}
|
|
|
|
.timeline-content {
|
|
padding: 24rpx;
|
|
}
|
|
|
|
.log-header {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 16rpx;
|
|
margin-bottom: 8rpx;
|
|
}
|
|
|
|
.log-type {
|
|
font-size: 30rpx;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.log-badge {
|
|
font-size: 22rpx;
|
|
padding: 4rpx 12rpx;
|
|
border-radius: 8rpx;
|
|
}
|
|
|
|
.badge-success {
|
|
background-color: rgba(74, 222, 128, 0.2);
|
|
color: var(--color-primary);
|
|
}
|
|
|
|
.badge-info {
|
|
background-color: rgba(96, 165, 250, 0.2);
|
|
color: #60A5FA;
|
|
}
|
|
|
|
.log-time {
|
|
font-size: 26rpx;
|
|
color: var(--color-text-secondary);
|
|
display: block;
|
|
margin-bottom: 12rpx;
|
|
}
|
|
|
|
.log-reason {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 8rpx;
|
|
background-color: var(--color-bg);
|
|
padding: 8rpx 16rpx;
|
|
border-radius: 16rpx;
|
|
margin-bottom: 8rpx;
|
|
}
|
|
|
|
.reason-icon {
|
|
font-size: 24rpx;
|
|
}
|
|
|
|
.reason-text {
|
|
font-size: 24rpx;
|
|
color: var(--color-text-secondary);
|
|
}
|
|
|
|
.log-interval {
|
|
font-size: 24rpx;
|
|
color: var(--color-text-muted);
|
|
display: block;
|
|
text-align: right;
|
|
}
|
|
|
|
.fab {
|
|
position: fixed;
|
|
right: 32rpx;
|
|
bottom: 140rpx;
|
|
width: 96rpx;
|
|
height: 96rpx;
|
|
background-color: var(--color-primary);
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
box-shadow: 0 8rpx 32rpx rgba(74, 222, 128, 0.4);
|
|
}
|
|
|
|
.fab-icon {
|
|
font-size: 48rpx;
|
|
color: var(--color-bg);
|
|
font-weight: 300;
|
|
}
|
|
</style>
|