+
+
+
+
+
+
@@ -164,9 +171,9 @@
@@ -179,7 +186,7 @@
¥{{ formatMoney(category.amount) }}
{{ category.percent }}%
-
+
@@ -260,6 +267,8 @@
+
+
@@ -331,12 +340,19 @@ const router = useRouter()
// 响应式数据
const loading = ref(true)
+const firstLoading = ref(true)
const refreshing = ref(false)
const showMonthPicker = ref(false)
+const showAllExpense = ref(false)
+const showAllIncome = ref(false)
+const showAllNone = ref(false)
const currentYear = ref(new Date().getFullYear())
const currentMonth = ref(new Date().getMonth() + 1)
const selectedDate = ref([new Date().getFullYear().toString(), (new Date().getMonth() + 1).toString().padStart(2, '0')])
+const transitionName = ref('slide-right')
+const dateKey = computed(() => `${currentYear.value}-${currentMonth.value}`)
+
// 账单列表相关
const billListVisible = ref(false)
const billListLoading = ref(false)
@@ -371,33 +387,70 @@ const incomeCategories = ref([])
const noneCategories = ref([])
const expenseCategoriesView = computed(() => {
- const sorted = [...expenseCategories.value]
- const unclassifiedIndex = sorted.findIndex(c => !c.classify)
+ const list = [...expenseCategories.value]
+ const unclassifiedIndex = list.findIndex(c => !c.classify)
if (unclassifiedIndex !== -1) {
- const [unclassified] = sorted.splice(unclassifiedIndex, 1)
- sorted.unshift(unclassified)
+ const [unclassified] = list.splice(unclassifiedIndex, 1)
+ list.unshift(unclassified)
}
- return sorted
+
+ if (showAllExpense.value || list.length <= 7) return list
+
+ const top = list.slice(0, 6)
+ const rest = list.slice(6)
+ top.push({
+ classify: '其他',
+ amount: rest.reduce((s, c) => s + c.amount, 0),
+ count: rest.reduce((s, c) => s + c.count, 0),
+ percent: parseFloat(rest.reduce((s, c) => s + c.percent, 0).toFixed(1)),
+ color: '#AAB7B8',
+ isOther: true
+ })
+ return top
})
const incomeCategoriesView = computed(() => {
- const sorted = [...incomeCategories.value]
- const unclassifiedIndex = sorted.findIndex(c => !c.classify)
+ const list = [...incomeCategories.value]
+ const unclassifiedIndex = list.findIndex(c => !c.classify)
if (unclassifiedIndex !== -1) {
- const [unclassified] = sorted.splice(unclassifiedIndex, 1)
- sorted.unshift(unclassified)
+ const [unclassified] = list.splice(unclassifiedIndex, 1)
+ list.unshift(unclassified)
}
- return sorted
+
+ if (showAllIncome.value || list.length <= 7) return list
+
+ const top = list.slice(0, 6)
+ const rest = list.slice(6)
+ top.push({
+ classify: '其他',
+ amount: rest.reduce((s, c) => s + c.amount, 0),
+ count: rest.reduce((s, c) => s + c.count, 0),
+ percent: parseFloat(rest.reduce((s, c) => s + c.percent, 0).toFixed(1)),
+ isOther: true
+ })
+ return top
})
const noneCategoriesView = computed(() => {
- const sorted = [...noneCategories.value]
- const unclassifiedIndex = sorted.findIndex(c => !c.classify)
+ const list = [...noneCategories.value]
+ const unclassifiedIndex = list.findIndex(c => !c.classify)
if (unclassifiedIndex !== -1) {
- const [unclassified] = sorted.splice(unclassifiedIndex, 1)
- sorted.unshift(unclassified)
+ const [unclassified] = list.splice(unclassifiedIndex, 1)
+ list.unshift(unclassified)
}
- return sorted
+
+ if (showAllNone.value || list.length <= 7) return list
+
+ const top = list.slice(0, 6)
+ const rest = list.slice(6)
+ top.push({
+ classify: '其他',
+ amount: rest.reduce((s, c) => s + c.amount, 0),
+ count: rest.reduce((s, c) => s + c.count, 0),
+ percent: parseFloat(rest.reduce((s, c) => s + c.percent, 0).toFixed(1)),
+ isOther: true
+ })
+ return top
})
// 趋势数据
@@ -418,7 +471,7 @@ const colors = [
const circumference = computed(() => 2 * Math.PI * 70)
const chartSegments = computed(() => {
let offset = 0
- return expenseCategories.value.map((category) => {
+ return expenseCategoriesView.value.map((category) => {
const percent = category.percent / 100
const length = circumference.value * percent
const segment = {
@@ -452,6 +505,31 @@ const isCurrentMonth = computed(() => {
return currentYear.value === now.getFullYear() && currentMonth.value === now.getMonth() + 1
})
+// 日期标签展示文字
+const dateTagLabel = computed(() => {
+ const now = new Date()
+ const todayYear = now.getFullYear()
+ const todayMonth = now.getMonth() + 1
+
+ if (currentYear.value === todayYear && currentMonth.value === todayMonth) {
+ return '本月'
+ }
+
+ // 计算上个月
+ let lastYear = todayYear
+ let lastMonth = todayMonth - 1
+ if (lastMonth === 0) {
+ lastMonth = 12
+ lastYear--
+ }
+
+ if (currentYear.value === lastYear && currentMonth.value === lastMonth) {
+ return `上月`
+ }
+
+ return `${currentYear.value}年${currentMonth.value}月`
+})
+
// 是否为未分类账单
const isUnclassified = computed(() => {
return selectedClassify.value === '未分类' || selectedClassify.value === ''
@@ -484,6 +562,7 @@ const getBarHeight = (value, maxValue) => {
// 切换月份
const changeMonth = (offset) => {
+ transitionName.value = offset > 0 ? 'slide-left' : 'slide-right'
let newMonth = currentMonth.value + offset
let newYear = currentYear.value
@@ -504,26 +583,51 @@ const changeMonth = (offset) => {
currentYear.value = newYear
currentMonth.value = newMonth
+
+ // 重置展开状态
+ showAllExpense.value = false
+ showAllIncome.value = false
+ showAllNone.value = false
+
fetchStatistics()
}
// 确认月份选择
const onMonthConfirm = ({ selectedValues }) => {
- currentYear.value = parseInt(selectedValues[0])
- currentMonth.value = parseInt(selectedValues[1])
+ const newYear = parseInt(selectedValues[0])
+ const newMonth = parseInt(selectedValues[1])
+
+ // 判断方向以应用动画
+ if (newYear > currentYear.value || (newYear === currentYear.value && newMonth > currentMonth.value)) {
+ transitionName.value = 'slide-left'
+ } else {
+ transitionName.value = 'slide-right'
+ }
+
+ currentYear.value = newYear
+ currentMonth.value = newMonth
showMonthPicker.value = false
+
+ // 重置展开状态
+ showAllExpense.value = false
+ showAllIncome.value = false
+ showAllNone.value = false
+
fetchStatistics()
}
// 下拉刷新
const onRefresh = async () => {
- await fetchStatistics()
+ await fetchStatistics(false)
refreshing.value = false
}
// 获取统计数据
-const fetchStatistics = async () => {
- loading.value = true
+const fetchStatistics = async (showLoading = true) => {
+ if (showLoading && firstLoading.value) {
+ loading.value = true
+ }
+
try {
await Promise.all([
fetchMonthlyData(),
@@ -535,6 +639,7 @@ const fetchStatistics = async () => {
showToast('获取统计数据失败')
} finally {
loading.value = false
+ firstLoading.value = false
}
}
@@ -863,8 +968,24 @@ onBeforeUnmount(() => {