206 lines
5.2 KiB
Vue
206 lines
5.2 KiB
Vue
<script lang="ts" setup>
|
||
import { computed } from 'vue'
|
||
import { useRouter } from 'vue-router'
|
||
import { getConfigApp } from '@/utils/config'
|
||
|
||
const router = useRouter()
|
||
|
||
const brandName = computed(
|
||
() => String(getConfigApp('name_fa') || getConfigApp('name') || 'خانه')
|
||
)
|
||
const logo = computed(() => String(getConfigApp('avatar') || ''))
|
||
|
||
function goHome() {
|
||
router.push({ name: 'home' })
|
||
}
|
||
|
||
function goBack() {
|
||
if (window.history.length > 1) {
|
||
router.back()
|
||
return
|
||
}
|
||
goHome()
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<div class="not-found" dir="rtl">
|
||
<div class="not-found__glow not-found__glow--a" aria-hidden="true" />
|
||
<div class="not-found__glow not-found__glow--b" aria-hidden="true" />
|
||
<div class="not-found__grid" aria-hidden="true" />
|
||
|
||
<div class="not-found__panel">
|
||
<div v-if="logo" class="not-found__brand">
|
||
<img :src="logo" :alt="brandName" class="not-found__logo">
|
||
<span class="not-found__brand-name">{{ brandName }}</span>
|
||
</div>
|
||
<p v-else class="not-found__brand-name not-found__brand-name--solo">
|
||
{{ brandName }}
|
||
</p>
|
||
|
||
<p class="not-found__code" aria-hidden="true">۴۰۴</p>
|
||
|
||
<h1 class="not-found__title">
|
||
صفحه مورد نظر یافت نشد
|
||
</h1>
|
||
<p class="not-found__subtitle">
|
||
این مسیر وجود ندارد یا جابهجا شده است. میتوانید به صفحه اصلی برگردید
|
||
و از آنجا ادامه دهید.
|
||
</p>
|
||
|
||
<div class="not-found__actions">
|
||
<a-button type="primary" size="large" class="not-found__cta" @click="goHome">
|
||
بازگشت به صفحه اصلی
|
||
</a-button>
|
||
<a-button size="large" class="not-found__secondary" @click="goBack">
|
||
صفحه قبل
|
||
</a-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.not-found {
|
||
@apply relative min-h-screen w-full overflow-hidden flex items-center justify-center px-4 py-10;
|
||
background:
|
||
radial-gradient(120% 80% at 50% -10%, color-mix(in srgb, var(--App-primary, #7f1d1d) 18%, transparent), transparent 55%),
|
||
linear-gradient(165deg, #f7f4f0 0%, #efe8e0 45%, #f8f6f3 100%);
|
||
color: #1c1917;
|
||
}
|
||
|
||
.not-found__glow {
|
||
@apply pointer-events-none absolute rounded-full;
|
||
filter: blur(48px);
|
||
opacity: 0.45;
|
||
animation: not-found-float 9s ease-in-out infinite;
|
||
}
|
||
|
||
.not-found__glow--a {
|
||
width: min(48vw, 22rem);
|
||
height: min(48vw, 22rem);
|
||
top: 8%;
|
||
left: 6%;
|
||
background: color-mix(in srgb, var(--App-primary, #7f1d1d) 35%, #c4a574);
|
||
}
|
||
|
||
.not-found__glow--b {
|
||
width: min(42vw, 18rem);
|
||
height: min(42vw, 18rem);
|
||
right: 4%;
|
||
bottom: 12%;
|
||
background: color-mix(in srgb, var(--App-primary, #7f1d1d) 22%, #d6c3a3);
|
||
animation-delay: -3.5s;
|
||
}
|
||
|
||
.not-found__grid {
|
||
@apply pointer-events-none absolute inset-0 opacity-40;
|
||
background-image:
|
||
linear-gradient(rgba(28, 25, 23, 0.04) 1px, transparent 1px),
|
||
linear-gradient(90deg, rgba(28, 25, 23, 0.04) 1px, transparent 1px);
|
||
background-size: 48px 48px;
|
||
mask-image: radial-gradient(ellipse at center, black 20%, transparent 75%);
|
||
}
|
||
|
||
.not-found__panel {
|
||
@apply relative z-1 w-full max-w-lg flex flex-col items-center text-center;
|
||
animation: not-found-rise 0.7s ease-out both;
|
||
}
|
||
|
||
.not-found__brand {
|
||
@apply flex items-center justify-center gap-2.5 mb-6;
|
||
}
|
||
|
||
.not-found__logo {
|
||
@apply w-11 h-11 rounded-full object-cover;
|
||
box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.9), 0 8px 24px rgba(28, 25, 23, 0.12);
|
||
}
|
||
|
||
.not-found__brand-name {
|
||
@apply text-base font-bold tracking-wide text-primary m-0;
|
||
}
|
||
|
||
.not-found__brand-name--solo {
|
||
@apply mb-6;
|
||
}
|
||
|
||
.not-found__code {
|
||
@apply m-0 font-black leading-none select-none;
|
||
font-size: clamp(5.5rem, 22vw, 9rem);
|
||
letter-spacing: -0.06em;
|
||
background: linear-gradient(
|
||
135deg,
|
||
var(--App-primary, #7f1d1d) 0%,
|
||
#b45309 48%,
|
||
var(--App-primary, #7f1d1d) 100%
|
||
);
|
||
-webkit-background-clip: text;
|
||
background-clip: text;
|
||
color: transparent;
|
||
filter: drop-shadow(0 12px 28px rgba(127, 29, 29, 0.18));
|
||
animation: not-found-pulse 5.5s ease-in-out infinite;
|
||
}
|
||
|
||
.not-found__title {
|
||
@apply m-0 mt-2 text-xl md:text-2xl font-bold text-stone-900;
|
||
}
|
||
|
||
.not-found__subtitle {
|
||
@apply m-0 mt-3 text-sm md:text-base leading-7 text-stone-500 max-w-md;
|
||
}
|
||
|
||
.not-found__actions {
|
||
@apply mt-8 flex flex-wrap items-center justify-center gap-3 w-full;
|
||
}
|
||
|
||
.not-found__cta {
|
||
@apply min-w-44 font-bold;
|
||
box-shadow: 0 10px 28px color-mix(in srgb, var(--App-primary, #7f1d1d) 28%, transparent);
|
||
}
|
||
|
||
.not-found__secondary {
|
||
@apply min-w-28;
|
||
}
|
||
|
||
@keyframes not-found-rise {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateY(18px);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
|
||
@keyframes not-found-float {
|
||
0%,
|
||
100% {
|
||
transform: translate3d(0, 0, 0) scale(1);
|
||
}
|
||
50% {
|
||
transform: translate3d(12px, -16px, 0) scale(1.05);
|
||
}
|
||
}
|
||
|
||
@keyframes not-found-pulse {
|
||
0%,
|
||
100% {
|
||
transform: translateY(0);
|
||
filter: drop-shadow(0 12px 28px rgba(127, 29, 29, 0.18));
|
||
}
|
||
50% {
|
||
transform: translateY(-6px);
|
||
filter: drop-shadow(0 18px 36px rgba(127, 29, 29, 0.26));
|
||
}
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.not-found__glow,
|
||
.not-found__code,
|
||
.not-found__panel {
|
||
animation: none;
|
||
}
|
||
}
|
||
</style>
|