feat: 统计面板与收支记录支持图表点击跳转与筛选双向联动

- 统计面板支出/收入分类饼图与支出/收入渠道饼图支持点击扇区跳转收支记录,并带上对应分类或渠道筛选
- 新增统计面板「查看收支记录」与收支记录「查看统计」按钮,实现两页双向跳转
- 收支记录跳转统计面板时按共享偏好还原类型、分类、渠道、标签、金额、备注、计入统计与时间范围
- 时间范围统一为具体起止区间,面板预设选择时换算为区间写入共享偏好
- 「计入统计」默认统一为「仅计入」,并修复「全部」与金额字段显式 null 的记忆
- 抽出公共跳转字段与共享筛选读写,收敛统计面板多处跳转的重复逻辑
This commit is contained in:
2026-09-23 10:00:58 +08:00
parent eb358d0ba1
commit 7172f6aa44
4 changed files with 171 additions and 29 deletions
+22 -5
View File
@@ -4,7 +4,7 @@ import { ref, onMounted, onBeforeUnmount, computed, h, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import { useMessage, useDialog, NButton, NSpace, NDataTable, NTag, NModal, NForm, NFormItem, NInput, NInputNumber, NSelect, NDatePicker, NPagination, NCard, NSwitch, NIcon, NPopover, NAutoComplete, NAlert, NDivider, NUpload, NUploadDragger, NTabs, NTabPane } from 'naive-ui'
import { EyeOutline, EyeOffOutline, SparklesOutline, RefreshOutline, CloudUploadOutline, CheckmarkCircleOutline } from '@vicons/ionicons5'
import { EyeOutline, EyeOffOutline, SparklesOutline, RefreshOutline, CloudUploadOutline, CheckmarkCircleOutline, StatsChartOutline } from '@vicons/ionicons5'
import type { DataTableColumns, UploadFileInfo } from 'naive-ui'
import { getTransactionPage, createTransaction, updateTransaction, deleteTransaction, exportTransaction, toggleTransactionStats, toggleRemarkHidden, batchUpdateCategory, batchTags, getChannels, batchDeleteTransactions, batchCreateTransactions } from '../api/transaction'
import { getAiConfig, suggestByAi, suggestOneByAi, applyAiSuggestions, queryByAi, parseRecordByAi, parseCategoryByAi, parseTagByAi, parseChannelByAi } from '../api/ai'
@@ -263,7 +263,7 @@ const filterType = ref<string[]>([]) // 类型筛选(多选)
const filterCategory = ref<number[]>([]) // 分类筛选(多选)
const filterChannel = ref<string[]>([]) // 交易渠道筛选(多选)
const filterDateRange = ref<[number, number] | null>(null) // 日期范围筛选(起止时间戳)
const filterIncludeInStats = ref<string | null>(null) // 计入统计筛选:null-全部,'true'-计入,'false'-不计入
const filterIncludeInStats = ref<string | null>('true') // 计入统计筛选:null-全部,'true'-计入,'false'-不计入(默认仅计入,与统计面板一致)
const filterRemark = ref('') // 备注关键字搜索
// 备注关键词历史存储键
const REMARK_HISTORY_KEY = STORAGE_KEYS.transactionRemarkHistory
@@ -341,10 +341,10 @@ function loadFilterPrefs() {
if (p.filterCategory) filterCategory.value = p.filterCategory
if (p.filterChannel) filterChannel.value = p.filterChannel
if (p.filterDateRange) filterDateRange.value = p.filterDateRange
if (p.filterIncludeInStats) filterIncludeInStats.value = p.filterIncludeInStats
if (p.filterIncludeInStats !== undefined) filterIncludeInStats.value = p.filterIncludeInStats
if (typeof p.filterRemark === 'string') filterRemark.value = p.filterRemark
if (typeof p.filterMinAmount === 'number') filterMinAmount.value = p.filterMinAmount
if (typeof p.filterMaxAmount === 'number') filterMaxAmount.value = p.filterMaxAmount
if (p.filterMinAmount !== undefined) filterMinAmount.value = p.filterMinAmount
if (p.filterMaxAmount !== undefined) filterMaxAmount.value = p.filterMaxAmount
if (typeof p.pageSize === 'number' && pageSizes.includes(p.pageSize)) pageSize.value = p.pageSize
if (typeof p.sortValue === 'string' && sortOptions.value.some((o) => o.value === p.sortValue)) sortValue.value = p.sortValue
if (p.filterTag) filterTag.value = p.filterTag
@@ -609,6 +609,17 @@ function goChannelManage() {
router.push('/channels')
}
/**
* 携带当前筛选条件跳转到统计面板
*
* 先将当前筛选写入共享偏好(与统计面板共用),再带来源标记跳转,
* 统计面板据此还原同一套筛选条件并生成对应统计信息。
*/
function goStatistics() {
saveFilterPrefs()
router.push({ path: '/dashboard', query: { from: 'transactions' } })
}
/** 加载「不计入统计」记录展示样式偏好,失败时保持默认删除线 */
async function loadExcludedDisplayStyle() {
try {
@@ -2018,6 +2029,12 @@ onBeforeUnmount(() => {
<NButton @click="showImportModal = true">{{ t('transaction.importBill') }}</NButton>
<NButton @click="goChannelManage">{{ t('channel.manageEntry') }}</NButton>
<NButton :loading="exporting" @click="handleExport">{{ t('transaction.exportExcel') }}</NButton>
<NButton @click="goStatistics">
<template #icon>
<NIcon :component="StatsChartOutline" />
</template>
{{ t('transaction.viewStatistics') }}
</NButton>
<NButton :loading="loading" @click="fetchData">
<template #icon>
<NIcon :component="RefreshOutline" />