fix some bugs from zarin

This commit is contained in:
2026-07-28 15:04:04 +03:30
parent b1685b47d5
commit 2cb7c605e6
3 changed files with 136 additions and 20 deletions
@@ -18,11 +18,11 @@
<Icon icon="vuesax-linear:arrow-left" :size="16" />
<Icon icon="vuesax-linear:arrow-right" :size="16" />
</div>
<div class="cs-stat cs-stat--zarin">
<div class="cs-stat cs-stat--zarin cs-stat--clickable" role="button" tabindex="0" @click="onTabChange('zarin')" @keydown.enter="onTabChange('zarin')">
<div class="cs-stat__num">
<template v-if="status?.zarin_total != null">{{ status.zarin_total }}</template>
<a-spin v-else-if="loadingZarin" size="small" />
<span v-else class="cs-stat__hint">تب دوم</span>
<span v-else class="cs-stat__hint">کلیک کنید</span>
</div>
<div class="cs-stat__label">مشتریان زرین</div>
</div>
@@ -38,11 +38,11 @@
</div>
<div class="cs-stat__label">پنل — بدون زرین</div>
</div>
<div class="cs-stat cs-stat--sky">
<div class="cs-stat cs-stat--sky cs-stat--clickable" role="button" tabindex="0" @click="onTabChange('zarin')" @keydown.enter="onTabChange('zarin')">
<div class="cs-stat__num">
<template v-if="status?.zarin_only_count != null">{{ status.zarin_only_count }}</template>
<a-spin v-else-if="loadingZarin" size="small" />
<span v-else class="cs-stat__hint">تب دوم</span>
<span v-else class="cs-stat__hint">کلیک کنید</span>
</div>
<div class="cs-stat__label">زرین — بدون پنل</div>
</div>
@@ -77,6 +77,7 @@
<Icon icon="vuesax-linear:arrow-right" :size="14" />
زرین ← پنل
<span v-if="status?.zarin_only_count" class="cs-tab__badge cs-tab__badge--sky">{{ status.zarin_only_count }}</span>
<span v-else-if="!zarinFetched && !loadingZarin" class="cs-tab__hint">بارگذاری</span>
</button>
</div>
@@ -193,6 +194,10 @@
<span class="cs-card__head-title">مشتریان زرین بدون حساب پنل</span>
</div>
<div class="cs-card__head-actions">
<a-button size="small" :loading="loadingZarin" @click="reloadZarin">
<template #icon><Icon icon="vuesax-linear:refresh" :size="13" /></template>
بروزرسانی از زرین
</a-button>
<a-button
type="primary"
size="small"
@@ -214,6 +219,18 @@
</div>
</div>
<div v-if="status?.zarin_group_filter && status.include_zarin" class="cs-group-filter">
گروه: <b>{{ status.zarin_group_filter }}</b>
</div>
<div v-if="zarinLoadFailed" class="cs-fetch-error">
<Icon icon="vuesax-linear:danger" :size="16" />
<div>
<div class="cs-fetch-error__title">دریافت لیست از زرین ممکن نشد</div>
<div class="cs-fetch-error__msg">اتصال را بررسی کنید و «بروزرسانی از زرین» را دوباره بزنید.</div>
</div>
</div>
<div v-if="syncingZarin || zarinSyncResult" class="cs-result">
<template v-if="syncingZarin">
<a-spin size="small" />
@@ -233,6 +250,12 @@
</div>
<div v-if="loadingZarin" class="cs-loading"><a-spin /></div>
<template v-else-if="zarinLoadFailed">
<div class="cs-empty cs-empty--error">
<div class="cs-empty__title">لیست مشتریان فعلاً در دسترس نیست</div>
<div class="cs-empty__sub">دکمه «بروزرسانی از زرین» را دوباره بزنید.</div>
</div>
</template>
<template v-else-if="zarinOnlyList.length">
<!-- موبایل -->
<div class="cs-mobile-list">
@@ -300,7 +323,10 @@
</template>
<div v-else class="cs-empty">
<div class="cs-empty__icon"><Icon icon="vuesax-linear:tick-circle" :size="28" /></div>
<div class="cs-empty__title">همه مشتریان زرین در پنل ثبت شده‌اند</div>
<div class="cs-empty__title">
<template v-if="(status?.zarin_total ?? 0) > 0">همه مشتریان زرین در پنل ثبت شده‌اند</template>
<template v-else>هنوز لیست زرین بارگذاری نشده یا خالی است</template>
</div>
</div>
</div>
@@ -329,6 +355,7 @@ const importStore = useCustomerImportStore()
// ─── state ───
const loadingPanel = ref(true) // شروع با true تا اولین load کامل بشه
const loadingZarin = ref(false)
const zarinLoadFailed = ref(false)
const syncingPanel = ref(false)
const syncingZarin = ref(false)
const activeTab = ref<'panel' | 'zarin'>('panel')
@@ -404,21 +431,51 @@ async function load(panelPage = 1) {
}
// ─── load zarin customers (کند — فقط وقتی تب زرین باز میشه) ───
async function loadZarin() {
if (zarinFetched.value) return
async function loadZarin(force = false) {
if (zarinFetched.value && !force) return
loadingZarin.value = true
zarinLoadFailed.value = false
try {
const res = await getZarinCustomerSyncStatus({ include_zarin: 1 } as any)
const res = await getZarinCustomerSyncStatus({ include_zarin: 1 })
if (status.value) {
status.value.zarin_total = res.zarin_total ?? 0
status.value.zarin_total = res.zarin_total ?? 0
status.value.zarin_only_count = res.zarin_only_count ?? 0
status.value.zarin_group_filter = res.zarin_group_filter ?? null
status.value.include_zarin = res.include_zarin ?? false
status.value.zarin_fetch_error = null
} else {
status.value = res
status.value = { ...res, zarin_fetch_error: null }
}
zarinOnlyList.value = res.zarin_only_list?.data ?? []
zarinFetched.value = true
} catch (e) { console.error(e) }
finally { loadingZarin.value = false }
if (res.zarin_fetch_error || res.include_zarin === false) {
zarinLoadFailed.value = true
if (status.value) {
status.value.zarin_total = 0
status.value.zarin_only_count = 0
}
message.error('دریافت لیست مشتریان از حسابداری ناموفق بود')
}
} catch (e) {
console.error(e)
zarinLoadFailed.value = true
zarinFetched.value = true
if (status.value) {
status.value.zarin_total = 0
status.value.zarin_only_count = 0
}
message.error('دریافت لیست مشتریان از حسابداری ناموفق بود')
} finally {
loadingZarin.value = false
}
}
function reloadZarin() {
zarinFetched.value = false
zarinLoadFailed.value = false
selectedZarin.clear()
zarinPage.value = 1
loadZarin(true)
}
function onTabChange(tab: 'panel' | 'zarin') {
@@ -609,6 +666,11 @@ onMounted(() => {
&--zarin { background: rgba(14,165,233,0.25); }
&--amber { background: rgba(251,191,36,0.2); }
&--sky { background: rgba(14,165,233,0.2); }
&--clickable {
cursor: pointer;
transition: background 0.15s ease;
&:hover { background: rgba(14,165,233,0.4); }
}
&__num {
font-size: 20px;
@@ -702,6 +764,15 @@ onMounted(() => {
&--amber { background: rgba(245,158,11,0.15); color: #d97706; }
&--sky { background: rgba(14,165,233,0.15); color: #0ea5e9; }
}
&__hint {
font-size: 10px;
font-weight: 600;
padding: 1px 6px;
border-radius: 99px;
background: rgba(14,165,233,0.12);
color: #0ea5e9;
}
}
/* ─── کارت ─── */
@@ -786,6 +857,33 @@ onMounted(() => {
.cs-loading { display: flex; justify-content: center; padding: 48px; }
.cs-group-filter {
padding: 8px 16px;
font-size: 12px;
color: var(--admin-text-secondary, #64748b);
border-bottom: 1px solid var(--admin-border);
background: var(--admin-bg-muted, transparent);
b { color: var(--admin-text); }
&__warn { color: #dc2626; font-weight: 600; }
}
.cs-fetch-error {
display: flex;
align-items: flex-start;
gap: 10px;
padding: 12px 16px;
border-bottom: 1px solid rgba(239, 68, 68, 0.25);
background: rgba(239, 68, 68, 0.08);
color: #dc2626;
font-size: 12px;
line-height: 1.6;
&__title { font-weight: 700; font-size: 13px; margin-bottom: 2px; }
&__msg { word-break: break-word; color: var(--admin-text); }
&__meta { margin-top: 4px; opacity: 0.8; font-family: ui-monospace, monospace; }
}
/* ─── موبایل ─── */
.cs-mobile-list {
display: block;
@@ -852,6 +950,8 @@ onMounted(() => {
&__icon { width: 52px; height: 52px; border-radius: 50%; background: rgba(52,211,153,0.12); color: #059669; display: flex; align-items: center; justify-content: center; }
&__title { font-size: 14px; font-weight: 700; color: var(--admin-text); }
&__sub { font-size: 12px; color: var(--admin-text-muted, #64748b); text-align: center; max-width: 360px; }
&--error &__title { color: #dc2626; }
}
.cs-pagination {