| @@ -15,6 +15,11 @@ export default { | |||||
| DatePicker, | DatePicker, | ||||
| }, | }, | ||||
| setup() { | setup() { | ||||
| const searchPage = ref(); | |||||
| const currentPage = ref(1); | |||||
| const totalPages = ref(1); | |||||
| const paginate = ref(20); | |||||
| const page = ref(1); | |||||
| const pdfLoading = ref(false); | const pdfLoading = ref(false); | ||||
| const csvLoading = ref(false); | const csvLoading = ref(false); | ||||
| const filterLoading = ref(false); | const filterLoading = ref(false); | ||||
| @@ -23,6 +28,27 @@ export default { | |||||
| const logs = ref(); | const logs = ref(); | ||||
| const startDate = ref(); | const startDate = ref(); | ||||
| const endDate = ref(); | const endDate = ref(); | ||||
| const visiblePages = computed(() => { | |||||
| const pages = []; | |||||
| if (totalPages.value <= 5) { | |||||
| for (let i = 1; i <= totalPages.value; i++) { | |||||
| pages.push(i); | |||||
| } | |||||
| } else { | |||||
| let start = currentPage.value - 2; | |||||
| let end = currentPage.value + 2; | |||||
| if (start < 1) start = 1; | |||||
| if (end > totalPages.value) end = totalPages.value; | |||||
| for (let i = start; i <= end; i++) { | |||||
| pages.push(i); | |||||
| } | |||||
| } | |||||
| return pages; | |||||
| }); | |||||
| const exportFile = (param) => { | const exportFile = (param) => { | ||||
| if (param === "pdf") { | if (param === "pdf") { | ||||
| console.log("pdf"); | console.log("pdf"); | ||||
| @@ -101,7 +127,7 @@ export default { | |||||
| selectedUser.value || "" | selectedUser.value || "" | ||||
| }&start_date=${startDate.value || ""}&end_date=${ | }&start_date=${startDate.value || ""}&end_date=${ | ||||
| endDate.value || "" | endDate.value || "" | ||||
| }`, | |||||
| }&paginate=${paginate.value || 10}&page=${page.value || 1}`, | |||||
| { | { | ||||
| headers: { | headers: { | ||||
| "Content-Type": "application/json", | "Content-Type": "application/json", | ||||
| @@ -111,13 +137,45 @@ export default { | |||||
| ) | ) | ||||
| .then((resp) => { | .then((resp) => { | ||||
| filterLoading.value = false; | filterLoading.value = false; | ||||
| logs.value = resp.data.data; | |||||
| logs.value = resp.data.data.data; | |||||
| currentPage.value = resp.data.data.current_page; | |||||
| totalPages.value = resp.data.data.last_page; | |||||
| }) | }) | ||||
| .catch(() => { | .catch(() => { | ||||
| filterLoading.value = false; | filterLoading.value = false; | ||||
| }); | }); | ||||
| }; | }; | ||||
| function handlePageInput() { | |||||
| if (searchPage.value < 1) { | |||||
| searchPage.value = 1; | |||||
| } else if (searchPage.value > totalPages.value) { | |||||
| searchPage.value = totalPages.value; | |||||
| } | |||||
| if (searchPage.value >= 1 && searchPage.value <= totalPages.value) { | |||||
| page.value = searchPage.value; | |||||
| } | |||||
| } | |||||
| watch(page, () => { | |||||
| getLogs(); | |||||
| }); | |||||
| const nextPage = () => { | |||||
| if (currentPage.value < totalPages.value) { | |||||
| page.value++; | |||||
| getLogs(); | |||||
| } | |||||
| }; | |||||
| const prevPage = () => { | |||||
| if (currentPage.value > 1) { | |||||
| page.value--; | |||||
| getLogs(); | |||||
| } | |||||
| }; | |||||
| const handleLocationUpdate = () => { | const handleLocationUpdate = () => { | ||||
| location.reload(); | location.reload(); | ||||
| }; | }; | ||||
| @@ -189,6 +247,15 @@ export default { | |||||
| pdfLoading, | pdfLoading, | ||||
| csvLoading, | csvLoading, | ||||
| exportFile, | exportFile, | ||||
| currentPage, | |||||
| totalPages, | |||||
| nextPage, | |||||
| prevPage, | |||||
| page, | |||||
| handlePageInput, | |||||
| searchPage, | |||||
| visiblePages, | |||||
| }; | }; | ||||
| }, | }, | ||||
| }; | }; | ||||
| @@ -309,6 +376,78 @@ export default { | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </BRow> | </BRow> | ||||
| <BRow> | |||||
| <BCol sm="12"> | |||||
| <div class="d-flex justify-content-center"> | |||||
| <nav aria-label="Page navigation"> | |||||
| <ul class="pagination"> | |||||
| <li class="page-item" :class="{ disabled: currentPage === 1 }"> | |||||
| <span class="page-link" @click="prevPage">قبلی</span> | |||||
| </li> | |||||
| <li v-if="currentPage > 2" class="page-item" @click="page = 1"> | |||||
| <a class="page-link" href="javascript:void(0)">1</a> | |||||
| </li> | |||||
| <li v-if="currentPage > 3" class="page-item" disabled> | |||||
| <span class="page-link">...</span> | |||||
| </li> | |||||
| <li | |||||
| v-for="n in visiblePages" | |||||
| :key="n" | |||||
| class="page-item" | |||||
| :class="{ active: currentPage === n }" | |||||
| > | |||||
| <a | |||||
| class="page-link" | |||||
| href="javascript:void(0)" | |||||
| @click="page = n" | |||||
| > | |||||
| {{ n }} | |||||
| </a> | |||||
| </li> | |||||
| <li | |||||
| v-if="currentPage < totalPages - 2" | |||||
| class="page-item" | |||||
| disabled | |||||
| > | |||||
| <span class="page-link">...</span> | |||||
| </li> | |||||
| <li | |||||
| v-if="currentPage < totalPages - 1" | |||||
| class="page-item" | |||||
| @click="page = totalPages" | |||||
| > | |||||
| <a class="page-link" href="javascript:void(0)">{{ | |||||
| totalPages | |||||
| }}</a> | |||||
| </li> | |||||
| <li | |||||
| class="page-item" | |||||
| :class="{ disabled: currentPage === totalPages }" | |||||
| > | |||||
| <span class="page-link" @click="nextPage">بعدی</span> | |||||
| </li> | |||||
| </ul> | |||||
| </nav> | |||||
| </div> | |||||
| </BCol> | |||||
| <BCol sm="4"> | |||||
| <div class="ms-0 search-number"> | |||||
| <input | |||||
| v-model="searchPage" | |||||
| type="text" | |||||
| class="form-control" | |||||
| placeholder="برو به صفحه" | |||||
| :max="totalPages" | |||||
| min="1" | |||||
| @input="handlePageInput" | |||||
| /> | |||||
| </div> | |||||
| </BCol> | |||||
| </BRow> | |||||
| </Layout> | </Layout> | ||||
| </template> | </template> | ||||
| @@ -333,4 +472,46 @@ export default { | |||||
| animation: spin 1s linear infinite; | animation: spin 1s linear infinite; | ||||
| margin: 20px auto; /* Center the loader */ | margin: 20px auto; /* Center the loader */ | ||||
| } | } | ||||
| .search-number { | |||||
| display: flex; | |||||
| align-items: center; | |||||
| } | |||||
| .search-number input { | |||||
| width: 150px; | |||||
| padding: 0.5rem; | |||||
| font-size: 1rem; | |||||
| border-radius: 0.375rem; | |||||
| margin-bottom: 7px; | |||||
| border: 1px solid #ced4da; | |||||
| transition: border-color 0.3s ease, box-shadow 0.3s ease; | |||||
| } | |||||
| .search-number input:focus { | |||||
| border-color: #007bff; | |||||
| box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.25); | |||||
| } | |||||
| .search-number input::placeholder { | |||||
| color: #6c757d; | |||||
| } | |||||
| .search-number input:disabled { | |||||
| background-color: #f8f9fa; | |||||
| cursor: not-allowed; | |||||
| } | |||||
| .pagination { | |||||
| display: flex; | |||||
| flex-wrap: wrap; | |||||
| gap: 5px; | |||||
| } | |||||
| .page-item { | |||||
| flex: 0 0 auto; | |||||
| } | |||||
| .page-link { | |||||
| cursor: pointer; | |||||
| user-select: none; | |||||
| } | |||||
| </style> | </style> | ||||