From 285ee03ae5e1b9f2286b8565e03d3619c720f390 Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Wed, 24 Dec 2025 18:40:11 +0330 Subject: [PATCH] installed device : add pagination and fix search bug --- pages/admin/gps/installed-device/index.vue | 152 +++++++++++++++++---- server/controllers/GPS.InstalledDevice.js | 61 ++++++--- 2 files changed, 163 insertions(+), 50 deletions(-) diff --git a/pages/admin/gps/installed-device/index.vue b/pages/admin/gps/installed-device/index.vue index 3a1b9ee..5c63e62 100644 --- a/pages/admin/gps/installed-device/index.vue +++ b/pages/admin/gps/installed-device/index.vue @@ -59,7 +59,7 @@ - + @@ -128,6 +128,17 @@ +
+ + +
@@ -139,21 +150,42 @@ export default { layout: 'admin', async asyncData({ $axios, error, query }) { try { - const { data } = await $axios.get('/api/admin/gps/register-device') + const page = parseInt(query.page) || 1 + const rows = parseInt(query.rows) || 10 + + const params = { + page, + rows + } + if (query.filter) params.filter = query.filter + if (query.search) params.search = query.search + + const { data } = await $axios.get('/api/admin/gps/register-device', { params }) return { - devices: data.data + devices: data.data || [], + totalDocuments: data.totalDocuments || 0, + totalPages: data.totalPages || 1 } } catch (error) { console.log(error) + return { + devices: [], + totalDocuments: 0, + totalPages: 1 + } } }, data() { return { devices: [], - device: {}, - filterText: '' + filterText: this.$route.query?.search || '', + loading: false, + searchTimeout: null, + pageSize: 10, + totalDocuments: 0, + totalPages: 1 } }, @@ -161,35 +193,97 @@ export default { config() { return this.$config }, - filteredItems() { - const filterUsersByType = this.devices.filter(item => { - if (this.filterType === 'all') return item - // eslint-disable-next-line eqeqeq - else if (this.filterType === 'rej') return item.status == 2 - // eslint-disable-next-line eqeqeq - else if (this.filterType === 'accept') return item.status == 1 - // eslint-disable-next-line eqeqeq - else if (this.filterType === 'pend') return item.status == 0 - else return null - }) - const filterText = this.filterText - if (!this.filterText.length) return filterUsersByType - else { - return this.devices.filter(item => { - return item.IMEI.includes(filterText) || item.deviceId.model.includes(filterText) - }) - } + currentPage() { + return parseInt(this.$route.query?.page) || 1 + }, + paginate() { + return this.totalPages > 1 }, filterType: { get() { - return this.$route.query?.filter + return this.$route.query?.filter || 'all' }, set(val) { - this.$router.push({ name: 'admin-gps-installed-device', query: { filter: val } }) + const query = { ...this.$route.query, filter: val, page: 1 } + if (!this.filterText) delete query.search + else query.search = this.filterText + this.$router.push({ name: 'admin-gps-installed-device', query }) } } }, + watch: { + '$route.query': { + handler(newQuery, oldQuery) { + // Sync filterText with route query + if (newQuery.search !== this.filterText) { + this.filterText = newQuery.search || '' + } + // Only fetch if query actually changed (not on initial mount) + if (oldQuery && ( + newQuery.filter !== oldQuery.filter || + newQuery.search !== oldQuery.search || + newQuery.page !== oldQuery.page + )) { + this.fetchData() + } + }, + deep: true, + immediate: false + }, + filterText(newVal) { + // Debounce search to avoid too many requests + if (this.searchTimeout) { + clearTimeout(this.searchTimeout) + } + this.searchTimeout = setTimeout(() => { + const query = { ...this.$route.query, page: 1 } + if (newVal) { + query.search = newVal + } else { + delete query.search + } + // Only update route if it's different to avoid unnecessary navigation + if (query.search !== this.$route.query.search || query.page !== this.$route.query.page) { + this.$router.push({ name: 'admin-gps-installed-device', query }) + } + }, 500) // 500ms debounce + } + }, methods: { + async fetchData() { + this.loading = true + try { + const page = this.currentPage + const params = { + page, + rows: this.pageSize + } + if (this.$route.query.filter) params.filter = this.$route.query.filter + if (this.$route.query.search) params.search = this.$route.query.search + + const { data } = await this.$axios.get('/api/admin/gps/register-device', { params }) + this.devices = data.data || [] + this.totalDocuments = data.totalDocuments || 0 + this.totalPages = data.totalPages || 1 + } catch (error) { + console.error('Error fetching devices:', error) + this.$message({ + type: 'error', + message: 'خطا در دریافت اطلاعات' + }) + } finally { + this.loading = false + } + }, + handlePageChange(page) { + this.$router.push({ + name: 'admin-gps-installed-device', + query: { + ...this.$route.query, + page + } + }) + }, hasUnread({ row, rowIndex }) { return row.status === 0 ? 'unreadMsgInCv' : null }, @@ -201,21 +295,21 @@ export default { type: 'success', message: res.data.msg }) - this.$nuxt.refresh() + this.fetchData() }) .catch(err => { - if (err.response.data.status === 401) { + if (err.response && err.response.data && err.response.data.status === 401) { return this.$message({ type: 'warning', message: 'لطفا دوباره وارد حساب خود شوید' }) } - if (err.response.status === 422) this.validation = err.response.data.validation + if (err.response && err.response.status === 422) this.validation = err.response.data.validation else { console.log(err) return this.$message({ type: 'warning', - message: err.msg + message: err.msg || 'خطا در انجام عملیات' }) } }) diff --git a/server/controllers/GPS.InstalledDevice.js b/server/controllers/GPS.InstalledDevice.js index d3a3ab2..9daff95 100644 --- a/server/controllers/GPS.InstalledDevice.js +++ b/server/controllers/GPS.InstalledDevice.js @@ -182,34 +182,53 @@ module.exports.getAllForUser = async (req, res) => { module.exports.getAllForAdmin = async (req, res) => { try { - // const page = parseInt(req.query.page) || 1; - // const rows = parseInt(req.query.rows) || 10; - // const search = req.query.search || ""; - // const sortField = req.query.sortField || "status"; - // const sortOrder = req.query.sortOrder === "desc" ? -1 : 1; + const search = req.query.search || '' + const filterType = req.query.filter || 'all' + const page = parseInt(req.query.page) || 1 + const rows = parseInt(req.query.rows) || 10 + const skip = (page - 1) * rows - // const skip = (page - 1) * rows; + // Build status filter + const filter = {} + if (filterType !== 'all') { + let statusValue + if (filterType === 'rej') statusValue = 2 + else if (filterType === 'accept') statusValue = 1 + else if (filterType === 'pend') statusValue = 0 + + if (statusValue !== undefined) { + filter.status = statusValue + } + } - // const filter = {}; - // if (search) { - // filter.$or = [ - // { IMEI: new RegExp(search, 'i') }, - // { 'userId.shopName': new RegExp(search, 'i') }, - // { 'userId.national_code': new RegExp(search, 'i') }, - // ]; - // } - - const data = await InstalledDevice.find() + // Fetch data with status filter (search will be done after populate) + let data = await InstalledDevice.find(filter) .sort({ created_at: -1 }) .populate('deviceId') .populate('userId', 'shopName national_code first_name last_name mobile_number city_name province_name') - // .skip(skip) - // .limit(rows); - // const totalDocuments = await InstalledDevice.countDocuments(filter); - // const totalPages = Math.ceil(totalDocuments / rows); + // Filter by IMEI and deviceId.model if search is provided (needs to be done after populate) + if (search) { + const searchLower = search.toLowerCase() + data = data.filter(item => { + const imeiMatch = item.IMEI && item.IMEI.toLowerCase().includes(searchLower) + const modelMatch = item.deviceId && item.deviceId.model && item.deviceId.model.toLowerCase().includes(searchLower) + return imeiMatch || modelMatch + }) + } - res.status(200).json({ data }) + // Calculate pagination + const totalDocuments = data.length + const totalPages = Math.ceil(totalDocuments / rows) + + // Apply pagination + const paginatedData = data.slice(skip, skip + rows) + + res.status(200).json({ + data: paginatedData, + totalDocuments, + totalPages + }) } catch (error) { console.error('Error:', error.message) res.status(500).json({ message: 'Server Error', error: error.message })