fix: bugs

This commit is contained in:
sajjadtalkhabi
2026-07-30 18:53:56 +03:30
parent bc9ce01d3f
commit 8fefb4568b
16 changed files with 529 additions and 52 deletions
@@ -8,10 +8,15 @@
:show-close-button="true"
>
<template #default>
<div v-if="!user" class="user-verification-details">
<div v-if="detailsLoading || !user" class="user-verification-details">
<p class="user-verification-details__loading">در حال بارگذاری</p>
</div>
<div v-else class="user-verification-details">
<div v-else ref="pdfContent" class="user-verification-details">
<header class="user-verification-details__pdf-header">
<h1>اطلاعات احراز هویت کاربر</h1>
<p>{{ fullName }}</p>
</header>
<section class="user-verification-details__section">
<div class="user-verification-details__grid user-verification-details__grid--6">
<LineInfoBlock title="نام و نام خانوادگی" :numeric-desc="fullName" />
@@ -178,15 +183,33 @@
class="user-verification-details__video"
/>
</section>
<div class="user-verification-details__divider" />
<div class="user-verification-details__actions" data-pdf-ignore>
<BaseButton
text="ذخیره به صورت PDF"
:loading="pdfLoading"
custom-class="user-verification-details__pdf-btn"
@click="onSaveAsPdf"
>
<template #appendIcon>
<SvgIcon name="arrow-left" :size="18" color="#fff" />
</template>
</BaseButton>
</div>
</div>
</template>
</BasicModal>
</template>
<script setup>
import { computed } from 'vue'
import { computed, ref } from 'vue'
import { toast } from 'vue3-toastify'
import useModal from '@/composables/useModal'
import BasicModal from '@/components/BasicModal.vue'
import BaseButton from '@/components/BaseButton.vue'
import SvgIcon from '@/components/icons/SvgIcon.vue'
import { formatJalaaliDate } from '@/utils/date-utils'
import LineTitleBlock from '@/components/LineTitleBlock.vue'
import VoiceRecorder from '@/components/form/VoiceRecorder.vue'
@@ -213,15 +236,19 @@ const modalData = computed(() => getModal('UserVerificationDetailsModal')?.data
const userId = computed(() => modalData.value.id ?? null)
const userIdRef = computed(() => userId.value)
const { data: fetched } = useAdminUserQuery(userIdRef, {
const { data: fetched, isLoading: userLoading } = useAdminUserQuery(userIdRef, {
enabled: () => !!userIdRef.value,
})
const { data: registerData } = useAdminUserRegisterDataQuery(userIdRef, {
enabled: () => !!userIdRef.value,
})
const { data: registerData, isLoading: registerDataLoading } = useAdminUserRegisterDataQuery(
userIdRef,
{
enabled: () => !!userIdRef.value,
}
)
const user = computed(() => modalData.value.user || fetched.value || null)
const detailsLoading = computed(() => userLoading.value || registerDataLoading.value)
const profile = computed(() => ({ ...user.value?.profile, ...registerData.value }))
@@ -263,6 +290,49 @@ const onlinePlatformDetails = computed(
// `faithProductionAudio` / `leaderMessageVideo` (each carries a `url`).
const faithProduction = computed(() => profile.value.faithProductionAudio || null)
const leaderMessage = computed(() => profile.value.leaderMessageVideo || null)
const pdfContent = ref(null)
const pdfLoading = ref(false)
const onSaveAsPdf = async () => {
if (!pdfContent.value || !userId.value || detailsLoading.value || pdfLoading.value) return
pdfLoading.value = true
try {
await document.fonts?.ready
const pdfSource = pdfContent.value.cloneNode(true)
pdfSource.classList.add('user-verification-details--pdf')
pdfSource.querySelectorAll('[data-pdf-ignore]').forEach((element) => element.remove())
const html2pdfModule = await import('html2pdf.js')
const html2pdf = html2pdfModule.default || html2pdfModule
await html2pdf()
.set({
margin: [8, 8, 8, 8],
filename: `user-verification-${userId.value}.pdf`,
image: { type: 'jpeg', quality: 0.98 },
html2canvas: {
scale: 2,
useCORS: true,
backgroundColor: '#ffffff',
logging: false,
},
jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' },
pagebreak: {
mode: ['css', 'legacy'],
avoid: ['.user-verification-details__section'],
},
})
.from(pdfSource)
.save()
} catch {
toast.error('ساخت فایل PDF با خطا مواجه شد.')
} finally {
pdfLoading.value = false
}
}
</script>
<style lang="scss" scoped>
@@ -270,6 +340,10 @@ const leaderMessage = computed(() => profile.value.leaderMessageVideo || null)
width: 100%;
text-align: start;
&__pdf-header {
display: none;
}
&__loading {
text-align: center;
padding: 2rem;
@@ -439,5 +513,61 @@ const leaderMessage = computed(() => profile.value.leaderMessageVideo || null)
&__pdf-btn {
min-width: 12rem;
}
&--pdf {
width: 100%;
padding: 0.25rem;
background: #fff;
box-sizing: border-box;
direction: rtl;
}
&--pdf &__pdf-header {
display: block;
margin-bottom: 1.5rem;
padding-bottom: 0.75rem;
border-bottom: 1px solid #eee;
text-align: right;
break-after: avoid;
h1 {
margin: 0;
font-family: var(--font-family-fa);
font-size: 1.25rem;
font-weight: 500;
color: #4b4b4b;
}
p {
margin: 0.25rem 0 0;
font-family: var(--font-family-fa);
font-size: 0.875rem;
color: #808080;
}
}
&--pdf &__section {
margin-bottom: 1rem;
break-inside: avoid;
page-break-inside: avoid;
}
&--pdf &__grid--4 {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
&--pdf &__grid--6 {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
&--pdf &__cell--span-3 {
grid-column: span 3;
}
&--pdf :deep(.line-info__title) {
overflow: visible;
text-overflow: initial;
white-space: normal;
}
}
</style>