docs(pages): clarify page driver component boundaries

Add inline comments to page components documenting how page models,
v-model state, and emitted user intents flow through the page driver.

This clarifies that page components remain presentation-focused while
routing, dialog state, CRUD side effects, and command handling stay in
the page driver or related composables.docs(pages): clarify page driver component boundaries

Add inline comments to page components documenting how page models,
v-model state, and emitted user intents flow through the page driver.

This clarifies that page components remain presentation-focused while
routing, dialog state, CRUD side effects, and command handling stay in
the page driver or related composables.
This commit is contained in:
skytek_xinliang
2026-05-22 15:09:54 +08:00
parent 9e8cf28d77
commit cad44db4c7
15 changed files with 556 additions and 1 deletions
@@ -8,5 +8,6 @@ defineProps<{
</script>
<template>
<!-- Page component 接收 page model再把頁面標題轉交給既有 editable grid feature component -->
<EditableStudentGrid :title="page.title" />
</template>
+1
View File
@@ -7,6 +7,7 @@ defineProps<{
</script>
<template>
<!-- Page component 只呈現 page driver 解析後的功能代碼不直接讀 route params -->
<v-sheet height="100%" width="100%">
{{ page.fncId }}
</v-sheet>
+3
View File
@@ -7,16 +7,19 @@ defineProps<{
selectedNews: HomeNewsItem | null
}>()
// 首頁互動都往上 emit,讓 page driver 統一處理 dialog、訊息中心與 snackbar。
const emit = defineEmits<{
news: [item: HomeNewsItem]
'message-center': []
quick: [item: HomeQuickItem]
}>()
// 新聞 dialog 開關是 PageIndex 的雙向 UI 狀態,由 view/page driver 持有。
const isNewsDialogOpen = defineModel<boolean>('newsDialogOpen', { default: false })
</script>
<template>
<!-- PageHome 作為 page component page model 拆給既有 PageIndex 外殼與事件 -->
<PageIndex
v-model:is-news-dialog-open="isNewsDialogOpen"
:news-items="page.newsItems"
+3
View File
@@ -6,6 +6,7 @@ defineProps<{
page: MaintenancePageModel
}>()
// PageMaintenance 只轉發維護頁使用者意圖,CRUD 副作用交給 page driver / command composable。
const emit = defineEmits<{
(e: 'create'): void
(e: 'edit', record: unknown): void
@@ -14,10 +15,12 @@ const emit = defineEmits<{
(e: 'search', criteria: Record<string, unknown>): void
}>()
// 搜尋面板開關是頁面 UI 狀態,用 v-model 交回 view/page driver。
const searchPanelOpen = defineModel<boolean>('searchPanelOpen', { default: false })
</script>
<template>
<!-- PageMaint 提供維護頁外殼搜尋欄位與表格內容由 slot 交給各頁組合 -->
<PageMaint
:title="page.title"
:search-panel-open="searchPanelOpen"
@@ -73,6 +73,7 @@ defineProps<{
const form = defineModel<StudentFormState>('form', { required: true })
const detailFormModel = defineModel<SemesterRecord | null>('detailForm', { required: true })
// 主檔表單、子檔表單與搜尋條件都由 page driver 持有,Page component 只透過 v-model 回寫。
const search = defineModel<{
studentId: string
name: string
@@ -82,6 +83,7 @@ const search = defineModel<{
}>('search', { required: true })
const searchPanelOpen = defineModel<boolean>('searchPanelOpen', { required: true })
// 主從維護頁的 CRUD 與導覽意圖都往上 emit,讓 page driver / command composable 統一處理。
const emit = defineEmits<{
(e: 'add-semester'): void
(e: 'cancel-detail-edit'): void
@@ -122,11 +124,13 @@ const emit = defineEmits<{
</script>
<template>
<!-- PageMaintenance 提供維護頁外殼主從頁在 slots 中組合搜尋表格與子檔內容 -->
<PageMaintenance
v-model:search-panel-open="searchPanelOpen"
:page="page"
@create="emit('create')"
>
<!-- 搜尋欄位沿用 SectionSearchPanel搜尋條件透過 v-model 回到 page driver -->
<template #search-fields>
<SectionSearchPanel
v-model="search"
@@ -136,6 +140,7 @@ const emit = defineEmits<{
@reset="emit('reset-search')"
/>
</template>
<!-- 主檔表格沿用 SectionDataTable列操作只 emit 使用者意圖 -->
<template #table>
<SectionDataTable
:current-page="currentPage"
@@ -1,10 +1,12 @@
<template>
<!-- Page component 組合 PageMaint 外殼主檔表格子檔區與 dialog流程狀態集中在本頁 script -->
<page-maint
:search-panel-open="searchPanelOpen"
:title="page.title"
@create="openAddDialog"
@toggle-search="searchPanelOpen = !searchPanelOpen"
>
<!-- 搜尋欄位放在 PageMaint search-fields slot讓外殼固定欄位由頁面決定 -->
<template #search-fields>
<v-col cols="12" md="2">
<div id="search-student-id-label" class="text-body-2 text-medium-emphasis pl-2">學號</div>
@@ -78,6 +80,7 @@
<v-btn color="primary" disabled :prepend-icon="mdiMagnify" variant="tonal">查詢</v-btn>
</v-col>
</template>
<!-- table slot 放主檔表格與列操作操作事件再交給頁面流程函式處理 -->
<template #table>
<v-data-table
v-model:page="currentPage"
@@ -1,10 +1,12 @@
<template>
<!-- Page component 組合 PageMaint 外殼主檔表格子檔區與 dialog流程狀態集中在本頁 script -->
<page-maint
:search-panel-open="searchPanelOpen"
:title="page.title"
@create="openAddDialog"
@toggle-search="searchPanelOpen = !searchPanelOpen"
>
<!-- 搜尋欄位放在 PageMaint search-fields slot讓外殼固定欄位由頁面決定 -->
<template #search-fields>
<v-col cols="12" md="2">
<div id="search-student-id-label" class="text-body-2 text-medium-emphasis pl-2">學號</div>
@@ -78,6 +80,7 @@
<v-btn color="primary" disabled :prepend-icon="mdiMagnify" variant="tonal">查詢</v-btn>
</v-col>
</template>
<!-- table slot 放主檔表格與列操作操作事件再交給頁面流程函式處理 -->
<template #table>
<v-data-table
v-model:page="currentPage"
@@ -0,0 +1,69 @@
<script setup lang="ts">
import BaseFormSelect from '@/components/base/BaseFormSelect.vue'
import BaseFormTextField from '@/components/base/BaseFormTextField.vue'
import SectionQueryPage from '@/components/sections/SectionQueryPage.vue'
import type {
ReportFilters,
SectionsDemoPageModel,
} from '@/composables/page-drivers/useSectionsDemoPage'
defineProps<{
page: SectionsDemoPageModel
}>()
// Page component 只接收 page driver 組好的 page model;查詢條件用 v-model 回寫給 view/page driver。
const queryFilters = defineModel<ReportFilters>('queryFilters', { required: true })
// 使用者意圖往上 emit,由 page driver 決定查詢、返回等副作用。
const emit = defineEmits<{
(e: 'back'): void
(e: 'search'): void
}>()
</script>
<template>
<SectionQueryPage
back-label="回到列表"
title="查詢頁DEMO"
@back="emit('back')"
@search="emit('search')"
>
<!-- SectionQueryPage 決定查詢頁外殼欄位內容由 filters slot 交給頁面自行組合 -->
<template #filters>
<v-col cols="12" md="4">
<BaseFormTextField v-model="queryFilters.keyword" label="關鍵字" />
</v-col>
<v-col cols="12" md="4">
<BaseFormSelect v-model="queryFilters.owner" label="單位" :items="page.ownerOptions" />
</v-col>
</template>
<!-- results slot 放查詢結果資料仍由 page model 提供這裡只負責呈現 -->
<template #results>
<v-alert v-if="page.queryMessage" class="mb-3" type="success" variant="tonal">
{{ page.queryMessage }}
</v-alert>
<v-table density="compact">
<thead class="bg-primary">
<tr>
<th>名稱</th>
<th>單位</th>
<th>狀態</th>
<th>更新日</th>
</tr>
</thead>
<tbody>
<tr v-if="page.reports.length === 0">
<td class="text-center" colspan="4">尚無查詢結果</td>
</tr>
<tr v-for="row in page.reports" :key="row.id">
<td>{{ row.title }}</td>
<td>{{ row.owner }}</td>
<td>{{ row.status }}</td>
<td>{{ row.updatedAt }}</td>
</tr>
</tbody>
</v-table>
</template>
</SectionQueryPage>
</template>
+1
View File
@@ -7,5 +7,6 @@ defineProps<{
</script>
<template>
<!-- Page component 只呈現 page driver 組好的設定頁 model -->
<div>{{ page.title }}</div>
</template>
+1 -1
View File
@@ -18,7 +18,7 @@ const emit = defineEmits<{
<template>
<v-container fluid class="pt-2 px-1">
<v-card>
<v-card class="mb-2">
<v-card-title class="text-title-large bg-primary">{{ title }}</v-card-title>
<v-card-text class="pa-4">
<v-alert v-if="error" class="mb-4" type="error" variant="tonal">{{ error }}</v-alert>