diff --git a/Web/src/components/Bill/BillListComponent.vue b/Web/src/components/Bill/BillListComponent.vue
index c0a0243..1f75e15 100644
--- a/Web/src/components/Bill/BillListComponent.vue
+++ b/Web/src/components/Bill/BillListComponent.vue
@@ -104,7 +104,16 @@
class="card-icon"
:style="{ backgroundColor: getIconBg(transaction.type) }"
>
+
+
+
图标的映射
+
// 筛选状态管理
const selectedType = ref(null) // null=全部, 0=支出, 1=收入, 2=不计入
const selectedCategory = ref(null) // null=全部
@@ -326,7 +341,9 @@ const showCalendar = ref(false)
const dateDropdown = ref(null)
const dateRangeText = computed(() => {
- if (!dateRange.value) {return '选择日期范围'}
+ if (!dateRange.value) {
+ return '选择日期范围'
+ }
return `${dateRange.value[0]} 至 ${dateRange.value[1]}`
})
@@ -424,6 +441,12 @@ const displayTransactions = computed(() => {
// 5.3 根据分类获取图标
const getIconByClassify = (classify) => {
+ // 优先使用从API加载的分类图标
+ if (categoryIconMap.value[classify]) {
+ return categoryIconMap.value[classify]
+ }
+
+ // 降级:使用本地映射(向后兼容)
const iconMap = {
餐饮: 'food-o',
购物: 'shopping-cart-o',
@@ -437,32 +460,53 @@ const getIconByClassify = (classify) => {
return iconMap[classify || ''] || 'star-o'
}
+// 判断是否为 Iconify 格式(collection:name)
+const isIconifyFormat = (icon) => {
+ return icon && icon.includes(':')
+}
+
// 5.3 根据类型获取图标背景色
const getIconBg = (type) => {
- if (type === 0) {return '#FEE2E2'} // 支出 - 浅红色
- if (type === 1) {return '#D1FAE5'} // 收入 - 浅绿色
+ if (type === 0) {
+ return '#FEE2E2'
+ } // 支出 - 浅红色
+ if (type === 1) {
+ return '#D1FAE5'
+ } // 收入 - 浅绿色
return '#E5E7EB' // 不计入 - 灰色
}
// 5.3 根据类型获取图标颜色
const getIconColor = (type) => {
- if (type === 0) {return '#EF4444'} // 支出 - 红色
- if (type === 1) {return '#10B981'} // 收入 - 绿色
+ if (type === 0) {
+ return '#EF4444'
+ } // 支出 - 红色
+ if (type === 1) {
+ return '#10B981'
+ } // 收入 - 绿色
return '#6B7280' // 不计入 - 灰色
}
// 5.4 格式化金额
const formatAmount = (amount, type) => {
const formatted = `¥${Number(amount).toFixed(2)}`
- if (type === 0) {return `- ${formatted}`}
- if (type === 1) {return `+ ${formatted}`}
+ if (type === 0) {
+ return `- ${formatted}`
+ }
+ if (type === 1) {
+ return `+ ${formatted}`
+ }
return formatted
}
// 5.4 获取金额样式类
const getAmountClass = (type) => {
- if (type === 0) {return 'amount-expense'}
- if (type === 1) {return 'amount-income'}
+ if (type === 0) {
+ return 'amount-expense'
+ }
+ if (type === 1) {
+ return 'amount-income'
+ }
return 'amount-neutral'
}
@@ -478,32 +522,46 @@ const getTypeName = (type) => {
// 5.5 获取类型标签类型
const getTypeTagType = (type) => {
- if (type === 0) {return 'danger'}
- if (type === 1) {return 'success'}
+ if (type === 0) {
+ return 'danger'
+ }
+ if (type === 1) {
+ return 'success'
+ }
return 'default'
}
// 5.5 获取分类标签样式类
const getClassifyTagClass = (type) => {
- if (type === 0) {return 'tag-expense'}
- if (type === 1) {return 'tag-income'}
+ if (type === 0) {
+ return 'tag-expense'
+ }
+ if (type === 1) {
+ return 'tag-income'
+ }
return 'tag-neutral'
}
// 5.6 格式化时间
const formatTime = (dateString) => {
- if (!dateString) {return ''}
+ if (!dateString) {
+ return ''
+ }
const date = new Date(dateString)
+ const month = String(date.getMonth() + 1).padStart(2, '0')
+ const day = String(date.getDate()).padStart(2, '0')
const hours = String(date.getHours()).padStart(2, '0')
const minutes = String(date.getMinutes()).padStart(2, '0')
- return `${hours}:${minutes}`
+ return `${month}-${day} ${hours}:${minutes}`
}
// ========== API 数据加载 ==========
// 3.2 初始加载逻辑
const fetchTransactions = async () => {
- if (props.dataSource !== 'api') {return}
+ if (props.dataSource !== 'api') {
+ return
+ }
try {
loading.value = true
@@ -560,7 +618,9 @@ const onLoad = () => {
// 3.4 筛选条件变更时的数据重载逻辑
const resetAndReload = () => {
- if (props.dataSource !== 'api') {return}
+ if (props.dataSource !== 'api') {
+ return
+ }
page.value = 1
rawTransactions.value = []
@@ -582,8 +642,33 @@ watch(
{ deep: true }
)
+// 加载分类列表及图标映射
+const loadCategories = async () => {
+ try {
+ const response = await getCategoryList()
+ if (response && response.success) {
+ categories.value = response.data || []
+
+ // 构建分类名称 -> 图标的映射
+ const iconMap = {}
+ categories.value.forEach(category => {
+ if (category.name && category.icon) {
+ iconMap[category.name] = category.icon
+ }
+ })
+ categoryIconMap.value = iconMap
+ }
+ } catch (error) {
+ console.error('加载分类列表失败:', error)
+ // 静默失败,使用降级图标
+ }
+}
+
// 组件挂载时初始加载
onMounted(() => {
+ // 加载分类列表(用于图标映射)
+ loadCategories()
+
if (props.dataSource === 'api') {
fetchTransactions()
}
@@ -748,17 +833,17 @@ const toggleSelection = (transaction) => {
.tag-expense {
background-color: rgba(239, 68, 68, 0.1);
- color: #EF4444;
+ color: #ef4444;
}
.tag-income {
background-color: rgba(16, 185, 129, 0.1);
- color: #10B981;
+ color: #10b981;
}
.tag-neutral {
background-color: rgba(107, 114, 128, 0.1);
- color: #6B7280;
+ color: #6b7280;
}
// 5.1 右侧金额区域
diff --git a/Web/src/views/TransactionsRecord.vue b/Web/src/views/TransactionsRecord.vue
index cce30ea..970edb3 100644
--- a/Web/src/views/TransactionsRecord.vue
+++ b/Web/src/views/TransactionsRecord.vue
@@ -74,6 +74,11 @@ let searchTimer = null
// 加载数据
const loadData = async (isRefresh = false) => {
+ // 防止并发加载:如果正在加载中且不是刷新操作,则直接返回
+ if (loading.value && !isRefresh) {
+ return
+ }
+
if (isRefresh) {
pageIndex.value = 1
transactionList.value = []