前端技術棧:Vue3 + TypeScript + Element Plus +?el-pagination
后端技術棧:Java + Spring Boot + Mybatis
應用異常情況說明:點擊頁碼2,會發送兩次請求,并且自動跳回頁碼1
?代碼:?Reagent.vue
<script setup lang="ts" name="Reagent">
import { reagentQueryService } from "@/api/reagent";
import BasePreventReClickButton from "@/components/base/BasePreventReClickButton.vue";
import type { IPageListResponse } from "@/interface";
import { Search } from "@element-plus/icons-vue";
import { ref } from "vue";
import type { IReagent, IReagentQueryObj } from "./reagent/types";// 試劑分頁列表
const reagentPageList = ref<IReagent[]>([]);
// 查詢總數
const total = ref(0);
// 加載標識
const loading = ref(false);
// 查詢對象
const queryObj = ref<IReagentQueryObj>({pageHelper: {page: 1,size: 20},reagentCategory: "",reagentNo: "",reagentName: ""
});
// 分頁頁數及顯示數量變動監聽標識,默認不監聽
const onPageOrSizeChangeValid = ref(false);// 查詢
const onQueryClick = async () => {try {loading.value = true;// 把列表數據清空,點擊查詢時,視覺上感知數據正在變化reagentPageList.value = [];total.value = 0;let result = <IPageListResponse<IReagent>>(await reagentQueryService(queryObj.value)).data;reagentPageList.value = result.rows;total.value = result.total;} catch (error) {} finally {loading.value = false;onPageOrSizeChangeValid.value = true;}
};// 改變頁碼、顯示數量,重新獲取數據
const onPageOrSizeChange = async (currentPage: number, pageSize: number) => {if (!onPageOrSizeChangeValid.value) {return;}queryObj.value.pageHelper.page = currentPage;queryObj.value.pageHelper.size = pageSize;// 獲取數據await onQueryClick();
};
</script><template><el-container class="container"><el-header class="header"><!-- 標題 --><div class="header-title">試劑耗材管理</div><!-- 操作欄 --><div class="header-operation"><div><el-button class="header-btn" type="primary" plain> 入庫 </el-button><el-button class="header-btn" type="primary" plain> 申領 </el-button><el-button class="header-btn" type="primary" plain> 出庫 </el-button><el-button class="header-btn" type="primary" plain> 明細查詢 </el-button><el-button class="header-btn" type="primary" plain> 刷新數據 </el-button></div><div class="query-div"><el-input v-model="queryObj.reagentName" placeholder="請輸入試劑關鍵字進行查詢" clearable><template #prefix><el-icon><Search /></el-icon></template></el-input><BasePreventReClickButton class="query-btn" type="primary" plain :onClick="onQueryClick" :delay="100">查詢</BasePreventReClickButton></div></div></el-header><el-main class="main"><!-- 展示區 --><el-table:data="reagentPageList"v-loading="loading"highlight-current-rowstripestyle="width: 100%; height: 100%"><el-table-columnprop="reagentName"label="名稱 規格"min-width="200"fixed="left"header-align="left"sortableshow-overflow-tooltip><template #default="scope"><span class="material-name">{{ scope.row.reagentName }}</span><span class="material-spec">{{ scope.row.reagentSpec }}</span></template></el-table-column><el-table-column prop="batchNo" label="批號" width="120" header-align="left" show-overflow-tooltip /><el-table-columnprop="validityDate"label="有效期至"width="110"header-align="center"align="center"show-overflow-tooltip /><el-table-column prop="amount" width="80" header-align="center" align="center" show-overflow-tooltip><template #header><div class="custom-table-column-header-amount-unit-div">庫存數量</div></template><template #default="scope"><div class="custom-table-row-default-amount-unit-div"><span>{{ scope.row.amount }}</span><span>{{ scope.row.reagentUnit }}</span></div></template></el-table-column></el-table></el-main><el-footer class="footer"><!-- 分頁 --><el-pagination:total="total":page-sizes="[20, 50, 100, 200, 500]"v-model:page-size="queryObj.pageHelper.size"v-model:current-page="queryObj.pageHelper.page"backgroundlayout="total, sizes, prev, pager, next, jumper":small="false"@change="onPageOrSizeChange" /></el-footer></el-container>
</template><style scoped lang="scss">
// 選擇 container 所有直接子元素(不包括孫級)
.container > * {margin: 0;padding: 0;
}
.container {height: 100%;border: 1px solid #ebeef5;.header {height: auto;.header-title {margin: 10px 10px 20px 10px;font-size: 18px;}.header-operation {margin: 10px;display: flex;justify-content: space-between;.query-div {display: flex;justify-content: flex-end;// 設置固定寬度,避免由于寬度自適應,導致頁面跳動width: 300px;// 因為點擊查詢按鈕時,會顯示加載動畫,撐大了原來的按鈕寬度,導致頁面跳動,所以設置寬度為100px,預留足夠的空間,避免頁面跳動.query-btn {width: 100px;}}}}.footer {height: auto;padding: 0 10px;}
}
.table-btn {margin: 0;padding: 0 5px;
}
.custom-table-column-header-amount-unit-div {text-align: justify;text-align-last: justify;
}
.custom-table-row-default-amount-unit-div {display: flex;justify-content: space-between;
}
</style>
問題分析:
都是?total.value = 0; 引起的
因為分頁組件 total 變化,會觸發分頁組件重新計算頁數(total / page-size),并且重置當前頁數 current-page 為 1,page 變化,又會觸發 onPageOrSizeChange 事件
最終修改后的完整代碼:?Reagent.vue
<script setup lang="ts" name="Reagent">
import { reagentQueryService } from "@/api/reagent";
import BasePreventReClickButton from "@/components/base/BasePreventReClickButton.vue";
import type { IPageListResponse } from "@/interface";
import { Search } from "@element-plus/icons-vue";
import { ref } from "vue";
import type { IReagent, IReagentQueryObj } from "./reagent/types";// 試劑分頁列表
const reagentPageList = ref<IReagent[]>([]);
// 查詢總數
const total = ref(0);
// 加載標識
const loading = ref(false);
// 查詢對象
const queryObj = ref<IReagentQueryObj>({pageHelper: {page: 1,size: 20},reagentCategory: "",reagentNo: "",reagentName: ""
});
// 分頁頁數及顯示數量變動監聽標識,默認不監聽
const onPageOrSizeChangeValid = ref(false);// 查詢
const onQueryClick = async () => {try {loading.value = true;// 把列表數據清空,點擊查詢時,視覺上感知數據正在變化reagentPageList.value = [];// 這個位置(發送查詢請求前),不能 total.value = 0; 改變 total.value 的值// 因為分頁組件 total 變化,會觸發分頁組件重新計算頁數(total / page-size),并且重置當前頁數 current-page 為 1// page 變化,又會觸發 onPageOrSizeChange 事件// total.value = 0;let result = <IPageListResponse<IReagent>>(await reagentQueryService(queryObj.value)).data;reagentPageList.value = result.rows;total.value = result.total;} catch (error) {// 出錯了,在這里設置查詢總數為 0total.value = 0;} finally {loading.value = false;onPageOrSizeChangeValid.value = true;}
};// 改變頁碼、顯示數量,重新獲取數據
const onPageOrSizeChange = async (currentPage: number, pageSize: number) => {if (!onPageOrSizeChangeValid.value) {return;}queryObj.value.pageHelper.page = currentPage;queryObj.value.pageHelper.size = pageSize;// 獲取數據await onQueryClick();
};
</script><template><el-container class="container"><el-header class="header"><!-- 標題 --><div class="header-title">試劑耗材管理</div><!-- 操作欄 --><div class="header-operation"><div><el-button class="header-btn" type="primary" plain> 入庫 </el-button><el-button class="header-btn" type="primary" plain> 申領 </el-button><el-button class="header-btn" type="primary" plain> 出庫 </el-button><el-button class="header-btn" type="primary" plain> 明細查詢 </el-button><el-button class="header-btn" type="primary" plain> 刷新數據 </el-button></div><div class="query-div"><el-input v-model="queryObj.reagentName" placeholder="請輸入試劑關鍵字進行查詢" clearable><template #prefix><el-icon><Search /></el-icon></template></el-input><BasePreventReClickButton class="query-btn" type="primary" plain :onClick="onQueryClick" :delay="100">查詢</BasePreventReClickButton></div></div></el-header><el-main class="main"><!-- 展示區 --><el-table:data="reagentPageList"v-loading="loading"highlight-current-rowstripestyle="width: 100%; height: 100%"><el-table-columnprop="reagentName"label="名稱 規格"min-width="200"fixed="left"header-align="left"sortableshow-overflow-tooltip><template #default="scope"><span class="material-name">{{ scope.row.reagentName }}</span><span class="material-spec">{{ scope.row.reagentSpec }}</span></template></el-table-column><el-table-column prop="batchNo" label="批號" width="120" header-align="left" show-overflow-tooltip /><el-table-columnprop="validityDate"label="有效期至"width="110"header-align="center"align="center"show-overflow-tooltip /><el-table-column prop="amount" width="80" header-align="center" align="center" show-overflow-tooltip><template #header><div class="custom-table-column-header-amount-unit-div">庫存數量</div></template><template #default="scope"><div class="custom-table-row-default-amount-unit-div"><span>{{ scope.row.amount }}</span><span>{{ scope.row.reagentUnit }}</span></div></template></el-table-column></el-table></el-main><el-footer class="footer"><!-- 分頁 --><el-pagination:total="total":page-sizes="[20, 50, 100, 200, 500]"v-model:page-size="queryObj.pageHelper.size"v-model:current-page="queryObj.pageHelper.page"backgroundlayout="total, sizes, prev, pager, next, jumper":small="false"@change="onPageOrSizeChange" /></el-footer></el-container>
</template><style scoped lang="scss">
// 選擇 container 所有直接子元素(不包括孫級)
.container > * {margin: 0;padding: 0;
}
.container {height: 100%;border: 1px solid #ebeef5;.header {height: auto;.header-title {margin: 10px 10px 20px 10px;font-size: 18px;}.header-operation {margin: 10px;display: flex;justify-content: space-between;.query-div {display: flex;justify-content: flex-end;// 設置固定寬度,避免由于寬度自適應,導致頁面跳動width: 300px;// 因為點擊查詢按鈕時,會顯示加載動畫,撐大了原來的按鈕寬度,導致頁面跳動,所以設置寬度為100px,預留足夠的空間,避免頁面跳動.query-btn {width: 100px;}}}}.footer {height: auto;padding: 0 10px;}
}
.table-btn {margin: 0;padding: 0 5px;
}
.custom-table-column-header-amount-unit-div {text-align: justify;text-align-last: justify;
}
.custom-table-row-default-amount-unit-div {display: flex;justify-content: space-between;
}
</style>
后端分頁相關代碼:ReagentServiceImpl.java
// 查詢試劑列表@Override@Transactional(readOnly = true) // 優化性能,避免寫鎖public PageBean<Reagent> query(ReagentQueryDTO queryDTO) {PageHelper.startPage(queryDTO.getPageHelper().getPage(), queryDTO.getPageHelper().getSize());List<Reagent> reagentList = reagentMapper.select(queryDTO, PublicUtils.getUserName());Page<Reagent> reagentPage = (Page<Reagent>) reagentList;return new PageBean<>(reagentPage.getTotal(), reagentPage.getResult());}