init
This commit is contained in:
@@ -0,0 +1,312 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user