Update API endpoints and enhance UI components. Changed appid in manifest.json, modified profile and log update methods from PUT to POST in profile.js and smoke.js, respectively. Added new statistics retrieval function in smoke.js and improved the smoke record dialog with updated styles and validation logic. Updated configuration for development environment and refined documentation for health recovery and savings calculations.
This commit is contained in:
@@ -1,62 +1,87 @@
|
||||
<template>
|
||||
<view v-if="show" class="dialog-mask" @tap="handleMaskClick">
|
||||
<view class="dialog-container" :class="{ 'dialog-show': showAnimation }" @tap.stop>
|
||||
<view class="dialog-handle"></view>
|
||||
<view class="dialog-header">
|
||||
<text class="dialog-title">{{ title }}</text>
|
||||
<view class="dialog-close" @tap="close">×</view>
|
||||
</view>
|
||||
|
||||
<view class="dialog-body">
|
||||
<view class="form-item">
|
||||
<text class="form-label">时间</text>
|
||||
<view class="form-input-row">
|
||||
<picker mode="date" :value="formData.smoke_time" @change="onDateChange">
|
||||
<view class="picker-value">{{ formData.smoke_time }}</view>
|
||||
</picker>
|
||||
<picker mode="time" :value="formData.smoke_time_only" @change="onTimeChange">
|
||||
<view class="picker-value">{{ formData.smoke_time_only }}</view>
|
||||
</picker>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="form-item" v-if="type === 'smoke'">
|
||||
<text class="form-label">数量</text>
|
||||
<view class="form-number">
|
||||
<view class="form-number-btn" @tap="decreaseNum">-</view>
|
||||
<input class="form-number-input" type="number" v-model.number="formData.num" />
|
||||
<view class="form-number-btn" @tap="increaseNum">+</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="form-item" v-if="type === 'smoke'">
|
||||
<text class="form-label">烟瘾等级</text>
|
||||
<view class="form-level">
|
||||
<view
|
||||
v-for="level in 5"
|
||||
:key="level"
|
||||
class="level-item"
|
||||
:class="{ 'level-active': formData.level === level }"
|
||||
@tap="selectLevel(level)"
|
||||
>
|
||||
{{ level }}
|
||||
<view class="form-row">
|
||||
<picker class="picker-card" mode="date" :value="formData.smoke_time" @change="onDateChange">
|
||||
<view class="input-card">
|
||||
<text class="input-label">日期</text>
|
||||
<view class="input-value-row">
|
||||
<view class="input-icon input-icon-date"></view>
|
||||
<text class="input-value">{{ formData.smoke_time || '选择日期' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</picker>
|
||||
<picker class="picker-card" mode="time" :value="formData.smoke_time_only" @change="onTimeChange">
|
||||
<view class="input-card">
|
||||
<text class="input-label">时间</text>
|
||||
<view class="input-value-row">
|
||||
<view class="input-icon input-icon-time"></view>
|
||||
<text class="input-value">{{ formData.smoke_time_only || '选择时间' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
<view class="section-card" v-if="type === 'smoke'">
|
||||
<view class="section-left">
|
||||
<view class="section-icon"></view>
|
||||
<text class="section-title">抽烟数量</text>
|
||||
</view>
|
||||
<view class="counter">
|
||||
<view class="counter-btn" @tap="decreaseNum">-</view>
|
||||
<text class="counter-value">{{ formData.num }}</text>
|
||||
<view class="counter-btn" @tap="increaseNum">+</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<text class="form-label">备注</text>
|
||||
<textarea
|
||||
class="form-textarea"
|
||||
v-model="formData.remark"
|
||||
:placeholder="type === 'smoke' ? '记录抽烟原因...' : '记录抵抗心得...'"
|
||||
maxlength="200"
|
||||
<view class="section-card" v-if="type === 'smoke'">
|
||||
<view class="level-header">
|
||||
<text class="section-title">烟瘾程度</text>
|
||||
<view class="level-badge">Level {{ formData.level }}</view>
|
||||
</view>
|
||||
<slider
|
||||
class="level-slider"
|
||||
:value="formData.level"
|
||||
:min="1"
|
||||
:max="5"
|
||||
:step="1"
|
||||
activeColor="#22C55E"
|
||||
backgroundColor="#E5E7EB"
|
||||
block-color="#22C55E"
|
||||
:block-size="20"
|
||||
@change="onLevelChange"
|
||||
/>
|
||||
<view class="level-scale">
|
||||
<text class="level-scale-text">无感</text>
|
||||
<text class="level-scale-text">强烈</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="remark-section">
|
||||
<text class="section-title">备注(可选)</text>
|
||||
<view class="remark-card">
|
||||
<textarea
|
||||
class="form-textarea"
|
||||
v-model="formData.remark"
|
||||
:placeholder="type === 'smoke' ? '记录此时的心情或诱因,如压力大、应酬...' : '记录抵抗心得或诱因...'"
|
||||
maxlength="200"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="dialog-footer">
|
||||
<view class="dialog-btn dialog-btn-cancel" @tap="close">取消</view>
|
||||
<view class="dialog-btn dialog-btn-confirm" @tap="submit">确定</view>
|
||||
<view class="dialog-btn-primary" @tap="submit">
|
||||
<view class="btn-icon"></view>
|
||||
<text class="btn-text">保存记录</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -94,7 +119,7 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
title() {
|
||||
return this.type === 'smoke' ? '记录抽烟' : '想抽忍住了'
|
||||
return '添加记录'
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -118,8 +143,8 @@ export default {
|
||||
smoke_time_only: this.initialData.smoke_time_only || '',
|
||||
smoke_at: this.initialData.smoke_at || '',
|
||||
remark: this.initialData.remark || '',
|
||||
level: this.initialData.level || 2,
|
||||
num: this.initialData.num || 1
|
||||
level: this.initialData.level ?? 2,
|
||||
num: this.initialData.num ?? 1
|
||||
}
|
||||
} else {
|
||||
// 新建模式,使用当前时间
|
||||
@@ -166,15 +191,43 @@ export default {
|
||||
increaseNum() {
|
||||
this.formData.num++
|
||||
},
|
||||
selectLevel(level) {
|
||||
this.formData.level = level
|
||||
onLevelChange(e) {
|
||||
this.formData.level = e.detail.value
|
||||
},
|
||||
isTimeValid() {
|
||||
const dateStr = this.formData.smoke_time
|
||||
const timeStr = this.formData.smoke_time_only
|
||||
|
||||
if (!dateStr || !timeStr) {
|
||||
uni.showToast({ title: '请选择日期和时间', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
|
||||
const selected = new Date(`${dateStr}T${timeStr}:00`)
|
||||
if (isNaN(selected.getTime())) {
|
||||
uni.showToast({ title: '时间格式有误', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
|
||||
const now = new Date()
|
||||
const maxTime = new Date(now.getTime() + 5 * 60 * 1000)
|
||||
if (selected > maxTime) {
|
||||
uni.showToast({ title: '时间不能超过当前时间5分钟', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
submit() {
|
||||
if (!this.isTimeValid()) {
|
||||
return
|
||||
}
|
||||
|
||||
const submitData = {
|
||||
smoke_time: this.formData.smoke_time,
|
||||
smoke_at: this.formData.smoke_at,
|
||||
remark: this.formData.remark,
|
||||
level: this.type === 'smoke' ? this.formData.level : 2,
|
||||
level: this.type === 'smoke' ? this.formData.level : 0,
|
||||
num: this.type === 'smoke' ? this.formData.num : 0
|
||||
}
|
||||
|
||||
@@ -192,7 +245,7 @@ export default {
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
background-color: rgba(0, 0, 0, 0.35);
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
@@ -200,179 +253,308 @@ export default {
|
||||
|
||||
.dialog-container {
|
||||
width: 100%;
|
||||
max-height: 80vh;
|
||||
max-height: 85vh;
|
||||
background-color: #FFFFFF;
|
||||
border-radius: 32rpx 32rpx 0 0;
|
||||
border-radius: 36rpx 36rpx 0 0;
|
||||
overflow: hidden;
|
||||
transform: translateY(100%);
|
||||
transition: transform 0.3s ease-out;
|
||||
padding-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.dialog-show {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.dialog-handle {
|
||||
width: 80rpx;
|
||||
height: 8rpx;
|
||||
background-color: #E5E7EB;
|
||||
border-radius: 999rpx;
|
||||
margin: 16rpx auto 0;
|
||||
}
|
||||
|
||||
.dialog-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 32rpx;
|
||||
border-bottom: 2rpx solid #F3F4F6;
|
||||
padding: 24rpx 32rpx 16rpx;
|
||||
}
|
||||
|
||||
.dialog-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #1F2937;
|
||||
font-size: 40rpx;
|
||||
font-weight: 700;
|
||||
color: #111827;
|
||||
}
|
||||
|
||||
.dialog-close {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 48rpx;
|
||||
font-size: 44rpx;
|
||||
color: #9CA3AF;
|
||||
line-height: 1;
|
||||
background-color: #F3F4F6;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.dialog-body {
|
||||
padding: 32rpx;
|
||||
max-height: 60vh;
|
||||
padding: 16rpx 32rpx 24rpx;
|
||||
max-height: 62vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.form-item {
|
||||
margin-bottom: 32rpx;
|
||||
.form-row {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.form-item:last-child {
|
||||
margin-bottom: 0;
|
||||
.picker-card {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
color: #6B7280;
|
||||
margin-bottom: 16rpx;
|
||||
.input-card {
|
||||
background-color: #F9FAFB;
|
||||
border-radius: 24rpx;
|
||||
padding: 20rpx 24rpx;
|
||||
border: 2rpx solid #F3F4F6;
|
||||
min-height: 120rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.input-label {
|
||||
font-size: 22rpx;
|
||||
color: #9CA3AF;
|
||||
margin-bottom: 12rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.form-input-row {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.picker-value {
|
||||
flex: 1;
|
||||
height: 80rpx;
|
||||
background-color: #F9FAFB;
|
||||
border-radius: 16rpx;
|
||||
padding: 0 24rpx;
|
||||
.input-value-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 28rpx;
|
||||
color: #1F2937;
|
||||
border: 2rpx solid #E5E7EB;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.form-number {
|
||||
.input-icon {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
border-radius: 8rpx;
|
||||
background-color: #DCFCE7;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.input-icon-date::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 6rpx;
|
||||
top: 8rpx;
|
||||
width: 20rpx;
|
||||
height: 16rpx;
|
||||
border: 2rpx solid #22C55E;
|
||||
border-top-width: 6rpx;
|
||||
border-radius: 4rpx;
|
||||
}
|
||||
|
||||
.input-icon-time::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 8rpx;
|
||||
top: 8rpx;
|
||||
width: 16rpx;
|
||||
height: 16rpx;
|
||||
border: 2rpx solid #22C55E;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.input-icon-time::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 16rpx;
|
||||
top: 12rpx;
|
||||
width: 2rpx;
|
||||
height: 8rpx;
|
||||
background-color: #22C55E;
|
||||
transform-origin: bottom;
|
||||
}
|
||||
|
||||
.input-value {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #111827;
|
||||
}
|
||||
|
||||
.section-card {
|
||||
background-color: #FFFFFF;
|
||||
border-radius: 24rpx;
|
||||
padding: 24rpx;
|
||||
border: 2rpx solid #F3F4F6;
|
||||
box-shadow: 0 4rpx 12rpx rgba(15, 23, 42, 0.04);
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.section-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.form-number-btn {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
background-color: #F9FAFB;
|
||||
.section-icon {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
background-color: #DCFCE7;
|
||||
border-radius: 16rpx;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.section-icon::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 18rpx;
|
||||
top: 18rpx;
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
border-radius: 6rpx;
|
||||
border: 3rpx solid #22C55E;
|
||||
border-top-color: transparent;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #111827;
|
||||
}
|
||||
|
||||
.counter {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
background-color: #F8FAFC;
|
||||
padding: 12rpx 16rpx;
|
||||
border-radius: 999rpx;
|
||||
border: 2rpx solid #F1F5F9;
|
||||
margin-top: 16rpx;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.counter-btn {
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
border-radius: 50%;
|
||||
background-color: #E8FFF1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 36rpx;
|
||||
color: #10B981;
|
||||
color: #22C55E;
|
||||
font-weight: 600;
|
||||
border: 2rpx solid #E5E7EB;
|
||||
}
|
||||
|
||||
.form-number-input {
|
||||
flex: 1;
|
||||
height: 80rpx;
|
||||
background-color: #F9FAFB;
|
||||
border-radius: 16rpx;
|
||||
padding: 0 24rpx;
|
||||
.counter-value {
|
||||
min-width: 40rpx;
|
||||
text-align: center;
|
||||
font-size: 32rpx;
|
||||
color: #1F2937;
|
||||
border: 2rpx solid #E5E7EB;
|
||||
font-weight: 700;
|
||||
color: #111827;
|
||||
}
|
||||
|
||||
.form-level {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.level-item {
|
||||
flex: 1;
|
||||
height: 80rpx;
|
||||
background-color: #F9FAFB;
|
||||
border-radius: 16rpx;
|
||||
.level-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 28rpx;
|
||||
color: #6B7280;
|
||||
border: 2rpx solid #E5E7EB;
|
||||
transition: all 0.3s;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.level-active {
|
||||
background-color: #10B981;
|
||||
color: #FFFFFF;
|
||||
border-color: #10B981;
|
||||
.level-badge {
|
||||
padding: 8rpx 18rpx;
|
||||
border-radius: 999rpx;
|
||||
background-color: #DCFCE7;
|
||||
color: #22C55E;
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.level-slider {
|
||||
margin: 8rpx 0 4rpx;
|
||||
}
|
||||
|
||||
.level-scale {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.level-scale-text {
|
||||
font-size: 22rpx;
|
||||
color: #9CA3AF;
|
||||
}
|
||||
|
||||
.remark-section {
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.remark-card {
|
||||
margin-top: 16rpx;
|
||||
background-color: #F9FAFB;
|
||||
border-radius: 20rpx;
|
||||
padding: 8rpx;
|
||||
border: 2rpx solid #F3F4F6;
|
||||
}
|
||||
|
||||
.form-textarea {
|
||||
width: 100%;
|
||||
min-height: 160rpx;
|
||||
min-height: 180rpx;
|
||||
background-color: #F9FAFB;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx;
|
||||
padding: 24rpx 20rpx;
|
||||
font-size: 28rpx;
|
||||
color: #1F2937;
|
||||
border: 2rpx solid #E5E7EB;
|
||||
color: #111827;
|
||||
border: 2rpx solid transparent;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
padding: 32rpx;
|
||||
border-top: 2rpx solid #F3F4F6;
|
||||
background-color: #FFFFFF;
|
||||
padding: 16rpx 32rpx 32rpx;
|
||||
}
|
||||
|
||||
.dialog-btn {
|
||||
flex: 1;
|
||||
height: 88rpx;
|
||||
border-radius: 44rpx;
|
||||
.dialog-btn-primary {
|
||||
height: 96rpx;
|
||||
border-radius: 32rpx;
|
||||
background-color: #22C55E;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 16rpx;
|
||||
box-shadow: 0 10rpx 24rpx rgba(34, 197, 94, 0.3);
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
border-radius: 50%;
|
||||
background-color: #0F172A;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.btn-icon::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 14rpx;
|
||||
top: 18rpx;
|
||||
width: 12rpx;
|
||||
height: 6rpx;
|
||||
border-left: 4rpx solid #FFFFFF;
|
||||
border-bottom: 4rpx solid #FFFFFF;
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
|
||||
.btn-text {
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.dialog-btn-cancel {
|
||||
background-color: #F3F4F6;
|
||||
color: #6B7280;
|
||||
}
|
||||
|
||||
.dialog-btn-confirm {
|
||||
background-color: #10B981;
|
||||
color: #FFFFFF;
|
||||
font-weight: 600;
|
||||
color: #0F172A;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user