feat: 用户体验优化 — 首页加载/继续记账/登录恢复
## 首页加载优化 - 分级加载:首屏只请求 overview + recentTx(2个请求) - 数据新鲜度:30秒内不重复请求 - 次要数据异步加载:budget + todayData - 非关键数据后台加载:userInfo + notifications ## 继续记账功能 - SaveSuccess 组件新增「继续记一笔」按钮 - 新增模式显示继续按钮,编辑模式自动返回 - 继续记账时保留分类和日期,清空金额和备注 ## 登录失败恢复 - 登录重试机制:最多3次,间隔1秒 - 失败后显示提示而非静默等待 ## 群组账单逻辑修正 - 群组账单 = 所有群组成员的个人账单之和 - 修改查询:WHERE user_id IN (群组成员)
This commit is contained in:
@@ -1,9 +1,19 @@
|
||||
<template>
|
||||
<view class="save-success" v-if="visible" @tap="hide">
|
||||
<view class="success-icon" :class="{ show: animating }">
|
||||
<Icon name="check" :size="80" color="#FFFFFF" />
|
||||
<view class="save-success" v-if="visible">
|
||||
<view class="success-content" :class="{ show: animating }">
|
||||
<view class="success-icon">
|
||||
<Icon name="check" :size="80" color="#FFFFFF" />
|
||||
</view>
|
||||
<text class="success-text">{{ text }}</text>
|
||||
<view class="success-actions" v-if="showContinue">
|
||||
<view class="action-btn continue" @tap="onContinue">
|
||||
<text class="action-text">继续记一笔</text>
|
||||
</view>
|
||||
<view class="action-btn back" @tap="onBack">
|
||||
<text class="action-text">返回</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<text class="success-text" :class="{ show: animating }">{{ text }}</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -14,51 +24,61 @@ import Icon from '@/components/Icon/Icon.vue'
|
||||
const props = withDefaults(defineProps<{
|
||||
visible: boolean
|
||||
text?: string
|
||||
}>(), { text: '保存成功' })
|
||||
showContinue?: boolean
|
||||
}>(), { text: '保存成功', showContinue: false })
|
||||
|
||||
const emit = defineEmits(['update:visible'])
|
||||
const emit = defineEmits(['update:visible', 'continue', 'back'])
|
||||
|
||||
const animating = ref(false)
|
||||
|
||||
watch(() => props.visible, (val) => {
|
||||
if (val) {
|
||||
setTimeout(() => { animating.value = true }, 50)
|
||||
setTimeout(() => { hide() }, 2000)
|
||||
// 如果不显示继续按钮,自动关闭
|
||||
if (!props.showContinue) {
|
||||
setTimeout(() => { onBack() }, 2000)
|
||||
}
|
||||
} else {
|
||||
animating.value = false
|
||||
}
|
||||
})
|
||||
|
||||
function hide() {
|
||||
function onContinue() {
|
||||
animating.value = false
|
||||
setTimeout(() => { emit('update:visible', false) }, 300)
|
||||
setTimeout(() => {
|
||||
emit('update:visible', false)
|
||||
emit('continue')
|
||||
}, 300)
|
||||
}
|
||||
|
||||
function onBack() {
|
||||
animating.value = false
|
||||
setTimeout(() => {
|
||||
emit('update:visible', false)
|
||||
emit('back')
|
||||
}, 300)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/styles/mixins.scss';
|
||||
|
||||
.save-success {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@include flex-center;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.success-icon {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, #7BC67E, #5BA85E);
|
||||
.success-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transform: scale(0);
|
||||
transform: scale(0.8);
|
||||
opacity: 0;
|
||||
transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
|
||||
@@ -68,18 +88,47 @@ function hide() {
|
||||
}
|
||||
}
|
||||
|
||||
.success-text {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #FFFFFF;
|
||||
margin-top: 24rpx;
|
||||
transform: translateY(20rpx);
|
||||
opacity: 0;
|
||||
transition: all 0.3s ease 0.2s;
|
||||
.success-icon {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, $success, #5BA85E);
|
||||
@include flex-center;
|
||||
}
|
||||
|
||||
&.show {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
.success-text {
|
||||
font-size: $font-xl;
|
||||
font-weight: 600;
|
||||
color: $surface;
|
||||
margin-top: $space-lg;
|
||||
}
|
||||
|
||||
.success-actions {
|
||||
display: flex;
|
||||
gap: $space-md;
|
||||
margin-top: $space-2xl;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
padding: $space-sm $space-xl;
|
||||
border-radius: $radius-xl;
|
||||
min-width: 200rpx;
|
||||
@include flex-center;
|
||||
|
||||
&.continue {
|
||||
background: $primary;
|
||||
&:active { opacity: 0.8; }
|
||||
}
|
||||
|
||||
&.back {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
&:active { background: rgba(255, 255, 255, 0.3); }
|
||||
}
|
||||
}
|
||||
|
||||
.action-text {
|
||||
font-size: $font-lg;
|
||||
font-weight: 600;
|
||||
color: $surface;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user