diff --git a/.codex/config.toml b/.codex/config.toml deleted file mode 100644 index 7001590..0000000 --- a/.codex/config.toml +++ /dev/null @@ -1,16 +0,0 @@ -[mcp_servers.chrome-devtools] -command = "npx" -args = ["chrome-devtools-mcp@latest", "--browserUrl", "http://localhost:9222"] - -[mcp_servers.code-review-graph] -command = "uvx" -args = ["code-review-graph", "serve"] - -[mcp_servers.code-review-graph.tools.get_minimal_context_tool] -approval_mode = "approve" - -[mcp_servers.code-review-graph.tools.query_graph_tool] -approval_mode = "approve" - -[mcp_servers.code-review-graph.tools.build_or_update_graph_tool] -approval_mode = "approve" diff --git a/.gitignore b/.gitignore index 0ac141e..d16aca4 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,5 @@ output/playwright/ /playwright/.auth/ # Added by code-review-graph .code-review-graph/ + +.codex/config.toml diff --git a/README.md b/README.md index cdd5bfd..5e0dd57 100644 --- a/README.md +++ b/README.md @@ -94,26 +94,10 @@ VITE_DEV_DEFAULT_PASSWORD= - `src/styles`:Vuetify SASS settings 與 themes。 - `src/language`:i18n JSON。 -## Template Core 與 Demo +## Template Core template core 是 app shell、router、layout、plugins、styles、services 基礎設施與全域 stores。一般專案會保留它們。 -demo/example 主要是 maintenance 與學生資料範例: - -- `src/views/Home.vue` -- `src/components/PageIndex.vue` -- `src/views/maint/*` -- `src/components/maint/*` -- `src/composables/maint/*` -- `src/components/PageMaint.vue` -- `src/stores/students.ts` -- `src/stores/semesters.ts` -- `src/views/FncPage.vue` -- `src/views/Settings.vue` -- `src/assets/*` 中的品牌或展示素材 - -建立正式專案時可依需求替換或刪除 demo/example。刪除時同步清理 routes、menu、language、assets 與引用。 - ## Documentation - [src/README.md](./src/README.md):`src` 開發入口。 diff --git a/docs/frontend-layering.md b/docs/frontend-layering.md index cc614ea..25574b7 100644 --- a/docs/frontend-layering.md +++ b/docs/frontend-layering.md @@ -4,8 +4,6 @@ 這份文件只描述目前 repo 已經落地的前端分層與命名規則,讓後續新增檔案、搬移檔案、或重構時有一致判斷基準。 -重點不是追求理想化架構,而是避免把舊名稱、過渡寫法、或已刪除的結構繼續當成規則。 - 目前專案的主要責任鏈如下: - `router` 決定 route 與 layout meta @@ -22,9 +20,9 @@ 目前路由集中在: -- [routes.ts](/home/carl/git/skt-vuetify-templates/src/router/routes.ts) -- [index.ts](/home/carl/git/skt-vuetify-templates/src/router/index.ts) -- [guards.ts](/home/carl/git/skt-vuetify-templates/src/router/guards.ts) +- [routes.ts](../src/router/routes.ts) +- [index.ts](../src/router/index.ts) +- [guards.ts](../src/router/guards.ts) 責任: @@ -34,12 +32,12 @@ 目前 `meta.layout` 已是 app shell 切換的正式入口: -- `default` 走 [MainLayout.vue](/home/carl/git/skt-vuetify-templates/src/components/layouts/MainLayout.vue) -- `none` 走 [PlainLayout.vue](/home/carl/git/skt-vuetify-templates/src/components/layouts/PlainLayout.vue) +- `default` 走 [MainLayout.vue](../src/components/layouts/MainLayout.vue) +- `none` 走 [PlainLayout.vue](../src/components/layouts/PlainLayout.vue) ### `src/App.vue` -[App.vue](/home/carl/git/skt-vuetify-templates/src/App.vue) 目前不是單純掛載入口,而是實際的應用組裝層。 +[App.vue](../src/App.vue) 目前不是單純掛載入口,而是實際的應用組裝層。 目前承擔的責任包含: @@ -61,23 +59,25 @@ 目前較薄的 view: -- [Home.vue](/home/carl/git/skt-vuetify-templates/src/views/Home.vue) -- [Login.vue](/home/carl/git/skt-vuetify-templates/src/views/Login.vue) -- [EditableGrid.vue](/home/carl/git/skt-vuetify-templates/src/views/maint/EditableGrid.vue) -- [Forbidden.vue](/home/carl/git/skt-vuetify-templates/src/views/errors/Forbidden.vue) -- [ServerError.vue](/home/carl/git/skt-vuetify-templates/src/views/errors/ServerError.vue) -- [ServiceUnavailable.vue](/home/carl/git/skt-vuetify-templates/src/views/errors/ServiceUnavailable.vue) -- [NetworkError.vue](/home/carl/git/skt-vuetify-templates/src/views/errors/NetworkError.vue) -- [Maintenance.vue](/home/carl/git/skt-vuetify-templates/src/views/errors/Maintenance.vue) -- [NotFound.vue](/home/carl/git/skt-vuetify-templates/src/views/errors/NotFound.vue) -- [FncPage.vue](/home/carl/git/skt-vuetify-templates/src/views/FncPage.vue) +- [Home.vue](../src/views/Home.vue) +- [Login.vue](../src/views/Login.vue) +- [EditableGrid.vue](../src/views/maint/EditableGrid.vue) +- [Forbidden.vue](../src/views/errors/Forbidden.vue) +- [ServerError.vue](../src/views/errors/ServerError.vue) +- [ServiceUnavailable.vue](../src/views/errors/ServiceUnavailable.vue) +- [NetworkError.vue](../src/views/errors/NetworkError.vue) +- [Maintenance.vue](../src/views/errors/Maintenance.vue) +- [NotFound.vue](../src/views/errors/NotFound.vue) +- [ErrorShell.vue](../src/views/errors/ErrorShell.vue) +- [FncPage.vue](../src/views/FncPage.vue) +- [Settings.vue](../src/views/Settings.vue) 目前仍偏厚的 view: -- [SingleRecord.vue](/home/carl/git/skt-vuetify-templates/src/views/maint/SingleRecord.vue) -- [MasterDetailA.vue](/home/carl/git/skt-vuetify-templates/src/views/maint/MasterDetailA.vue) -- [MasterDetailB.vue](/home/carl/git/skt-vuetify-templates/src/views/maint/MasterDetailB.vue) -- [MasterDetailC.vue](/home/carl/git/skt-vuetify-templates/src/views/maint/MasterDetailC.vue) +- [SingleRecord.vue](../src/views/maint/SingleRecord.vue) +- [MasterDetailA.vue](../src/views/maint/MasterDetailA.vue) +- [MasterDetailB.vue](../src/views/maint/MasterDetailB.vue) +- [MasterDetailC.vue](../src/views/maint/MasterDetailC.vue) `views` 應遵守的原則: @@ -94,9 +94,9 @@ 目前以下元件實際上扮演 page component: -- [PageLogin.vue](/home/carl/git/skt-vuetify-templates/src/components/PageLogin.vue) -- [PageIndex.vue](/home/carl/git/skt-vuetify-templates/src/components/PageIndex.vue) -- [PageMaint.vue](/home/carl/git/skt-vuetify-templates/src/components/PageMaint.vue) +- [PageLogin.vue](../src/components/PageLogin.vue) +- [PageIndex.vue](../src/components/PageIndex.vue) +- [PageMaint.vue](../src/components/PageMaint.vue) 這些檔案的責任是: @@ -114,14 +114,14 @@ 登入頁的較細 UI 區塊已集中到: -- [CreateAccountLink.vue](/home/carl/git/skt-vuetify-templates/src/components/login/CreateAccountLink.vue) -- [LoginAnnouncementBoard.vue](/home/carl/git/skt-vuetify-templates/src/components/login/LoginAnnouncementBoard.vue) -- [LoginBrand.vue](/home/carl/git/skt-vuetify-templates/src/components/login/LoginBrand.vue) -- [LoginForm.vue](/home/carl/git/skt-vuetify-templates/src/components/login/LoginForm.vue) -- [LoginHeader.vue](/home/carl/git/skt-vuetify-templates/src/components/login/LoginHeader.vue) -- [LoginIllustration.vue](/home/carl/git/skt-vuetify-templates/src/components/login/LoginIllustration.vue) -- [LoginToolBar.vue](/home/carl/git/skt-vuetify-templates/src/components/login/LoginToolBar.vue) -- [LoginVerify.vue](/home/carl/git/skt-vuetify-templates/src/components/login/LoginVerify.vue) +- [CreateAccountLink.vue](../src/components/login/CreateAccountLink.vue) +- [LoginAnnouncementBoard.vue](../src/components/login/LoginAnnouncementBoard.vue) +- [LoginBrand.vue](../src/components/login/LoginBrand.vue) +- [LoginForm.vue](../src/components/login/LoginForm.vue) +- [LoginHeader.vue](../src/components/login/LoginHeader.vue) +- [LoginIllustration.vue](../src/components/login/LoginIllustration.vue) +- [LoginToolBar.vue](../src/components/login/LoginToolBar.vue) +- [LoginVerify.vue](../src/components/login/LoginVerify.vue) 這一層的定位是: @@ -133,7 +133,7 @@ 目前 `components/base` 只剩下: -- [DraggableDialog.vue](/home/carl/git/skt-vuetify-templates/src/components/base/DraggableDialog.vue) +- [DraggableDialog.vue](../src/components/base/DraggableDialog.vue) 目前判斷原則很直接: @@ -144,18 +144,18 @@ 目前 layout 實作集中於: -- [MainLayout.vue](/home/carl/git/skt-vuetify-templates/src/components/layouts/MainLayout.vue) -- [PlainLayout.vue](/home/carl/git/skt-vuetify-templates/src/components/layouts/PlainLayout.vue) +- [MainLayout.vue](../src/components/layouts/MainLayout.vue) +- [PlainLayout.vue](../src/components/layouts/PlainLayout.vue) - `src/components/layouts/main-layout/*` 其中 `main-layout/*` 是 `MainLayout` 底下拆出的骨架子元件: -- [AppBarBreadcrumbCol.vue](/home/carl/git/skt-vuetify-templates/src/components/layouts/main-layout/AppBarBreadcrumbCol.vue) -- [AppBarFavoritesCol.vue](/home/carl/git/skt-vuetify-templates/src/components/layouts/main-layout/AppBarFavoritesCol.vue) -- [AppBarTopCol.vue](/home/carl/git/skt-vuetify-templates/src/components/layouts/main-layout/AppBarTopCol.vue) -- [DrawerDesktopMenu.vue](/home/carl/git/skt-vuetify-templates/src/components/layouts/main-layout/DrawerDesktopMenu.vue) -- [DrawerMobileFavoritesPanel.vue](/home/carl/git/skt-vuetify-templates/src/components/layouts/main-layout/DrawerMobileFavoritesPanel.vue) -- [DrawerMobileMenuPanel.vue](/home/carl/git/skt-vuetify-templates/src/components/layouts/main-layout/DrawerMobileMenuPanel.vue) +- [AppBarBreadcrumbCol.vue](../src/components/layouts/main-layout/AppBarBreadcrumbCol.vue) +- [AppBarFavoritesCol.vue](../src/components/layouts/main-layout/AppBarFavoritesCol.vue) +- [AppBarTopCol.vue](../src/components/layouts/main-layout/AppBarTopCol.vue) +- [DrawerDesktopMenu.vue](../src/components/layouts/main-layout/DrawerDesktopMenu.vue) +- [DrawerMobileFavoritesPanel.vue](../src/components/layouts/main-layout/DrawerMobileFavoritesPanel.vue) +- [DrawerMobileMenuPanel.vue](../src/components/layouts/main-layout/DrawerMobileMenuPanel.vue) layout 應只承擔: @@ -172,22 +172,22 @@ layout 不應承擔: 這個目錄目前是最接近 feature folder 的區域,放 maintenance 領域的 page component 與 domain component: -- [PageMaint.vue](/home/carl/git/skt-vuetify-templates/src/components/PageMaint.vue) -- [CommonConfirmDialog.vue](/home/carl/git/skt-vuetify-templates/src/components/maint/CommonConfirmDialog.vue) -- [EditableGrid.vue](/home/carl/git/skt-vuetify-templates/src/components/maint/EditableGrid.vue) -- [MasterFileFormFields.vue](/home/carl/git/skt-vuetify-templates/src/components/maint/MasterFileFormFields.vue) -- [MntDialogCard.vue](/home/carl/git/skt-vuetify-templates/src/components/maint/MntDialogCard.vue) -- [MntRecordNavToolbar.vue](/home/carl/git/skt-vuetify-templates/src/components/maint/MntRecordNavToolbar.vue) +- [PageMaint.vue](../src/components/PageMaint.vue) +- [CommonConfirmDialog.vue](../src/components/maint/CommonConfirmDialog.vue) +- [EditableGrid.vue](../src/components/maint/EditableGrid.vue) +- [MasterFileFormFields.vue](../src/components/maint/MasterFileFormFields.vue) +- [MntDialogCard.vue](../src/components/maint/MntDialogCard.vue) +- [MntRecordNavToolbar.vue](../src/components/maint/MntRecordNavToolbar.vue) - `master-detail/*` `master-detail/*` 目前屬於維護頁專用的較細組件群: -- [CourseMobilePanel.vue](/home/carl/git/skt-vuetify-templates/src/components/maint/master-detail/CourseMobilePanel.vue) -- [DetailCollapseGropus.vue](/home/carl/git/skt-vuetify-templates/src/components/maint/master-detail/DetailCollapseGropus.vue) -- [DetailFullHeightPanel.vue](/home/carl/git/skt-vuetify-templates/src/components/maint/master-detail/DetailFullHeightPanel.vue) -- [DetailNavigation.vue](/home/carl/git/skt-vuetify-templates/src/components/maint/master-detail/DetailNavigation.vue) -- [DetailSidePanel.vue](/home/carl/git/skt-vuetify-templates/src/components/maint/master-detail/DetailSidePanel.vue) -- [DetailSimpleList.vue](/home/carl/git/skt-vuetify-templates/src/components/maint/master-detail/DetailSimpleList.vue) +- [CourseMobilePanel.vue](../src/components/maint/master-detail/CourseMobilePanel.vue) +- [DetailCollapseGropus.vue](../src/components/maint/master-detail/DetailCollapseGropus.vue) +- [DetailFullHeightPanel.vue](../src/components/maint/master-detail/DetailFullHeightPanel.vue) +- [DetailNavigation.vue](../src/components/maint/master-detail/DetailNavigation.vue) +- [DetailSidePanel.vue](../src/components/maint/master-detail/DetailSidePanel.vue) +- [DetailSimpleList.vue](../src/components/maint/master-detail/DetailSimpleList.vue) 結論: @@ -204,12 +204,12 @@ layout 不應承擔: 代表性檔案: -- [useAdminLayoutState.ts](/home/carl/git/skt-vuetify-templates/src/composables/layout/useAdminLayoutState.ts) -- [useThemeToggle.ts](/home/carl/git/skt-vuetify-templates/src/composables/layout/useThemeToggle.ts) -- [useMaintenanceCrudFlow.ts](/home/carl/git/skt-vuetify-templates/src/composables/maint/useMaintenanceCrudFlow.ts) -- [useStudentMaintenanceForm.ts](/home/carl/git/skt-vuetify-templates/src/composables/maint/useStudentMaintenanceForm.ts) -- [useEditableStudentGrid.ts](/home/carl/git/skt-vuetify-templates/src/composables/maint/useEditableStudentGrid.ts) -- [useApiCall.ts](/home/carl/git/skt-vuetify-templates/src/composables/useApiCall.ts) +- [useAdminLayoutState.ts](../src/composables/layout/useAdminLayoutState.ts) +- [useThemeToggle.ts](../src/composables/layout/useThemeToggle.ts) +- [useMaintenanceCrudFlow.ts](../src/composables/maint/useMaintenanceCrudFlow.ts) +- [useStudentMaintenanceForm.ts](../src/composables/maint/useStudentMaintenanceForm.ts) +- [useEditableStudentGrid.ts](../src/composables/maint/useEditableStudentGrid.ts) +- [useApiCall.ts](../src/composables/useApiCall.ts) `composables` 的責任: @@ -223,21 +223,23 @@ layout 不應承擔: 代表性檔案: -- [auth.ts](/home/carl/git/skt-vuetify-templates/src/stores/auth.ts) -- [menu.ts](/home/carl/git/skt-vuetify-templates/src/stores/menu.ts) -- [breadcrumbs.ts](/home/carl/git/skt-vuetify-templates/src/stores/breadcrumbs.ts) -- [favorites.ts](/home/carl/git/skt-vuetify-templates/src/stores/favorites.ts) -- [messages.ts](/home/carl/git/skt-vuetify-templates/src/stores/messages.ts) -- [snackbar.ts](/home/carl/git/skt-vuetify-templates/src/stores/snackbar.ts) -- [loginAnnouncements.ts](/home/carl/git/skt-vuetify-templates/src/stores/loginAnnouncements.ts) -- [students.ts](/home/carl/git/skt-vuetify-templates/src/stores/students.ts) -- [semesters.ts](/home/carl/git/skt-vuetify-templates/src/stores/semesters.ts) +- [auth.ts](../src/stores/auth.ts) +- [app.ts](../src/stores/app.ts) +- [menu.ts](../src/stores/menu.ts) +- [breadcrumbs.ts](../src/stores/breadcrumbs.ts) +- [favorites.ts](../src/stores/favorites.ts) +- [messages.ts](../src/stores/messages.ts) +- [snackbar.ts](../src/stores/snackbar.ts) +- [loginAnnouncements.ts](../src/stores/loginAnnouncements.ts) +- [students.ts](../src/stores/students.ts) +- [semesters.ts](../src/stores/semesters.ts) 責任: - 承接跨頁共享狀態 - 承接畫面快取與顯示狀態 - 作為 view 與 services 之間的狀態收斂點 +- `app.ts` 目前是空的 Pinia scaffold,尚未承擔實際 app state 規則: @@ -250,13 +252,13 @@ layout 不應承擔: 代表性檔案: -- [client.ts](/home/carl/git/skt-vuetify-templates/src/services/client.ts) -- [interceptors.ts](/home/carl/git/skt-vuetify-templates/src/services/interceptors.ts) -- [error.ts](/home/carl/git/skt-vuetify-templates/src/services/error.ts) -- [http-error.ts](/home/carl/git/skt-vuetify-templates/src/services/http-error.ts) -- [http-toast.ts](/home/carl/git/skt-vuetify-templates/src/services/http-toast.ts) -- [session.ts](/home/carl/git/skt-vuetify-templates/src/services/session.ts) -- [token.ts](/home/carl/git/skt-vuetify-templates/src/services/token.ts) +- [client.ts](../src/services/client.ts) +- [interceptors.ts](../src/services/interceptors.ts) +- [error.ts](../src/services/error.ts) +- [http-error.ts](../src/services/http-error.ts) +- [http-toast.ts](../src/services/http-toast.ts) +- [session.ts](../src/services/session.ts) +- [token.ts](../src/services/token.ts) - `services/modules/*` 責任: @@ -272,61 +274,6 @@ layout 不應承擔: ## 目前已落地的分層模式 -## Template Core 與 Demo 邊界 - -### Template Core - -以下檔案屬於 template core,負責 app shell、layout、route、plugin、theme、HTTP 基礎設施與全域狀態: - -- [main.ts](/home/carl/git/skt-vuetify-templates/src/main.ts) -- [App.vue](/home/carl/git/skt-vuetify-templates/src/App.vue) -- `src/router/index.ts` -- `src/router/guards.ts` -- `src/components/layouts/*` -- `src/views/Login.vue` -- `src/plugins/*` -- `src/styles/*` -- `src/services/client.ts` -- `src/services/interceptors.ts` -- `src/services/token.ts` -- `src/services/session.ts` -- `src/services/error.ts` -- `src/services/http-error.ts` -- `src/services/http-toast.ts` -- `src/stores/auth.ts` -- `src/stores/menu.ts` -- `src/stores/breadcrumbs.ts` -- `src/stores/favorites.ts` -- `src/stores/messages.ts` -- `src/stores/snackbar.ts` -- `src/composables/layout/*` - -一般功能開發優先不要修改 template core;只有需求明確要求改變框架行為時才調整。 - -`src/router/routes.ts` 是新增功能 route 的正式入口,可新增 route,但不要破壞既有 layout meta、auth meta 與 catch-all route 規則。 - -### Demo / Example - -以下檔案偏向示範資料與範例頁,可在正式專案中替換或刪除: - -- `src/views/Home.vue` -- `src/components/PageIndex.vue` -- `src/views/maint/*` -- `src/components/maint/*` -- `src/composables/maint/*` -- `src/components/PageMaint.vue` -- `src/stores/students.ts` -- `src/stores/semesters.ts` -- `src/views/FncPage.vue` -- `src/views/Settings.vue` -- `src/assets/logo.png` -- `src/assets/logo.svg` -- `src/assets/robot-svgrepo-com.svg` - -`maint` 是可參考的 demo feature,不是所有新專案都必須保留的核心功能。 - -移除 demo/example 時,要同步清理 routes、menu、language、store import、component import 與 assets。 - ### 模式 1:`view -> page component -> page family components` 已落地頁面: @@ -351,11 +298,11 @@ layout 不應承擔: 這一層目前是 maintenance 領域最清楚的結構: - `views/maint/*` 承接 route 與頁面流程協調 -- [PageMaint.vue](/home/carl/git/skt-vuetify-templates/src/components/PageMaint.vue) 承接維護頁共用頁面骨架 +- [PageMaint.vue](../src/components/PageMaint.vue) 承接維護頁共用頁面骨架 - `components/maint/*` 承接維護頁專用元件 - `composables/maint/*` 承接 CRUD 流程、表單狀態與 editable grid 狀態 -[EditableGrid.vue](/home/carl/git/skt-vuetify-templates/src/views/maint/EditableGrid.vue) 是目前最接近薄 view 的 maintenance 頁面。 +[EditableGrid.vue](../src/views/maint/EditableGrid.vue) 是目前最接近薄 view 的 maintenance 頁面。 ### 模式 3:`router meta -> App.vue -> layout` @@ -377,9 +324,9 @@ layout 不應承擔: 目前例子: -- [PageLogin.vue](/home/carl/git/skt-vuetify-templates/src/components/PageLogin.vue) -- [PageIndex.vue](/home/carl/git/skt-vuetify-templates/src/components/PageIndex.vue) -- [PageMaint.vue](/home/carl/git/skt-vuetify-templates/src/components/PageMaint.vue) +- [PageLogin.vue](../src/components/PageLogin.vue) +- [PageIndex.vue](../src/components/PageIndex.vue) +- [PageMaint.vue](../src/components/PageMaint.vue) ### 資料夾命名 @@ -397,30 +344,6 @@ layout 不應承擔: - 不要為了抽象而保留含糊的舊前綴 - 若元件只在 maint 領域使用,就留在 `components/maint` -## 目前仍待整理的區域 - -### 高優先度 - -- 繼續瘦身: - - [SingleRecord.vue](/home/carl/git/skt-vuetify-templates/src/views/maint/SingleRecord.vue) - - [MasterDetailA.vue](/home/carl/git/skt-vuetify-templates/src/views/maint/MasterDetailA.vue) - - [MasterDetailB.vue](/home/carl/git/skt-vuetify-templates/src/views/maint/MasterDetailB.vue) - - [MasterDetailC.vue](/home/carl/git/skt-vuetify-templates/src/views/maint/MasterDetailC.vue) - -原因: - -- 這些頁面仍保留較多頁面內資料轉換與事件協調 - -### 中優先度 - -- 檢查 `components/maint` 內是否仍有可再明確命名的舊名稱 -- 視 `PageMaint` 的後續演進,決定是否維持在 `components` 根目錄 - -### 中低優先度 - -- 持續檢查 `views` 是否有可再下放到 page component 的模板片段 -- 清理命名調整後留下的空資料夾或死連結 - ## 新增或修改檔案時的判斷準則 1. 這個檔案是否直接被 route 載入? diff --git a/docs/llm-development-guide.md b/docs/llm-development-guide.md index e5fc73b..c071615 100644 --- a/docs/llm-development-guide.md +++ b/docs/llm-development-guide.md @@ -63,6 +63,7 @@ router -> App.vue -> layout -> view -> component -> composable/store -> service - 一般被主 layout 包住的頁面:參考 `src/views/Home.vue`、`src/views/maint/EditableGrid.vue` - 登入相關 UI:參考 `src/components/PageLogin.vue` 與 `src/components/login/*` - 維護頁:參考 `src/views/maint/*`、`src/components/maint/*`、`src/composables/maint/*` +- 維護頁範本選擇:參考 `src/views/maint/README.md` - API 呼叫:參考 `src/services/modules/*` 與使用它們的 store/composable - 全域提示:參考 `src/stores/snackbar.ts` 與 `src/composables/useApiCall.ts` @@ -91,6 +92,7 @@ router -> App.vue -> layout -> view -> component -> composable/store -> service - `src/stores/favorites.ts` - `src/stores/messages.ts` - `src/stores/snackbar.ts` +- `src/stores/app.ts` - `src/composables/layout/*` 以下內容偏向 demo/example,建立正式專案時可依需求替換或刪除: diff --git a/src/components/PAGE_INDEX_SPEC.md b/src/components/PAGE_INDEX_SPEC.md new file mode 100644 index 0000000..f82bad6 --- /dev/null +++ b/src/components/PAGE_INDEX_SPEC.md @@ -0,0 +1,337 @@ +# PageIndex 規格表 + +來源元件:`src/components/PageIndex.vue` + +`PageIndex` 是首頁主畫面展示元件,負責組合歡迎區、最新消息、訊息中心入口、快速存取與最新消息 dialog。元件本身不直接呼叫後端 API;所有資料都透過 props 傳入,互動則透過 emit 交給外層 view 處理。 + +目前使用位置:`src/views/Home.vue` + +## 功能總覽 + +| 功能區塊 | 功能說明 | 主要輸入 | 主要輸出事件 | 需要後端 API | +|---|---|---|---|---| +| 歡迎區 | 顯示固定歡迎文字與操作提示。 | 無 | 無 | 否,目前為靜態文案。 | +| 最新消息列表 | 以 `v-data-iterator` 顯示消息清單,包含日期、月份、標題、NEW 標籤、摘要、單位與瀏覽次數。 | `newsItems` | `news(item)` | 一般需要;現況由 `Home.vue` 靜態陣列提供。 | +| 最新消息詳情 dialog | 顯示被選取消息的標題、日期、單位、瀏覽次數與內容。開關狀態由外層控制。 | `selectedNews`, `isNewsDialogOpen` | `update:isNewsDialogOpen` | 視需求;若列表已含完整內容可不另打詳情 API,若需完整內文或附件則建議補 API。 | +| 訊息中心入口 | 顯示訊息中心卡片與未讀文字,點擊後通知外層開啟訊息中心。 | 無 | `message-center` | 一般需要;現況文字 `12 筆未讀` 為靜態內容,點擊後只開啟共用 dialog。 | +| 快速存取 | 顯示常用操作入口卡片。 | `quickItems` | `quick(item)` | 視需求;現況由 `Home.vue` 靜態陣列提供,點擊只顯示 snackbar。 | + +## 現況資料來源 + +| 資料 | 目前來源 | 說明 | 後端需求 | +|---|---|---|---| +| `newsItems` | `src/views/Home.vue` 靜態陣列 | 包含 3 筆示意最新消息。 | 現況否;正式系統通常需要。 | +| `quickItems` | `src/views/Home.vue` 靜態陣列 | 包含線上加選、線上退選、成績查詢、個人課表、網路請假、場地借用。 | 視需求。 | +| `selectedNews` | `src/views/Home.vue` 本機 `ref` | 點擊消息後設定。 | 否。 | +| `isNewsDialogOpen` | `src/views/Home.vue` 本機 `ref` | 控制消息 dialog 開關。 | 否。 | +| 訊息中心開關 | `src/stores/messages.ts` | store 只管理 dialog 開關狀態,不包含訊息資料。 | 否,但訊息內容正式化時需要。 | +| 訊息中心未讀文字 | `PageIndex.vue` 靜態文案 | 固定顯示 `12 筆未讀`。 | 正式系統建議需要。 | + +## 一般建議補齊的 API 配合清單 + +這份清單是以一般後台首頁實作來看,列出 `PageIndex` 常見會需要後端配合的資料。現況沒有任何 API 直接供應 `PageIndex`;所有資料都是外層靜態資料或本機狀態。 + +| 類別 | 建議 API | 對應首頁功能 | 必要性 | 說明 | +|---|---|---|---|---| +| 最新消息列表 | `Announcement/GetLatest` 或 `News/GetLatest` | 最新消息列表 `newsItems` | 建議 | 回傳首頁要顯示的最新消息,通常需支援發布狀態、排序、置頂、有效日期與使用者可見範圍。 | +| 最新消息詳情 | `Announcement/GetDetail` 或 `News/GetDetail` | 最新消息詳情 dialog | 視需求 | 若列表只回摘要,點擊後應用 detail API 取得完整內容、附件、連結或已讀狀態。 | +| 消息瀏覽次數 | `Announcement/AddView` 或由 detail API 自動累計 | `views` 顯示 | 視需求 | 若瀏覽次數要準確,通常由後端統計;目前 `views` 是靜態文字。 | +| 訊息未讀數 | `Message/GetUnreadCount` 或 `Notification/GetUnreadCounts` | 訊息中心入口未讀文字 | 建議 | 首頁卡片與 layout toolbar badge 可共用同一份未讀數 API。 | +| 訊息清單 | `Message/GetMessages` | 訊息中心 dialog | 建議 | 點擊首頁訊息中心後應取得訊息列表;現況只開啟共用 dialog,訊息內容在 `App.vue` 仍是示意資料。 | +| 訊息已讀 | `Message/MarkAsRead`, `Message/MarkAllAsRead` | 訊息中心互動與未讀數歸零 | 視需求 | 若訊息中心有未讀狀態,通常需要已讀更新 API。 | +| 快速存取查詢 | `Shortcut/GetHomeShortcuts` 或 `Menu/GetQuickAccess` | 快速存取 `quickItems` | 視需求 | 若快速存取要依角色、權限或個人偏好變動,應由後端或既有選單資料推導。 | +| 快速存取維護 | `Shortcut/SaveHomeShortcuts`, `Shortcut/UpdateOrder` | 自訂首頁快捷入口 | 可選 | 只有開放使用者自訂首頁快捷時才需要。 | +| 快速存取導頁 | 可沿用 `Menu/GetMenu` 回傳路徑 | 快速存取點擊後導頁 | 建議 | 現況 `quickItems` 沒有 path,點擊只顯示 snackbar;正式系統應提供可導頁資訊。 | +| 首頁統計摘要 | `Dashboard/GetSummary` | 未來若加入待辦、申請、課程、公告統計卡片 | 可選 | 目前 `PageIndex` 沒有統計卡片;只有產品需要首頁 dashboard 時才補。 | + +## API 優先順序建議 + +| 優先順序 | API / 功能 | 建議理由 | +|---|---|---| +| 1 | `Announcement/GetLatest` | 最新消息是首頁主要內容,正式系統不應長期使用靜態資料。 | +| 2 | `Message/GetUnreadCount` 或共用 `Notification/GetUnreadCounts` | 首頁訊息中心入口目前有固定未讀數,需改為真實資料。 | +| 3 | `Message/GetMessages` | 點擊訊息中心後應顯示真實訊息清單。 | +| 4 | 快速存取 path / 導頁資料 | 現況快速存取無法真的導頁,只顯示 snackbar。 | +| 5 | `Announcement/GetDetail` | 若列表資料不足以顯示完整內容,再補詳情 API。 | +| 6 | 快速存取自訂與排序 API | 屬個人化體驗,不影響首頁基本可用性,可後續處理。 | + +## 建議 API 回傳格式 + +以下格式是給後端製作 API 時的建議契約。若沿用現有 service 包裝,前端實際讀取位置可能是 `res.data.data`;欄位命名可配合既有後端規範調整,但資料語意應保持一致。 + +### `Announcement/GetLatest` 或 `News/GetLatest` + +```json +{ + "success": true, + "message": "", + "data": { + "items": [ + { + "id": 1, + "title": "113學年度第2學期加退選開始", + "summary": "加退選時間為1月29日至2月9日止,請同學把握時間完成選課作業。", + "departmentId": "academic", + "departmentName": "教務處", + "publishedAt": "2026-01-29T09:00:00+08:00", + "views": 1234, + "isNew": true, + "isPinned": false + } + ], + "total": 1 + } +} +``` + +### `Announcement/GetDetail` 或 `News/GetDetail` + +```json +{ + "success": true, + "message": "", + "data": { + "id": 1, + "title": "113學年度第2學期加退選開始", + "content": "加退選時間為1月29日至2月9日止,請同學把握時間完成選課作業。", + "summary": "加退選時間為1月29日至2月9日止。", + "departmentId": "academic", + "departmentName": "教務處", + "publishedAt": "2026-01-29T09:00:00+08:00", + "views": 1235, + "isNew": true, + "attachments": [ + { + "id": "file-1", + "fileName": "加退選說明.pdf", + "url": "/service/files/file-1" + } + ], + "links": [ + { + "title": "前往選課系統", + "url": "/course-add" + } + ] + } +} +``` + +### `Announcement/AddView` + +```json +{ + "success": true, + "message": "", + "data": { + "id": 1, + "views": 1235 + } +} +``` + +### `Message/GetUnreadCount` + +```json +{ + "success": true, + "message": "", + "data": { + "unreadCount": 12 + } +} +``` + +### `Notification/GetUnreadCounts` + +```json +{ + "success": true, + "message": "", + "data": { + "notifications": 3, + "messages": 12 + } +} +``` + +### `Message/GetMessages` + +```json +{ + "success": true, + "message": "", + "data": { + "items": [ + { + "id": "msg-1", + "title": "系統維護提醒", + "summary": "系統將於週六凌晨維護。", + "sender": "資訊中心", + "sentAt": "2026-05-07T09:00:00+08:00", + "isRead": false, + "link": "/messages/msg-1" + } + ], + "total": 1, + "unreadCount": 1 + } +} +``` + +### `Message/MarkAsRead` + +```json +{ + "success": true, + "message": "已標記為已讀", + "data": { + "id": "msg-1", + "isRead": true, + "unreadCount": 0 + } +} +``` + +### `Message/MarkAllAsRead` + +```json +{ + "success": true, + "message": "已全部標記為已讀", + "data": { + "unreadCount": 0 + } +} +``` + +### `Shortcut/GetHomeShortcuts` 或 `Menu/GetQuickAccess` + +```json +{ + "success": true, + "message": "", + "data": [ + { + "id": "course-add", + "title": "線上加選", + "path": "/course-add", + "icon": "mdiPlus", + "sort": 1, + "source": "menu" + }, + { + "id": "score-query", + "title": "成績查詢", + "path": "/score-query", + "icon": "mdiChartBar", + "sort": 2, + "source": "menu" + } + ] +} +``` + +### `Shortcut/SaveHomeShortcuts` + +```json +{ + "success": true, + "message": "快捷已儲存", + "data": [ + { + "id": "course-add", + "title": "線上加選", + "path": "/course-add", + "icon": "mdiPlus", + "sort": 1 + } + ] +} +``` + +### `Shortcut/UpdateOrder` + +```json +{ + "success": true, + "message": "排序已更新", + "data": [ + { + "id": "course-add", + "sort": 1 + }, + { + "id": "score-query", + "sort": 2 + } + ] +} +``` + +### `Dashboard/GetSummary` + +```json +{ + "success": true, + "message": "", + "data": { + "todoCount": 2, + "pendingApplicationCount": 1, + "todayCourseCount": 4, + "unreadMessageCount": 12, + "latestAnnouncementCount": 3 + } +} +``` + +## 可維持前端處理的功能 + +| 功能 | 原因 | +|---|---| +| 最新消息 dialog 開關 | 純 UI 狀態,使用 `selectedNews` 與 `isNewsDialogOpen` 即可。 | +| 最新消息列表解包 `resolveNewsItem` | 只是處理 Vuetify `v-data-iterator` wrapper,不需後端配合。 | +| 歡迎區文案 | 若各角色顯示相同文字,可維持靜態。 | +| 快速存取卡片排版 | 展示方式屬前端 UI,不需後端參與。 | +| 點擊快速存取後的前端導頁 | 只要資料含 path,router push 可由前端處理。 | + +## Props 與狀態來源 + +| Prop / 狀態 | 用途 | 預設值或目前來源 | 後端需求 | +|---|---|---|---| +| `newsItems` | 最新消息列表資料。 | `Home.vue` 靜態陣列。 | 正式系統建議來自最新消息 API。 | +| `quickItems` | 快速存取卡片資料。 | `Home.vue` 靜態陣列。 | 視需求,可由選單、權限或快捷 API 提供。 | +| `selectedNews` | 最新消息 dialog 顯示的目前消息。 | `Home.vue` 點擊消息後設定。 | 否。 | +| `isNewsDialogOpen` | 最新消息 dialog 開關。 | `Home.vue` 本機 `ref`。 | 否。 | + +## 建議資料結構 + +### 最新消息 `NewsItem` + +| 欄位 | 型別 | 用途 | API 建議 | +|---|---|---|---| +| `id` | `number` | 列表 key 與詳情查詢識別。 | 後端提供。 | +| `date` | `string` | 日期 badge 的日期。 | 可由後端直接提供,或前端由發布日期格式化。 | +| `month` | `string` | 日期 badge 的月份。 | 可由後端直接提供,或前端由發布日期格式化。 | +| `title` | `string` | 消息標題。 | 後端提供。 | +| `desc` | `string` | 消息摘要或內容。 | 列表 API 可回摘要;詳情 API 可回完整內容。 | +| `dept` | `string` | 發布單位。 | 後端提供。 | +| `views` | `string` | 瀏覽次數顯示文字。 | 建議後端回 number,前端格式化。 | +| `isNew` | `boolean` | 是否顯示 NEW 標籤。 | 可由後端提供,或前端依發布日期推導。 | + +### 快速存取 `QuickItem` + +| 欄位 | 型別 | 用途 | API 建議 | +|---|---|---|---| +| `icon` | `string` | 卡片圖示文字。 | 若正式使用 MDI icon,建議改為 icon key 或路徑。 | +| `title` | `string` | 卡片標題。 | 後端或前端設定提供。 | +| `path` | `string` | 目前型別尚未定義;正式導頁建議補上。 | 建議由後端或 `Menu/GetMenu` 對應功能提供。 | + +## 事件契約 + +| 事件 | 觸發時機 | 外層目前處理 | +|---|---|---| +| `news(item)` | 點擊最新消息卡片。 | `Home.vue` 設定 `selectedNews` 並開啟 dialog。 | +| `message-center` | 點擊訊息中心卡片。 | `Home.vue` 呼叫 `messageStore.open()`。 | +| `quick(item)` | 點擊快速存取卡片。 | `Home.vue` 顯示 snackbar:`前往:${item.title}`。 | +| `update:isNewsDialogOpen(value)` | dialog 開關狀態變更或點擊關閉。 | `Home.vue` 寫回 `isNewsDialogOpen`。 | diff --git a/src/components/layouts/MAIN_LAYOUT_SPEC.md b/src/components/layouts/MAIN_LAYOUT_SPEC.md new file mode 100644 index 0000000..99925e7 --- /dev/null +++ b/src/components/layouts/MAIN_LAYOUT_SPEC.md @@ -0,0 +1,430 @@ +# MainLayout 規格表 + +來源元件:`src/components/layouts/MainLayout.vue` + +`MainLayout` 是預設後台版型的 app shell,負責組合側邊選單、頂部工具列、常用功能列、breadcrumb、內容區與輔助視窗。元件本身不直接呼叫後端 API;所有資料都透過 props 傳入,互動則透過 emit 交給外層處理。 + +## 功能總覽 + +| 功能區塊 | 功能說明 | 主要輸入 | 主要輸出事件 | 需要後端 API | +|---|---|---|---|---| +| 系統品牌區 | 顯示系統標題、副標題,或由 `title` slot 覆蓋。 | `systemTitle`, `systemSubtitle`, `title` slot | 無 | 否,目前為 props/default。 | +| 使用者資訊 | 顯示使用者頭像文字、姓名、角色。可用 feature toggle 關閉。 | `userProfile`, `features.showUserInfo` | 無 | 否,目前為 props/default。 | +| 側邊選單 | 桌面版顯示多層 drawer menu;支援展開群組、收合 rail、選單項目導頁。 | `menuItems`, `isRail`, `drawerConfig` | `select`, `toggle-sidebar`, `update:isRail` | 是,`menuItems` 目前由 `GetMenu` 取得後轉換。 | +| 行動版選單 | 行動版 drawer 使用階層式選單面板,點擊有子層項目會進入下一層,點擊葉節點會選取並關閉 drawer。 | `menuItems`, Vuetify display 狀態 | `select`, `toggle-sidebar` | 是,資料同 `menuItems`,目前由 `GetMenu` 取得後轉換。 | +| 行動版階層導覽 | 在 drawer 上方顯示「主選單」與目前進入的選單層級,可返回任一層。 | `menuItems` 衍生出的 `mobileMenuLevels` | 無 | 是,層級內容間接來自 `GetMenu`。 | +| 常用功能列 | 桌面版顯示常用功能 chip,可選取、移除,也可顯示新增按鈕。 | `favoriteItems`, `favoritesConfig`, `favoritesBarVisible`, `features.showFavorites` | `select`, `add-favorite`, `remove-favorite`, `update:favoritesBarVisible` | 否,目前不是由後端提供;`GetFavorite` 已有 service/store 方法但登入流程中未啟用。 | +| 行動版常用功能 | 行動版 drawer 可切換到常用功能面板並選取項目。 | `favoriteItems`, `features.showFavorites` | `select` | 否,目前不是由後端提供。 | +| 搜尋列 | 輸入關鍵字後,按 Enter 或按鈕才觸發搜尋;觸發後清空輸入。 | `searchConfig`, `features.showSearch` | `search` | 否,`MainLayout` 只送出關鍵字;目前外層用已載入的選單做前端比對。 | +| 工具列通知 | 顯示通知按鈕與 badge 數量。 | `toolbarActions.notificationsLabel`, `toolbarCounts.notifications` | `action('notifications')` | 否,目前只是按鈕與 props 數字。 | +| 工具列訊息 | 顯示訊息按鈕與 badge 數量。 | `toolbarActions.messagesLabel`, `toolbarCounts.messages` | `action('messages')` | 否,目前外層開啟示意訊息 dialog,沒有 API。 | +| 工具列設定 | 顯示設定 menu,可切換常用功能列與 breadcrumb 顯示狀態。 | `showFavoritesBar`, `breadcrumbBarVisible` | `update:favoritesBarVisible`, `update:breadcrumbBarVisible` | 否,屬本機 UI 狀態。 | +| 登出 | 顯示登出按鈕,點擊後交由外層處理。 | `logoutLabel` | `logout` | 否,layout 本身不呼叫 API。 | +| 主題切換 | feature 開啟時顯示主題切換按鈕,透過 `useThemeToggle` 切換 Vuetify theme。 | `features.showThemeToggle`, `themeToggleLabel` | `toggle-theme` | 否,本機 theme 狀態。 | +| Breadcrumb | 桌面版顯示目前頁面路徑;未傳入項目時顯示預設首頁。可插入 `breadcrumb-actions` slot。 | `breadcrumbItems`, `breadcrumbConfig`, `breadcrumbBarVisible`, `features.showBreadcrumb` | `update:breadcrumbBarVisible`, `update:favoritesBarVisible` | 否,目前由外層依路由與已載入選單推導。 | +| 內容區 | 以 `slot` 承載各頁面內容,並依 app bar 高度動態設定 `v-main` padding。 | default slot | 無 | 否。 | +| 操作說明浮窗 | 收到 `help` action 時顯示暫時說明內容,可關閉。 | `toolbarActions.helpLabel` | `action('help')` | 否,目前內容為靜態文字,且 help 按鈕在子元件中未顯示。 | + +## 後端 API 需求 + +| API | 目前狀態 | 呼叫位置 | 提供給 `MainLayout` 的資料 | Request | Response 對應 | +|---|---|---|---|---|---| +| `Menu/GetMenu` | 已啟用 | `src/views/Login.vue` 登入成功後呼叫 `menuStore.getMenu(authStore.user?.id ?? '')` | `menuItems` | `{ userID: string }` | `res.data.data` 存入 `menuStore.menu`,再由 `toLayoutMenuItems()` 轉成 layout 選單。 | +| `Menu/GetFavorite` | 未啟用 | service/store 已存在,但登入流程呼叫被註解 | 無 | `{ userID: string }` | 若未來啟用,可轉成 `favoriteItems`;目前不列為後端需求。 | + +## 一般建議補齊的 API 配合清單 + +這份清單是以一般後台系統實作來看,列出 `MainLayout` 常見會需要後端配合的資料。現況仍只有 `Menu/GetMenu` 已接上;其餘項目可依產品需求決定是否實作。 + +| 類別 | 建議 API | 對應 layout 功能 | 必要性 | 說明 | +|---|---|---|---|---| +| 選單與權限 | `Menu/GetMenu` | 側邊選單、行動版選單、前端選單搜尋、breadcrumb 推導 | 必要,已啟用 | 依使用者、角色、權限回傳可用功能。 | +| 使用者資訊 | `User/GetCurrentUser` 或 `User/GetProfile` | 使用者資訊區 `userProfile` | 建議 | 回傳姓名、角色、單位、頭像文字或頭像 URL。現況使用 default props。 | +| 常用功能查詢 | `Menu/GetFavorite` 或 `Favorite/GetFavorites` | 常用功能列、行動版常用功能 | 建議 | 若常用功能要跨裝置保存,就應由後端提供。現況 service/store 已有 `Menu/GetFavorite`,但登入流程未啟用。 | +| 常用功能維護 | `Favorite/AddFavorite`, `Favorite/RemoveFavorite`, `Favorite/UpdateFavoriteOrder` | 新增常用、移除常用、常用排序 | 建議 | 現況常用功能主要由前端本機 store 處理;若要保存到帳號需補 API。 | +| 未讀數量 | `Notification/GetUnreadCounts` | 通知 badge、訊息 badge `toolbarCounts` | 建議 | 可一次回傳 `{ notifications, messages }`,避免 layout 分別打多支 API。現況預設皆為 `0`。 | +| 通知清單 | `Notification/GetNotifications` | 通知按鈕點擊後的通知列表 | 視需求 | 目前 layout 只 emit `action('notifications')`,外層尚未實作通知 UI。 | +| 通知已讀 | `Notification/MarkAsRead`, `Notification/MarkAllAsRead` | 通知清單互動、badge 歸零 | 視需求 | 若有通知列表,通常需要搭配已讀狀態更新。 | +| 訊息清單 | `Message/GetMessages` | 訊息按鈕點擊後的訊息 dialog | 建議 | 現況 `App.vue` 使用示意資料,不含 API。 | +| 訊息已讀 | `Message/MarkAsRead`, `Message/MarkAllAsRead` | 訊息清單互動、badge 歸零 | 視需求 | 若訊息中心要顯示未讀數,通常需要已讀 API。 | +| 搜尋 | `Search/SearchMenu` 或 `Search/GlobalSearch` | 搜尋列 | 視需求 | 只搜尋目前已載入選單可維持前端搜尋;若要搜尋公告、頁面、業務資料或權限內功能,應補後端搜尋 API。 | +| 登出 | `Auth/Logout` 或 `Auth/RevokeToken` | 登出按鈕 | 視認證架構 | 若後端有 session、refresh token 或 token revoke 機制,需要呼叫後端;若只是清 local token,可維持前端處理。 | +| 使用者偏好 | `UserPreference/GetLayoutSettings`, `UserPreference/SaveLayoutSettings` | 側欄收合、常用列顯示、breadcrumb 顯示、主題 | 可選 | 目前可用 localStorage/store 處理;只有需要跨裝置同步時才需要 API。 | +| 操作說明 | `Help/GetPageHelp` 或 CMS API | 操作說明浮窗 | 可選 | 現況為靜態暫時文字,且 help 按鈕未顯示;若說明內容需依頁面、角色或版本管理才需要 API。 | + +## API 優先順序建議 + +| 優先順序 | API / 功能 | 建議理由 | +|---|---|---| +| 1 | `Menu/GetMenu` | layout 最核心資料,決定使用者可見功能與導頁入口。 | +| 2 | `User/GetCurrentUser` | 使用者資訊區不應長期使用假資料,且常被其他功能共用。 | +| 3 | `Favorite/GetFavorites` 與常用功能維護 API | 常用功能若要符合使用者帳號體驗,需要後端保存。 | +| 4 | `Notification/GetUnreadCounts` / `Message/GetMessages` | toolbar badge 與訊息中心目前是 demo 狀態,若要上線需補。 | +| 5 | `Search/GlobalSearch` | 只有當搜尋範圍超過目前選單時才需要。 | +| 6 | `UserPreference` 類 API | 屬體驗同步,不影響核心操作,可最後處理。 | + +## 建議 API 回傳格式 + +以下格式是給後端製作 API 時的建議契約。若沿用現有 service 包裝,前端實際讀取位置可能是 `res.data.data`;欄位命名可配合既有後端規範調整,但資料語意應保持一致。 + +### `Menu/GetMenu` + +```json +{ + "success": true, + "message": "", + "data": [ + { + "mdl_id": "student", + "mdl_name": "學生資訊", + "children": [ + { + "unt_id": "course", + "unt_name": "選課作業", + "children": [ + { + "fnc_id": "course-add", + "fnc_name": "線上加選" + } + ] + } + ] + } + ] +} +``` + +### `User/GetCurrentUser` 或 `User/GetProfile` + +```json +{ + "success": true, + "message": "", + "data": { + "id": "A123456789", + "name": "王小明", + "role": "資訊工程系 - 學生", + "avatarText": "王", + "departmentId": "CS", + "departmentName": "資訊工程系" + } +} +``` + +### `Menu/GetFavorite` 或 `Favorite/GetFavorites` + +```json +{ + "success": true, + "message": "", + "data": [ + { + "id": "fav-1", + "title": "線上加選", + "path": "/course-add", + "icon": "mdiPlus", + "sort": 1 + } + ] +} +``` + +### `Favorite/AddFavorite` + +```json +{ + "success": true, + "message": "新增成功", + "data": { + "id": "fav-1", + "title": "線上加選", + "path": "/course-add", + "icon": "mdiPlus", + "sort": 1 + } +} +``` + +### `Favorite/RemoveFavorite` + +```json +{ + "success": true, + "message": "移除成功", + "data": { + "id": "fav-1", + "path": "/course-add" + } +} +``` + +### `Favorite/UpdateFavoriteOrder` + +```json +{ + "success": true, + "message": "排序已更新", + "data": [ + { + "id": "fav-1", + "path": "/course-add", + "sort": 1 + }, + { + "id": "fav-2", + "path": "/score-query", + "sort": 2 + } + ] +} +``` + +### `Notification/GetUnreadCounts` + +```json +{ + "success": true, + "message": "", + "data": { + "notifications": 3, + "messages": 12 + } +} +``` + +### `Notification/GetNotifications` + +```json +{ + "success": true, + "message": "", + "data": { + "items": [ + { + "id": "notice-1", + "title": "系統維護提醒", + "content": "系統將於週六凌晨維護。", + "source": "資訊中心", + "publishedAt": "2026-05-07T09:00:00+08:00", + "isRead": false, + "link": "/announcements/notice-1" + } + ], + "total": 1, + "unreadCount": 1 + } +} +``` + +### `Notification/MarkAsRead` + +```json +{ + "success": true, + "message": "已標記為已讀", + "data": { + "id": "notice-1", + "isRead": true, + "unreadCount": 0 + } +} +``` + +### `Notification/MarkAllAsRead` + +```json +{ + "success": true, + "message": "已全部標記為已讀", + "data": { + "unreadCount": 0 + } +} +``` + +### `Message/GetMessages` + +```json +{ + "success": true, + "message": "", + "data": { + "items": [ + { + "id": "msg-1", + "title": "教務處公告", + "summary": "加退選時間即將開始。", + "sender": "教務處", + "sentAt": "2026-05-07T10:30:00+08:00", + "isRead": false, + "link": "/messages/msg-1" + } + ], + "total": 1, + "unreadCount": 1 + } +} +``` + +### `Message/MarkAsRead` + +```json +{ + "success": true, + "message": "已標記為已讀", + "data": { + "id": "msg-1", + "isRead": true, + "unreadCount": 0 + } +} +``` + +### `Message/MarkAllAsRead` + +```json +{ + "success": true, + "message": "已全部標記為已讀", + "data": { + "unreadCount": 0 + } +} +``` + +### `Search/SearchMenu` 或 `Search/GlobalSearch` + +```json +{ + "success": true, + "message": "", + "data": { + "items": [ + { + "id": "course-add", + "type": "menu", + "title": "線上加選", + "path": "/course-add", + "parents": ["學生資訊", "選課作業"], + "icon": "mdiPlus" + } + ], + "total": 1 + } +} +``` + +### `Auth/Logout` 或 `Auth/RevokeToken` + +```json +{ + "success": true, + "message": "登出成功", + "data": { + "revoked": true + } +} +``` + +### `UserPreference/GetLayoutSettings` + +```json +{ + "success": true, + "message": "", + "data": { + "isRail": false, + "favoritesBarVisible": true, + "breadcrumbBarVisible": true, + "themeName": "light" + } +} +``` + +### `UserPreference/SaveLayoutSettings` + +```json +{ + "success": true, + "message": "設定已儲存", + "data": { + "isRail": false, + "favoritesBarVisible": true, + "breadcrumbBarVisible": true, + "themeName": "light", + "updatedAt": "2026-05-07T10:30:00+08:00" + } +} +``` + +### `Help/GetPageHelp` + +```json +{ + "success": true, + "message": "", + "data": { + "pageKey": "home", + "title": "操作說明", + "content": "這裡顯示目前頁面的操作說明。", + "updatedAt": "2026-05-07T10:30:00+08:00" + } +} +``` + +## 可維持前端處理的功能 + +| 功能 | 原因 | +|---|---| +| Breadcrumb 顯示與路徑推導 | 可由目前 route、`menuItems`、`favoriteItems` 推導,不一定要後端提供。 | +| 側欄收合狀態 | 屬使用者介面偏好,localStorage 即可;除非要求跨裝置同步。 | +| 常用列顯示 / breadcrumb 顯示 | 屬畫面偏好,localStorage/store 即可。 | +| 主題切換 | 本機 theme 狀態即可;除非要求登入後跨裝置一致。 | +| 前端選單搜尋 | 若搜尋範圍只限已載入的 `menuItems`,不需要後端 API。 | + +## `GetMenu` 欄位轉換 + +| 後端節點層級 | 後端欄位 | Layout 欄位 | 說明 | +|---|---|---|---| +| 模組層 | `mdl_name` | `title` | 第一層選單標題。 | +| 模組層 | `children` | `subItems` | 第二層單元清單。 | +| 單元層 | `unt_name` | `title` | 第二層選單標題。 | +| 單元層 | `children` | `subItems` | 第三層功能清單。 | +| 功能層 | `fnc_name` | `title` | 葉節點功能名稱。 | +| 功能層 | `fnc_id` | `path` | 有值時轉成 `/${fnc_id}`,作為 router path。 | +| 模組層與單元層 | 無 | `navigable: false` | 群組節點預設不可導頁。 | + +## Props 與狀態來源 + +| Prop / 狀態 | 用途 | 預設值或目前來源 | 後端需求 | +|---|---|---|---| +| `menuItems` | 桌面與行動版主選單。 | `App.vue` 使用 `menuStore.menuItems` 加上固定選單合併後傳入。 | 是,來自 `GetMenu`。 | +| `favoriteItems` | 常用功能列與行動版常用面板。 | `App.vue` 合併 `menuStore.favoriteItems` 與 `favoritesStore.layoutItems`;目前 `GetFavorite` 未啟用。 | 否。 | +| `breadcrumbItems` | Breadcrumb 顯示。 | `breadcrumbStore` 依 route、`menuItems`、`favoriteItems` 推導。 | 否。 | +| `userProfile` | 使用者資訊區。 | `MainLayout` default props。 | 否。 | +| `toolbarCounts` | 通知、訊息 badge。 | `MainLayout` default props,預設皆為 `0`。 | 否。 | +| `searchConfig` | 搜尋 placeholder 與 label。 | `MainLayout` default props。 | 否。 | +| `toolbarActions` | 通知、訊息、說明、設定 label。 | `MainLayout` default props。 | 否。 | +| `favoritesConfig` | 常用列 label、新增按鈕 label、是否顯示新增。 | `MainLayout` default props。 | 否。 | +| `breadcrumbConfig` | 首頁 breadcrumb label、disabled、icon。 | `MainLayout` default props。 | 否。 | +| `features` | 控制主題切換、常用列、breadcrumb、搜尋、工具列、使用者資訊是否顯示。 | `MainLayout` default props。 | 否。 | +| `drawerConfig` | drawer 寬度與 rail 寬度。 | `MainLayout` default props。 | 否。 | +| `isRail` | 桌面側欄是否收合。 | `App.vue` 以 `v-model:is-rail` 綁定 `menuStore.isRail`,store 會寫入 localStorage。 | 否。 | +| `favoritesBarVisible` | 常用功能列是否顯示。 | `App.vue` 以 `v-model:favorites-bar-visible` 綁定 `favoritesStore`。 | 否。 | +| `breadcrumbBarVisible` | Breadcrumb 是否顯示。 | `App.vue` 以 `v-model:breadcrumb-bar-visible` 綁定 `favoritesStore`。 | 否。 | + +## 事件契約 + +| 事件 | 觸發時機 | 外層目前處理 | +|---|---|---| +| `select(item)` | 選取側邊選單、常用功能或搜尋結果延伸選取時。 | `App.vue` 呼叫 `router.push(item.path)`。 | +| `search(keyword)` | 搜尋列按 Enter 或搜尋按鈕。 | `App.vue` 以已載入的合併選單做前端搜尋並顯示 dialog。 | +| `action(type)` | 點擊通知、訊息、說明等工具列 action。 | `messages` 會開啟訊息 dialog;其他目前無處理。 | +| `logout` | 點擊登出按鈕。 | `App.vue` 清除 auth、tabs,導回 login。 | +| `toggle-sidebar(payload)` | 點擊 drawer 收合/展開按鈕。 | 目前外層未綁定。 | +| `toggle-theme(themeName)` | 切換主題成功。 | 目前外層未綁定。 | +| `add-favorite` | 點擊常用功能新增按鈕。 | 目前外層未綁定。 | +| `remove-favorite(item)` | 點擊常用 chip close。 | `App.vue` 從本機常用清單切換移除。 | +| `update:isRail(value)` | 受控模式下更新側欄 rail 狀態。 | `v-model:is-rail` 寫回 `menuStore.isRail`。 | +| `update:favoritesBarVisible(value)` | 更新常用列顯示狀態。 | `v-model:favorites-bar-visible` 寫回 `favoritesStore`。 | +| `update:breadcrumbBarVisible(value)` | 更新 breadcrumb 顯示狀態。 | `v-model:breadcrumb-bar-visible` 寫回 `favoritesStore`。 | diff --git a/src/views/maint/README.md b/src/views/maint/README.md new file mode 100644 index 0000000..11efea8 --- /dev/null +++ b/src/views/maint/README.md @@ -0,0 +1,91 @@ +# Maintenance View 複製指南 + +## 文件目的 + +`src/views/maint/*.vue` 是維護頁 demo/example。新增相似維護頁時,可以複製最接近的檔案再替換欄位、資料來源、文案、route 與 component import。 + +這份指南只說明應該從哪個 view 開始複製,不代表新功能一定要留在 `maint` 目錄。正式 feature 若有自己的 domain,優先建立 `src/views/`、`src/components/` 與 `src/composables/`。 + +## 共通規則 + +複製任何維護頁後,至少同步調整: + +- `src/router/routes.ts` 的 `path`、`name`、`component` 與 `meta.layout` +- 頁面標題、查詢欄位、表格欄位與 action 文案 +- form 型別、初始值、驗證規則與送出流程 +- store、service、composable 與 component import +- 對應的語系、menu、breadcrumb 或 favorites 資料 + +一般維護頁 route 使用 `meta: { layout: 'default' }`。不要在 view 內直接 import 或包住 `MainLayout.vue`。 + +## 檔案選擇 + +### `EditableGrid.vue` + +適合複製的情況: + +- 頁面主體是可直接編輯的表格 +- view 只需要當 route 入口 +- 主要 UI 與狀態已經能放在單一 feature component + +目前這個檔案只載入 `@/components/maint/EditableGrid.vue`。如果新頁面需要同樣的薄 view 形狀,優先複製這個檔案,再把 component import 換成新的 feature component。 + +### `SingleRecord.vue` + +適合複製的情況: + +- 主畫面是查詢條件加資料表 +- 使用者從資料表開啟單筆資料 dialog +- dialog 只維護一個主檔表單 +- 需要新增、檢視、修改、刪除、換筆、未儲存變更確認 + +這是單主檔維護頁範本。若新需求沒有明細檔、巢狀清單或 master-detail 操作,優先從這個檔案開始。 + +### `MasterDetailA.vue` + +適合複製的情況: + +- dialog 需要同時呈現主檔與明細檔 +- desktop 需要主檔 card 搭配側邊明細 panel +- 明細有獨立編輯狀態,使用者會先選一筆明細再編輯內容 +- mobile 需要在 master/detail panel 之間切換 + +這個版本的重點是 `DetailSidePanel` 與 `DetailNavigation`。如果明細內容比較像側邊抽屜或右側檢視編輯區,優先複製這個檔案。 + +### `MasterDetailB.vue` + +適合複製的情況: + +- dialog 需要主檔加多組可展開的明細群組 +- 明細適合用 collapse/accordion 方式呈現 +- desktop 希望主檔與明細在同一個 full-height card 內操作 +- mobile detail 需要全高明細 panel + +這個版本的重點是 `DetailCollapseGropus` 與 `DetailFullHeightPanel`。如果明細資料有群組、階層或大量列項,優先複製這個檔案。 + +### `MasterDetailC.vue` + +適合複製的情況: + +- dialog 需要主檔加簡化明細清單 +- 明細操作比 `MasterDetailB.vue` 輕量 +- desktop 主要是在主檔表單下方操作簡單清單 +- mobile 需要獨立明細 panel,但不需要 full-height collapse 群組 + +這個版本的重點是 `DetailSimpleList` 與 `CourseMobilePanel`。如果明細資料較少、互動較直接,優先複製這個檔案。 + +## 不建議直接複製的情況 + +- 只新增一般靜態或 dashboard 頁面:參考 `src/views/Home.vue` +- 只新增登入頁區塊:修改 `src/components/login/*` +- 只新增共用 dialog 或基礎元件:放到適合的 feature component,必要時才放 `src/components/base` +- 新頁面屬於正式 domain:複製後應改放到新 domain 目錄,不要把所有正式功能都塞進 `maint` + +## 複製後的瘦身方向 + +`SingleRecord.vue`、`MasterDetailA.vue`、`MasterDetailB.vue`、`MasterDetailC.vue` 仍偏厚。若複製後要長期維護,優先把下列內容拆出: + +- 大段表單欄位拆到 `src/components/` +- 可重用的表格 action、dialog content 或 detail panel 拆到 feature component +- 表單狀態、CRUD 流程、明細編輯流程拆到 `src/composables/` +- 跨頁共享資料或快取才放到 `src/stores`