From 3909f614d24c87e2d03ad6a47a974befa83d0c92 Mon Sep 17 00:00:00 2001 From: xiaomai Date: Tue, 21 Oct 2025 14:16:27 +0800 Subject: [PATCH] feat(ui): implement dashboard layout and refactor pages This commit replaces the previous simple layout with a comprehensive dashboard interface using Nuxt UI Pro components. - Introduced a new default layout with `UDashboardSidebar` and `UDashboardPanel`. - Refactored the main page (`index.vue`) by splitting it into `ItemManageStatistics` and `ItemManageTable` components. - Added new computed stats for monthly growth and total images, and displayed them in new statistic cards. - Reorganized components from `item/` to a new `itemManage/` directory. - Added custom primary and secondary theme colors. --- app/assets/css/main.css | 5 + .../{item => itemManage}/DeleteModal.vue | 0 .../{item => itemManage}/EditModal.vue | 0 app/components/itemManage/Statistics.vue | 76 +++++ app/components/itemManage/Table.vue | 215 +++++++++++++ app/composables/items.ts | 39 +++ app/layouts/default.vue | 54 ++-- app/layouts/normal.vue | 54 ++++ app/pages/export/index.vue | 20 +- app/pages/index.vue | 284 +----------------- 10 files changed, 451 insertions(+), 296 deletions(-) rename app/components/{item => itemManage}/DeleteModal.vue (100%) rename app/components/{item => itemManage}/EditModal.vue (100%) create mode 100644 app/components/itemManage/Statistics.vue create mode 100644 app/components/itemManage/Table.vue create mode 100644 app/layouts/normal.vue diff --git a/app/assets/css/main.css b/app/assets/css/main.css index 6379526..f7e0e8c 100644 --- a/app/assets/css/main.css +++ b/app/assets/css/main.css @@ -1,6 +1,11 @@ @import "tailwindcss"; @import "@nuxt/ui"; +@theme { + --color-primary: #3a4dfb; + --color-secondary: #919cfc; +} + .router-link-exact-active { @apply text-blue-600 bg-blue-50; } diff --git a/app/components/item/DeleteModal.vue b/app/components/itemManage/DeleteModal.vue similarity index 100% rename from app/components/item/DeleteModal.vue rename to app/components/itemManage/DeleteModal.vue diff --git a/app/components/item/EditModal.vue b/app/components/itemManage/EditModal.vue similarity index 100% rename from app/components/item/EditModal.vue rename to app/components/itemManage/EditModal.vue diff --git a/app/components/itemManage/Statistics.vue b/app/components/itemManage/Statistics.vue new file mode 100644 index 0000000..9751017 --- /dev/null +++ b/app/components/itemManage/Statistics.vue @@ -0,0 +1,76 @@ + + + + + diff --git a/app/components/itemManage/Table.vue b/app/components/itemManage/Table.vue new file mode 100644 index 0000000..4647cf4 --- /dev/null +++ b/app/components/itemManage/Table.vue @@ -0,0 +1,215 @@ + + + + + diff --git a/app/composables/items.ts b/app/composables/items.ts index f5326ad..fb35cbb 100644 --- a/app/composables/items.ts +++ b/app/composables/items.ts @@ -61,6 +61,45 @@ const _useItems = () => { item.createdAt?.getFullYear() === thisYear ).length; }), + // Compared to last month + comparedToLastMonth: computed(() => { + const now = new Date(); + const thisMonth = now.getMonth(); + const thisYear = now.getFullYear(); + + const lastMonthDate = new Date(now); + lastMonthDate.setMonth(thisMonth - 1); + const lastMonth = lastMonthDate.getMonth(); + const lastMonthYear = lastMonthDate.getFullYear(); + + const thisMonthCount = (items.value ?? []).filter( + (item) => + item.createdAt?.getMonth() === thisMonth && + item.createdAt?.getFullYear() === thisYear + ).length; + + const lastMonthCount = (items.value ?? []).filter( + (item) => + item.createdAt?.getMonth() === lastMonth && + item.createdAt?.getFullYear() === lastMonthYear + ).length; + + // if (lastMonthCount === 0) { + // return thisMonthCount === 0 ? 0 : 100; + // } + + // return ((thisMonthCount - lastMonthCount) / lastMonthCount) * 100; + if (lastMonthCount === 0) { + // 上个月为 0,本月不为 0 → 直接视为无限增长(返回 100 * thisMonthCount) + return thisMonthCount === 0 ? 0 : thisMonthCount * 100; + } + + // 允许超过 100% 的差值百分比 + return ((thisMonthCount - lastMonthCount) / lastMonthCount) * 100; + }), + totalImages: computed(() => { + return (items.value ?? []).filter((item) => item.imageUrl).length; + }), }; const utils = { diff --git a/app/layouts/default.vue b/app/layouts/default.vue index 3ab660b..d585d11 100644 --- a/app/layouts/default.vue +++ b/app/layouts/default.vue @@ -1,29 +1,38 @@ + + + + + + + + @@ -32,6 +41,9 @@ import type { NavigationMenuItem } from "@nuxt/ui"; const route = useRoute(); +const sidebarOpen = ref(false); +const groups = ref([]); + const items = computed(() => [ { label: "物品管理", diff --git a/app/layouts/normal.vue b/app/layouts/normal.vue new file mode 100644 index 0000000..3ab660b --- /dev/null +++ b/app/layouts/normal.vue @@ -0,0 +1,54 @@ + + + diff --git a/app/pages/export/index.vue b/app/pages/export/index.vue index ad3e5c9..00962dd 100644 --- a/app/pages/export/index.vue +++ b/app/pages/export/index.vue @@ -1,6 +1,14 @@