feat: 更新 EditableGrid 元件以支持分頁功能並改善顯示資訊
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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/*"
|
||||||
|
|||||||
Reference in New Issue
Block a user