Show accounting adjust notes on transactions and polish input/lottie boot.

Transaction detail surfaces note text for adjustments; Persian digit input and local Lottie preload are included.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-30 21:18:29 +03:30
co-authored by Cursor
parent 449fad5ed1
commit 5b06034255
16 changed files with 94 additions and 28 deletions
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
{"nm":"Pre-comp 1","ddd":0,"h":1200,"w":1200,"meta":{"g":"LottieFiles AE 0.1.21"},"layers":[{"ty":4,"nm":"arrow down Outlines","sr":1,"st":0,"op":1020,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[256,256,0],"ix":1},"s":{"a":0,"k":[46.875,46.875,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,528,0],"t":0,"ti":[0,0,0],"to":[0,-15.333,0]},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,436,0],"t":13,"ti":[0,-15.333,0],"to":[0,0,0]},{"s":[600,528,0],"t":27}],"ix":2,"x":"var $bm_rt;\n$bm_rt = loopOut('cycle');"},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":4,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[7.831,7.793],[7.792,-7.828],[0,0],[0,0],[11.045,0],[0,-11.046],[0,0],[0,0],[7.829,-7.792],[-7.791,-7.829],[0,0],[-0.005,-0.006],[-7.819,7.833],[-0.005,0.006],[0,0]],"o":[[-7.829,-7.792],[0,0],[0,0],[0,-11.046],[-11.046,0],[0,0],[0,0],[-7.792,-7.828],[-7.83,7.793],[0,0],[0.006,0.007],[7.792,7.809],[0.006,-0.007],[0,0],[7.79,-7.828]],"v":[[118.109,117.328],[89.825,117.394],[20.001,187.557],[20.001,-236],[0.001,-256],[-19.999,-236],[-19.999,187.557],[-89.824,117.393],[-118.107,117.327],[-118.175,145.612],[-14.175,250.116],[-14.157,250.134],[14.158,250.135],[14.176,250.117],[118.176,145.613]]},"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.1608,0.1608,0.3843],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[255.999,256],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":1},{"ty":4,"nm":"Shape Layer 1","sr":1,"st":0,"op":1020,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[141,141,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[622.56,380.04,0],"t":0,"ti":[0,0,0],"to":[0,-15.333,0]},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[622.56,288.04,0],"t":13,"ti":[0,-15.333,0],"to":[0,0,0]},{"s":[622.56,380.04,0],"t":27}],"ix":2,"x":"var $bm_rt;\n$bm_rt = loopOut('cycle');"},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Ellipse 1","ix":1,"cix":2,"np":3,"it":[{"ty":"el","bm":0,"hd":false,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"s":{"a":0,"k":[144,144],"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[-16,156],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":2}],"v":"5.5.7","fr":30,"op":1020,"ip":0,"assets":[]}
File diff suppressed because one or more lines are too long
+7
View File
@@ -93,6 +93,13 @@ body {
}
}
@media (min-width: 500px) {
.login-vip-page__body{
height: 20vh !important;
overflow: auto;
}
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s ease;
+1 -6
View File
@@ -42,7 +42,6 @@ import {
normalizeDecimalInput,
persianToEnglishDigits,
shouldNormalizeDecimalSeparators,
shouldNormalizeInputDigits,
} from '@/utils/persianDigits';
//Props
@@ -93,11 +92,7 @@ function normalizeValue(value: unknown) {
})
}
let normalized = String(value)
if (shouldNormalizeInputDigits(props)) {
normalized = persianToEnglishDigits(normalized)
}
return normalized
return persianToEnglishDigits(String(value))
}
const handlerChange = (e: Event | string) => {
+2
View File
@@ -3,6 +3,7 @@ import './assets/css/main.less'
import 'virtual:uno.css'
import { initPwaInstallCapture } from '@/modules/pwa/manifest'
import { preloadLottieAssets } from '@/utils/lottieAssets'
import { bootstrapApp } from '@/app/bootstrap'
export {
@@ -15,4 +16,5 @@ export {
import '@unocss/reset/tailwind-compat.css'
initPwaInstallCapture()
void preloadLottieAssets()
bootstrapApp()
+1 -1
View File
@@ -13,7 +13,7 @@ export interface Transaction {
unit: string
hash?: string
id?: number
note?: null
note?: string | null
title: string
order_id?: number
transaction_type?: string
+37
View File
@@ -0,0 +1,37 @@
/** Lottieهای لوکال (`public/lottie`) — پیش‌دانلود تا مودال/لیست خالی نماند */
const LOTTIE_FILES = {
logoutConfirm: 'lottie/logoutConfirm.json',
emptyList: 'lottie/emptyList.json',
goldBars: 'lottie/goldBars.json',
} as const
export type LottieAssetKey = keyof typeof LOTTIE_FILES
const cache = new Map<LottieAssetKey, string>()
export function lottieSrc(key: LottieAssetKey): string {
const base = import.meta.env.BASE_URL || '/'
const path = `${base}${LOTTIE_FILES[key]}`
return path.replace(/([^:]\/)\/+/g, '$1')
}
export function getLottieData(key: LottieAssetKey): string | undefined {
return cache.get(key)
}
export async function preloadLottieAssets(): Promise<void> {
const keys = Object.keys(LOTTIE_FILES) as LottieAssetKey[]
await Promise.all(
keys.map(async (key) => {
if (cache.has(key)) return
try {
const res = await fetch(lottieSrc(key))
if (!res.ok) return
cache.set(key, await res.text())
} catch {
/* شبکه/آفلاین — بعداً از src لوکال می‌خواند */
}
}),
)
}
+3 -9
View File
@@ -56,19 +56,13 @@ export function parseDecimalInput(
return Number(normalized)
}
export function shouldNormalizeInputDigits(options: {
export function shouldNormalizeInputDigits(_options?: {
isPassword?: boolean
type?: string
inputmode?: string
}): boolean {
if (options.isPassword) {
return false
}
if (options.type === 'price' || options.type === 'number') {
return true
}
const mode = String(options.inputmode ?? '').trim().toLowerCase()
return mode === 'numeric' || mode === 'decimal' || mode === 'number'
// همیشه ارقام فارسی/عربی → انگلیسی (لاگین، گرم، تعداد، مظنه، …)
return true
}
/** فیلدهای وزن/مبلغ/عدد اعشاری که باید `/` را ممیز بگیرند */
@@ -5,7 +5,7 @@
<DotLottieVue
class="w-11rem h-11rem"
autoplay
src="https://lottie.host/129cf3a1-df84-4758-a856-58c133ae6013/R5RqclXA8G.json"
v-bind="lottieBind"
/>
<div class="p-5 text-sm">آیا از خروج خود اطمینان دارید؟</div>
</div>
@@ -23,7 +23,8 @@
<script setup lang="ts">
import DialogVant from '@/components/DialogVant.vue'
import { DotLottieVue } from '@lottiefiles/dotlottie-vue'
import {computed} from "vue";
import {computed, ref, watch} from 'vue'
import {getLottieData, lottieSrc, preloadLottieAssets} from '@/utils/lottieAssets'
const props = defineProps<{
show: boolean
@@ -34,8 +35,29 @@ const emit = defineEmits<{
confirm: []
}>()
const cachedData = ref(getLottieData('logoutConfirm'))
const lottieBind = computed(() => {
if (cachedData.value) return {data: cachedData.value}
return {src: lottieSrc('logoutConfirm')}
})
async function ensureLottie() {
if (cachedData.value) return
await preloadLottieAssets()
cachedData.value = getLottieData('logoutConfirm')
}
watch(
() => props.show,
(open) => {
if (open) void ensureLottie()
},
{immediate: true},
)
const visible = computed({
get: () => props.show,
set: (val) => emit('update:show', val)
})
</script>
</script>
+1 -1
View File
@@ -347,7 +347,7 @@
<DotLottieVue
class="w-11rem h-11rem"
autoplay
src="https://lottie.host/129cf3a1-df84-4758-a856-58c133ae6013/R5RqclXA8G.json"
src="/lottie/logoutConfirm.json"
/>
<div class="p-5 text-sm">آیا از خروج خود اطمینان دارید؟</div>
</div>
+2 -2
View File
@@ -33,7 +33,7 @@
class="w-5rem h-5rem mt-[-1.6rem] ml-[-2rem]"
autoplay
loop
src="https://lottie.host/3d46ebb3-a580-41eb-ade7-b8829c32b071/PJPuSKbpze.json"
src="/lottie/goldBars.json"
/>
</div>
</div>
@@ -315,7 +315,7 @@
<DotLottieVue
class="w-11rem h-11rem"
autoplay
src="https://lottie.host/129cf3a1-df84-4758-a856-58c133ae6013/R5RqclXA8G.json"
src="/lottie/logoutConfirm.json"
/>
<div class="p-5 text-sm">آیا از خروج خود اطمینان دارید؟</div>
</div>
@@ -115,6 +115,10 @@
<div :class="variant === 'desktop' ? 'dtxn-detail__label' : 'text-primary'">مهلت ارسال فیش:</div>
<div :class="variant === 'desktop' ? 'dtxn-detail__value' : ''">{{ item.eaj }}</div>
</div>
<div v-if="item.note" :class="variant === 'desktop' ? 'dtxn-detail__row' : 'flex justify-between mb-3'">
<div :class="variant === 'desktop' ? 'dtxn-detail__label' : 'text-primary'">توضیحات سند:</div>
<div :class="variant === 'desktop' ? 'dtxn-detail__value' : ''">{{ item.note }}</div>
</div>
</div>
</template>
@@ -42,7 +42,7 @@
<DotLottieVue
class="w-44 h-44"
autoplay
src="https://lottie.host/51d3315a-cf88-4c18-bb4d-a31511fd4325/KjSBzcMbi6.json"
src="/lottie/emptyList.json"
/>
<p class="dtxn-empty__text">{{ emptyText }}</p>
</div>
@@ -28,7 +28,7 @@
<DotLottieVue
class="w-11rem h-11rem"
autoplay
src="https://lottie.host/51d3315a-cf88-4c18-bb4d-a31511fd4325/KjSBzcMbi6.json"
src="/lottie/emptyList.json"
/>
<div class="font-bold">{{ emptyText }}</div>
</div>
+6 -4
View File
@@ -142,6 +142,7 @@
import { mobileValidation, required } from '@/utils/inputRules'
// import type {UserMobile} from "@/models/login";
import BasicInput from '@/components/BasicPacks/BasicInput.vue'
import { persianToEnglishDigits } from '@/utils/persianDigits'
import BasicButton from '@/components/BasicPacks/BasicButton.vue'
import InstallButton from '@/components/InstallButton.vue'
import { authenticateWithFingerprint, getToken, loading, removeToken, setToken, URLUserType } from '@/utils'
@@ -219,12 +220,12 @@ function useStyle() {
}
function handleOnChange(value: string) {
userFormState.token = value
userFormState.token = persianToEnglishDigits(String(value ?? ''))
// formRef.value?.validateFields()
}
async function handleOnComplete(value: string) {
userFormState.token = value
userFormState.token = persianToEnglishDigits(String(value ?? ''))
phoneVerification()
}
@@ -457,6 +458,7 @@ const getcode=()=>{
import { mobileValidation, required } from '@/utils/inputRules'
// import type {UserMobile} from "@/models/login";
import BasicInput from '@/components/BasicPacks/BasicInput.vue'
import { persianToEnglishDigits } from '@/utils/persianDigits'
import BasicButton from '@/components/BasicPacks/BasicButton.vue'
import {
authenticateWithFingerprint,
@@ -558,12 +560,12 @@ function useStyle() {
}
function handleOnChange(value: string) {
userFormState.token = value
userFormState.token = persianToEnglishDigits(String(value ?? ''))
// formRef.value?.validateFields()
}
async function handleOnComplete(value: string) {
userFormState.token = value
userFormState.token = persianToEnglishDigits(String(value ?? ''))
phoneVerification()
}