diff --git a/frontend/src/components/TransactionDetailModal.vue b/frontend/src/components/TransactionDetailModal.vue index a9777e6..f16ccb5 100644 --- a/frontend/src/components/TransactionDetailModal.vue +++ b/frontend/src/components/TransactionDetailModal.vue @@ -33,10 +33,14 @@ interface ImageItem { loading: boolean } -const props = defineProps<{ +const props = withDefaults(defineProps<{ show: boolean transaction: DetailTransaction | null -}>() + /** 是否显示「编辑」按钮;日历等只读场景传 false */ + editable?: boolean +}>(), { + editable: true, +}) const emit = defineEmits<{ (e: 'update:show', value: boolean): void @@ -239,7 +243,7 @@ function clearImages() { diff --git a/frontend/src/locales/messages.ts b/frontend/src/locales/messages.ts index 56d2da8..8d4cc3d 100644 --- a/frontend/src/locales/messages.ts +++ b/frontend/src/locales/messages.ts @@ -88,6 +88,7 @@ export default { appTitle: { zh: '汇账', en: 'Ledgerly', ja: 'Ledgerly' }, appSubtitle: { zh: '个人财务管理', en: 'Personal Finance', ja: '個人財務管理' }, dashboard: { zh: '统计面板', en: 'Dashboard', ja: 'ダッシュボード' }, + calendar: { zh: '日历', en: 'Calendar', ja: 'カレンダー' }, transactions: { zh: '收支记录', en: 'Transactions', ja: '収支記録' }, categories: { zh: '分类管理', en: 'Categories', ja: 'カテゴリ管理' }, tags: { zh: '标签管理', en: 'Tags', ja: 'タグ管理' }, @@ -414,6 +415,30 @@ export default { chartActionView: { zh: '查看明细', en: 'View Details', ja: '明細を表示' }, }, + // 日历 + calendar: { + title: { zh: '日历', en: 'Calendar', ja: 'カレンダー' }, + hint: { zh: '点击日期查看当天交易明细', en: "Click a date to view that day's transactions", ja: '日付をクリックするとその日の明細を表示します' }, + loadFailed: { zh: '加载日历数据失败', en: 'Failed to load calendar data', ja: 'カレンダーデータの読み込みに失敗しました' }, + transactionCount: { zh: '交易笔数', en: 'Transactions', ja: '取引件数' }, + today: { zh: '今天', en: 'Today', ja: '今日' }, + viewMonth: { zh: '月视图', en: 'Month', ja: '月' }, + viewYear: { zh: '年视图', en: 'Year', ja: '年' }, + prevPeriod: { zh: '上一个', en: 'Previous', ja: '前へ' }, + nextPeriod: { zh: '下一个', en: 'Next', ja: '次へ' }, + income: { zh: '收入', en: 'Income', ja: '収入' }, + expense: { zh: '支出', en: 'Expense', ja: '支出' }, + balance: { zh: '结余', en: 'Balance', ja: '収支' }, + noData: { zh: '无数据', en: 'No data', ja: 'データなし' }, + monthEmpty: { zh: '本月暂无交易数据', en: 'No transactions this month', ja: '今月の取引はありません' }, + yearEmpty: { zh: '本年暂无交易数据', en: 'No transactions this year', ja: '今年の取引はありません' }, + detailTitle: { zh: '{date} 交易明细', en: 'Transactions on {date}', ja: '{date} の取引明細' }, + detailEmpty: { zh: '当天没有交易记录', en: 'No transactions on this day', ja: 'この日の取引はありません' }, + detailLoadFailed: { zh: '加载明细失败', en: 'Failed to load details', ja: '明細の読み込みに失敗しました' }, + detailMore: { zh: '仅显示前 {shown} 条,共 {total} 条', en: 'Showing first {shown} of {total}', ja: '先頭 {shown} 件を表示(全 {total} 件)' }, + viewInTransactions: { zh: '在收支记录中查看', en: 'View in Transactions', ja: '収支記録で表示' }, + }, + // 收支记录 transaction: { title: { zh: '收支记录', en: 'Transactions', ja: '収支記録' }, diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts index d8cea35..acd278a 100644 --- a/frontend/src/router/index.ts +++ b/frontend/src/router/index.ts @@ -34,13 +34,18 @@ const router = createRouter({ { path: '/', component: () => import('../views/Layout.vue'), // 主布局(带侧边栏) - redirect: '/dashboard', // 默认跳转到统计面板 + redirect: '/calendar', // 默认跳转到日历 children: [ { path: 'dashboard', name: 'Dashboard', component: () => import('../views/Dashboard.vue'), // 统计面板 }, + { + path: 'calendar', + name: 'Calendar', + component: () => import('../views/Calendar.vue'), // 日历(每日收支概览) + }, { path: 'smart-query', name: 'SqlQuery', @@ -139,7 +144,7 @@ router.beforeEach(async (to, _from, next) => { // 已配置数据库:配置引导页不再需要,跳转到登录或首页 if (to.path === '/database-setup') { const userStore = useUserStore() - next(userStore.token ? '/dashboard' : '/login') + next(userStore.token ? '/calendar' : '/login') return } @@ -151,7 +156,7 @@ router.beforeEach(async (to, _from, next) => { const target = typeof redirect === 'string' && redirect.startsWith('/') && !redirect.startsWith('//') ? redirect - : '/dashboard' + : '/calendar' next(target) return } diff --git a/frontend/src/utils/navItems.ts b/frontend/src/utils/navItems.ts index dfd51cb..2aee45c 100644 --- a/frontend/src/utils/navItems.ts +++ b/frontend/src/utils/navItems.ts @@ -1,7 +1,7 @@ // 侧边导航清单(单一数据源):定义各导航项的标识、文案、图标与默认显隐/顺序,供主布局与设置页共用 import { markRaw, type Component } from 'vue' import { - BarChartOutline, ListOutline, FolderOpenOutline, PricetagsOutline, OptionsOutline, MailOutline, SparklesOutline, + BarChartOutline, CalendarOutline, ListOutline, FolderOpenOutline, PricetagsOutline, OptionsOutline, MailOutline, SparklesOutline, } from '@vicons/ionicons5' /** 导航项定义 */ @@ -20,6 +20,7 @@ export interface NavItemDef { /** 默认导航项清单,数组顺序即默认展示顺序;图标用 markRaw 避免被响应式包装 */ export const NAV_ITEMS: NavItemDef[] = [ + { key: 'calendar', labelKey: 'layout.calendar', icon: markRaw(CalendarOutline), aiDependent: false, defaultVisible: true }, { key: 'dashboard', labelKey: 'layout.dashboard', icon: markRaw(BarChartOutline), aiDependent: false, defaultVisible: true }, { key: 'smart-query', labelKey: 'layout.sqlQuery', icon: markRaw(SparklesOutline), aiDependent: true, defaultVisible: true }, { key: 'transactions', labelKey: 'layout.transactions', icon: markRaw(ListOutline), aiDependent: false, defaultVisible: true }, diff --git a/frontend/src/utils/storageKeys.ts b/frontend/src/utils/storageKeys.ts index 236e176..8f00177 100644 --- a/frontend/src/utils/storageKeys.ts +++ b/frontend/src/utils/storageKeys.ts @@ -13,6 +13,10 @@ export const STORAGE_KEYS = { billImportBillType: 'ledgerly.billImport.billType', // 统计面板偏好 dashboardPrefs: 'ledgerly.dashboard.prefs', + // 日历视图模式(month-月视图 / year-年视图) + calendarView: 'ledgerly.calendar.view', + // 日历统计口径(INCLUDED-仅计入 / ALL-全部 / EXCLUDED-仅不计入) + calendarStatsScope: 'ledgerly.calendar.statsScope', // 统计面板 AI 查询历史 dashboardAiSearchHistory: 'ledgerly.dashboard.aiSearchHistory', // 统计面板 AI 查询浮窗位置尺寸 diff --git a/frontend/src/views/Calendar.vue b/frontend/src/views/Calendar.vue new file mode 100644 index 0000000..dcbb474 --- /dev/null +++ b/frontend/src/views/Calendar.vue @@ -0,0 +1,906 @@ + + + + + diff --git a/frontend/src/views/Layout.vue b/frontend/src/views/Layout.vue index 2757842..f801565 100644 --- a/frontend/src/views/Layout.vue +++ b/frontend/src/views/Layout.vue @@ -32,6 +32,7 @@ const showDrawer = ref(false) const activeKey = computed(() => { const map: Record = { '/dashboard': 'dashboard', + '/calendar': 'calendar', '/smart-query': 'smart-query', '/transactions': 'transactions', '/categories': 'categories', diff --git a/frontend/src/views/Login.vue b/frontend/src/views/Login.vue index dd495de..c7c12da 100644 --- a/frontend/src/views/Login.vue +++ b/frontend/src/views/Login.vue @@ -39,7 +39,7 @@ function resolveRedirect(): string { if (typeof target === 'string' && target.startsWith('/') && !target.startsWith('//')) { return target } - return '/dashboard' + return '/calendar' } /** 处理登录操作:校验表单 -> 调用登录接口 -> 保存 token -> 跳转首页 */