feat: 更新 EditableGrid 元件以支持分頁功能並改善顯示資訊

This commit is contained in:
skytek_xinliang
2026-04-13 14:32:33 +08:00
parent da96d64f75
commit 3650776ed9
3 changed files with 104 additions and 23 deletions
+38 -13
View File
@@ -1,16 +1,41 @@
# 1. 強制檢查 Node 版本 # 指定下載來源
engine-strict=true registry=https://registry.npmjs.org/
# 2. 自動安裝 Peer Dependencies # 自動儲存精確版本號 (不帶 ^ 或 ~),避免版本漂移
auto-install-peers=true
# 3. 提升特定套件 (選配)
# 如果遇到某些舊套件找不到 Vue 或 Vuetify,開啟這個可以模擬 npm 的扁平化結構
# shamefully-hoist=true
# 4. 鎖定版本 (如果您希望版本極度穩定)
# save-exact=true # save-exact=true
# 5. 針對 WSL 的優化 (選配) # 安全防禦:禁止安裝發布未滿 7 天的套件 (預防供應鏈攻擊)
# 如果您在 WSL 存取 Windows 磁碟區(如 /mnt/c)時遇到權限問題,可以開啟 # npm v11.10+
# node-linker=hoisted min-release-age=7
# pnpm
minimum-release-age=10080
# 嚴格版本檢查:若 Node 或 pnpm 版本不符 package.json 定義則報錯
# engine-strict=true
# 讓 pnpm 自動安裝缺失的 peer dependencies,減少手動維護的負擔
auto-install-peers=true
# 效能優化:讓 pnpm 盡可能解析出唯一的依賴版本
resolution-mode=highest
# ==========================================
# 團隊協作規範
# ==========================================
# 當 Lockfile 有變動但未對應安裝時,在 CI 環境直接報錯
# frozen-lockfile=true
# ==========================================
# Monorepo 結構與依賴管理
# ==========================================
# 強制使用 workspace: 協議,確保子專案互相引用時是指向原始碼而非 npm 上的版本
# save-workspace-protocol=true
# 禁止子專案之間出現循環依賴,避免構建時陷入死循環
# disallow-workspace-cycles=true
# 從根目錄解析 peerDependencies,確保全專案的 peer 依賴版本統一,減少重複打包
# resolve-peers-from-workspace-root=true
# 執行遞迴指令 (pnpm -r) 時包含根目錄 (適用於根目錄有腳本或工具時)
# include-workspace-root=true
+65 -8
View File
@@ -85,15 +85,16 @@
<div ref="tableContainerRef"> <div ref="tableContainerRef">
<v-data-table <v-data-table
v-model:page="currentPage"
class="student-table"
density="comfortable" density="comfortable"
fixed-header fixed-header
:headers="tableHeaders" :headers="tableHeaders"
:height="tableHeight" :height="tableHeight"
hide-default-footer
item-value="id" item-value="id"
:items="filteredStudents" :items="filteredStudents"
:items-per-page="10" :items-per-page="itemsPerPage"
items-per-page-text="每頁筆數"
page-text=" {0}-{1} / {2} "
> >
<template #[`header.select`]> <template #[`header.select`]>
<v-checkbox-btn <v-checkbox-btn
@@ -295,6 +296,48 @@
</v-btn> </v-btn>
</div> </div>
</template> </template>
<template #bottom>
<div class="d-flex align-center justify-space-between px-4 py-3">
<div class="text-body-2 text-medium-emphasis">
{{ pageSummary }}
</div>
<div class="d-flex align-center ga-2">
<v-btn
:disabled="currentPage <= 1"
size="small"
variant="text"
@click="currentPage = 1"
>
第一頁
</v-btn>
<v-btn
:disabled="currentPage <= 1"
size="small"
variant="text"
@click="currentPage -= 1"
>
上一頁
</v-btn>
<span class="text-body-2">{{ currentPage }} / {{ pageCount }}</span>
<v-btn
:disabled="currentPage >= pageCount"
size="small"
variant="text"
@click="currentPage += 1"
>
下一頁
</v-btn>
<v-btn
:disabled="currentPage >= pageCount"
size="small"
variant="text"
@click="currentPage = pageCount"
>
最後頁
</v-btn>
</div>
</div>
</template>
</v-data-table> </v-data-table>
</div> </div>
</v-card-text> </v-card-text>
@@ -330,7 +373,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { mdiContentSave, mdiDelete, mdiMagnify, mdiRestore } from '@mdi/js' import { mdiContentSave, mdiDelete, mdiMagnify, mdiRestore } from '@mdi/js'
import { computed, ref } from 'vue' import { computed, ref, watch } from 'vue'
import ConfirmDialog from '@/components/maint/CommonConfirmDialog.vue' import ConfirmDialog from '@/components/maint/CommonConfirmDialog.vue'
import { useEditableStudentGrid } from '@/composables/maint/useEditableStudentGrid' import { useEditableStudentGrid } from '@/composables/maint/useEditableStudentGrid'
@@ -360,6 +403,18 @@ const {
resetAllRows, resetAllRows,
} = useEditableStudentGrid() } = useEditableStudentGrid()
const itemsPerPage = 10
const currentPage = ref(1)
const pageCount = computed(() => Math.max(1, Math.ceil(filteredStudents.value.length / itemsPerPage)))
const pageSummary = computed(() => {
const total = filteredStudents.value.length
if (total === 0) return '第 0-0 筆 / 共 0 筆'
const start = (currentPage.value - 1) * itemsPerPage + 1
const end = Math.min(currentPage.value * itemsPerPage, total)
return `${start}-${end} 筆 / 共 ${total}`
})
const confirmDeleteSingleVisible = ref(false) const confirmDeleteSingleVisible = ref(false)
const confirmDeleteSelectedVisible = ref(false) const confirmDeleteSelectedVisible = ref(false)
const confirmSaveVisible = ref(false) const confirmSaveVisible = ref(false)
@@ -383,6 +438,12 @@ const selectedDeleteMessage = computed(
`確定要刪除目前選取的 ${selectedRowIds.value.length} 筆資料嗎?此操作會在儲存後正式生效。` `確定要刪除目前選取的 ${selectedRowIds.value.length} 筆資料嗎?此操作會在儲存後正式生效。`
) )
watch(pageCount, (value) => {
if (currentPage.value > value) {
currentPage.value = value
}
})
function requestDeleteSingleRow(id: number) { function requestDeleteSingleRow(id: number) {
pendingDeleteRowId.value = id pendingDeleteRowId.value = id
confirmDeleteSingleVisible.value = true confirmDeleteSingleVisible.value = true
@@ -427,8 +488,4 @@ function confirmSaveAllRows() {
padding-bottom: 0 !important; padding-bottom: 0 !important;
min-height: 32px; min-height: 32px;
} }
:deep(.v-data-table-footer) {
padding: 4px 0 0;
}
</style> </style>
-1
View File
@@ -12,7 +12,6 @@
"composite": true, "composite": true,
"rootDir": ".", "rootDir": ".",
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"baseUrl": ".",
"paths": { "paths": {
"@/*": [ "@/*": [
"./src/*" "./src/*"