diff --git a/frontend/src/locales/messages.ts b/frontend/src/locales/messages.ts index 8d4cc3d..d167c26 100644 --- a/frontend/src/locales/messages.ts +++ b/frontend/src/locales/messages.ts @@ -526,6 +526,9 @@ export default { remarkPlaceholder: { zh: '可选备注说明', en: 'Optional remark', ja: '備考(任意)' }, remarkHistoryClear: { zh: '清空历史', en: 'Clear history', ja: '履歴をクリア' }, tagPlaceholder: { zh: '选择标签', en: 'Select tags', ja: 'タグを選択' }, + newCategoryButton: { zh: '新建分类', en: 'New Category', ja: 'カテゴリを新規作成' }, + newTagButton: { zh: '新建标签', en: 'New Tag', ja: 'タグを新規作成' }, + newChannelButton: { zh: '新建渠道', en: 'New Channel', ja: 'チャネルを新規作成' }, noRawData: { zh: '该记录没有原始账单数据', en: 'This record has no raw bill data', ja: 'この記録には元の明細データがありません' }, remarkHiddenSuccess: { zh: '已隐藏备注', en: 'Remark hidden', ja: '備考を非表示にしました' }, remarkShownSuccess: { zh: '已取消隐藏', en: 'Remark unhidden', ja: '備考の非表示を解除しました' }, diff --git a/frontend/src/utils/storageKeys.ts b/frontend/src/utils/storageKeys.ts index 8f00177..e05678f 100644 --- a/frontend/src/utils/storageKeys.ts +++ b/frontend/src/utils/storageKeys.ts @@ -15,6 +15,10 @@ export const STORAGE_KEYS = { dashboardPrefs: 'ledgerly.dashboard.prefs', // 日历视图模式(month-月视图 / year-年视图) calendarView: 'ledgerly.calendar.view', + // 日历最近浏览的年份(月视图与年视图共用,字符串形式的四位年份) + calendarYear: 'ledgerly.calendar.year', + // 日历月视图最近浏览的月份(字符串形式的 1-12) + calendarMonth: 'ledgerly.calendar.month', // 日历统计口径(INCLUDED-仅计入 / ALL-全部 / EXCLUDED-仅不计入) calendarStatsScope: 'ledgerly.calendar.statsScope', // 统计面板 AI 查询历史 diff --git a/frontend/src/views/Calendar.vue b/frontend/src/views/Calendar.vue index dcbb474..69249d2 100644 --- a/frontend/src/views/Calendar.vue +++ b/frontend/src/views/Calendar.vue @@ -69,6 +69,20 @@ const statsScope = useStringPreference(STORAGE_KEYS.calendarStatsScope, 'INCLUDE /** 传给统计接口的口径参数(限定为接口联合类型) */ const scopeParam = computed(() => statsScope.value as 'INCLUDED' | 'ALL' | 'EXCLUDED') +/** 当前统计口径对应的明细过滤参数:INCLUDED-仅计入、EXCLUDED-仅不计入、ALL-全部不传 */ +function statsScopeFilter(): boolean | undefined { + if (statsScope.value === 'INCLUDED') return true + if (statsScope.value === 'EXCLUDED') return false + return undefined +} + +/** 当前统计口径对应收支记录页的筛选值:'true'-计入、'false'-不计入、null-全部 */ +function statsScopePref(): string | null { + if (statsScope.value === 'INCLUDED') return 'true' + if (statsScope.value === 'EXCLUDED') return 'false' + return null +} + /** 统计口径选项 */ const statsScopeOptions = computed(() => [ { label: t('dashboard.statsScope.included'), value: 'INCLUDED' }, @@ -76,11 +90,20 @@ const statsScopeOptions = computed(() => [ { label: t('dashboard.statsScope.excluded'), value: 'EXCLUDED' }, ]) -// 月视图面板年月(月份为 1-based) -const panelYear = ref(new Date().getFullYear()) -const panelMonth = ref(new Date().getMonth() + 1) +// 最近浏览的年月偏好:字符串存取,非法值回退当前年月(URL 参数优先,见下方初始化) +const initialDate = new Date() +const lastYearPref = useStringPreference(STORAGE_KEYS.calendarYear, String(initialDate.getFullYear()), { + validate: (v) => /^\d{4}$/.test(v) && Number(v) > 0, +}) +const lastMonthPref = useStringPreference(STORAGE_KEYS.calendarMonth, String(initialDate.getMonth() + 1), { + validate: (v) => /^\d{1,2}$/.test(v) && Number(v) >= 1 && Number(v) <= 12, +}) + +// 月视图面板年月(月份为 1-based):默认恢复上次浏览,URL 参数会在下方覆盖 +const panelYear = ref(Number(lastYearPref.value)) +const panelMonth = ref(Number(lastMonthPref.value)) // 年视图当前年份 -const yearViewYear = ref(new Date().getFullYear()) +const yearViewYear = ref(panelYear.value) // 选中日期的时间戳(默认今天) const selectedTs = ref(new Date().setHours(0, 0, 0, 0)) // NCalendar 依赖 defaultValue 决定初始面板,程序化切月时通过自增该 key 重建组件 @@ -365,6 +388,7 @@ async function openDetail(year: number, month: number, date: number) { size: DETAIL_PAGE_SIZE, startTime: `${key}T00:00:00`, endTime: `${key}T23:59:59`, + includeInStats: statsScopeFilter(), sortField: 'transactionTime', sortOrder: 'asc', }) @@ -386,6 +410,7 @@ function goToTransactions() { new Date(y, m - 1, d, 0, 0, 0).getTime(), new Date(y, m - 1, d, 23, 59, 59).getTime(), ], + filterIncludeInStats: statsScopePref(), }) detailShow.value = false router.push('/transactions') @@ -419,6 +444,19 @@ watch([() => panelYear.value, () => panelMonth.value, () => yearViewYear.value, immediate: true, }) +// 浏览年月变化时写入偏好,供下次进入日历恢复上次年月 +watch([panelYear, panelMonth], ([y, m]) => { + lastYearPref.value = String(y) + lastMonthPref.value = String(m) +}) + +// 年视图切换年份时写入年份偏好,使下次进入年视图也恢复上次年份 +watch(yearViewYear, (y) => { + if (viewMode.value === 'year') { + lastYearPref.value = String(y) + } +}) + // 切换统计口径时按当前视图重新加载数据 watch(statsScope, () => { if (viewMode.value === 'year') { @@ -562,9 +600,10 @@ onMounted(() => {