fix withdraw page search bug

This commit is contained in:
2025-12-23 16:38:09 +03:30
parent 0d6ac60459
commit 7963af93cd
2 changed files with 220 additions and 70 deletions
+117 -37
View File
@@ -180,17 +180,33 @@ export default {
try {
const page = parseInt(query.page) || 1
const rows = parseInt(query.rows) || 10
// Map filter query to status
let status = undefined
if (query.filter === 'dep') status = 2
else if (query.filter === 'accept') status = 1
else if (query.filter === 'pend') status = 0
const params = {
page,
rows
}
if (status !== undefined) {
params.status = status
}
if (query.search) {
params.search = query.search
}
const { data } = await $axios.get('/api/admin/gps/withdraw', {
params: {
page,
rows
}
params
})
return {
requestsData: data.data,
totalDocuments: data.totalDocuments || 0,
totalPages: data.totalPages || 1
totalPages: data.totalPages || 1,
filterText: query.search || ''
}
} catch (error) {
console.log(error)
@@ -213,31 +229,16 @@ export default {
config() {
return this.$config
},
statusValue() {
// Map filterType to status integer
if (this.filterType === 'dep') return 2
if (this.filterType === 'accept') return 1
if (this.filterType === 'pend') return 0
return undefined // 'all' returns undefined to show all
},
filteredItems() {
console.log('res data', this.requestsData)
const filterUsersByType = this.requestsData.filter(item => {
if (this.filterType === 'all') return item
// eslint-disable-next-line eqeqeq
else if (this.filterType === 'dep') 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 filterUsersByType.filter(item => {
return (
item.userId.mobile_number.includes(filterText) ||
item.userId.national_code.includes(filterText) ||
item.userId.last_name.includes(filterText) ||
item.userId.first_name.includes(filterText)
)
})
}
// No client-side filtering needed - all filtering is done on backend
return this.requestsData
},
totalFilteredItems() {
return this.filteredItems.length
@@ -253,33 +254,112 @@ export default {
},
filterType: {
get() {
return this.$route.query?.filter
return this.$route.query?.filter || 'all'
},
set(val) {
this.$router.push({ name: 'admin-gps-withdraw', query: { filter: val } })
this.$router.push({
name: 'admin-gps-withdraw',
query: {
filter: val,
page: 1 // Reset to first page when filter changes
}
})
}
}
},
mounted() {
// Initialize filterText from route query
if (this.$route.query.search) {
this.filterText = this.$route.query.search
}
},
beforeDestroy() {
// Clear search timeout on component destroy
if (this.searchTimeout) {
clearTimeout(this.searchTimeout)
}
},
watch: {
filterText() {
this.fetchData(1)
filterText(newVal, oldVal) {
// Only update route if value actually changed (avoid circular updates)
if (newVal === oldVal) return
// Clear existing timeout
if (this.searchTimeout) {
clearTimeout(this.searchTimeout)
}
// If search is cleared (empty), update immediately without debounce
const isCleared = !newVal || !newVal.trim()
const wasNotEmpty = oldVal && oldVal.trim()
if (isCleared && wasNotEmpty) {
// Immediately update when clearing
this.updateSearchQuery(newVal)
} else {
// Debounce search to avoid too many requests
this.searchTimeout = setTimeout(() => {
this.updateSearchQuery(newVal)
}, 500) // 500ms debounce
}
},
filterType() {
this.fetchData(1)
},
'$route.query.page'() {
this.fetchData()
},
'$route.query.search'(newVal, oldVal) {
// Sync filterText with route query (only if different to avoid loop)
const searchValue = newVal || ''
if (searchValue !== this.filterText) {
this.filterText = searchValue
}
// Always fetch data when search query changes
if (newVal !== oldVal) {
this.fetchData(1)
}
}
},
methods: {
updateSearchQuery(searchValue) {
// Prevent circular update - check if route query already matches
const currentSearch = this.$route.query.search || ''
const newSearch = searchValue && searchValue.trim() ? searchValue.trim() : undefined
if (currentSearch === (newSearch || '')) return
// Update route query - this will trigger $route.query.search watcher
this.$router.push({
name: 'admin-gps-withdraw',
query: {
...this.$route.query,
search: newSearch,
page: 1 // Reset to first page when search changes
}
})
},
async fetchData(page = null) {
const currentPage = page || this.currentPage
try {
const params = {
page: currentPage,
rows: this.pageSize
}
// Add status parameter if filterType is set
if (this.statusValue !== undefined) {
params.status = this.statusValue
}
// Add search parameter from route query (source of truth)
const searchQuery = this.$route.query.search
if (searchQuery && searchQuery.trim()) {
params.search = searchQuery.trim()
}
const { data } = await this.$axios.get('/api/admin/gps/withdraw', {
params: {
page: currentPage,
rows: this.pageSize
}
params
})
this.requestsData = data.data
this.totalDocuments = data.totalDocuments || 0
+103 -33
View File
@@ -131,48 +131,118 @@ module.exports.getAllForAdmin = async (req, res) => {
const page = parseInt(req.query.page) || 1
const rows = parseInt(req.query.rows) || 10
const search = req.query.search || ''
const status = req.query.status !== undefined ? parseInt(req.query.status) : undefined
const sortField = req.query.sortField || 'status'
const sortOrder = req.query.sortOrder === 'desc' ? -1 : 1
const skip = (page - 1) * rows
const matchStage = search
? {
$or: [{ IMEI: new RegExp(search, 'i') }, { trackingNumber: new RegExp(search, 'i') }]
}
: {}
// If search is provided, use aggregation to search in user fields
if (search) {
const matchStage = {}
// const aggregationPipeline = [
// { $match: matchStage },
// {
// $lookup: {
// from: 'usergps',
// localField: 'userId',
// foreignField: '_id',
// as: 'userId'
// }
// },
// { $unwind: '$userId' },
// { $sort: { [sortField]: sortOrder } },
// { $skip: skip },
// { $limit: rows }
// ]
// const data = await Withdraw.aggregate(aggregationPipeline)
const data = await Withdraw.find(matchStage)
.sort({ [sortField]: sortOrder })
.skip(skip)
.limit(rows)
.populate('userId')
if (status !== undefined) {
matchStage.status = status
}
const totalDocuments = await Withdraw.countDocuments(matchStage)
// Build search conditions
const searchConditions = [
{ 'userId.mobile_number': new RegExp(search, 'i') },
{ 'userId.national_code': new RegExp(search, 'i') },
{ 'userId.first_name': new RegExp(search, 'i') },
{ 'userId.last_name': new RegExp(search, 'i') },
{ 'userId.shopName': new RegExp(search, 'i') },
{ trackingNumber: new RegExp(search, 'i') }
]
// const [data, totalDocuments] = await Promise.all([
// Withdraw.aggregate(aggregationPipeline),
// Withdraw.countDocuments(matchStage)
// ])
// Add amount search if search is a number
if (!isNaN(search) && search.trim() !== '') {
searchConditions.push({ amount: parseInt(search) })
}
const totalPages = Math.ceil(totalDocuments / rows)
const aggregationPipeline = [
{ $match: matchStage },
{
$lookup: {
from: 'usergps',
let: { userIdStr: '$userId' },
pipeline: [
{
$match: {
$expr: {
$eq: ['$_id', { $toObjectId: '$$userIdStr' }]
}
}
}
],
as: 'userId'
}
},
{ $unwind: { path: '$userId', preserveNullAndEmptyArrays: false } },
{
$match: {
$or: searchConditions
}
},
{ $sort: { [sortField]: sortOrder } },
{ $skip: skip },
{ $limit: rows }
]
res.status(200).json({ data, totalDocuments, totalPages })
const countPipeline = [
{ $match: matchStage },
{
$lookup: {
from: 'usergps',
let: { userIdStr: '$userId' },
pipeline: [
{
$match: {
$expr: {
$eq: ['$_id', { $toObjectId: '$$userIdStr' }]
}
}
}
],
as: 'userId'
}
},
{ $unwind: { path: '$userId', preserveNullAndEmptyArrays: false } },
{
$match: {
$or: searchConditions
}
},
{ $count: 'total' }
]
const [data, countResult] = await Promise.all([
Withdraw.aggregate(aggregationPipeline),
Withdraw.aggregate(countPipeline)
])
const totalDocuments = countResult[0]?.total || 0
const totalPages = Math.ceil(totalDocuments / rows)
res.status(200).json({ data, totalDocuments, totalPages })
} else {
// No search - use simple find with populate
const matchStage = {}
if (status !== undefined) {
matchStage.status = status
}
const data = await Withdraw.find(matchStage)
.sort({ [sortField]: sortOrder })
.skip(skip)
.limit(rows)
.populate('userId')
const totalDocuments = await Withdraw.countDocuments(matchStage)
const totalPages = Math.ceil(totalDocuments / rows)
res.status(200).json({ data, totalDocuments, totalPages })
}
} catch (error) {
res.status(500).json({ message: 'An error occurred, please try again.', error: error.message })
}