update selected and load data

This commit is contained in:
Warunee Tamkoo 2026-03-25 17:48:24 +07:00
parent 263e122b60
commit bebf684069
2 changed files with 157 additions and 53 deletions

View file

@ -125,7 +125,7 @@ export const useChangeRoundDataStore = defineStore(
async function fetchDataForCardId(dataDetail: any, type?: string) { async function fetchDataForCardId(dataDetail: any, type?: string) {
if (dataDetail) { if (dataDetail) {
showLoader(); // showLoader();
const url = const url =
type && type == "emp" type && type == "emp"
? config.API.leaveSearchEMP() ? config.API.leaveSearchEMP()
@ -170,7 +170,7 @@ export const useChangeRoundDataStore = defineStore(
console.log(e); console.log(e);
}) })
.finally(() => { .finally(() => {
hideLoader(); // hideLoader();
}); });
} }
} }

View file

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, watch, reactive, onMounted } from "vue"; import { ref, watch, reactive, onMounted, nextTick } from "vue";
import { useQuasar } from "quasar"; import { useQuasar } from "quasar";
import http from "@/plugins/http"; import http from "@/plugins/http";
@ -18,13 +18,7 @@ import Dialogform from "@/modules/09_leave/components/03_ChangeRound/DialogForm.
const route = useRoute(); const route = useRoute();
const mixin = useCounterMixin(); const mixin = useCounterMixin();
const { const { showLoader, hideLoader, success, messageError, dialogConfirm } = mixin;
showLoader,
hideLoader,
success,
messageError,
dialogConfirm,
} = mixin;
const dataStore = useChangeRoundDataStore(); const dataStore = useChangeRoundDataStore();
const { fetchStructureTree } = useStructureTree(); const { fetchStructureTree } = useStructureTree();
@ -48,7 +42,7 @@ const formData = reactive<DataPost>({
const pagination = ref({ const pagination = ref({
page: 1, page: 1,
rowsPerPage: formData.pageSize, rowsPerPage: 0,
}); });
/** โครงสร้างข้อมูลต้นไม้ขององค์กร **/ /** โครงสร้างข้อมูลต้นไม้ขององค์กร **/
@ -59,6 +53,19 @@ const filter = ref<string>("");
const selected = ref<any[]>([]); const selected = ref<any[]>([]);
const isMultiple = ref<boolean>(false); const isMultiple = ref<boolean>(false);
/** client-side data & batch loading **/
const allRows = ref<any[]>([]);
const isLoadingAll = ref<boolean>(false);
const loadAllProgress = ref<number>(0);
const totalToLoad = ref<number | null>(null);
const BATCH_SIZE = 250;
function waitForUi() {
return new Promise((resolve) => {
setTimeout(resolve, 0);
});
}
/** function fetch ข้อมูลของ Tree*/ /** function fetch ข้อมูลของ Tree*/
async function fetchDataTree() { async function fetchDataTree() {
nodeTree.value = await fetchStructureTree(route.meta.Key as string, true); nodeTree.value = await fetchStructureTree(route.meta.Key as string, true);
@ -135,27 +142,75 @@ function save() {
}); });
} }
/** /** Function โหลดข้อมูลทั้งหมดแบบ batch แล้วให้ QTable ทำ pagination เอง */
* function updatePagination async function fetchAllData() {
* @param newPagination อม Pagination ใหม isLoadingAll.value = true;
*/ allRows.value = [];
function updatePagination(newPagination: any) { loadAllProgress.value = 0;
formData.pageSize = totalToLoad.value = null;
newPagination.rowsPerPage === 0 ? 3000 : newPagination.rowsPerPage; selected.value = [];
// UI API
await nextTick();
await waitForUi();
try {
await dataStore.fetchDataForCardId({
...formData,
page: 1,
pageSize: BATCH_SIZE,
});
allRows.value = dataStore.rows.slice();
const total = dataStore.totalListMain;
totalToLoad.value = total;
const totalPages = Math.ceil(total / BATCH_SIZE);
loadAllProgress.value =
totalPages <= 1 ? 100 : Math.round(100 / totalPages);
await nextTick();
for (let page = 2; page <= totalPages; page++) {
await dataStore.fetchDataForCardId({
...formData,
page,
pageSize: BATCH_SIZE,
});
allRows.value.push(...dataStore.rows);
loadAllProgress.value = Math.round((page / totalPages) * 100);
await nextTick();
await waitForUi();
}
} finally {
isLoadingAll.value = false;
totalToLoad.value = null;
}
} }
/** Function ค้นหาข้อมูล */ /** Function ค้นหาข้อมูล */
async function searchData() { async function searchData() {
await dataStore.fetchDataForCardId(formData); await fetchAllData();
}
function submitSearchByEnter() {
if (isLoadingAll.value) return;
if (!checkPermission(route)?.attrIsGet) return;
formData.page = 1;
searchData();
}
/** Function เลือกทั้งหมด */
function selectAllRows() {
selected.value = [...allRows.value];
} }
function onSelectedOrgTree(data: any) { function onSelectedOrgTree(data: any) {
if (isLoadingAll.value) return;
selected.value = []; selected.value = [];
allRows.value = [];
orgTreeId.value = data.orgTreeId; orgTreeId.value = data.orgTreeId;
formData.selectedNodeId = data.orgTreeDnaId; formData.selectedNodeId = data.orgTreeDnaId;
formData.selectedNode = data.orgLevel; formData.selectedNode = data.orgLevel;
formData.page = 1; formData.page = 1;
searchData();
} }
function handleSelectMultiple() { function handleSelectMultiple() {
@ -171,14 +226,14 @@ function resetSelected() {
watch( watch(
() => formData.pageSize, () => formData.pageSize,
() => { () => {
formData.page = 1; // pageSize QTable (client-side) fetch API
searchData();
} }
); );
onMounted(() => { onMounted(() => {
fetchDataTree(); fetchDataTree();
dataStore.rows = []; dataStore.rows = [];
allRows.value = [];
}); });
</script> </script>
<template> <template>
@ -238,7 +293,6 @@ onMounted(() => {
<q-card-section <q-card-section
class="col-lg-9 col-md-8 col-xs-12 q-gutter-sm scroll" class="col-lg-9 col-md-8 col-xs-12 q-gutter-sm scroll"
style="height: 80vh" style="height: 80vh"
v-if="orgTreeId"
> >
<div class="row col-xs-12 col-sm-9"> <div class="row col-xs-12 col-sm-9">
<q-card flat bordered class="bg-grey-2 col-12 bg-white q-pa-lg"> <q-card flat bordered class="bg-grey-2 col-12 bg-white q-pa-lg">
@ -247,7 +301,7 @@ onMounted(() => {
</div> </div>
<div class="row justify-between q-gutter-y-sm"> <div class="row justify-between q-gutter-y-sm">
<q-input <q-input
:readonly="!checkPermission($route)?.attrIsGet" :readonly="!checkPermission($route)?.attrIsGet || isLoadingAll"
v-model="formData.cardId" v-model="formData.cardId"
outlined outlined
label="เลขประจำตัวประชาชน" label="เลขประจำตัวประชาชน"
@ -255,28 +309,32 @@ onMounted(() => {
dense dense
hide-bottom-space hide-bottom-space
maxlength="13" maxlength="13"
@keyup.enter="submitSearchByEnter"
/> />
<q-input <q-input
:readonly="!checkPermission($route)?.attrIsGet" :readonly="!checkPermission($route)?.attrIsGet || isLoadingAll"
v-model="formData.firstName" v-model="formData.firstName"
outlined outlined
label="ชื่อ" label="ชื่อ"
class="col-5 col-md-3 bg-white inputgreen" class="col-5 col-md-3 bg-white inputgreen"
dense dense
hide-bottom-space hide-bottom-space
@keyup.enter="submitSearchByEnter"
/> />
<q-input <q-input
:readonly="!checkPermission($route)?.attrIsGet" :readonly="!checkPermission($route)?.attrIsGet || isLoadingAll"
v-model="formData.lastName" v-model="formData.lastName"
outlined outlined
label="นามสกุล" label="นามสกุล"
class="col-6 col-md-3 bg-white inputgreen" class="col-6 col-md-3 bg-white inputgreen"
dense dense
hide-bottom-space hide-bottom-space
@keyup.enter="submitSearchByEnter"
/> />
<q-btn <q-btn
v-if="checkPermission($route)?.attrIsGet" v-if="checkPermission($route)?.attrIsGet"
@click="(formData.page = 1), searchData()" @click="(formData.page = 1), searchData()"
:disable="isLoadingAll"
for="#search" for="#search"
dense dense
unelevated unelevated
@ -289,7 +347,11 @@ onMounted(() => {
</q-card> </q-card>
</div> </div>
<div <div
v-if="dataStore.rows.length === 0 && dataStore.checkCilck === true" v-if="
allRows.length === 0 &&
dataStore.checkCilck === true &&
!isLoadingAll
"
> >
<q-card <q-card
flat flat
@ -298,30 +360,81 @@ onMounted(() => {
>ไมพบขอม</q-card >ไมพบขอม</q-card
> >
</div> </div>
<div v-if="dataStore.rows.length !== 0" class="col-12"> <div v-if="allRows.length !== 0 || isLoadingAll" class="col-12">
<q-banner
v-if="isLoadingAll"
rounded
class="bg-blue-1 text-primary q-mb-sm"
>
<div class="row items-center q-gutter-sm">
<q-spinner color="primary" size="22px" />
<div v-if="totalToLoad === null || totalToLoad <= 500">
กำลงคนหาขอม กรณารอสกคร
</div>
<div v-else>
กำลงโหลดขอมลจำนวนมาก กรณารอสกคร
<div class="text-caption text-grey-7">
ระบบกำลงดงขอมลทงหมด
{{ totalToLoad.toLocaleString() }} รายการ
</div>
</div>
</div>
</q-banner>
<q-linear-progress
v-if="isLoadingAll && totalToLoad === null"
indeterminate
color="primary"
class="q-mb-xs"
/>
<q-linear-progress
v-if="isLoadingAll && totalToLoad !== null"
:value="loadAllProgress / 100"
color="primary"
class="q-mb-xs"
/>
<div
v-if="isLoadingAll && totalToLoad !== null"
class="text-caption text-grey-6 q-mb-xs"
>
กำลงโหลด... {{ allRows.length.toLocaleString() }} /
{{ totalToLoad.toLocaleString() }} รายการ
</div>
<div class="row justify-between items-center q-mb-sm"> <div class="row justify-between items-center q-mb-sm">
<q-btn <div class="row q-gutter-sm">
:disable="selected.length === 0" <!-- <q-btn
:color="selected.length === 0 ? 'grey' : 'info'" color="secondary"
dense dense
icon="mdi-shuffle-variant" icon="mdi-checkbox-multiple-marked-outline"
label="เปลี่ยนรอบการลงเวลา" label="เลือกทั้งหมด"
@click="handleSelectMultiple()" :disable="isLoadingAll || allRows.length === 0"
/> @click="selectAllRows()"
/> -->
<q-btn
:disable="selected.length === 0 || isLoadingAll"
:color="selected.length === 0 ? 'grey' : 'info'"
dense
icon="mdi-shuffle-variant"
:label="`เปลี่ยนรอบการลงเวลา${
selected.length > 0 ? ` (${selected.length})` : ''
}`"
@click="handleSelectMultiple()"
/>
</div>
</div> </div>
<d-table <d-table
ref="table" ref="table"
:columns="dataStore.columns" :columns="dataStore.columns"
:rows="dataStore.rows" :rows="allRows"
row-key="profileId" row-key="profileId"
flat flat
bordered bordered
dense dense
virtual-scroll
table-style="max-height: 58vh"
class="custom-header-table" class="custom-header-table"
:visible-columns="dataStore.visibleColumns" :visible-columns="dataStore.visibleColumns"
:rows-per-page-options="[10, 25, 50, 100]" hide-pagination
@update:pagination="updatePagination"
v-model:pagination="pagination" v-model:pagination="pagination"
selection="multiple" selection="multiple"
v-model:selected="selected" v-model:selected="selected"
@ -333,6 +446,7 @@ onMounted(() => {
keep-color keep-color
color="primary" color="primary"
dense dense
:disable="isLoadingAll"
v-model="props.selected" v-model="props.selected"
/> />
</q-th> </q-th>
@ -354,6 +468,7 @@ onMounted(() => {
keep-color keep-color
color="primary" color="primary"
dense dense
:disable="isLoadingAll"
v-model="props.selected" v-model="props.selected"
/> />
</q-td> </q-td>
@ -429,21 +544,10 @@ onMounted(() => {
</q-td> </q-td>
</q-tr> </q-tr>
</template> </template>
<template v-slot:pagination="scope">
งหมด {{ dataStore.totalListMain }} รายการ
<q-pagination
v-model="formData.page"
active-color="primary"
color="dark"
:max="Number(dataStore.maxPageMain)"
size="sm"
boundary-links
direction-links
:max-pages="5"
@update:model-value="dataStore.fetchDataForCardId(formData)"
></q-pagination>
</template>
</d-table> </d-table>
<div class="text-caption text-grey-7 q-mt-sm">
งหมด {{ allRows.length }} รายการ
</div>
</div> </div>
</q-card-section> </q-card-section>
</q-card-section> </q-card-section>