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 @@
+
+
+
+
+
+
+ {{ tab.title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
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 @@
+
+
+
+
+
+ 搜尋結果
+
+ 關鍵字:{{ searchKeyword }}
+
+
+
+ 查無結果
+
+
+
+
+
+
+ {{ item.title }}
+
+ {{ item.parents.join(' / ') }}
+
+
+
+
+
+
+ 關閉
+
+
+
+
+
+
+ 訊息清單
+
+ 僅示意資料,不含延伸功能
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ resolveMessageItem(wrapped).title }}
+
+
+ {{ resolveMessageItem(wrapped).meta }}
+
+
+
+
+
+
+
+ 關閉
+
+
+
+
+
+ {{ snackbar.message }}
+
+
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[] = [
{