Files
smt/pages/logs/index.vue
T
nepiedg c883ae7b17 init
2026-01-25 11:45:16 +08:00

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>