# 新增頁面範例
這份文件示範如何用目前 `src/` 慣例新增一個被 `MainLayout` 包住的一般功能頁。
範例功能:`reports`
## 1. 新增 route view
```vue
```
view 的責任是頁面資料組裝與事件協調。畫面區塊交給 feature component。
## 2. 新增 feature component
```vue
報表清單
| 名稱 |
負責單位 |
| {{ row.title }} |
{{ row.owner }} |
```
component 以 props 接收資料,以 emit 回報事件。不要在 component 裡直接處理 route 或底層 HTTP。
## 3. 加入 route
route 加在 `src/router/routes.ts` 的 `routes` 陣列中,並放在 `/:pathMatch(.*)*` catch-all route 前面。
```ts
// src/router/routes.ts
{
path: '/reports',
name: 'reports',
component: () => import('@/views/reports/Reports.vue'),
meta: { layout: 'default', requiresAuth: true },
}
```
`layout: 'default'` 會讓頁面被 `MainLayout` 包住。登入頁、錯誤頁、維護中頁才使用 `layout: 'none'`。
若頁面需要出現在 drawer menu、favorites 或 breadcrumb:
- menu 來源目前由 `src/stores/menu.ts` 轉換後端選單資料。
- breadcrumb 會依 route path、menu/favorite items 與 fallback title 產生。
- 新功能若使用後端選單,優先調整後端選單資料或對應 API mock,不要把頁面專屬選單邏輯塞進 layout。
- 若只是新增 route,通常不需要修改 `MainLayout.vue`。
## 4. 需要 API 時新增 service module
```ts
// src/services/modules/reports.ts
import { httpClient } from '../client'
export interface ReportSummary {
id: number
title: string
owner: string
}
export const reportsApi = {
list: async () => ({
data: await httpClient.get('Reports').json(),
}),
}
```
service 只封裝 HTTP 細節,不持有 UI 狀態。
`httpClient` 的 `baseURL` 來自 `VITE_API_BASE_URL`。template 預設值見 `.env.example`,通常使用 `/service/api` 搭配 Vite proxy。
## 5. 需要共享狀態時新增 store
```ts
// src/stores/reports.ts
import { defineStore } from 'pinia'
import { ref } from 'vue'
import { reportsApi, type ReportSummary } from '@/services/modules/reports'
export const useReportsStore = defineStore('reports', () => {
const items = ref([])
const loading = ref(false)
const load = async () => {
loading.value = true
try {
const { data } = await reportsApi.list()
items.value = data
} finally {
loading.value = false
}
}
return {
items,
loading,
load,
}
})
```
只有跨頁共享、需要快取、或全域狀態才新增 store。單頁暫時狀態留在 view、component 或 composable。
## 6. 驗證
至少執行:
```bash
pnpm type-check
pnpm build
```
若有 route、layout 或主要互動流程變更,再啟動 dev server 並用瀏覽器確認。