docs: document visual cues for section page selection

Add guidance for choosing SectionFormPage and SectionQueryPage based on
visible UI patterns in prototypes or screenshots. Document required visual
feature descriptions for new page/section components and expand section
component usage notes with query page guidance.docs: document visual cues for section page selection

Add guidance for choosing SectionFormPage and SectionQueryPage based on
visible UI patterns in prototypes or screenshots. Document required visual
feature descriptions for new page/section components and expand section
component usage notes with query page guidance.
This commit is contained in:
skytek_xinliang
2026-05-20 17:41:19 +08:00
parent 8af82f5900
commit ea1aec67dc
4 changed files with 202 additions and 0 deletions
+1
View File
@@ -30,6 +30,7 @@
- `src/**/GUIDE.md` 只保留該層/目錄的**約束、慣例與索引**,不要塞入詳細 API 文件。 - `src/**/GUIDE.md` 只保留該層/目錄的**約束、慣例與索引**,不要塞入詳細 API 文件。
- 當新增 pattern、目錄或慣例影響層邊界時,建立或更新對應的 `src/**/GUIDE.md`,並確保 `docs/llm-development-guide.md` 將其列入索引。 - 當新增 pattern、目錄或慣例影響層邊界時,建立或更新對應的 `src/**/GUIDE.md`,並確保 `docs/llm-development-guide.md` 將其列入索引。
- 元件的 Props/Slots/Emits 詳細說明放在各子目錄的 `GUIDE.md`(如 `src/components/base/GUIDE.md``src/components/sections/GUIDE.md`),不要放在上層 `src/components/GUIDE.md` - 元件的 Props/Slots/Emits 詳細說明放在各子目錄的 `GUIDE.md`(如 `src/components/base/GUIDE.md``src/components/sections/GUIDE.md`),不要放在上層 `src/components/GUIDE.md`
- **新增 page/section 元件時,必須一併描述「視覺特徵」**:說明畫面上出現哪些元素(如標題卡片、按鈕類型、表格位置),讓 LLM 能從截圖或設計稿判斷該用哪個元件。視覺特徵寫在對應子目錄 `GUIDE.md` 的「視覺特徵」小節。
## Stack ## Stack
- Framework: Vue 3 + Vite - Framework: Vue 3 + Vite
+15
View File
@@ -58,6 +58,8 @@
- 新 route:讀 `src/router/GUIDE.md` - 新 route:讀 `src/router/GUIDE.md`
- 一般頁面:讀 `src/views/GUIDE.md``src/components/GUIDE.md``src/composables/GUIDE.md` - 一般頁面:讀 `src/views/GUIDE.md``src/components/GUIDE.md``src/composables/GUIDE.md`
- 維護頁:讀 `src/views/maint/GUIDE.md` - 維護頁:讀 `src/views/maint/GUIDE.md`
- 查詢/列表頁(篩選 + 表格):讀 `src/components/sections/GUIDE.md``SectionQueryPage`)。
- 申請/填寫頁(送出按鈕):讀 `src/components/sections/GUIDE.md``SectionFormPage`)。
- layout / AppShell / tabs / global overlay:讀 `src/shell/GUIDE.md``src/components/layouts/GUIDE.md` - layout / AppShell / tabs / global overlay:讀 `src/shell/GUIDE.md``src/components/layouts/GUIDE.md`
- API 串接:讀 `src/services/GUIDE.md` - API 串接:讀 `src/services/GUIDE.md`
- 跨頁共享狀態:讀 `src/stores/GUIDE.md` - 跨頁共享狀態:讀 `src/stores/GUIDE.md`
@@ -77,6 +79,19 @@
- 是否應定義新的 model 型別(`src/models/`)。 - 是否應定義新的 model 型別(`src/models/`)。
- 是否需要更新語系、menu、breadcrumb、favorites。 - 是否需要更新語系、menu、breadcrumb、favorites。
## 從視覺特徵選擇 section 元件
當收到 prototype 截圖或設計稿時,依畫面特徵選擇 section 外殼:
| 特徵 | 選擇 |
|------|------|
| 有「送出/存檔」按鈕,且畫面為填寫表單(欄位 + 配合事項 + 動作按鈕) | `SectionFormPage` |
| 有「查詢」按鈕,且畫面為篩選條件 + 結果表格/列表 | `SectionQueryPage` |
| 純粹表格列表(無送出/查詢按鈕,只有 CRUD 操作) | 不用 section 外殼,直接組合 `v-data-table` |
| 混合結構(有查詢也有表單填寫) | 評估是否拆成兩頁;若必須同頁,不用通用外殼 |
判斷順序:先看有無「送出/存檔」→ 再看有無「查詢」→ 其餘視為一般列表頁。
## 完成前驗證 ## 完成前驗證
- Vue / TypeScript 結構有變更:`pnpm -s type-check` - Vue / TypeScript 結構有變更:`pnpm -s type-check`
+140
View File
@@ -20,6 +20,14 @@
不適用情境:純粹列表/查詢頁面(無送出按鈕)、結構差異過大的頁面。 不適用情境:純粹列表/查詢頁面(無送出按鈕)、結構差異過大的頁面。
### 視覺特徵
- 頂部標題卡片(`bg-primary`
- 中間為表單欄位區(`v-text-field`/`v-select`
- 可能有子區段卡片(明細表格)
- 底部有「配合事項」提示區(`bg-yellow-lighten-5`
- 最底部為動作按鈕列(存檔 + 清除 + 返回)
### Props ### Props
| Prop | 型別 | 預設 | 說明 | | Prop | 型別 | 預設 | 說明 |
@@ -82,3 +90,135 @@
</template> </template>
</SectionFormPage> </SectionFormPage>
``` ```
## SectionQueryPage
查詢/列表頁面通用外殼。包含標題卡片、篩選條件區、查詢按鈕、結果表格區與返回按鈕。
### 使用時機
- 頁面具有**篩選條件** + **查詢按鈕** + **結果表格**的固定結構
- 例如:單筆查詢、列表查詢、報表查詢等頁面
不適用情境:
- 純粹 CRUD 維護頁面(含新增/編輯/刪除操作)→ 用 `SectionFormPage`
- 頁面結構差異過大(如沒有篩選條件或沒有結果表格)
### 視覺特徵
- 頂部標題卡片(`bg-primary`
- 標題下方為篩選條件區(`v-text-field`/`v-select` + 查詢按鈕)
- 下方為結果區:可能是單一表格,也可能是多張獨立卡片表格
- 最底部為返回按鈕
-`SectionFormPage` 最大差異:**沒有「存檔」按鈕,也沒有「配合事項」區**
### Props
| Prop | 型別 | 預設 | 說明 |
|------|------|------|------|
| `title` | `string` | — | 頁面標題 |
| `loading` | `boolean` | `undefined` | 是否顯示 loading |
| `error` | `string` | `undefined` | 錯誤訊息 |
| `backLabel` | `string` | `'返回'` | 返回按鈕文字 |
### Slots
| Slot | 用途 |
|------|------|
| `#filters` | 篩選條件欄位,用 `v-col` 配置 |
| `#results` | 單一結果表格區(會自動包一層 `v-card` |
| `#sections` | 多區段結果卡片(需自行用 `v-card` 包覆,適用多表格情境) |
`#results``#sections` 擇一使用:
- 單一表格結果 → 用 `#results`
- 多張獨立表格/列表 → 用 `#sections`,在 slot 內自行配置 `v-card` 與標題
### Emits
| Emit | 說明 |
|------|------|
| `@search` | 點擊查詢時觸發 |
| `@back` | 點擊返回時觸發 |
### 範例:單一結果表格
```vue
<SectionQueryPage
title="全校設備查詢"
:loading="loading"
:error="error"
@search="search"
@back="router.push('/venue/query-choose')"
>
<template #filters>
<v-col cols="12" md="4">
<BaseFormSelect v-model="filters.facId" label="設備" :items="facilityItems" />
</v-col>
<v-col cols="12" md="4">
<BaseFormTextField v-model="filters.asOfDate" label="截止日" />
</v-col>
</template>
<template #results>
<v-table density="compact">
<thead class="bg-primary">
<tr>
<th>設備代碼</th>
<th>名稱</th>
</tr>
</thead>
<tbody>
<tr v-if="!result">
<td class="text-center" colspan="2">尚無查詢結果</td>
</tr>
<tr v-else>
<td>{{ result.facId }}</td>
<td>{{ result.facName }}</td>
</tr>
</tbody>
</v-table>
</template>
</SectionQueryPage>
```
### 範例:多區段結果(多表格)
```vue
<SectionQueryPage
title="我的申請紀錄"
:loading="loading"
:error="error"
@search="search"
@back="router.push('/venue/apply-choose')"
>
<template #filters>
<v-col cols="12" md="3">
<BaseFormTextField v-model="filters.startDate" label="查詢起日" />
</v-col>
<v-col cols="12" md="3">
<BaseFormTextField v-model="filters.endDate" label="查詢迄日" />
</v-col>
<v-col cols="12" md="3">
<BaseFormSelect v-model="filters.status" label="狀態" :items="statusItems" />
</v-col>
</template>
<template #sections>
<v-card>
<v-card-title class="text-title-medium font-weight-bold py-2">場地申請</v-card-title>
<v-table density="compact">
<!-- 場地表格 -->
</v-table>
</v-card>
<v-card>
<v-card-title class="text-title-medium font-weight-bold py-2">設備申請</v-card-title>
<v-table density="compact">
<!-- 設備表格 -->
</v-table>
</v-card>
</template>
</SectionQueryPage>
```
@@ -0,0 +1,46 @@
<script setup lang="ts">
interface Props {
title: string
backLabel?: string
error?: string
loading?: boolean
}
withDefaults(defineProps<Props>(), {
backLabel: '返回',
})
const emit = defineEmits<{
search: []
back: []
}>()
</script>
<template>
<v-container fluid class="pt-2 px-1">
<v-card>
<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>
<v-row density="compact" align="center">
<slot name="filters" />
<v-col cols="12" md="auto" class="d-md-flex justify-md-end pr-md-2">
<v-btn color="primary" :loading="loading" @click="emit('search')">查詢</v-btn>
</v-col>
</v-row>
</v-card-text>
</v-card>
<v-card v-if="$slots.results">
<v-card-text>
<slot name="results" />
</v-card-text>
</v-card>
<slot name="sections" />
<v-row class="pa-4">
<v-btn variant="tonal" @click="emit('back')">{{ backLabel }}</v-btn>
</v-row>
</v-container>
</template>