fix: 全面修复 22 项问题

严重 Bug:
- NaN 金额校验绕过(add 页面 parseFloat 空字符串)
- 4 个页面补充 waitForReady(add/budget/category-manage/group-manage)
- 导出 Loading 遮罩不消失(profile exportData 缺 hideLoading)
- Store 吞掉请求错误不 re-throw(transaction/stats/budget/user)
- 分类排序 SQL 拼接改参数化查询(category PUT /sort)
- 备份接口添加管理员权限检查(backup requireAdmin)

中等问题:
- 群组加入竞态条件改为 INSERT IGNORE
- 统计页切月/切Tab添加 loadSeq 防竞态
- stats store 补全 loading 状态 + Overview 初始值补全 7 字段
- 保存成功后返回不再弹「放弃修改」确认框
- 群组管理创建/加入添加 saving 双重提交保护
- 预算页添加 onShow 刷新
- 服务端输入校验(name/color/invite_code/微信code)

UI/样式:
- 触摸目标: bell/nav-back/nav-save→88rpx, avatar-badge→64rpx, action-btn→80rpx
- :active 反馈: bell/profile-card/identity-card/id-manage/arrow/action-btn/group-btn/cat-info
- 内联样式改动态绑定(4处 spacer 使用 capsuleRight)
- FAB 文字+改用 Icon 组件
- prefers-reduced-motion 动画降级(Skeleton/profile/budget/add)
- 胶囊适配(add/category-manage/group-manage)
- CategoryIcon !important 改为 iconStyle 计算属性

文档:
- CLAUDE.md 补充 SaveSuccess/ChartWrapper 组件 + category/budget/index API
This commit is contained in:
2026-06-04 17:06:02 +08:00
parent 21f4d81959
commit e110cd5c5d
25 changed files with 314 additions and 88 deletions

View File

@@ -7,7 +7,7 @@
<Icon name="arrowLeft" :size="36" color="#2D1B1B" />
</view>
<text class="title">分类管理</text>
<view style="width: 88rpx;"></view>
<view :style="{ width: capsuleRight + 88 + 'px' }"></view>
</view>
</view>
@@ -38,7 +38,7 @@
<!-- 浮动添加按钮 -->
<view class="fab" @tap="showAddModal = true">
<text class="fab-icon">+</text>
<Icon name="plus" :size="48" color="#FFFFFF" />
</view>
<!-- 添加弹窗 -->
@@ -112,10 +112,11 @@
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { useCategoryStore, type Category } from '@/stores/category'
import { waitForReady } from '@/utils/app-ready'
import { getCategoryTransactionCount } from '@/api/category'
import CategoryIcon from '@/components/CategoryIcon/CategoryIcon.vue'
import Icon from '@/components/Icon/Icon.vue'
import { statusBarHeight } from '@/utils/system'
import { statusBarHeight, capsuleRight } from '@/utils/system'
const catStore = useCategoryStore()
const tabType = ref<'expense' | 'income'>('expense')
@@ -143,7 +144,10 @@ const migrateTargets = computed(() => {
return currentCategories.value.filter(c => c.id !== deletingCat.value?.id)
})
onMounted(() => catStore.fetchCategories())
onMounted(async () => {
await waitForReady()
catStore.fetchCategories()
})
function goBack() {
uni.navigateBack()
@@ -331,6 +335,9 @@ async function onMigrateConfirm() {
align-items: center;
gap: 16rpx;
flex: 1;
padding: 8rpx 0;
&:active { opacity: 0.6; }
}
.cat-name { font-size: 28rpx; color: #2D1B1B; font-weight: 500; }
@@ -349,8 +356,8 @@ async function onMigrateConfirm() {
}
.action-btn {
width: 64rpx;
height: 64rpx;
width: 80rpx;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
@@ -376,13 +383,6 @@ async function onMigrateConfirm() {
&:active { transform: scale(0.95); }
}
.fab-icon {
font-size: 56rpx;
color: #FFFFFF;
font-weight: 300;
line-height: 1;
}
.color-picker {
display: flex;
gap: 20rpx;