fix bug of pargar 4

This commit is contained in:
2026-07-26 21:29:10 +03:30
parent 9d20b99579
commit 993de3a4cf
4 changed files with 170 additions and 1 deletions
+32
View File
@@ -85,6 +85,38 @@ export const syncAllWalletsFromPargarChunk = async (params?: {
force: params?.force ?? false,
})
export interface StuckOutboundRow {
id: number
export_id: string
type: string
amount: number | null
desc: string
order_id: number | null
withdraw_id: number | null
customer_id: number | null
customer_name?: string | null
customer_mobile?: string | null
pargar_account_id?: string | number | null
created_at?: string | null
waited_minutes: number
}
export interface StuckOutboundSummary {
count: number
customers: number
waited_minutes: number
oldest_at: string | null
last_confirmed_at: string | null
threshold_minutes: number
rows: StuckOutboundRow[]
}
/** اسناد پنل که پرگار هنوز تحویل نگرفته — ماندهٔ این مشتری‌ها اعمال نمی‌شود */
export const getStuckOutbound = async (minutes?: number) =>
await useAxios.get<StuckOutboundSummary>('/admin/pargar/stuck-outbound', {
params: minutes === undefined ? undefined : { minutes },
})
export interface PanelOnlyCustomer {
id: number
name: string
+11 -1
View File
@@ -1,6 +1,8 @@
<template>
<div class="dashboard-v2 box-border min-h-full w-full max-w-full overflow-x-hidden p-2 md:p-3">
<a-spin :spinning="loading">
<PargarStuckOutboundAlert v-if="pargarEnabled" compact class="mb-2" />
<!-- Hero -->
<section
class="dashboard-hero rounded-xl bg-gradient-to-l from-[#0f766e] via-[#1e3a5f] to-[#0f172a] px-3 py-2.5 text-white shadow-md"
@@ -451,8 +453,9 @@
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import { computed, onMounted, ref } from 'vue'
import DashboardLabelWithHelp from '@/pages/dashboard/components/DashboardLabelWithHelp.vue'
import PargarStuckOutboundAlert from '@/pages/menuPages/accounting/components/PargarStuckOutboundAlert.vue'
import DashboardQuickActionsBar from '@/pages/dashboard/components/DashboardQuickActionsBar.vue'
import DashboardQuickWalletModal from '@/pages/dashboard/components/DashboardQuickWalletModal.vue'
import DashboardSectionHeader from '@/pages/dashboard/components/DashboardSectionHeader.vue'
@@ -478,6 +481,13 @@ import { metalBalanceTitle, metalLabel } from '@/utils/adminMetalLabels'
import { formatOrderServiceFee } from '@/utils/orderServiceFee'
import { getMainMetal } from '@/utils/metalLabels'
import { TRADE_WEIGHT_UNIT_GRAM, TRADE_WEIGHT_UNIT_KILOGRAM } from '@/utils/weightUnit'
import { initAccountingSettingsFromApi, pargarEnabledSetting } from '@/utils/config'
const pargarEnabled = pargarEnabledSetting
onMounted(() => {
void initAccountingSettingsFromApi()
})
const copy = {
hero: dashboardHero,
@@ -0,0 +1,125 @@
<template>
<a-alert v-if="summary && summary.count > 0" type="warning" show-icon>
<template #message>
<div class="flex flex-wrap items-center gap-x-2 gap-y-1">
<span class="font-bold">{{ summary.count }} سند پنل هنوز در پرگار ثبت نشده</span>
<span v-if="summary.customers" class="text-xs text-gray-600">
ماندهٔ {{ summary.customers }} مشتری تا ثبت این اسناد روی کیف اعمال نمی‌شود
</span>
<a-button type="link" size="small" :loading="loading" @click="load">بروزرسانی</a-button>
</div>
</template>
<template #description>
<div class="flex flex-col gap-2 text-xs">
<div class="text-gray-600">
قدیمی‌ترین سند {{ waitLabel(summary.waited_minutes) }} در صف است.
<template v-if="lastConfirmedLabel">
آخرین سندی که پرگار تحویل گرفته: {{ lastConfirmedLabel }}
</template>
</div>
<template v-if="!compact">
<div class="flex flex-col gap-1">
<div v-for="row in visibleRows" :key="row.id" class="stuck-row">
<span class="font-bold">{{ pargarTypeLabel(row.type) }} {{ row.export_id }}</span>
<span>{{ row.customer_name || 'بدون مشتری' }}</span>
<span dir="ltr">{{ amountLabel(row) }}</span>
<span class="text-gray-500">{{ waitLabel(row.waited_minutes) }}</span>
</div>
</div>
<a-button
v-if="summary.rows.length > PREVIEW_COUNT"
type="link"
size="small"
class="self-start"
@click="expanded = !expanded"
>
{{ expanded ? 'بستن لیست' : `${summary.rows.length - PREVIEW_COUNT} سند دیگر` }}
</a-button>
</template>
<div class="text-gray-500">
پرگار این اسناد را با «دریافت اسناد» می‌گیرد؛ تا آن زمان ماندهٔ این مشتری‌ها عمداً اعمال نمی‌شود.
</div>
</div>
</template>
</a-alert>
</template>
<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref } from 'vue'
import { getStuckOutbound, type StuckOutboundRow, type StuckOutboundSummary } from '@/api/pargar'
import { pargarTypeLabel } from '../utils/pargarDisplay'
import { usePrice } from '@core/utils/usePrice'
import { toJalali } from '@core/utils/useDataTime'
const props = withDefaults(
defineProps<{
/** فقط خلاصه بدون لیست اسناد — برای داشبورد */
compact?: boolean
pollMs?: number
/** سندی که کمتر از این مدت در صف است هنوز «گیرکرده» نیست */
thresholdMinutes?: number
}>(),
{ compact: false, pollMs: 60000, thresholdMinutes: 15 }
)
const PREVIEW_COUNT = 5
const summary = ref<StuckOutboundSummary>()
const loading = ref(false)
const expanded = ref(false)
let timer: ReturnType<typeof setInterval> | undefined
const visibleRows = computed(() => {
const rows = summary.value?.rows ?? []
return expanded.value ? rows : rows.slice(0, PREVIEW_COUNT)
})
const lastConfirmedLabel = computed(() =>
summary.value?.last_confirmed_at ? toJalali(summary.value.last_confirmed_at) : ''
)
function waitLabel(minutes: number): string {
if (minutes < 60) return `${minutes} دقیقه`
const hours = Math.floor(minutes / 60)
if (hours < 24) return `${hours} ساعت`
return `${Math.floor(hours / 24)} روز`
}
function amountLabel(row: StuckOutboundRow): string {
if (row.amount == null) return '—'
if (row.type === 'wallet_cash') return usePrice(Math.abs(row.amount))
return `${Math.abs(row.amount).toFixed(4)} گرم`
}
async function load() {
try {
loading.value = true
summary.value = await getStuckOutbound(props.thresholdMinutes)
} catch (e) {
console.error(e)
} finally {
loading.value = false
}
}
onMounted(() => {
void load()
if (props.pollMs > 0) {
timer = setInterval(() => void load(), props.pollMs)
}
})
onUnmounted(() => {
if (timer) clearInterval(timer)
})
defineExpose({ load })
</script>
<style scoped>
.stuck-row {
@apply flex flex-wrap items-center gap-x-3 gap-y-1 rounded-lg bg-white/60 px-2 py-1;
}
</style>
+2
View File
@@ -5,6 +5,7 @@
class="flex h-full w-full min-w-0 max-w-full flex-col overflow-x-hidden"
:class="activeTab === 'customer-sync' || activeTab === 'pargar-customer-sync' || activeTab === 'zarin-customer-sync' ? 'overflow-y-auto' : 'overflow-hidden'"
>
<PargarStuckOutboundAlert v-if="pargarEnabled" class="mb-3 shrink-0" />
<ATabs v-model:activeKey="activeTab" class="mb-3 shrink-0">
<ATabPane key="reports" tab="گزارشات" />
<ATabPane v-if="tahesabEnabled" key="tahesab" tab="اسناد ته‌حساب" />
@@ -155,6 +156,7 @@ import zarinTbl from './components/zarinTable.vue'
import zarinCustomerSync from './components/ZarinCustomerSync.vue'
import pargarTbl from './components/pargarTable.vue'
import pargarCustomerSync from './components/PargarCustomerSync.vue'
import PargarStuckOutboundAlert from './components/PargarStuckOutboundAlert.vue'
import type { FormInstance } from 'ant-design-vue'
import BasicInfoModal from '@/components/BasicInfoModal/index.vue'
import { message } from 'ant-design-vue/es'