feat: 交互优化与代码质量改进

- SvgIcon 改为 PNG 图标方案,组件重命名为 Icon
- 修复 TransactionItem tap 事件名冲突,改为 item-tap
- 修复 Stats 页面切换 tab 时 overview 不更新
- 修复 CategoryIcon bgColor 必填问题
- 修复 ChartWrapper import 顺序
- 修复 budget onDelete 空字符串问题
- 清理 profile 页面未使用 CSS
- 统一所有页面 header 为 sticky 定位
- 修复导航栏返回按钮不统一
- 添加导出功能 loading 状态
- 优化 scroll-into-view 延迟
This commit is contained in:
2026-06-02 16:04:17 +08:00
parent 36baa9a2b5
commit d5e5655b88
34 changed files with 318 additions and 451 deletions

View File

@@ -0,0 +1,90 @@
<template>
<image v-if="iconSrc" class="icon" :src="iconSrc" :style="sizeStyle" mode="aspectFit" />
<view v-else class="icon-fallback" :style="sizeStyle">?</view>
</template>
<script setup lang="ts">
import { computed } from 'vue'
const props = withDefaults(defineProps<{
name: string
size?: number
color?: string
}>(), { size: 40, color: '#2D1B1B' })
// 图标路径映射表name + color -> PNG 文件路径
const iconMap: Record<string, Record<string, string>> = {
arrowLeft: {
'#2D1B1B': '/static/icons/arrow-left-dark.png',
'#8B7E7E': '/static/icons/arrow-left-gray.png',
},
arrowRight: {
'#8B7E7E': '/static/icons/arrow-right-gray.png',
'#E0D6D0': '/static/icons/arrow-right-pale.png',
},
calendar: {
'#8B7E7E': '/static/icons/calendar-gray.png',
},
edit: {
'#8B7E7E': '/static/icons/edit-gray.png',
'#BFB3B3': '/static/icons/edit-light.png',
},
'alert-circle': {
'#BFB3B3': '/static/icons/alert-circle-light.png',
},
user: {
'#8B7E7E': '/static/icons/user-gray.png',
'#FFFFFF': '/static/icons/user-white.png',
},
bell: {
'#8B7E7E': '/static/icons/bell-gray.png',
},
'trend-down': {
'#FF6B6B': '/static/icons/trend-down-red.png',
},
'trend-up': {
'#7BC67E': '/static/icons/trend-up-green.png',
},
chevronRight: {
'#BFB3B3': '/static/icons/chevron-right-light.png',
},
check: {
'#FFFFFF': '/static/icons/check-white.png',
},
trash: {
'#FF8C69': '/static/icons/trash-orange.png',
'#FF6B6B': '/static/icons/trash-red.png',
'#FFFFFF': '/static/icons/trash-white.png',
},
}
const iconSrc = computed(() => {
const variants = iconMap[props.name]
if (!variants) {
console.warn(`[Icon] 图标 "${props.name}" 不存在`)
return ''
}
// 精确匹配颜色,找不到则使用第一个变体
return variants[props.color] || Object.values(variants)[0] || ''
})
const sizeStyle = computed(() => {
const s = props.size + 'rpx'
return { width: s, height: s }
})
</script>
<style scoped>
.icon {
display: inline-block;
}
.icon-fallback {
display: inline-flex;
align-items: center;
justify-content: center;
background: #F0E0D6;
border-radius: 4rpx;
color: #BFB3B3;
font-size: 20rpx;
}
</style>