Implement login functionality and UI updates across the application. Added silent login process in App.vue, updated styles for various components, and integrated smoke record dialog. Enhanced onboarding and profile pages with improved layouts and user experience. Updated manifest and configuration files for deployment. Added easycom configuration for component auto-import.
This commit is contained in:
@@ -0,0 +1,378 @@
|
||||
<template>
|
||||
<view v-if="show" class="dialog-mask" @tap="handleMaskClick">
|
||||
<view class="dialog-container" :class="{ 'dialog-show': showAnimation }" @tap.stop>
|
||||
<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>
|
||||
</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>
|
||||
</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>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'SmokeRecordDialog',
|
||||
props: {
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'smoke' // 'smoke' 或 'resisted'
|
||||
},
|
||||
initialData: {
|
||||
type: Object,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showAnimation: false,
|
||||
formData: {
|
||||
smoke_time: '',
|
||||
smoke_time_only: '',
|
||||
smoke_at: '',
|
||||
remark: '',
|
||||
level: 2,
|
||||
num: 1
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
title() {
|
||||
return this.type === 'smoke' ? '记录抽烟' : '想抽忍住了'
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
show(newVal) {
|
||||
if (newVal) {
|
||||
this.initFormData()
|
||||
setTimeout(() => {
|
||||
this.showAnimation = true
|
||||
}, 50)
|
||||
} else {
|
||||
this.showAnimation = false
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initFormData() {
|
||||
// 如果有初始数据(编辑模式),使用初始数据
|
||||
if (this.initialData) {
|
||||
this.formData = {
|
||||
smoke_time: this.initialData.smoke_time || '',
|
||||
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
|
||||
}
|
||||
} else {
|
||||
// 新建模式,使用当前时间
|
||||
const now = new Date()
|
||||
const dateStr = now.toISOString().split('T')[0]
|
||||
const timeStr = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`
|
||||
const datetimeStr = `${dateStr} ${timeStr}:00`
|
||||
|
||||
this.formData = {
|
||||
smoke_time: dateStr,
|
||||
smoke_time_only: timeStr,
|
||||
smoke_at: datetimeStr,
|
||||
remark: '',
|
||||
level: 2,
|
||||
num: this.type === 'smoke' ? 1 : 0
|
||||
}
|
||||
}
|
||||
},
|
||||
handleMaskClick() {
|
||||
this.close()
|
||||
},
|
||||
close() {
|
||||
this.showAnimation = false
|
||||
setTimeout(() => {
|
||||
this.$emit('update:show', false)
|
||||
}, 300)
|
||||
},
|
||||
onDateChange(e) {
|
||||
this.formData.smoke_time = e.detail.value
|
||||
this.updateSmokeAt()
|
||||
},
|
||||
onTimeChange(e) {
|
||||
this.formData.smoke_time_only = e.detail.value
|
||||
this.updateSmokeAt()
|
||||
},
|
||||
updateSmokeAt() {
|
||||
this.formData.smoke_at = `${this.formData.smoke_time} ${this.formData.smoke_time_only}:00`
|
||||
},
|
||||
decreaseNum() {
|
||||
if (this.formData.num > 1) {
|
||||
this.formData.num--
|
||||
}
|
||||
},
|
||||
increaseNum() {
|
||||
this.formData.num++
|
||||
},
|
||||
selectLevel(level) {
|
||||
this.formData.level = level
|
||||
},
|
||||
submit() {
|
||||
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,
|
||||
num: this.type === 'smoke' ? this.formData.num : 0
|
||||
}
|
||||
|
||||
this.$emit('submit', submitData)
|
||||
this.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dialog-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.dialog-container {
|
||||
width: 100%;
|
||||
max-height: 80vh;
|
||||
background-color: #FFFFFF;
|
||||
border-radius: 32rpx 32rpx 0 0;
|
||||
overflow: hidden;
|
||||
transform: translateY(100%);
|
||||
transition: transform 0.3s ease-out;
|
||||
}
|
||||
|
||||
.dialog-show {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.dialog-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 32rpx;
|
||||
border-bottom: 2rpx solid #F3F4F6;
|
||||
}
|
||||
|
||||
.dialog-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #1F2937;
|
||||
}
|
||||
|
||||
.dialog-close {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 48rpx;
|
||||
color: #9CA3AF;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.dialog-body {
|
||||
padding: 32rpx;
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.form-item {
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.form-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
color: #6B7280;
|
||||
margin-bottom: 16rpx;
|
||||
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;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 28rpx;
|
||||
color: #1F2937;
|
||||
border: 2rpx solid #E5E7EB;
|
||||
}
|
||||
|
||||
.form-number {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.form-number-btn {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
background-color: #F9FAFB;
|
||||
border-radius: 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 36rpx;
|
||||
color: #10B981;
|
||||
font-weight: 600;
|
||||
border: 2rpx solid #E5E7EB;
|
||||
}
|
||||
|
||||
.form-number-input {
|
||||
flex: 1;
|
||||
height: 80rpx;
|
||||
background-color: #F9FAFB;
|
||||
border-radius: 16rpx;
|
||||
padding: 0 24rpx;
|
||||
text-align: center;
|
||||
font-size: 32rpx;
|
||||
color: #1F2937;
|
||||
border: 2rpx solid #E5E7EB;
|
||||
}
|
||||
|
||||
.form-level {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.level-item {
|
||||
flex: 1;
|
||||
height: 80rpx;
|
||||
background-color: #F9FAFB;
|
||||
border-radius: 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 28rpx;
|
||||
color: #6B7280;
|
||||
border: 2rpx solid #E5E7EB;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.level-active {
|
||||
background-color: #10B981;
|
||||
color: #FFFFFF;
|
||||
border-color: #10B981;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.form-textarea {
|
||||
width: 100%;
|
||||
min-height: 160rpx;
|
||||
background-color: #F9FAFB;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx;
|
||||
font-size: 28rpx;
|
||||
color: #1F2937;
|
||||
border: 2rpx solid #E5E7EB;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
padding: 32rpx;
|
||||
border-top: 2rpx solid #F3F4F6;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
.dialog-btn {
|
||||
flex: 1;
|
||||
height: 88rpx;
|
||||
border-radius: 44rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.dialog-btn-cancel {
|
||||
background-color: #F3F4F6;
|
||||
color: #6B7280;
|
||||
}
|
||||
|
||||
.dialog-btn-confirm {
|
||||
background-color: #10B981;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user