From 9e8cf28d77cdad429fb1a9391f58ff4c581443ff Mon Sep 17 00:00:00 2001 From: skytek_xinliang Date: Fri, 22 May 2026 11:17:32 +0800 Subject: [PATCH] fix: docing --- docs/add-page-example.md | 136 ++++++++++---- docs/analyse-now.md | 82 +++++---- docs/frontend-layering.md | 365 +++++++++++++++++--------------------- src/views/GUIDE.md | 11 ++ 4 files changed, 329 insertions(+), 265 deletions(-) diff --git a/docs/add-page-example.md b/docs/add-page-example.md index d8bc437..58cf1d4 100644 --- a/docs/add-page-example.md +++ b/docs/add-page-example.md @@ -4,56 +4,99 @@ 範例功能:`reports` -## 1. 新增 route view +目前新增一般頁面的預設資料流: -```vue - - - - +```txt +router -> view -> page driver -> page component -> sections/items + ↓ + store/composable -> service ``` -view 的責任是頁面資料組裝與事件協調。畫面區塊交給 feature component。 +## 1. 新增 page driver -## 2. 新增 feature component +頁面資料、事件與暫時 UI state 優先放在 page driver,view 只負責掛載。 -```vue - - ``` -component 以 props 接收資料,以 emit 回報事件。不要在 component 裡直接處理 route 或底層 HTTP。 +若畫面是固定的「篩選條件 + 查詢按鈕 + 結果表格」,優先使用 `components/sections/SectionQueryPage.vue`。若是「表單欄位 + 送出/存檔按鈕」,優先使用 `components/sections/SectionFormPage.vue`。 -## 3. 加入 route +## 3. 新增 route view + +view 維持薄層,只呼叫 page driver 並掛載 page component。 + +```vue + + + + +``` + +## 4. 加入 route route 加在 `src/router/routes.ts` 的 `routes` 陣列中,並放在 `/:pathMatch(.*)*` catch-all route 前面。 @@ -84,9 +145,9 @@ route 加在 `src/router/routes.ts` 的 `routes` 陣列中,並放在 `/:pathMa - menu 來源目前由 `src/stores/menu.ts` 轉換後端選單資料。 - breadcrumb 會依 route path、menu/favorite items 與 fallback title 產生。 - 新功能若使用後端選單,優先調整後端選單資料或對應 API mock,不要把頁面專屬選單邏輯塞進 layout。 -- 若只是新增 route,通常不需要修改 `MainLayout.vue`。 +- 若只是新增 route,通常不需要修改 `MainLayout.vue` 或 `src/shell/*`。 -## 4. 需要 API 時新增 service module +## 5. 需要 API 時新增 service module ```ts // src/services/modules/reports.ts @@ -107,9 +168,11 @@ export const reportsApi = { service 只封裝 HTTP 細節,不持有 UI 狀態。 -`httpClient` 的 `baseURL` 來自 `VITE_API_BASE_URL`。template 預設值見 `.env.example`,通常使用 `/service/api` 搭配 Vite proxy。 +`httpClient` 的 `baseURL` 來自 `VITE_API_BASE_URL`,沒有設定時預設 `/service/api`。開發模式下,Vite proxy 會將 `/service/*` 轉送到 `VITE_PROXY_TARGET`。 -## 5. 需要共享狀態時新增 store +## 6. 需要共享狀態時新增 store + +只有跨頁共享、需要快取、或全域狀態才新增 store。單頁暫時狀態留在 view、component 或 composable。 ```ts // src/stores/reports.ts @@ -139,15 +202,18 @@ export const useReportsStore = defineStore('reports', () => { }) ``` -只有跨頁共享、需要快取、或全域狀態才新增 store。單頁暫時狀態留在 view、component 或 composable。 - -## 6. 驗證 +## 7. 驗證 至少執行: ```bash -pnpm type-check -pnpm build +pnpm -s type-check +``` + +需要確認建置產物時再執行: + +```bash +pnpm -s build ``` 若有 route、layout 或主要互動流程變更,再啟動 dev server 並用瀏覽器確認。 diff --git a/docs/analyse-now.md b/docs/analyse-now.md index 879c923..2310a07 100644 --- a/docs/analyse-now.md +++ b/docs/analyse-now.md @@ -1,47 +1,65 @@ ## 二、我們專案的現況診斷 -### 2.1 App.vue 過度臃腫(~590 行) +本文件是 `docs/architecture-strategy.md` 第二章的現況快照。分層細節以 `docs/architecture-strategy.md` 與 `src/**/GUIDE.md` 為準。 -| 職責 | 行數 | 應屬層級 | -|------|------|----------| -| Layout 切換 | ~20 | App Shell | -| Tabs 管理 | ~80 | Page Driver | -| Breadcrumb 組裝 | ~40 | Layout | -| Favorites 管理 | ~60 | Store | -| Search Dialog | ~80 | App Shell / Widget | -| Message Dialog | ~60 | App Shell / Widget | -| Snackbar | ~10 | Global Overlay | -| Logout / Force logout | ~30 | Auth Flow | -| HTTP Toast | ~20 | Service Layer | +### 2.1 App Shell 已拆分 -- **問題**:App.vue 同時承擔 App Shell、Page Driver、Global Widget、Auth Flow 四種責任。 -- **對比**:App Store 的 `App.svelte` 只有 161 行,只負責 `Navigation + PageResolver + Footer`。 +`App.vue` 目前只掛載 `src/shell/AppShell.vue`,不再承擔 layout props、tabs、搜尋 dialog、訊息 dialog 或 snackbar 的具體組裝。 -### 2.2 Views 過厚(SingleRecord.vue ~830 行) +目前責任分布: -- 混雜:表格呈現、搜尋表單、dialog 模板、表單狀態、CRUD 流程、驗證邏輯、分頁、snackbar。 -- **對比**:App Store 的 `ProductPage.svelte` 只有 77 行,只負責「把 page 轉成 DefaultPageRequirements + 一個 slot override」。 +| 職責 | 目前位置 | +|------|----------| +| Layout 切換 | `src/shell/AppShell.vue` | +| Tabs / keep-alive router-view | `src/shell/AppTabs.vue` | +| Breadcrumb / favorites / menu wiring | `src/composables/layout/useAppShell.ts` + `AppShell.vue` | +| Search Dialog / Message Dialog / Snackbar | `src/shell/GlobalOverlays.vue` | +| Logout / force logout | `src/composables/layout/useAppShell.ts` | +| HTTP Toast | `src/services/http-toast.ts` + `GlobalOverlays.vue` | -### 2.3 缺乏統一的頁面資料門面 +### 2.2 Views 已大幅變薄 -``` -現況: - view → store → service(直接鏈式呼叫) - view 自己管理 loading / error / dialog visible +維護頁與一般頁面目前多數已轉為 route-level wiring: -App Store: - UI → jet.dispatch(intent) → runtime → controller → page model - UI 只接收 page model,不管理載入狀態 +- `Home.vue`:呼叫 `useHomePage()`,掛載 `PageHome`。 +- `Settings.vue`:呼叫 `useSettingsPage()`,掛載 `PageSettings`。 +- `FncPage.vue`:呼叫 `useFunctionPage()`,掛載 `PageFunction`。 +- `views/maint/*`:呼叫對應 page driver,掛載 `components/pages/*Maintenance.vue`。 + +`SingleRecord.vue` 已不再直接管理 store mutation、大型 dialog 模板、表格分頁與 CRUD 細節;這些流程已移到 page driver、section component、item component 與 command composable。 + +`Login.vue` 是 template core 例外,仍負責登入頁組合、功能開關、小型提示 dialog 與登入流程協調。登入頁的 captcha、announcement、忘記密碼與記住帳號流程已透過 composable / props / emits 拆分,後續調整應維持該模式。 + +### 2.3 Page Driver / Command / Page Component 已落地 + +目前已存在的主要分層: + +```txt +view -> page driver -> page component -> section/item + ↓ + command/store/service ``` -### 2.4 Dialog 狀態與模板內嵌於 View +- `src/composables/page-drivers/*`:組裝 page model、route/query 轉換與頁面事件。 +- `src/composables/commands/useCrudCommands.ts`:承接維護頁 CRUD 命令流程。 +- `src/components/pages/*`:完整頁面的主畫面組裝。 +- `src/components/sections/*`:搜尋區、表格區、表單 dialog/panel、表單/查詢頁外殼。 +- `src/components/items/*`:欄位群組或單筆資料呈現。 -- `SingleRecord.vue` 內含 5 個 `ConfirmDialog` 實例 + 1 個大 form overlay。 -- 任何 dialog 更動都需要修改 view 檔案。 +### 2.4 Dialog 與區塊拆分狀態 -### 2.5 沒有容器/內容分離的 Section 層 +維護頁的大型 dialog 與表單欄位已從 view 抽出: -- 表格、表單、搜尋區塊都是直接寫在 view 或 page component 中。 -- 缺乏類似 `ShelfItemLayout` 的通用佈局抽象:「這一區是水平捲軸還是網格」應該由容器決定,裡面的內容元件不應該知道。 +- `SectionFormPanel.vue`:維護頁表單 overlay/dialog shell。 +- `MntDialogCard.vue`、`MntRecordNavToolbar.vue`:維護頁 dialog 內部骨架。 +- `ItemFormFieldGroup.vue`:表單欄位群組。 ---- +新增頁面時,若只是小型提示 dialog 且只屬於單一路由,可先留在 page driver / page component。若 dialog 包含大型表單、確認流程或可重用骨架,優先抽到 section 或 feature component。 + +### 2.5 仍需注意的邊界 + +- `src/models/page.ts` 目前主要服務 maintenance page model;部分頁面仍在各自 page driver 內定義局部 page model 型別。 +- `components/maint/*` 與 maintenance page components 屬於 demo / maintenance 領域,不應直接升格為全域 base 元件。 +- `src/components/base` 目前只放跨頁共用基礎元件,例如 `DraggableDialog`、`BaseFormTextField`、`BaseFormSelect`。 +- `src/stores/app.ts` 仍是 Pinia scaffold,尚未承擔實際 app state。 +- 一般功能需求不應修改 `App.vue`、`src/shell/*`、layout、router guard 或 HTTP core,除非需求明確牽涉這些 template core。 diff --git a/docs/frontend-layering.md b/docs/frontend-layering.md index 25574b7..67a4896 100644 --- a/docs/frontend-layering.md +++ b/docs/frontend-layering.md @@ -2,17 +2,17 @@ ## 目的 -這份文件只描述目前 repo 已經落地的前端分層與命名規則,讓後續新增檔案、搬移檔案、或重構時有一致判斷基準。 +這份文件描述目前 repo 已經落地的前端分層與命名規則,讓後續新增檔案、搬移檔案或重構時有一致判斷基準。 + +本文件是現況快照;新增功能與重構的細節規範以 `docs/architecture-strategy.md`、`docs/llm-development-guide.md` 與各層 `src/**/GUIDE.md` 為準。 目前專案的主要責任鏈如下: -- `router` 決定 route 與 layout meta -- `App.vue` 根據 route meta 組裝 app shell 與全域 UI -- `views` 承接路由入口與頁面資料協調 -- `components` 承接 layout、page component、domain component 與較細的 UI 區塊 -- `composables` 承接可重用流程與 UI state -- `stores` 承接跨頁狀態、快取與全域顯示狀態 -- `services` 承接 HTTP client、API 模組、token 與錯誤處理 +```txt +router -> App.vue -> AppShell -> layout -> view -> page component -> section -> item + ↓ + page driver / command composable -> store -> service +``` ## 目前目錄的責任邊界 @@ -26,93 +26,117 @@ 責任: -- 定義 route 與 route meta -- 指定頁面使用哪種 layout -- 串接導航守衛 +- 定義 route 與 route meta。 +- 指定頁面使用哪種 layout。 +- 串接導航守衛。 -目前 `meta.layout` 已是 app shell 切換的正式入口: +目前 `meta.layout` 是 app shell 切換的正式入口: -- `default` 走 [MainLayout.vue](../src/components/layouts/MainLayout.vue) -- `none` 走 [PlainLayout.vue](../src/components/layouts/PlainLayout.vue) +- `default` 走 [MainLayout.vue](../src/components/layouts/MainLayout.vue)。 +- `none` 走 [PlainLayout.vue](../src/components/layouts/PlainLayout.vue)。 -### `src/App.vue` +### `src/App.vue` 與 `src/shell` -[App.vue](../src/App.vue) 目前不是單純掛載入口,而是實際的應用組裝層。 +[App.vue](../src/App.vue) 目前只掛載 [AppShell.vue](../src/shell/AppShell.vue),不再直接承擔全域 UI 組裝。 -目前承擔的責任包含: +`src/shell` 是 App Shell 層: -- 根據 `route.meta.layout` 切換 layout -- 組裝 breadcrumb / favorites / menu 等 layout props -- 放置全域搜尋結果 dialog -- 放置全域訊息中心 dialog -- 放置全域 snackbar -- 串接 layout event 與路由跳轉 +- [AppShell.vue](../src/shell/AppShell.vue):layout 切換、layout props/events、breadcrumb actions、tabs router-view 與 `GlobalOverlays` 掛載。 +- [AppTabs.vue](../src/shell/AppTabs.vue):default layout 下的 tabs 與 keep-alive router-view 容器。 +- [GlobalOverlays.vue](../src/shell/GlobalOverlays.vue):全域 snackbar、搜尋 dialog、訊息 dialog。 判斷原則: -- 與整個 app shell 共享、且不屬於單一路由頁面的 UI,可留在 `App.vue` -- 只屬於單一路由頁面的對話框或互動,不應堆到 `App.vue` +- 與整個 app shell 共享、且不屬於單一路由頁面的 UI,可放在 `src/shell`。 +- 只屬於單一路由頁面的對話框或互動,不應放進 `src/shell`。 +- shell 狀態協調優先放在 `src/composables/layout/useAppShell.ts`。 ### `src/views` -`views` 目前整體方向是「路由入口 + 頁面資料協調 + 頁面事件協調」。 +`views` 是 route entry,方向是薄層:呼叫 page driver、掛載 page component、協調 route-level 事件。 -目前較薄的 view: +目前較典型的薄 view: - [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: - +- [FncPage.vue](../src/views/FncPage.vue) - [SingleRecord.vue](../src/views/maint/SingleRecord.vue) +- [EditableGrid.vue](../src/views/maint/EditableGrid.vue) - [MasterDetailA.vue](../src/views/maint/MasterDetailA.vue) - [MasterDetailB.vue](../src/views/maint/MasterDetailB.vue) - [MasterDetailC.vue](../src/views/maint/MasterDetailC.vue) +錯誤頁集中在 `src/views/errors`,通常使用 `meta.layout = 'none'`,並由 [ErrorShell.vue](../src/views/errors/ErrorShell.vue) 共用錯誤頁骨架。 + +[Login.vue](../src/views/Login.vue) 是 template core 例外:它仍負責登入頁組合、功能開關、小型提示 dialog 與登入流程協調。登入頁 UI 拆在 `components/login/*`,captcha 與 announcement 流程拆在頂層 login composable。 + `views` 應遵守的原則: -- 可以持有 route、store、頁面資料組裝、頁面事件協調 -- 可以管理只屬於該頁的 dialog 顯示狀態 -- 不應長期承擔大量可抽出的模板片段 -- 不應把可重用流程直接留在頁面內重複複製 +- 可以持有 route、page driver 掛載、頁面資料組裝與頁面事件協調。 +- 可以管理只屬於該頁的小型 dialog 顯示狀態。 +- 不應長期承擔大型表格、表單、dialog 模板或可重用流程。 +- 不應直接處理底層 HTTP 細節。 ### `src/components` -目前 `components` 已經分成幾種不同角色,不能再用單一規則描述。 +`components` 依角色分層,不再用單一規則描述。 -#### 1. 頁面型元件 +#### 1. Root page/template components -目前以下元件實際上扮演 page component: +目前仍放在 `src/components` 根目錄的頁面外殼: - [PageLogin.vue](../src/components/PageLogin.vue) - [PageIndex.vue](../src/components/PageIndex.vue) - [PageMaint.vue](../src/components/PageMaint.vue) -這些檔案的責任是: +這些是既有 template 頁面外殼或登入頁組裝元件。新增一般功能頁時,優先使用 `src/components/pages`。 -- 接收 view 組好的資料與事件 -- 組裝某個完整頁面的主畫面 -- 再往下使用較小的子元件或 domain component +#### 2. `components/pages` -命名規則: +`components/pages` 是完整頁面主畫面組裝層: -- 只要是 page component,檔名以 `Page` 為前綴 -- page component 可以放在 `components` 根目錄 -- 不要把 page component 丟進 `base` +- [PageHome.vue](../src/components/pages/PageHome.vue) +- [PageSettings.vue](../src/components/pages/PageSettings.vue) +- [PageFunction.vue](../src/components/pages/PageFunction.vue) +- [PageMaintenance.vue](../src/components/pages/PageMaintenance.vue) +- [PageEditableGridMaintenance.vue](../src/components/pages/PageEditableGridMaintenance.vue) +- [PageMasterDetailAMaintenance.vue](../src/components/pages/PageMasterDetailAMaintenance.vue) +- [PageMasterDetailBMaintenance.vue](../src/components/pages/PageMasterDetailBMaintenance.vue) +- [PageMasterDetailCMaintenance.vue](../src/components/pages/PageMasterDetailCMaintenance.vue) -#### 2. `components/login` +責任: -登入頁的較細 UI 區塊已集中到: +- 接收 view/page driver 組好的資料與事件。 +- 組裝完整頁面的主要 section 順序。 +- 再往下使用 sections、items、feature/domain components。 + +#### 3. `components/sections` + +`components/sections` 是頁面區塊容器: + +- [SectionSearchPanel.vue](../src/components/sections/SectionSearchPanel.vue) +- [SectionDataTable.vue](../src/components/sections/SectionDataTable.vue) +- [SectionFormPanel.vue](../src/components/sections/SectionFormPanel.vue) +- [SectionFormPage.vue](../src/components/sections/SectionFormPage.vue) +- [SectionQueryPage.vue](../src/components/sections/SectionQueryPage.vue) + +責任: + +- 決定區塊布局與區塊互動。 +- 以 props 接收資料,以 emit 回報事件。 +- 不知道 route,不直接呼叫 API。 + +#### 4. `components/items` + +`components/items` 是欄位群組或單筆資料呈現層: + +- [ItemFormFieldGroup.vue](../src/components/items/ItemFormFieldGroup.vue) + +item 不應知道自己被放在表格、grid、dialog 或頁面哪個位置。 + +#### 5. `components/login` + +登入頁的較細 UI 區塊集中在: - [CreateAccountLink.vue](../src/components/login/CreateAccountLink.vue) - [LoginAnnouncementBoard.vue](../src/components/login/LoginAnnouncementBoard.vue) @@ -123,56 +147,12 @@ - [LoginToolBar.vue](../src/components/login/LoginToolBar.vue) - [LoginVerify.vue](../src/components/login/LoginVerify.vue) -這一層的定位是: +這一層服務 `PageLogin`,不是全域 base library。 -- 服務 `PageLogin` -- 屬於 login 頁面家族 -- 不是全域 base library +#### 6. `components/maint` -#### 3. `components/base` +`components/maint` 是 maintenance demo / domain component 區域: -目前 `components/base` 只剩下: - -- [DraggableDialog.vue](../src/components/base/DraggableDialog.vue) - -目前判斷原則很直接: - -- `base` 只放真正可跨頁重用、且不屬於特定 domain 的元件 -- 若元件只服務單一頁面家族或單一 domain,優先放回對應資料夾 - -#### 4. `components/layouts` - -目前 layout 實作集中於: - -- [MainLayout.vue](../src/components/layouts/MainLayout.vue) -- [PlainLayout.vue](../src/components/layouts/PlainLayout.vue) -- `src/components/layouts/main-layout/*` - -其中 `main-layout/*` 是 `MainLayout` 底下拆出的骨架子元件: - -- [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 應只承擔: - -- app shell -- drawer / app bar / favorites / breadcrumb 等框架 UI -- 與 layout 視覺結構直接相關的互動 - -layout 不應承擔: - -- 頁面專屬業務流程 -- 特定 domain 的資料規則 - -#### 5. `components/maint` - -這個目錄目前是最接近 feature folder 的區域,放 maintenance 領域的 page component 與 domain component: - -- [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) @@ -180,46 +160,48 @@ layout 不應承擔: - [MntRecordNavToolbar.vue](../src/components/maint/MntRecordNavToolbar.vue) - `master-detail/*` -`master-detail/*` 目前屬於維護頁專用的較細組件群: +若只是維護頁專用子元件,不要搬到 `base`。 -- [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) +#### 7. `components/layouts` -結論: +layout 實作集中於: -- `components/maint` 主要扮演 maintenance domain component 層 -- `CommonConfirmDialog` 可以直接在 maintenance 頁或元件使用,不需要再包一層 CRUD dialog aggregator -- 若只是維護頁專用子元件,不要搬到 `base` +- [MainLayout.vue](../src/components/layouts/MainLayout.vue) +- [PlainLayout.vue](../src/components/layouts/PlainLayout.vue) +- `src/components/layouts/main-layout/*` + +layout 只承擔 app shell、drawer、app bar、favorites、breadcrumb 等框架 UI,不承擔頁面專屬業務流程。 + +#### 8. `components/base` + +`components/base` 放真正跨頁共用且不屬於特定 domain 的基礎元件: + +- [DraggableDialog.vue](../src/components/base/DraggableDialog.vue) +- [BaseFormTextField.vue](../src/components/base/BaseFormTextField.vue) +- [BaseFormSelect.vue](../src/components/base/BaseFormSelect.vue) + +只服務單一頁面家族或單一 domain 的元件不要放進 `base`。 ### `src/composables` -目前已明確分成兩組: +目前 composables 分成: -- `composables/layout/*` -- `composables/maint/*` +- `page-drivers/*`:頁面資料協調與 page model 組裝。 +- `commands/*`:命令式副作用流程,例如 create/edit/save/delete。 +- `layout/*`:AppShell / layout 狀態與事件協調。 +- `maint/*`:maintenance demo 的表單、CRUD、editable grid 狀態。 +- 頂層 login / utility composable:`useLoginCaptcha.ts`、`useLoginAnnouncements.ts`、`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` 的責任: - -- 放可重用流程 -- 放可測試的 UI state -- 放與模板結構耦合較低的狀態機 +- 放可重用流程。 +- 放可測試的 UI state。 +- 放與模板結構耦合較低的狀態機。 +- 不 import component 或 view。 ### `src/stores` -目前 store 已經是正式分層的一部分,而不只是暫時狀態容器。 +目前 store 是跨頁共享狀態、快取與全域顯示狀態的正式分層。 代表性檔案: @@ -230,25 +212,20 @@ layout 不應承擔: - [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 +- 承接跨頁共享狀態。 +- 承接畫面快取與全域顯示狀態。 +- 作為 view/page driver/composable 與 services 之間的狀態收斂點。 -規則: - -- store 檔案直接放在 `src/stores/*.ts` -- 不要建立 `src/stores/stores/*` 這類重複巢狀目錄 +`app.ts` 目前是空的 Pinia scaffold,尚未承擔實際 app state。 ### `src/services` -`services` 現在已經是一層明確的資料存取邊界,不應再被視為附屬工具資料夾。 +`services` 是 HTTP 與外部 API 邊界。 代表性檔案: @@ -263,101 +240,93 @@ layout 不應承擔: 責任: -- 提供 HTTP client -- 封裝 API 模組 -- 統一 token、session 與錯誤處理 +- 提供 `httpClient`。 +- 封裝 API 模組。 +- 統一 token、session 與錯誤處理。 規則: -- 元件不直接處理底層 HTTP 細節 -- 可共享的請求流程優先收斂到 store 或 composable,再由它們呼叫 service +- 元件不直接處理底層 HTTP 細節。 +- service module 不持有 UI 狀態。 +- 可共享的請求流程優先收斂到 store、page driver 或 composable,再由它們呼叫 service。 ## 目前已落地的分層模式 -### 模式 1:`view -> page component -> page family components` +### 模式 1:`view -> page driver -> page component` 已落地頁面: -- `Login` - `Home` - -目前的穩定模式是: - -- `view` 負責資料準備與事件協調 -- page component 負責頁面主畫面組裝 -- 較細的視覺區塊再拆到對應頁面家族資料夾,例如 `components/login/*` - -### 模式 2:`view -> page component / domain components + maint composables` - -已落地區域: - +- `Settings` +- `FncPage` - `views/maint/*` -- `components/maint/*` -- `composables/maint/*` -這一層目前是 maintenance 領域最清楚的結構: +穩定模式: -- `views/maint/*` 承接 route 與頁面流程協調 -- [PageMaint.vue](../src/components/PageMaint.vue) 承接維護頁共用頁面骨架 -- `components/maint/*` 承接維護頁專用元件 -- `composables/maint/*` 承接 CRUD 流程、表單狀態與 editable grid 狀態 +- view 負責掛載 page driver 與 page component。 +- page driver 負責 page model、事件與頁面狀態協調。 +- page component 負責頁面主畫面組裝。 -[EditableGrid.vue](../src/views/maint/EditableGrid.vue) 是目前最接近薄 view 的 maintenance 頁面。 +### 模式 2:`Login.vue -> PageLogin -> login components/composables` -### 模式 3:`router meta -> App.vue -> layout` +登入頁是 template core,功能開關集中在 `Login.vue`: + +- `withCaptcha` +- `withAnnouncement` +- `withForgotPassword` +- `withRememberAccount` + +資料流與 side effect 分別由 `useLoginCaptcha()`、`useLoginAnnouncements()`、`PageLogin` 與 `LoginForm` 承接。 + +### 模式 3:`router meta -> AppShell -> layout` 這一層已正式成立: -- route 決定 layout 類型 -- `App.vue` 決定套用哪個 shell -- layout 專注在骨架與共用框架 UI - -這代表 layout 的責任邊界不應再回頭混入頁面內部流程。 +- route 決定 layout 類型。 +- `AppShell` 決定套用哪個 shell layout。 +- layout 專注在骨架與共用框架 UI。 ## 命名規則 ### 頁面與 page component -- 直接被 route 載入的檔案放 `views` -- 負責完整頁面畫面組裝的元件,檔名用 `Page` 前綴 -- page component 不放進 `base` - -目前例子: - -- [PageLogin.vue](../src/components/PageLogin.vue) -- [PageIndex.vue](../src/components/PageIndex.vue) -- [PageMaint.vue](../src/components/PageMaint.vue) +- 直接被 route 載入的檔案放 `views`。 +- 負責完整頁面畫面組裝的元件,檔名用 `Page` 前綴。 +- page component 優先放 `components/pages`;既有 template 外殼可保留在 `components` 根目錄。 +- page component 不放進 `base`。 ### 資料夾命名 -- 多字資料夾一律使用 `kebab-case` -- 不新增 `snake_case` 或 `PascalCase` 資料夾 +- 多字資料夾一律使用 `kebab-case`。 +- 不新增 `snake_case` 或 `PascalCase` 資料夾。 目前例子: - `main-layout` - `master-detail` +- `page-drivers` -### domain component 命名 +### component 命名 -- 與特定領域強綁定的元件,優先用領域意圖命名 -- 不要為了抽象而保留含糊的舊前綴 -- 若元件只在 maint 領域使用,就留在 `components/maint` +- Page component:`PageXxx.vue` +- Section component:`SectionXxx.vue` +- Item component:`ItemXxx.vue` +- Base component:不使用 `Page` / `Section` / `Item` 前綴,直接以功能命名。 ## 新增或修改檔案時的判斷準則 1. 這個檔案是否直接被 route 載入? - - 是:優先放 `views` + - 是:優先放 `views`。 2. 這個檔案是否負責某個完整頁面的主畫面組裝? - - 是:用 `Page` 前綴,放 page component 層,不要塞進 `base` + - 是:用 `Page` 前綴,優先放 `components/pages`,不要塞進 `base`。 3. 這段重複的是模板還是流程? - - 模板:抽元件 - - 流程:抽 composable 或 store + - 模板:抽元件。 + - 流程:抽 composable、page driver、command 或 store。 4. 這個狀態是否跨頁共享,或需要快取 / 全域顯示控制? - - 是:優先考慮 store + - 是:優先考慮 store。 5. 這個邏輯是否在處理 API、token、session、錯誤正規化? - - 是:放 `services` -6. 這個元件是否只屬於單一 domain? - - 是:優先放到該 domain 目錄,例如 `components/maint` + - 是:放 `services`。 +6. 這個元件是否只屬於單一 domain 或單一頁面家族? + - 是:優先放到該 domain / feature 目錄,例如 `components/maint` 或 `components/login`。 7. 這個抽象是否真的降低重複與理解成本? - - 否:不要抽 + - 否:不要抽。 diff --git a/src/views/GUIDE.md b/src/views/GUIDE.md index 7a6080b..a9ae71f 100644 --- a/src/views/GUIDE.md +++ b/src/views/GUIDE.md @@ -26,6 +26,17 @@ const page = useReportsPage() ``` +## Login.vue 開關 + +`Login.vue` 是登入頁的組合層,登入頁功能開關集中在 view 內宣告,再透過 `PageLogin` / composable 往下傳遞,不在子元件各自決定是否啟用。 + +- `withCaptcha`:控制驗證碼 UI、captcha API 載入/刷新,以及登入 payload 是否帶 captcha 資料。關閉時不應發出 captcha API,也不應檢查或送出 captcha 欄位。 +- `withAnnouncement`:控制公告 UI、公告 mock data/composable 資料流與公告詳情互動。關閉時公告板、手機公告列與公告對話框資料來源都應停用。 +- `withForgotPassword`:控制忘記密碼連結與事件。關閉時 UI 不顯示,也不應觸發忘記密碼事件。 +- `withRememberAccount`:控制記住帳號 UI 與 localStorage 讀寫。關閉時不顯示 checkbox、不讀寫記住帳號 storage,送出資料固定視為未記住帳號。 + +新增登入頁選配功能時,優先維持同樣模式:view 宣告開關、composable 負責資料流與 side effect、page/form component 只依 props 呈現 UI 與發出事件。 + ## 子目錄 - `views/maint` 是 maintenance demo route entry。詳見 `src/views/maint/GUIDE.md`。