docs: clarify optional page drivers in page guide
Update documentation to show that simple pages can define page models directly in views without creating a page driver. Adjust examples, section numbering, and naming guidance to better distinguish simple view state from reusable page-driver patterns.docs: clarify optional page drivers in page guide Update documentation to show that simple pages can define page models directly in views without creating a page driver. Adjust examples, section numbering, and naming guidance to better distinguish simple view state from reusable page-driver patterns.
This commit is contained in:
@@ -20,7 +20,7 @@
|
||||
- `src/services/modules/<domain>.ts` — service modules
|
||||
- Examples of correct vs. incorrect naming:
|
||||
- ❌ `PageStudentMaintenance.vue` → ✅ `PageMaintenance.vue`
|
||||
- ❌ `useStudentMaintenancePage.ts` → ✅ `useMaintenancePage.ts`
|
||||
- ❌ `useStudentMaintenancePage.ts` → ✅ `useSingleRecordMaintenancePage.ts`
|
||||
- ❌ `ItemStudentRow.vue` → ✅ `ItemDataRow.vue`
|
||||
- ❌ `useStudentCrudCommands.ts` → ✅ `useCrudCommands.ts`
|
||||
- ✅ `models/student.ts`, `stores/students.ts` — domain layer, specific names are correct
|
||||
|
||||
+21
-43
@@ -7,18 +7,22 @@
|
||||
目前新增一般頁面的預設資料流:
|
||||
|
||||
```txt
|
||||
router -> view -> page driver -> page component -> sections/items
|
||||
router -> view -> (page driver) -> page component -> sections/items
|
||||
↓
|
||||
store/composable -> service
|
||||
```
|
||||
|
||||
## 1. 新增 page driver
|
||||
Page driver 不是必備層:若頁面邏輯只有簡單的 `computed` page model(無搜尋、無 dialog、無複雜事件協調),直接在 view 裡寫即可,不需要建立 page driver。
|
||||
|
||||
頁面資料、事件與暫時 UI state 優先放在 page driver,view 只負責掛載。
|
||||
## 1. 新增 view(含 page model)
|
||||
|
||||
```ts
|
||||
// src/composables/page-drivers/useReportsPage.ts
|
||||
簡單頁面的 page model 直接在 view 裡用 `computed` 組裝,不需要額外建立 page driver。
|
||||
|
||||
```vue
|
||||
<!-- src/views/reports/Reports.vue -->
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import PageReports from '@/components/pages/PageReports.vue'
|
||||
import { useSnackbarStore } from '@/stores/snackbar'
|
||||
|
||||
export interface ReportSummary {
|
||||
@@ -27,21 +31,14 @@ export interface ReportSummary {
|
||||
owner: string
|
||||
}
|
||||
|
||||
export interface ReportsPageModel {
|
||||
title: string
|
||||
rows: ReportSummary[]
|
||||
}
|
||||
|
||||
const initialRows: ReportSummary[] = [
|
||||
{ id: 1, title: '學生統計', owner: '教務處' },
|
||||
{ id: 2, title: '課程統計', owner: '課務組' },
|
||||
]
|
||||
|
||||
export function useReportsPage() {
|
||||
const snackbar = useSnackbarStore()
|
||||
const rows = ref<ReportSummary[]>(initialRows)
|
||||
|
||||
const pageModel = computed<ReportsPageModel>(() => ({
|
||||
const pageModel = computed(() => ({
|
||||
title: '報表清單',
|
||||
rows: rows.value,
|
||||
}))
|
||||
@@ -49,15 +46,14 @@ export function useReportsPage() {
|
||||
function openReport(row: ReportSummary) {
|
||||
snackbar.show({ message: `開啟:${row.title}`, color: 'info' })
|
||||
}
|
||||
</script>
|
||||
|
||||
return {
|
||||
pageModel,
|
||||
openReport,
|
||||
}
|
||||
}
|
||||
<template>
|
||||
<PageReports :page="pageModel" @open="openReport" />
|
||||
</template>
|
||||
```
|
||||
|
||||
若資料來自 API,page driver 可呼叫 store 或 composable;底層 HTTP 細節仍放在 `services/modules/*`。
|
||||
若頁面需要協調多個 composable(搜尋、表單、CRUD flow、dialog 狀態),才建立 page driver。page driver 的慣例見 `src/composables/GUIDE.md`。
|
||||
|
||||
## 2. 新增 page component
|
||||
|
||||
@@ -66,10 +62,10 @@ export function useReportsPage() {
|
||||
```vue
|
||||
<!-- src/components/pages/PageReports.vue -->
|
||||
<script setup lang="ts">
|
||||
import type { ReportSummary, ReportsPageModel } from '@/composables/page-drivers/useReportsPage'
|
||||
import type { ReportSummary } from '@/views/reports/Reports.vue'
|
||||
|
||||
defineProps<{
|
||||
page: ReportsPageModel
|
||||
page: { title: string; rows: ReportSummary[] }
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -106,25 +102,7 @@ const emit = defineEmits<{
|
||||
|
||||
若畫面是固定的「篩選條件 + 查詢按鈕 + 結果表格」,優先使用 `components/sections/SectionQueryPage.vue`。若是「表單欄位 + 送出/存檔按鈕」,優先使用 `components/sections/SectionFormPage.vue`。
|
||||
|
||||
## 3. 新增 route view
|
||||
|
||||
view 維持薄層,只呼叫 page driver 並掛載 page component。
|
||||
|
||||
```vue
|
||||
<!-- src/views/reports/Reports.vue -->
|
||||
<script setup lang="ts">
|
||||
import PageReports from '@/components/pages/PageReports.vue'
|
||||
import { useReportsPage } from '@/composables/page-drivers/useReportsPage'
|
||||
|
||||
const page = useReportsPage()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PageReports :page="page.pageModel.value" @open="page.openReport" />
|
||||
</template>
|
||||
```
|
||||
|
||||
## 4. 加入 route
|
||||
## 3. 加入 route
|
||||
|
||||
route 加在 `src/router/routes.ts` 的 `routes` 陣列中,並放在 `/:pathMatch(.*)*` catch-all route 前面。
|
||||
|
||||
@@ -147,7 +125,7 @@ route 加在 `src/router/routes.ts` 的 `routes` 陣列中,並放在 `/:pathMa
|
||||
- 新功能若使用後端選單,優先調整後端選單資料或對應 API mock,不要把頁面專屬選單邏輯塞進 layout。
|
||||
- 若只是新增 route,通常不需要修改 `MainLayout.vue` 或 `src/shell/*`。
|
||||
|
||||
## 5. 需要 API 時新增 service module
|
||||
## 4. 需要 API 時新增 service module
|
||||
|
||||
```ts
|
||||
// src/services/modules/reports.ts
|
||||
@@ -170,7 +148,7 @@ service 只封裝 HTTP 細節,不持有 UI 狀態。
|
||||
|
||||
`httpClient` 的 `baseURL` 來自 `VITE_API_BASE_URL`,沒有設定時預設 `/service/api`。開發模式下,Vite proxy 會將 `/service/*` 轉送到 `VITE_PROXY_TARGET`。
|
||||
|
||||
## 6. 需要共享狀態時新增 store
|
||||
## 5. 需要共享狀態時新增 store
|
||||
|
||||
只有跨頁共享、需要快取、或全域狀態才新增 store。單頁暫時狀態留在 view、component 或 composable。
|
||||
|
||||
@@ -202,7 +180,7 @@ export const useReportsStore = defineStore('reports', () => {
|
||||
})
|
||||
```
|
||||
|
||||
## 7. 驗證
|
||||
## 6. 驗證
|
||||
|
||||
至少執行:
|
||||
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
## 二、我們專案的現況診斷
|
||||
|
||||
本文件是 `docs/architecture-strategy.md` 第二章的現況快照。分層細節以 `docs/architecture-strategy.md` 與 `src/**/GUIDE.md` 為準。
|
||||
|
||||
### 2.1 App Shell 已拆分
|
||||
|
||||
`App.vue` 目前只掛載 `src/shell/AppShell.vue`,不再承擔 layout props、tabs、搜尋 dialog、訊息 dialog 或 snackbar 的具體組裝。
|
||||
|
||||
目前責任分布:
|
||||
|
||||
| 職責 | 目前位置 |
|
||||
|------|----------|
|
||||
| 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.2 Views 已大幅變薄
|
||||
|
||||
維護頁與一般頁面目前多數已轉為 route-level wiring:
|
||||
|
||||
- `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
|
||||
```
|
||||
|
||||
- `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/*`:欄位群組或單筆資料呈現。
|
||||
|
||||
### 2.4 Dialog 與區塊拆分狀態
|
||||
|
||||
維護頁的大型 dialog 與表單欄位已從 view 抽出:
|
||||
|
||||
- `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。
|
||||
@@ -67,23 +67,15 @@ Read only when needed: [analyse now](./analyse-now.md)
|
||||
範例:
|
||||
|
||||
```ts
|
||||
// src/composables/usePageDriver.ts
|
||||
export function useMaintenancePage() {
|
||||
// views/maint/Example.vue — 簡單頁面直接在 view 組裝 page model
|
||||
const studentStore = useStudentStore()
|
||||
const { records, loading, error, load } = useCrudDriver({
|
||||
store: studentStore,
|
||||
loadAction: () => studentStore.fetchStudents(),
|
||||
})
|
||||
|
||||
const pageModel = computed(() => ({
|
||||
const pageModel = computed<MaintenancePageModel>(() => ({
|
||||
type: 'maintenance',
|
||||
title: '單筆資料維護',
|
||||
records: records.value,
|
||||
loading: loading.value,
|
||||
error: error.value,
|
||||
records: studentStore.students,
|
||||
loading: false,
|
||||
error: null,
|
||||
}))
|
||||
|
||||
return { pageModel, load }
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 查詢(Query)與命令(Command)分離
|
||||
@@ -164,8 +156,8 @@ src/
|
||||
│ └── DraggableDialog.vue
|
||||
│
|
||||
├── composables/
|
||||
│ ├── page-drivers/ ← 新增:頁面資料協調
|
||||
│ │ └── useMaintenancePage.ts
|
||||
│ ├── page-drivers/ ← 新增:頁面資料協調(僅複雜頁面需要)
|
||||
│ │ └── useSingleRecordMaintenancePage.ts
|
||||
│ ├── commands/ ← 新增:命令流程(對齊 Jet Action)
|
||||
│ │ └── useCrudCommands.ts
|
||||
│ ├── forms/ ← 維持/重組:表單狀態機
|
||||
@@ -201,14 +193,10 @@ src/
|
||||
```vue
|
||||
<!-- views/maint/SingleRecord.vue(優化後) -->
|
||||
<script setup lang="ts">
|
||||
import { useMaintenancePage } from '@/composables/page-drivers/useMaintenancePage'
|
||||
import PageMaintenance from '@/components/pages/PageMaintenance.vue'
|
||||
import { useSingleRecordMaintenancePage } from '@/composables/page-drivers/useSingleRecordMaintenancePage'
|
||||
|
||||
const { pageModel, load } = useMaintenancePage({
|
||||
title: '單筆資料維護',
|
||||
records: [],
|
||||
})
|
||||
load()
|
||||
const { pageModel, commands, formPanelProps, formPanelEvents } = useSingleRecordMaintenancePage()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -356,9 +344,10 @@ views/xxx.vue
|
||||
3. [x] 新增 `src/components/pages/`:建立第一個 `PageMaintenance.vue`(可從 `PageMaint.vue` 擴展)。
|
||||
- 定義 `MaintenancePageModel` props 與 `create/edit/view/delete/search` emits。
|
||||
- 使用 `PageMaint.vue` 作為佈局外殼,搜尋與表格區塊以 slot 開放,不綁定特定領域型別。
|
||||
4. [x] 新增 `src/composables/page-drivers/`:建立 `useMaintenancePage.ts`。
|
||||
- 透過 options 傳入 title 與 records,協調搜尋條件、分頁與 `pageModel`。
|
||||
4. [x] 新增 `src/composables/page-drivers/`:建立第一個 page driver 範例。
|
||||
- 協調搜尋條件、分頁與 `pageModel`。
|
||||
- 提供 `load()` 與 `resetSearch()` 供 Page Driver 呼叫。
|
||||
- 後續已刪除純包裝型 driver(如 `useMaintenancePage`)。僅當頁面需要協調多個 composable 時才建立 page driver。
|
||||
|
||||
### Phase 2:遷移最厚的 view(SingleRecord.vue) ✅ 已完成
|
||||
|
||||
@@ -382,6 +371,8 @@ views/xxx.vue
|
||||
|
||||
### Phase 3:推廣到所有 maintenance 頁面 ✅ 已完成
|
||||
|
||||
> 後續簡化時,B/C/EditableGrid 的薄 page driver 已 inline 回 view,只保留有真實複雜邏輯的 driver。
|
||||
|
||||
1. [x] `EditableGrid.vue` 依 Page Driver + Page Component 模式重構。
|
||||
- `src/views/maint/EditableGrid.vue` 縮減為 10 行 route-level wiring。
|
||||
- 新增 `src/composables/page-drivers/useEditableGridMaintenancePage.ts`。
|
||||
@@ -399,6 +390,8 @@ views/xxx.vue
|
||||
|
||||
### Phase 4:非 maintenance 頁面統一 ✅ 已完成
|
||||
|
||||
> 後續簡化時,Settings/FncPage 的薄 page driver 已 inline 回 view,型別移至 page component 自身。
|
||||
|
||||
1. [x] `Home.vue`、`Settings.vue`、`FncPage.vue` 套用 Page Driver + Page Component 模式。
|
||||
- `src/views/Home.vue` 縮減為 17 行,新增 `src/components/pages/PageHome.vue` 與 `src/composables/page-drivers/useHomePage.ts`。
|
||||
- `src/views/Settings.vue` 縮減為 10 行,新增 `src/components/pages/PageSettings.vue` 與 `src/composables/page-drivers/useSettingsPage.ts`。
|
||||
@@ -421,7 +414,7 @@ views/xxx.vue
|
||||
| Item / Atom | `src/components/items/` | `ItemDataRow.vue`、`ItemFormField.vue` |
|
||||
| Layout | `src/components/layouts/` | `MainLayout.vue`(維持) |
|
||||
| Base | `src/components/base/` | `DraggableDialog.vue`(維持) |
|
||||
| Page Driver Composable | `src/composables/page-drivers/` | `useMaintenancePage.ts` |
|
||||
| Page Driver Composable | `src/composables/page-drivers/` | `useSingleRecordMaintenancePage.ts` |
|
||||
| Command Composable | `src/composables/commands/` | `useCrudCommands.ts` |
|
||||
| Form Composable | `src/composables/forms/` | `useForm.ts` |
|
||||
| Domain Store | `src/stores/` | `students.ts`(維持) |
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
## 一、Apple App Store 專案的核心架構特徵
|
||||
|
||||
### 1.1 單一業務邏輯門面(Jet Facade)
|
||||
|
||||
```
|
||||
browser.ts → bootstrap → Jet.load → runtime + objectGraph
|
||||
↓
|
||||
UI 層僅透過 jet.dispatch(intent) / jet.perform(action) 溝通
|
||||
```
|
||||
|
||||
- **Jet** 封裝所有業務邏輯:路由、資料取得、動作分發、metrics。
|
||||
- UI 層**不直接**呼叫 API、不直接操作 storage、不直接操作 history。
|
||||
- 所有外部依賴(fetch、storage、locale、user)統一注入 `Dependencies`,再組裝成 `ObjectGraph`。
|
||||
|
||||
### 1.2 Intent / Action 分離(查詢與命令)
|
||||
|
||||
| 類型 | 職責 | 回傳值 | 例子 |
|
||||
|------|------|--------|------|
|
||||
| **Intent** | 取得頁面資料(Query) | `Promise<Page>` | `RouteUrlIntent` → 回傳 `ProductPage` |
|
||||
| **Action** | 執行副作用(Command) | `'performed' \| 'unsupported'` | `FlowAction` → 導航到新頁面 |
|
||||
|
||||
- `FlowAction` 是主要導航機制:內含 `destination Intent` + `pageUrl` + `presentationContext`。
|
||||
- Action handler 註冊採用**型別註冊制**:`jet.onAction('flowAction', handler)`。
|
||||
|
||||
### 1.3 Page Model 驅動 UI(資料驅動)
|
||||
|
||||
```ts
|
||||
// App.svelte 的介面極簡
|
||||
export let page: Promise<Page> | Page
|
||||
```
|
||||
|
||||
- 整個應用由單一 `page` prop 驅動。
|
||||
- `PageResolver` 處理 `Promise<Page>` 的 loading / error 狀態。
|
||||
- `Page.svelte` 用 type guard 分發到對應的 page component:`isProductPage(page)` → `<ProductPage>`。
|
||||
- **Page 是 union type**,不是 route-based 的硬編碼映射。
|
||||
|
||||
### 1.4 Shelf / Item 分層(容器與內容分離)
|
||||
|
||||
```
|
||||
Page (TodayPage / ProductPage / ...)
|
||||
└── Shelf[] (水平捲軸 / 網格)
|
||||
└── ShelfItemLayout (佈局抽象:HorizontalShelf or Grid)
|
||||
└── Item (BrickItem / LargeLockupItem / ...)
|
||||
```
|
||||
|
||||
- **Shelf** = 容器邏輯:決定是水平捲軸還是網格、rowsPerColumn、邊框。
|
||||
- **ShelfItemLayout** = 佈局中介:根據 `isHorizontal` 選擇 `HorizontalShelf` 或 `Grid`。
|
||||
- **Item** = 純粹內容渲染:只關心單一資料單位的呈現,不知道自己是水平還是網格。
|
||||
- **FallbackShelf** = 優雅的降級策略:遇到未實作的 shelf 類型顯示 placeholder,不 crash。
|
||||
|
||||
### 1.5 Svelte Context 作為跨層依賴注入
|
||||
|
||||
```ts
|
||||
// bootstrap.ts
|
||||
context.set('jet', jet)
|
||||
context.set('i18n', i18nStore)
|
||||
|
||||
// 深層元件
|
||||
const jet = getJet() // 從 Svelte Context 取得
|
||||
const i18n = getI18n() // 從 Svelte Context 取得
|
||||
```
|
||||
|
||||
- 避免 props drilling:Jet、i18n、accessibility layout、today-card layout 都透過 context 傳遞。
|
||||
- Context 在啟動時注入,生命周期與應用一致,不是用來傳遞 UI 狀態的。
|
||||
|
||||
### 1.6 命令式外殼 + 聲明式 UI
|
||||
|
||||
```ts
|
||||
// browser.ts(命令式啟動層)
|
||||
const app = new App({ target: container, context, hydrate: true })
|
||||
registerActionHandlers({
|
||||
jet,
|
||||
updateApp: (props) => app.$set(props), // 橋接命令式 → 聲明式
|
||||
})
|
||||
```
|
||||
|
||||
- 導航、歷史管理、scroll 復原由命令式的 action handler 處理。
|
||||
- UI 渲染完全聲明式,只接收 `page` 與 `isFirstPage` 兩個 prop。
|
||||
|
||||
---
|
||||
|
||||
+1
-1
@@ -76,7 +76,7 @@ router -> AppShell -> layout -> view(Page Driver) -> Page Component -> Section -
|
||||
|
||||
1. 新增或修改 `views/*` route entry。
|
||||
2. 若有完整頁面 UI,新增 `components/pages/PageXxx.vue`。
|
||||
3. 若有頁面資料協調或 route param 轉換,新增 `composables/page-drivers/useXxxPage.ts`。
|
||||
3. 若有複雜的資料協調(多 composable、搜尋狀態、CRUD flow、dialog 狀態),新增 `composables/page-drivers/useXxxPage.ts`。簡單頁面直接在 view 用 `computed` 組裝 page model。
|
||||
4. 若畫面有獨立區塊,拆到 `components/sections/*`。
|
||||
5. 若區塊內有欄位群組或單筆資料呈現,拆到 `components/items/*`。
|
||||
6. 跨頁共享狀態才新增或修改 `stores/*`。
|
||||
|
||||
+1
-1
@@ -23,4 +23,4 @@
|
||||
- 各頁面的 specific model 擴展 `BasePageModel`(例如 `MaintenancePageModel` 加 `type`、`records`)。
|
||||
- `PageModel` union 供 page component props 型別使用。
|
||||
|
||||
新增頁面類型時,先擴充 `PageModel` union 再新增對應的 page driver。
|
||||
新增頁面類型時,先擴充 `PageModel` union。若頁面需要協調多個 composable(搜尋、表單、CRUD flow、dialog 狀態),再建立對應的 page driver;簡單頁面直接在 view 用 `computed` 組裝 page model 即可。
|
||||
|
||||
+4
-2
@@ -18,14 +18,16 @@
|
||||
import PageReports from '@/components/pages/PageReports.vue'
|
||||
import { useReportsPage } from '@/composables/page-drivers/useReportsPage'
|
||||
|
||||
const page = useReportsPage()
|
||||
const { pageModel } = useReportsPage()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PageReports :page="page.pageModel.value" />
|
||||
<PageReports :page="pageModel" />
|
||||
</template>
|
||||
```
|
||||
|
||||
若頁面只是簡單的 `computed` 組裝(無搜尋、無 dialog、無複雜事件),直接在 view 寫 page model,不需要建立 page driver。以 destructure 方式取用 composable 回傳值,模板不寫 `.value`。
|
||||
|
||||
## Login.vue 開關
|
||||
|
||||
`Login.vue` 是登入頁的組合層,登入頁功能開關集中在 view 內宣告,再透過 `PageLogin` / composable 往下傳遞,不在子元件各自決定是否啟用。
|
||||
|
||||
Reference in New Issue
Block a user