Show percent progress bar during long Excel imports.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -25,6 +25,8 @@ const mapping = reactive<Record<string, string>>({});
|
||||
const submitting = ref(false);
|
||||
const phase = ref<'idle' | 'preview' | 'import'>('idle');
|
||||
const progressLabel = ref('');
|
||||
/** درصد ۰–۱۰۰؛ در مرحلهٔ خواندن فایل null */
|
||||
const progressPct = ref<number | null>(null);
|
||||
|
||||
const requiredKeys = computed(
|
||||
() => preview.value?.fields.filter((f) => f.required).map((f) => f.key) ?? [],
|
||||
@@ -41,6 +43,9 @@ const busyMessage = computed(() => {
|
||||
}
|
||||
return 'در حال خواندن و بررسی فایل اکسل…';
|
||||
});
|
||||
const busyProgress = computed(() =>
|
||||
phase.value === 'import' ? progressPct.value : null,
|
||||
);
|
||||
|
||||
function pick() {
|
||||
if (props.disabled || submitting.value) return;
|
||||
@@ -53,6 +58,7 @@ function reset() {
|
||||
submitting.value = false;
|
||||
phase.value = 'idle';
|
||||
progressLabel.value = '';
|
||||
progressPct.value = null;
|
||||
for (const key of Object.keys(mapping)) delete mapping[key];
|
||||
}
|
||||
|
||||
@@ -112,9 +118,21 @@ async function postImportChunk(opts: {
|
||||
return data;
|
||||
}
|
||||
|
||||
function updateImportProgress(processed: number, total: number) {
|
||||
if (total <= 0) {
|
||||
progressPct.value = null;
|
||||
progressLabel.value = 'در حال ورود اطلاعات از اکسل…';
|
||||
return;
|
||||
}
|
||||
const pct = Math.max(0, Math.min(100, Math.round((processed / total) * 100)));
|
||||
progressPct.value = pct;
|
||||
progressLabel.value = `ورود فایل: ${processed.toLocaleString('fa-IR')} از ${total.toLocaleString('fa-IR')} ردیف`;
|
||||
}
|
||||
|
||||
async function runChunkedImport(file: File, map?: Record<string, string>) {
|
||||
phase.value = 'import';
|
||||
progressLabel.value = 'در حال ورود اطلاعات از اکسل…';
|
||||
progressLabel.value = 'در حال آمادهسازی و خواندن فایل…';
|
||||
progressPct.value = 0;
|
||||
|
||||
let acc: ImportContactsResult = { created: 0, skipped: 0, errors: [] };
|
||||
let sessionId: string | undefined;
|
||||
@@ -134,9 +152,7 @@ async function runChunkedImport(file: File, map?: Record<string, string>) {
|
||||
|
||||
const total = part.total ?? 0;
|
||||
const processed = part.processed ?? 0;
|
||||
if (total > 0) {
|
||||
progressLabel.value = `ورود چندمرحلهای: ${processed.toLocaleString('fa-IR')} از ${total.toLocaleString('fa-IR')} ردیف`;
|
||||
}
|
||||
updateImportProgress(processed, total);
|
||||
|
||||
// خطای سراسری (مثلاً سقف ردیف) — ادامه نده
|
||||
if (
|
||||
@@ -145,7 +161,10 @@ async function runChunkedImport(file: File, map?: Record<string, string>) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!part.hasMore || !part.sessionId) break;
|
||||
if (!part.hasMore || !part.sessionId) {
|
||||
if (total > 0) updateImportProgress(total, total);
|
||||
break;
|
||||
}
|
||||
sessionId = part.sessionId;
|
||||
offset = part.nextOffset ?? processed;
|
||||
}
|
||||
@@ -228,6 +247,7 @@ async function confirmMapping() {
|
||||
phase.value = 'idle';
|
||||
submitting.value = false;
|
||||
progressLabel.value = '';
|
||||
progressPct.value = null;
|
||||
emit('settled');
|
||||
}
|
||||
}
|
||||
@@ -247,7 +267,12 @@ async function confirmMapping() {
|
||||
/>
|
||||
</span>
|
||||
|
||||
<UiBusyOverlay :open="busyOpen" :message="busyMessage" nest />
|
||||
<UiBusyOverlay
|
||||
:open="busyOpen"
|
||||
:message="busyMessage"
|
||||
:progress="busyProgress"
|
||||
nest
|
||||
/>
|
||||
|
||||
<UiModal :open="!!preview" title="تطبیق ستونهای اکسل" @close="closeMapper">
|
||||
<p class="lead">
|
||||
|
||||
Reference in New Issue
Block a user