From 9ae91418e0706fe1cdd57b2c5f2325c0368a9dd7 Mon Sep 17 00:00:00 2001 From: skytek_xinliang Date: Tue, 19 May 2026 11:35:01 +0800 Subject: [PATCH] feat(shell): add app shell and maintenance page driver Introduce reusable shell components for layout, tabs, and global overlays. Add maintenance page model, wrapper component, and composable driver to standardize maintenance page state, search, and pagination handling.feat(shell): add app shell and maintenance page driver Introduce reusable shell components for layout, tabs, and global overlays. Add maintenance page model, wrapper component, and composable driver to standardize maintenance page state, search, and pagination handling. --- src/components/pages/PageMaintenance.vue | 34 +++ .../page-drivers/useMaintenancePage.ts | 46 ++++ src/models/page.ts | 12 ++ src/models/student.ts | 13 ++ src/shell/AppShell.vue | 35 +++ src/shell/AppTabs.vue | 114 ++++++++++ src/shell/GlobalOverlays.vue | 199 ++++++++++++++++++ src/stores/menu.ts | 1 + src/stores/students.ts | 15 +- 9 files changed, 456 insertions(+), 13 deletions(-) create mode 100644 src/components/pages/PageMaintenance.vue create mode 100644 src/composables/page-drivers/useMaintenancePage.ts create mode 100644 src/models/page.ts create mode 100644 src/models/student.ts create mode 100644 src/shell/AppShell.vue create mode 100644 src/shell/AppTabs.vue create mode 100644 src/shell/GlobalOverlays.vue diff --git a/src/components/pages/PageMaintenance.vue b/src/components/pages/PageMaintenance.vue new file mode 100644 index 0000000..83c2072 --- /dev/null +++ b/src/components/pages/PageMaintenance.vue @@ -0,0 +1,34 @@ + + + diff --git a/src/composables/page-drivers/useMaintenancePage.ts b/src/composables/page-drivers/useMaintenancePage.ts new file mode 100644 index 0000000..23a920f --- /dev/null +++ b/src/composables/page-drivers/useMaintenancePage.ts @@ -0,0 +1,46 @@ +import { computed, ref } from 'vue' +import type { MaintenancePageModel } from '@/models/page' + +export interface UseMaintenancePageOptions { + title: string + records: unknown[] + itemsPerPage?: number +} + +export function useMaintenancePage(options: UseMaintenancePageOptions) { + const search = ref>({}) + const searchPanelOpen = ref(false) + const currentPage = ref(1) + const itemsPerPage = options.itemsPerPage ?? 10 + + const pageCount = computed(() => + Math.max(1, Math.ceil(options.records.length / itemsPerPage)) + ) + + const pageModel = computed(() => ({ + type: 'maintenance', + title: options.title, + records: options.records, + loading: false, + error: null, + })) + + function load() { + // 由呼叫方在 load 中觸發資料載入;未來可擴充為非同步 + } + + function resetSearch() { + search.value = {} + } + + return { + pageModel, + search, + searchPanelOpen, + currentPage, + itemsPerPage, + pageCount, + load, + resetSearch, + } +} diff --git a/src/models/page.ts b/src/models/page.ts new file mode 100644 index 0000000..0a32be8 --- /dev/null +++ b/src/models/page.ts @@ -0,0 +1,12 @@ +export interface BasePageModel { + title: string + loading?: boolean + error?: string | null +} + +export interface MaintenancePageModel extends BasePageModel { + type: 'maintenance' + records: unknown[] +} + +export type PageModel = MaintenancePageModel diff --git a/src/models/student.ts b/src/models/student.ts new file mode 100644 index 0000000..2e32a29 --- /dev/null +++ b/src/models/student.ts @@ -0,0 +1,13 @@ +export interface StudentRecord { + id: number + studentId: string + name: string + department: string + grade: number + enrollYear: number + credits: number + advisor: string + email: string + phone: string + status: string +} diff --git a/src/shell/AppShell.vue b/src/shell/AppShell.vue new file mode 100644 index 0000000..376d7a0 --- /dev/null +++ b/src/shell/AppShell.vue @@ -0,0 +1,35 @@ + + + diff --git a/src/shell/AppTabs.vue b/src/shell/AppTabs.vue new file mode 100644 index 0000000..e958e66 --- /dev/null +++ b/src/shell/AppTabs.vue @@ -0,0 +1,114 @@ + + + diff --git a/src/shell/GlobalOverlays.vue b/src/shell/GlobalOverlays.vue new file mode 100644 index 0000000..d8a7e65 --- /dev/null +++ b/src/shell/GlobalOverlays.vue @@ -0,0 +1,199 @@ + + + diff --git a/src/stores/menu.ts b/src/stores/menu.ts index 4b01f08..14ed286 100644 --- a/src/stores/menu.ts +++ b/src/stores/menu.ts @@ -6,6 +6,7 @@ import { menuApi, type MenuNode } from '@/services/modules/menu' export interface LayoutMenuItem { title: string path?: string + icon?: string navigable?: boolean subItems?: LayoutMenuItem[] } diff --git a/src/stores/students.ts b/src/stores/students.ts index 8dcda7a..3d89d35 100644 --- a/src/stores/students.ts +++ b/src/stores/students.ts @@ -1,19 +1,8 @@ +import type { StudentRecord } from '@/models/student' import { defineStore } from 'pinia' import { ref } from 'vue' -export interface StudentRecord { - id: number - studentId: string - name: string - department: string - grade: number - enrollYear: number - credits: number - advisor: string - email: string - phone: string - status: string -} +export type { StudentRecord } from '@/models/student' const seedStudents: StudentRecord[] = [ {