Files
tehran_taj/apps/user-app-v2/src/components/ui/SheetArt.vue
T
s.esmaeiliandClaude Haiku 5.5 532ceee189 feat: move brand name to one config and redesign the accounting card
اپ: نام برند در همه‌ی صفحه‌ها و پیام‌ها یکسان شده و از یک تنظیم مرکزی خوانده می‌شود.
اپ: کارت حسابداری در صفحه‌ی مشتری بازطراحی شد؛ مانده‌ها و وضعیت لینک واضح‌تر نمایش داده می‌شوند.
پنل: کارت حسابداری در پرونده‌ی مشتری بازطراحی شد؛ مانده‌ها، آخرین ارسال و فرم لینک دستی واضح‌تر شده‌اند.
پنل: ستون «کد» در جدول‌ها به‌طور پیش‌فرض پنهان است و از انتخاب ستون‌ها قابل نمایش است.
پنل: تغییر نمایش ستون‌ها در جدول‌ها بلافاصله اعمال می‌شود.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-14 13:31:20 +03:30

331 lines
12 KiB
Vue

<!--
Inline, animated illustrations for IslandSheet — one per job the sheet does,
so the picture says what the action is before the text does:
logout — a door swung open with an arrow walking out of it
login — a phone showing a sign-in form, with a lock badge easing open
order — a gold shopping bag that a coin keeps dropping into, ticked
update — a rocket lifting off (a new version "launched")
success — the uploaded documents, stamped with a green check
Inline SVG rather than image files so they can animate, pick up the
sheet's tone, and cost no network request (the update dialog used to pull
a Lottie file from a third-party host on every launch).
Gradient/clip ids go through useId(): several sheets can be mounted on one
page, and a duplicate id would resolve to the first copy in the document —
which, inside a hidden sheet, renders as nothing.
The one-off entrance animations (badge pop, check drawing) replay every
time the sheet opens, because the sheet hides with display:none and CSS
animations restart when an element is displayed again.
-->
<template>
<svg class="sa" :class="`sa--${name}`" viewBox="0 0 120 120" fill="none" aria-hidden="true">
<!-- logout -->
<template v-if="name === 'logout'">
<defs>
<linearGradient :id="ids.a" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stop-color="#A0386A" />
<stop offset="1" stop-color="#5A0027" />
</linearGradient>
<radialGradient :id="ids.b" cx=".35" cy=".5" r=".75">
<stop offset="0" stop-color="#FFF7E6" />
<stop offset="1" stop-color="#F4C57A" />
</radialGradient>
</defs>
<ellipse class="sa-shadow" cx="62" cy="106" rx="36" ry="4" fill="#7C0035" opacity=".12" />
<rect x="48" y="18" width="40" height="84" rx="5" :fill="`url(#${ids.b})`" stroke="#7C0035" stroke-width="3" />
<g class="sa-door">
<path d="M88 18 L88 102 L69 108 L69 12 Z" :fill="`url(#${ids.a})`" stroke="#5A0027" stroke-width="2" stroke-linejoin="round" />
<circle cx="73.5" cy="61" r="2.6" fill="#F4C57A" />
</g>
<g class="sa-arrow" stroke-linecap="round" stroke-linejoin="round">
<path d="M60 60 H22 M33 48 L21 60 L33 72" stroke="#fff" stroke-width="12" />
<path d="M60 60 H22 M33 48 L21 60 L33 72" stroke="#7C0035" stroke-width="6" />
</g>
</template>
<!-- login -->
<template v-else-if="name === 'login'">
<ellipse class="sa-shadow" cx="60" cy="110" rx="28" ry="4" fill="#1C2D56" opacity=".12" />
<g class="sa-float">
<rect x="34" y="10" width="52" height="92" rx="12" fill="#1C2D56" />
<rect x="38" y="14" width="44" height="84" rx="9" fill="#fff" />
<rect x="52" y="18.5" width="16" height="4" rx="2" fill="#1C2D56" opacity=".85" />
<circle cx="60" cy="42" r="12" fill="#E3E8F4" />
<circle cx="60" cy="39" r="4.6" fill="#1C2D56" />
<path d="M51.5 51 a8.5 6.5 0 0 1 17 0 Z" fill="#1C2D56" />
<rect x="44" y="62" width="32" height="6" rx="3" fill="#ECEDEF" />
<rect class="sa-typing" x="44" y="62" width="22" height="6" rx="3" fill="#CACED8" />
<rect x="44" y="74" width="32" height="10" rx="5" fill="#1C2D56" />
</g>
<g class="sa-pop">
<circle cx="85" cy="86" r="15" fill="#D9A441" stroke="#fff" stroke-width="3" />
<path class="sa-shackle" d="M80 85 v-3.4 a4 4 0 0 1 8 0 V85" stroke="#fff" stroke-width="2.4" stroke-linecap="round" />
<rect x="77.5" y="84.5" width="15" height="10.5" rx="2.6" fill="#fff" />
<circle cx="85" cy="89.8" r="1.7" fill="#D9A441" />
</g>
</template>
<!-- order -->
<template v-else-if="name === 'order'">
<defs>
<linearGradient :id="ids.a" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#EBC06A" />
<stop offset="1" stop-color="#B5843B" />
</linearGradient>
<radialGradient :id="ids.b" cx=".38" cy=".35" r=".7">
<stop offset="0" stop-color="#FFF1C7" />
<stop offset="1" stop-color="#E2AC4A" />
</radialGradient>
</defs>
<ellipse class="sa-shadow" cx="60" cy="108" rx="32" ry="4" fill="#8D6A46" opacity=".16" />
<path d="M46 57 V40 a14 14 0 0 1 28 0 V57" stroke="#8D6A46" stroke-width="4.5" stroke-linecap="round" />
<g class="sa-coin">
<circle cx="60" cy="31" r="10" :fill="`url(#${ids.b})`" stroke="#B5843B" stroke-width="2" />
<circle cx="60" cy="31" r="5.5" stroke="#B5843B" stroke-width="1.5" opacity=".7" />
</g>
<path d="M30 48 H90 L85 100 a6 6 0 0 1 -6 5.5 H41 a6 6 0 0 1 -6 -5.5 Z" :fill="`url(#${ids.a})`" />
<path d="M30 48 H90 L89.4 54.5 H30.6 Z" fill="#8D6A46" opacity=".28" />
<path d="M37 60 L39.5 96" stroke="#fff" stroke-width="3" stroke-linecap="round" opacity=".35" />
<circle cx="46" cy="57" r="2.4" fill="#8D6A46" />
<circle cx="74" cy="57" r="2.4" fill="#8D6A46" />
<g class="sa-pop">
<circle cx="60" cy="80" r="13" fill="#fff" />
<path class="sa-draw" d="M54 80.5 l4.2 4.2 L66.5 76" stroke="#16a34a" stroke-width="3.4" stroke-linecap="round" stroke-linejoin="round" />
</g>
</template>
<!-- update -->
<template v-else-if="name === 'update'">
<defs>
<linearGradient :id="ids.a" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#FFD27A" />
<stop offset="1" stop-color="#F2793B" />
</linearGradient>
<clipPath :id="ids.b">
<path d="M60 10 C76 22 80 44 76 72 H44 C40 44 44 22 60 10 Z" />
</clipPath>
</defs>
<g stroke="#CACED8" stroke-width="3" stroke-linecap="round">
<path class="sa-trail" d="M28 28 v14" />
<path class="sa-trail sa-trail--2" d="M94 40 v12" />
<path class="sa-trail sa-trail--3" d="M22 64 v10" />
<path class="sa-trail sa-trail--4" d="M99 70 v12" />
</g>
<g class="sa-float">
<g class="sa-flame">
<path d="M51 77 Q60 112 69 77 Z" :fill="`url(#${ids.a})`" />
<path d="M55.5 77 Q60 96 64.5 77 Z" fill="#FFF1C9" />
</g>
<path d="M44 56 L31 76 Q31 80 35 79 L46 74 Z" fill="#D9A441" />
<path d="M76 56 L89 76 Q89 80 85 79 L74 74 Z" fill="#D9A441" />
<path d="M60 10 C76 22 80 44 76 72 H44 C40 44 44 22 60 10 Z" fill="#fff" />
<g :clip-path="`url(#${ids.b})`">
<rect x="30" y="0" width="60" height="31" fill="#1C2D56" />
<rect x="30" y="64" width="60" height="10" fill="#E3E8F4" />
</g>
<path d="M60 10 C76 22 80 44 76 72 H44 C40 44 44 22 60 10 Z" stroke="#1C2D56" stroke-width="3" stroke-linejoin="round" />
<circle cx="60" cy="46" r="7.5" fill="#DCE5F7" stroke="#1C2D56" stroke-width="3" />
<circle cx="57.6" cy="43.6" r="2" fill="#fff" />
<rect x="49" y="71" width="22" height="7" rx="2.5" fill="#1C2D56" />
</g>
</template>
<!-- success -->
<template v-else-if="name === 'success'">
<ellipse class="sa-shadow" cx="58" cy="108" rx="32" ry="4" fill="#16a34a" opacity=".14" />
<rect x="26" y="24" width="50" height="70" rx="8" fill="#D6F0DF" transform="rotate(-9 51 59)" />
<rect x="34" y="16" width="50" height="72" rx="8" fill="#fff" stroke="#1f7a45" stroke-width="2.5" />
<circle cx="46" cy="31" r="6" fill="#BFE8CD" />
<rect x="56" y="27" width="20" height="4" rx="2" fill="#E1EFE6" />
<rect x="56" y="34" width="13" height="4" rx="2" fill="#E1EFE6" />
<rect x="42" y="48" width="34" height="4" rx="2" fill="#E1EFE6" />
<rect x="42" y="56" width="27" height="4" rx="2" fill="#E1EFE6" />
<rect x="42" y="64" width="31" height="4" rx="2" fill="#E1EFE6" />
<circle class="sa-ring" cx="84" cy="85" r="17" fill="#16a34a" />
<g class="sa-pop">
<circle cx="84" cy="85" r="17" fill="#16a34a" stroke="#fff" stroke-width="3" />
<path class="sa-draw" d="M76.5 85.5 l5 5 L92 80" stroke="#fff" stroke-width="3.6" stroke-linecap="round" stroke-linejoin="round" />
</g>
<g class="sa-confetti">
<rect x="18" y="30" width="6" height="6" rx="1.5" fill="#F4C57A" transform="rotate(20 21 33)" />
<circle cx="98" cy="26" r="3.2" fill="#16a34a" />
<rect x="100" y="52" width="7" height="3.5" rx="1.75" fill="#A0386A" transform="rotate(-30 103.5 53.75)" />
<circle cx="20" cy="72" r="2.6" fill="#335CB1" />
<rect x="88" y="8" width="3.5" height="7" rx="1.75" fill="#D9A441" transform="rotate(35 89.75 11.5)" />
</g>
</template>
</svg>
</template>
<script setup lang="ts">
import { useId } from 'vue'
export type SheetArtName = 'logout' | 'login' | 'order' | 'update' | 'success'
defineProps<{ name: SheetArtName }>()
const uid = useId()
const ids = { a: `${uid}-a`, b: `${uid}-b` }
</script>
<style scoped lang="less">
.sa {
width: 100%;
height: 100%;
overflow: visible;
}
// Everything that moves scales/rotates about its own box, not the SVG origin.
.sa * {
transform-box: fill-box;
transform-origin: center;
}
// Entrances (once per open)
.sa-pop {
animation: sa-pop 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) 0.25s both;
}
.sa-draw {
stroke-dasharray: 30;
stroke-dashoffset: 30;
animation: sa-draw 0.45s ease-out 0.6s forwards;
}
// Loops
.sa-shadow {
animation: sa-shadow 3.2s ease-in-out infinite;
}
.sa-float {
animation: sa-float 3.2s ease-in-out infinite;
}
.sa-door {
transform-origin: right center;
animation: sa-door 2.8s ease-in-out infinite;
}
.sa-arrow {
animation: sa-nudge 2.8s ease-in-out infinite;
}
.sa-typing {
transform-origin: right center; // the form is RTL — text grows from the right
animation: sa-typing 2.6s ease-in-out infinite;
}
.sa-shackle {
animation: sa-shackle 2.6s ease-in-out 0.9s infinite;
}
.sa-coin {
animation: sa-coin 2.4s cubic-bezier(0.55, 0, 0.75, 0.4) 0.4s infinite backwards;
}
.sa-flame {
transform-origin: center top;
animation: sa-flame 0.22s ease-in-out infinite alternate;
}
.sa-trail {
animation: sa-trail 1.1s linear infinite;
&--2 { animation-delay: 0.35s; }
&--3 { animation-delay: 0.6s; }
&--4 { animation-delay: 0.85s; }
}
.sa-ring {
animation: sa-ring 2s ease-out 0.8s infinite;
opacity: 0;
}
.sa-confetti > * {
animation: sa-confetti 0.7s cubic-bezier(0.34, 1.56, 0.64, 1) both;
&:nth-child(1) { animation-delay: 0.7s; }
&:nth-child(2) { animation-delay: 0.78s; }
&:nth-child(3) { animation-delay: 0.86s; }
&:nth-child(4) { animation-delay: 0.94s; }
&:nth-child(5) { animation-delay: 1.02s; }
}
@keyframes sa-pop {
from { transform: scale(0); }
to { transform: scale(1); }
}
@keyframes sa-draw {
to { stroke-dashoffset: 0; }
}
@keyframes sa-shadow {
50% { transform: scaleX(0.82); opacity: 0.08; }
}
@keyframes sa-float {
50% { transform: translateY(-4px); }
}
@keyframes sa-door {
0%, 100% { transform: scaleX(1); }
50% { transform: scaleX(0.72); }
}
@keyframes sa-nudge {
0%, 100% { transform: translateX(0); }
50% { transform: translateX(-6px); }
}
@keyframes sa-typing {
0% { transform: scaleX(0.15); }
60%, 100% { transform: scaleX(1); }
}
@keyframes sa-shackle {
0%, 55%, 100% { transform: translateY(0); }
20%, 40% { transform: translateY(-3px); }
}
@keyframes sa-coin {
0% { transform: translateY(-24px); opacity: 0; }
20% { opacity: 1; }
100% { transform: translateY(26px); opacity: 1; }
}
@keyframes sa-flame {
from { transform: scaleY(0.82) scaleX(0.94); }
to { transform: scaleY(1.12) scaleX(1.04); }
}
@keyframes sa-trail {
from { transform: translateY(-8px); opacity: 0; }
30% { opacity: 1; }
to { transform: translateY(22px); opacity: 0; }
}
@keyframes sa-ring {
from { transform: scale(1); opacity: 0.35; }
to { transform: scale(1.7); opacity: 0; }
}
@keyframes sa-confetti {
from { transform: scale(0) translateY(6px); opacity: 0; }
to { transform: scale(1) translateY(0); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
.sa * {
animation: none !important;
}
.sa-draw {
stroke-dashoffset: 0;
}
}
</style>