added gholak
This commit is contained in:
@@ -6,12 +6,13 @@ import type { TableOutput } from '@/models/table'
|
||||
import { getProductInventory } from '@/api/products'
|
||||
|
||||
export const useFundUserStore = defineStore('fund', () => {
|
||||
/** داراییها / موجودی محصول (صفحهٔ /fund فعلی) */
|
||||
const myFunds = ref<TableOutput<Fund> | undefined>(undefined)
|
||||
/** قلکهای واقعی (مدل Fund) */
|
||||
const myPiggyFunds = ref<TableOutput<Fund> | undefined>(undefined)
|
||||
|
||||
async function getMyFund() {
|
||||
try {
|
||||
// داراییها — inventory؛ سازگار با صفحهٔ /fund
|
||||
myFunds.value = await getProductInventory()
|
||||
return myFunds.value
|
||||
} catch (e) {
|
||||
|
||||
@@ -68,101 +68,84 @@
|
||||
<div class="mt-3 p-1 shadow-lg from-primary rounded-lg p-px to-transparent border-solid border border-primary">
|
||||
<div class=" p-1 py-6 rounded-lg bg-white">
|
||||
<div class="flex justify-between items-start gap-1">
|
||||
<!-- <div
|
||||
class="text-center w-full flex-flex-col items-center home_appear_fade_in_up"
|
||||
style="text-align: -webkit-center"
|
||||
@click="router.push({ name: 'buyProduct', query: { state: 'sell' } })"
|
||||
>
|
||||
<div
|
||||
class="rounded-full w-[42px] h-[42px] bg-primary flex items-center justify-center"
|
||||
style="border: solid 1px #a72b77"
|
||||
|
||||
>
|
||||
<Icon class="text-tertiary" icon="vuesax-linear:money-send" />
|
||||
</div>
|
||||
<div class="text-primary">فروش</div>
|
||||
</div>-->
|
||||
<div
|
||||
class="text-center min-w-0 flex-1 flex-flex-col items-center home_appear_fade_in_up"
|
||||
class="text-center min-w-0 flex-1 home_appear_fade_in_up"
|
||||
style="text-align: -webkit-center"
|
||||
@click="router.push({ name: 'gold' })"
|
||||
>
|
||||
<div
|
||||
class="rounded-full w-[50px] h-[50px] bg-primary flex items-center justify-center"
|
||||
class="rounded-full w-[44px] h-[44px] mx-auto bg-primary flex items-center justify-center"
|
||||
style="border: solid 1px #a72b77"
|
||||
|
||||
>
|
||||
<Icon class="text-white" icon="vuesax-linear:money-receive" />
|
||||
</div>
|
||||
<div class="text-primary">خرید و فروش</div>
|
||||
<div class="text-primary text-[11px] leading-tight mt-1">خرید و فروش</div>
|
||||
</div>
|
||||
<div
|
||||
class="text-center w-full flex-flex-col items-center home_appear_fade_in_down"
|
||||
class="text-center min-w-0 flex-1 home_appear_fade_in_down"
|
||||
style="text-align: -webkit-center"
|
||||
@click="router.push({ name: 'wallet', query: { state: 'deposit' } })"
|
||||
>
|
||||
<div
|
||||
class="rounded-full w-[50px] h-[50px] bg-primary flex items-center justify-center"
|
||||
class="rounded-full w-[44px] h-[44px] mx-auto bg-primary flex items-center justify-center"
|
||||
style="border: solid 1px #a72b77"
|
||||
|
||||
>
|
||||
<Icon class="text-white" icon="vuesax-linear:card-add" />
|
||||
</div>
|
||||
<div class="text-primary">واریز نقدی</div>
|
||||
<div class="text-primary text-[11px] leading-tight mt-1">واریز نقدی</div>
|
||||
</div>
|
||||
<div
|
||||
class="text-center w-full flex-flex-col items-center home_appear_fade_in_up"
|
||||
class="text-center min-w-0 flex-1 home_appear_fade_in_up"
|
||||
style="text-align: -webkit-center"
|
||||
@click="openWholeBalanceVisible()"
|
||||
>
|
||||
<div
|
||||
class="rounded-full w-[50px] h-[50px] bg-primary flex items-center justify-center "
|
||||
class="rounded-full w-[44px] h-[44px] mx-auto bg-primary flex items-center justify-center"
|
||||
style="border: solid 1px #a72b77"
|
||||
>
|
||||
<Icon class="text-white" icon="vuesax-linear:receipt-item" />
|
||||
</div>
|
||||
<div class="text-primary">موجودی</div>
|
||||
<div class="text-primary text-[11px] leading-tight mt-1">موجودی</div>
|
||||
</div>
|
||||
<div
|
||||
class="text-center w-full flex-flex-col items-center home_appear_fade_in_down"
|
||||
class="text-center min-w-0 flex-1 home_appear_fade_in_down"
|
||||
style="text-align: -webkit-center"
|
||||
@click="router.push({ name: 'fund' })"
|
||||
>
|
||||
<div
|
||||
class="rounded-full w-[50px] h-[50px] bg-primary flex items-center justify-center"
|
||||
class="rounded-full w-[44px] h-[44px] mx-auto bg-primary flex items-center justify-center"
|
||||
style=" border: solid 1px #a72b77"
|
||||
>
|
||||
<Icon class="text-white font-bold" icon="vuesax-linear:archive-1" />
|
||||
</div>
|
||||
<div class="text-primary">دارایی ها</div>
|
||||
<div class="text-primary text-[11px] leading-tight mt-1">دارایی ها</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="productFundsEnabled"
|
||||
class="text-center w-full flex-flex-col items-center home_appear_fade_in_down"
|
||||
class="text-center min-w-0 flex-1 home_appear_fade_in_down"
|
||||
style="text-align: -webkit-center"
|
||||
@click="router.push({ name: 'piggy' })"
|
||||
>
|
||||
<div
|
||||
class="rounded-full w-[50px] h-[50px] bg-primary flex items-center justify-center"
|
||||
class="rounded-full w-[44px] h-[44px] mx-auto bg-primary flex items-center justify-center"
|
||||
style=" border: solid 1px #a72b77"
|
||||
>
|
||||
<Icon class="text-white font-bold" icon="vuesax-linear:safe-home" />
|
||||
</div>
|
||||
<div class="text-primary">قلک</div>
|
||||
<div class="text-primary text-[11px] leading-tight mt-1">قلک</div>
|
||||
</div>
|
||||
<div
|
||||
class="text-center w-full flex-flex-col items-center home_appear_fade_in_down"
|
||||
class="text-center min-w-0 flex-1 home_appear_fade_in_down"
|
||||
style="text-align: -webkit-center"
|
||||
@click="router.push({ name: 'earnMoney' })"
|
||||
>
|
||||
<div
|
||||
class="rounded-full w-[50px] h-[50px] bg-primary flex items-center justify-center"
|
||||
class="rounded-full w-[44px] h-[44px] mx-auto bg-primary flex items-center justify-center"
|
||||
style=" border: solid 1px #a72b77"
|
||||
|
||||
>
|
||||
<Icon class="text-white font-bold" icon="vuesax-linear:dollar-circle" />
|
||||
</div>
|
||||
<div class="text-primary">کسب درآمد</div>
|
||||
<div class="text-primary text-[11px] leading-tight mt-1">کسب درآمد</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
+161
-92
@@ -1,60 +1,94 @@
|
||||
<template>
|
||||
<div class="piggy-page">
|
||||
<header class="piggy-topbar">
|
||||
<button type="button" class="piggy-icon-btn" aria-label="بازگشت" @click="goBack">
|
||||
<header class="piggy-bar">
|
||||
<button type="button" class="piggy-bar__btn" aria-label="بازگشت" @click="goBack">
|
||||
<BasicIcon icon="vuesax-linear:arrow-right-1" size="22" />
|
||||
</button>
|
||||
<h1 class="piggy-topbar__title">قلک</h1>
|
||||
<span class="w-9" />
|
||||
<h1 class="piggy-bar__title">قلکها</h1>
|
||||
<button type="button" class="piggy-bar__btn piggy-bar__btn--primary" aria-label="قلک جدید" @click="goAdd">
|
||||
<BasicIcon icon="vuesax-linear:add" size="20" />
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<a-spin :spinning="busy">
|
||||
<section class="piggy-hero">
|
||||
<div class="piggy-hero__text">
|
||||
<p class="piggy-hero__label">موجودی قلکها</p>
|
||||
<p class="piggy-hero__value" dir="ltr">
|
||||
<span>{{ formatPiggyAmount(totalGold) }}</span>
|
||||
<span class="piggy-hero__unit">گرم</span>
|
||||
</p>
|
||||
<p class="piggy-hero__hint">{{ rows.length }} قلک فعال</p>
|
||||
</div>
|
||||
<img class="piggy-hero__art" :src="heroImg" alt="" />
|
||||
</section>
|
||||
|
||||
<section class="piggy-sheet">
|
||||
<div v-if="!rows.length" class="piggy-empty">
|
||||
<img :src="emptyImg" alt="" class="piggy-empty__img" />
|
||||
<p class="piggy-empty__title">هنوز قلکی نساختهاید</p>
|
||||
<p class="piggy-empty__desc">موجودی هر محصول را در قلکهای جدا نگه دارید</p>
|
||||
</div>
|
||||
|
||||
<div v-else class="piggy-grid">
|
||||
<button
|
||||
v-for="item in rows"
|
||||
:key="item.id"
|
||||
type="button"
|
||||
class="piggy-tile"
|
||||
@click="openInfo(item.id!)"
|
||||
>
|
||||
<div class="piggy-tile__icon-wrap">
|
||||
<img :src="piggyIconForTitle(item.title)" alt="" class="piggy-tile__icon" />
|
||||
<section class="piggy-summary">
|
||||
<div class="piggy-summary__card">
|
||||
<div class="piggy-summary__row">
|
||||
<div>
|
||||
<p class="piggy-summary__label">جمع موجودی داخل قلکها</p>
|
||||
<p class="piggy-summary__value" dir="ltr">
|
||||
{{ formatPiggyAmount(totalGold) }}
|
||||
<span class="piggy-summary__unit">گرم</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="piggy-tile__title">{{ item.title || 'بدون عنوان' }}</div>
|
||||
<div class="piggy-tile__product">{{ fundProductLabel(item) }}</div>
|
||||
<div class="piggy-tile__amount" dir="ltr">
|
||||
{{ formatPiggyAmount(fundWalletGold(item)) }}
|
||||
<span class="piggy-tile__unit">گرم</span>
|
||||
<div class="piggy-summary__badge">
|
||||
<BasicIcon icon="vuesax-linear:safe-home" size="22" />
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div class="piggy-summary__meta">
|
||||
<span>{{ rows.length }} قلک</span>
|
||||
<span class="piggy-summary__dot" />
|
||||
<span>{{ groups.length }} محصول</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<BasicButton class="!w-full mt-5" type="primary" size="middle" block @click="goAdd">
|
||||
<BasicButton class="!w-full mt-3" type="primary" size="middle" block @click="goAdd">
|
||||
<div class="flex items-center justify-center gap-2">
|
||||
<BasicIcon icon="vuesax-linear:add-circle" size="18" />
|
||||
<span>افزودن قلک</span>
|
||||
<span>{{ rows.length ? 'افزودن قلک' : 'ساخت اولین قلک' }}</span>
|
||||
</div>
|
||||
</BasicButton>
|
||||
</section>
|
||||
|
||||
<section class="piggy-body">
|
||||
<div v-if="!rows.length" class="piggy-empty">
|
||||
<div class="piggy-empty__icon">
|
||||
<BasicIcon icon="vuesax-linear:safe-home" size="36" />
|
||||
</div>
|
||||
<p class="piggy-empty__title">قلکی ندارید</p>
|
||||
<p class="piggy-empty__desc">
|
||||
برای جدا کردن بخشی از موجودی هر محصول (مثلاً آبشده یا سکه) یک قلک بسازید.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div v-else class="piggy-groups">
|
||||
<div v-for="group in groups" :key="group.productId" class="piggy-group">
|
||||
<div class="piggy-group__head">
|
||||
<div class="min-w-0">
|
||||
<p class="piggy-group__name">{{ group.productTitle }}</p>
|
||||
<p class="piggy-group__count">{{ group.funds.length }} قلک</p>
|
||||
</div>
|
||||
<p class="piggy-group__total" dir="ltr">
|
||||
{{ formatPiggyAmount(group.totalGold) }}
|
||||
<span>گرم</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="piggy-group__list">
|
||||
<button
|
||||
v-for="item in group.funds"
|
||||
:key="item.id"
|
||||
type="button"
|
||||
class="piggy-row"
|
||||
@click="openInfo(item.id!)"
|
||||
>
|
||||
<div class="piggy-row__icon">
|
||||
<BasicIcon :icon="piggyIconName(item.title)" size="20" />
|
||||
</div>
|
||||
<div class="piggy-row__main">
|
||||
<div class="piggy-row__title">{{ item.title || 'بدون عنوان' }}</div>
|
||||
<div class="piggy-row__sub">کد {{ item.id }}</div>
|
||||
</div>
|
||||
<div class="piggy-row__amount" dir="ltr">
|
||||
<strong>{{ formatPiggyAmount(fundWalletGold(item)) }}</strong>
|
||||
<span>گرم</span>
|
||||
</div>
|
||||
<BasicIcon class="piggy-row__chevron" icon="vuesax-linear:arrow-left-1" size="16" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</a-spin>
|
||||
</div>
|
||||
</template>
|
||||
@@ -66,21 +100,19 @@ import { useFundUserStore } from '@/store/fundStore'
|
||||
import { useSettingsStore } from '@/store/settingsStore'
|
||||
import { message } from 'ant-design-vue'
|
||||
import type { Fund } from '@/models/fund'
|
||||
import cashImg from '@/assets/img/cash_img.svg'
|
||||
import emptyImg from '@/assets/img/cash_empty.svg'
|
||||
import {
|
||||
formatPiggyAmount,
|
||||
fundProductLabel,
|
||||
fundWalletGold,
|
||||
piggyIconForTitle,
|
||||
groupFundsByProduct,
|
||||
piggyIconName,
|
||||
} from './piggyIcons'
|
||||
|
||||
const fundStore = useFundUserStore()
|
||||
const settingsStore = useSettingsStore()
|
||||
const busy = ref(false)
|
||||
const heroImg = cashImg
|
||||
|
||||
const rows = computed(() => (fundStore.myPiggyFunds?.rows ?? []) as Fund[])
|
||||
const groups = computed(() => groupFundsByProduct(rows.value))
|
||||
const totalGold = computed(() => rows.value.reduce((sum, item) => sum + fundWalletGold(item), 0))
|
||||
|
||||
function goBack() {
|
||||
@@ -111,77 +143,114 @@ onMounted(async () => {
|
||||
|
||||
<style scoped>
|
||||
.piggy-page {
|
||||
@apply min-h-full pb-28;
|
||||
background: linear-gradient(180deg, #f3efe8 0%, #f8fafc 42%, #f8fafc 100%);
|
||||
@apply relative min-h-full bg-gray-50 pb-24;
|
||||
}
|
||||
.piggy-topbar {
|
||||
@apply sticky top-0 z-20 flex items-center justify-between px-3 py-3;
|
||||
background: rgba(243, 239, 232, 0.92);
|
||||
backdrop-filter: blur(8px);
|
||||
.piggy-bar {
|
||||
@apply sticky top-0 z-20 flex items-center justify-between bg-white px-3 py-3;
|
||||
box-shadow: 0 1px 0 rgba(15, 23, 42, 0.06);
|
||||
}
|
||||
.piggy-topbar__title {
|
||||
.piggy-bar__title {
|
||||
@apply m-0 text-base font-bold text-gray-900;
|
||||
}
|
||||
.piggy-icon-btn {
|
||||
@apply flex h-9 w-9 items-center justify-center rounded-full border-none bg-white/80 text-gray-800;
|
||||
.piggy-bar__btn {
|
||||
@apply flex h-9 w-9 items-center justify-center rounded-full border-none bg-gray-100 text-gray-800;
|
||||
}
|
||||
.piggy-hero {
|
||||
@apply relative flex items-end justify-between gap-3 overflow-hidden px-5 pb-8 pt-2;
|
||||
.piggy-bar__btn--primary {
|
||||
@apply bg-primary text-white;
|
||||
}
|
||||
.piggy-hero__label {
|
||||
@apply m-0 text-sm text-gray-600;
|
||||
.piggy-summary {
|
||||
@apply px-4 pt-4;
|
||||
}
|
||||
.piggy-hero__value {
|
||||
@apply m-0 mt-1 flex items-baseline gap-2 text-3xl font-bold text-gray-900;
|
||||
.piggy-summary__card {
|
||||
@apply rounded-2xl bg-primary p-4 text-white;
|
||||
}
|
||||
.piggy-hero__unit {
|
||||
@apply text-sm font-bold text-gray-500;
|
||||
.piggy-summary__row {
|
||||
@apply flex items-start justify-between gap-3;
|
||||
}
|
||||
.piggy-hero__hint {
|
||||
@apply m-0 mt-2 text-xs text-gray-500;
|
||||
.piggy-summary__label {
|
||||
@apply m-0 text-sm text-white/80;
|
||||
}
|
||||
.piggy-hero__art {
|
||||
@apply h-28 w-28 shrink-0 object-contain;
|
||||
.piggy-summary__value {
|
||||
@apply m-0 mt-1 flex items-baseline gap-2 text-3xl font-bold;
|
||||
}
|
||||
.piggy-sheet {
|
||||
@apply relative z-10 -mt-2 rounded-t-3xl bg-white px-4 pb-6 pt-5 shadow-sm;
|
||||
min-height: 55vh;
|
||||
.piggy-summary__unit {
|
||||
@apply text-sm font-medium text-white/75;
|
||||
}
|
||||
.piggy-grid {
|
||||
@apply grid grid-cols-2 gap-3;
|
||||
.piggy-summary__badge {
|
||||
@apply flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-white/15;
|
||||
}
|
||||
.piggy-tile {
|
||||
@apply flex flex-col items-center rounded-2xl border-none bg-gray-50 p-3 text-center;
|
||||
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
|
||||
.piggy-summary__meta {
|
||||
@apply mt-4 flex items-center gap-2 text-xs text-white/75;
|
||||
}
|
||||
.piggy-tile__icon-wrap {
|
||||
@apply mb-2 flex h-20 w-full items-center justify-center rounded-xl bg-sky-50;
|
||||
.piggy-summary__dot {
|
||||
@apply h-1 w-1 rounded-full bg-white/50;
|
||||
}
|
||||
.piggy-tile__icon {
|
||||
@apply h-16 w-16 object-contain;
|
||||
.piggy-body {
|
||||
@apply px-4 pt-4;
|
||||
}
|
||||
.piggy-tile__title {
|
||||
@apply w-full truncate text-sm font-bold text-gray-900;
|
||||
.piggy-groups {
|
||||
@apply flex flex-col gap-4;
|
||||
}
|
||||
.piggy-tile__product {
|
||||
@apply mt-0.5 w-full truncate text-[11px] text-gray-500;
|
||||
.piggy-group {
|
||||
@apply overflow-hidden rounded-2xl bg-white;
|
||||
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.05);
|
||||
}
|
||||
.piggy-tile__amount {
|
||||
@apply mt-2 flex items-baseline justify-center gap-1 text-sm font-bold text-primary;
|
||||
.piggy-group__head {
|
||||
@apply flex items-center justify-between gap-3 border-0 border-b border-solid border-gray-100 px-4 py-3;
|
||||
}
|
||||
.piggy-tile__unit {
|
||||
.piggy-group__name {
|
||||
@apply m-0 truncate text-sm font-bold text-gray-900;
|
||||
}
|
||||
.piggy-group__count {
|
||||
@apply m-0 mt-0.5 text-[11px] text-gray-500;
|
||||
}
|
||||
.piggy-group__total {
|
||||
@apply m-0 flex shrink-0 items-baseline gap-1 text-sm font-bold text-primary;
|
||||
}
|
||||
.piggy-group__total span {
|
||||
@apply text-[11px] font-medium text-gray-500;
|
||||
}
|
||||
.piggy-empty {
|
||||
@apply flex flex-col items-center px-4 py-10 text-center;
|
||||
.piggy-group__list {
|
||||
@apply flex flex-col;
|
||||
}
|
||||
.piggy-empty__img {
|
||||
@apply mb-3 h-28 w-28 object-contain opacity-90;
|
||||
.piggy-row {
|
||||
@apply flex w-full items-center gap-3 border-0 border-b border-solid border-gray-50 bg-transparent px-3 py-3 text-right last:border-b-0;
|
||||
}
|
||||
.piggy-row__icon {
|
||||
@apply flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-gray-50 text-primary;
|
||||
}
|
||||
.piggy-row__main {
|
||||
@apply min-w-0 flex-1;
|
||||
}
|
||||
.piggy-row__title {
|
||||
@apply truncate text-sm font-bold text-gray-900;
|
||||
}
|
||||
.piggy-row__sub {
|
||||
@apply mt-0.5 text-[11px] text-gray-400;
|
||||
}
|
||||
.piggy-row__amount {
|
||||
@apply flex shrink-0 flex-col items-end leading-tight;
|
||||
}
|
||||
.piggy-row__amount strong {
|
||||
@apply text-sm font-bold text-gray-900;
|
||||
}
|
||||
.piggy-row__amount span {
|
||||
@apply text-[11px] text-gray-500;
|
||||
}
|
||||
.piggy-row__chevron {
|
||||
@apply shrink-0 text-gray-300;
|
||||
}
|
||||
.piggy-empty {
|
||||
@apply rounded-2xl bg-white px-5 py-10 text-center;
|
||||
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.05);
|
||||
}
|
||||
.piggy-empty__icon {
|
||||
@apply mx-auto mb-3 flex h-16 w-16 items-center justify-center rounded-2xl bg-gray-50 text-primary;
|
||||
}
|
||||
.piggy-empty__title {
|
||||
@apply m-0 text-base font-bold text-gray-800;
|
||||
@apply m-0 text-base font-bold text-gray-900;
|
||||
}
|
||||
.piggy-empty__desc {
|
||||
@apply m-0 mt-2 text-sm text-gray-500;
|
||||
@apply m-0 mt-2 text-sm leading-6 text-gray-500;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,57 +1,59 @@
|
||||
<template>
|
||||
<div class="piggy-info">
|
||||
<header class="piggy-info__top">
|
||||
<button type="button" class="piggy-icon-btn" aria-label="بازگشت" @click="goBack">
|
||||
<BasicIcon icon="vuesax-linear:arrow-right-1" size="22" />
|
||||
</button>
|
||||
<h1 class="piggy-info__title">{{ fund?.title || 'قلک' }}</h1>
|
||||
<button type="button" class="piggy-icon-btn piggy-icon-btn--danger" aria-label="حذف" @click="onDelete">
|
||||
<BasicIcon icon="vuesax-linear:trash" size="20" />
|
||||
</button>
|
||||
<header class="piggy-info__hero">
|
||||
<div class="piggy-info__top">
|
||||
<button type="button" class="piggy-info__btn" aria-label="بازگشت" @click="goBack">
|
||||
<BasicIcon icon="vuesax-linear:arrow-right-1" size="22" />
|
||||
</button>
|
||||
<h1 class="piggy-info__title">{{ fund?.title || 'قلک' }}</h1>
|
||||
<button type="button" class="piggy-info__btn" aria-label="حذف" @click="onDelete">
|
||||
<BasicIcon icon="vuesax-linear:trash" size="18" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<a-spin :spinning="busy">
|
||||
<div class="piggy-info__balance-block">
|
||||
<p class="piggy-info__product">{{ fund?.product?.title || '—' }}</p>
|
||||
<p class="piggy-info__label">موجودی قلک</p>
|
||||
<p class="piggy-info__value" dir="ltr">
|
||||
{{ formatPiggyAmount(potGold) }}
|
||||
<span>گرم</span>
|
||||
</p>
|
||||
<p class="piggy-info__free">
|
||||
آزاد قابل واریز:
|
||||
<strong dir="ltr">{{ formatPiggyAmount(freeGold) }}</strong>
|
||||
گرم
|
||||
</p>
|
||||
</div>
|
||||
</a-spin>
|
||||
</header>
|
||||
|
||||
<a-spin :spinning="busy">
|
||||
<section class="piggy-info__hero">
|
||||
<div class="piggy-info__icon-wrap">
|
||||
<img :src="iconSrc" alt="" class="piggy-info__icon" />
|
||||
</div>
|
||||
<p class="piggy-info__product">{{ fund?.product?.title || '—' }}</p>
|
||||
<p class="piggy-info__balance-label">موجودی این قلک</p>
|
||||
<p class="piggy-info__balance" dir="ltr">
|
||||
<span>{{ formatPiggyAmount(potGold) }}</span>
|
||||
<span class="piggy-info__unit">گرم</span>
|
||||
</p>
|
||||
<p class="piggy-info__free">
|
||||
آزاد قابل واریز:
|
||||
<span dir="ltr" class="font-bold text-gray-800">{{ formatPiggyAmount(freeGold) }}</span>
|
||||
گرم
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<div class="piggy-info__sheet">
|
||||
<div class="piggy-info__actions">
|
||||
<BasicButton class="!flex-1" size="middle" @click="openTx(false)">
|
||||
<div class="flex items-center justify-center gap-2">
|
||||
<BasicIcon icon="vuesax-linear:add-circle" size="18" />
|
||||
<span>برداشت</span>
|
||||
<BasicIcon icon="vuesax-linear:export" size="18" />
|
||||
<span>برداشت به آزاد</span>
|
||||
</div>
|
||||
</BasicButton>
|
||||
<BasicButton class="!flex-1" type="primary" size="middle" @click="openTx(true)">
|
||||
<div class="flex items-center justify-center gap-2">
|
||||
<BasicIcon icon="vuesax-linear:arrow-up-1" size="18" />
|
||||
<span>واریز</span>
|
||||
<BasicIcon icon="vuesax-linear:import" size="18" />
|
||||
<span>واریز از آزاد</span>
|
||||
</div>
|
||||
</BasicButton>
|
||||
</div>
|
||||
|
||||
<section class="piggy-info__tx">
|
||||
<h2 class="piggy-info__tx-title">تراکنشها</h2>
|
||||
<h2 class="piggy-info__tx-title">تراکنشهای این قلک</h2>
|
||||
<div v-if="!transactions.length" class="piggy-info__tx-empty">
|
||||
<img :src="emptyImg" alt="" class="piggy-info__tx-empty-img" />
|
||||
<p class="m-0 text-sm text-gray-500">هیچ تراکنشی یافت نشد</p>
|
||||
<BasicIcon icon="vuesax-linear:receipt-2" size="28" />
|
||||
<p class="m-0 mt-2 text-sm text-gray-500">هنوز جابهجایی ثبت نشده</p>
|
||||
</div>
|
||||
<div v-else class="piggy-info__tx-list">
|
||||
<div v-for="tx in transactions" :key="tx.id" class="piggy-info__tx-row">
|
||||
<div class="min-w-0 text-right">
|
||||
<div class="piggy-info__tx-dot" />
|
||||
<div class="min-w-0 flex-1 text-right">
|
||||
<div class="truncate text-sm font-bold text-gray-900">
|
||||
{{ tx.type_str || tx.type || 'تراکنش' }}
|
||||
</div>
|
||||
@@ -63,12 +65,16 @@
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</a-spin>
|
||||
</div>
|
||||
|
||||
<BasicModal v-model:visible="txVisible" :title="isDeposit ? 'واریز به قلک' : 'برداشت از قلک'">
|
||||
<div class="flex flex-col gap-3 p-1">
|
||||
<p class="m-0 text-xs text-gray-500">
|
||||
{{ isDeposit ? `حداکثر آزاد: ${formatPiggyAmount(freeGold)} گرم` : `حداکثر قلک: ${formatPiggyAmount(potGold)} گرم` }}
|
||||
{{
|
||||
isDeposit
|
||||
? `از موجودی آزاد «${fund?.product?.title || 'محصول'}» — حداکثر ${formatPiggyAmount(freeGold)} گرم`
|
||||
: `برگرداندن به آزاد — حداکثر ${formatPiggyAmount(potGold)} گرم`
|
||||
}}
|
||||
</p>
|
||||
<BasicInput v-model:value="txValue" type="number" placeholder="مقدار به گرم" />
|
||||
<BasicButton class="!w-full" type="primary" block :disabled="busy" @click="submitTx">
|
||||
@@ -95,8 +101,7 @@ import { useWalletAssets } from '@/composables/useWalletAssets'
|
||||
import { message, Modal } from 'ant-design-vue'
|
||||
import type { Fund } from '@/models/fund'
|
||||
import type { Transaction } from '@/models/transaction'
|
||||
import emptyImg from '@/assets/img/cash_empty.svg'
|
||||
import { formatPiggyAmount, piggyIconForTitle } from './piggyIcons'
|
||||
import { formatPiggyAmount } from './piggyIcons'
|
||||
|
||||
const fundStore = useFundUserStore()
|
||||
const settingsStore = useSettingsStore()
|
||||
@@ -110,7 +115,6 @@ const isDeposit = ref(true)
|
||||
const txValue = ref<string | number | undefined>()
|
||||
|
||||
const fundId = computed(() => parseInt(String(router.currentRoute.value.params.id), 10))
|
||||
const iconSrc = computed(() => piggyIconForTitle(fund.value?.title))
|
||||
|
||||
const potGold = computed(() => {
|
||||
const wallets = Array.isArray(fund.value?.wallet)
|
||||
@@ -216,61 +220,63 @@ onMounted(async () => {
|
||||
.piggy-info {
|
||||
@apply min-h-full bg-gray-50 pb-28;
|
||||
}
|
||||
.piggy-info__hero {
|
||||
@apply bg-primary px-3 pb-8 pt-3 text-white;
|
||||
}
|
||||
.piggy-info__top {
|
||||
@apply sticky top-0 z-20 flex items-center justify-between bg-white px-3 py-3 shadow-sm;
|
||||
@apply flex items-center justify-between;
|
||||
}
|
||||
.piggy-info__btn {
|
||||
@apply flex h-9 w-9 items-center justify-center rounded-full border-none bg-white/15 text-white;
|
||||
}
|
||||
.piggy-info__title {
|
||||
@apply m-0 max-w-[55%] truncate text-base font-bold text-gray-900;
|
||||
@apply m-0 max-w-[55%] truncate text-base font-bold text-white;
|
||||
}
|
||||
.piggy-icon-btn {
|
||||
@apply flex h-9 w-9 items-center justify-center rounded-full border-none bg-gray-50 text-gray-800;
|
||||
}
|
||||
.piggy-icon-btn--danger {
|
||||
@apply text-red-500;
|
||||
}
|
||||
.piggy-info__hero {
|
||||
@apply flex flex-col items-center px-4 pt-6;
|
||||
}
|
||||
.piggy-info__icon-wrap {
|
||||
@apply flex h-36 w-36 items-center justify-center rounded-3xl bg-sky-50;
|
||||
}
|
||||
.piggy-info__icon {
|
||||
@apply h-28 w-28 object-contain;
|
||||
.piggy-info__balance-block {
|
||||
@apply mt-6 text-center;
|
||||
}
|
||||
.piggy-info__product {
|
||||
@apply m-0 mt-3 rounded-full bg-white px-3 py-1 text-xs text-gray-600 shadow-sm;
|
||||
@apply m-0 inline-flex rounded-full bg-white/15 px-3 py-1 text-xs text-white/90;
|
||||
}
|
||||
.piggy-info__balance-label {
|
||||
@apply m-0 mt-4 text-sm text-gray-500;
|
||||
.piggy-info__label {
|
||||
@apply m-0 mt-4 text-sm text-white/75;
|
||||
}
|
||||
.piggy-info__balance {
|
||||
@apply m-0 mt-1 flex items-baseline gap-2 text-3xl font-bold text-gray-900;
|
||||
.piggy-info__value {
|
||||
@apply m-0 mt-1 flex items-baseline justify-center gap-2 text-4xl font-bold;
|
||||
}
|
||||
.piggy-info__unit {
|
||||
@apply text-sm font-bold text-gray-500;
|
||||
.piggy-info__value span {
|
||||
@apply text-sm font-medium text-white/75;
|
||||
}
|
||||
.piggy-info__free {
|
||||
@apply m-0 mt-2 text-xs text-gray-500;
|
||||
@apply m-0 mt-3 text-xs text-white/70;
|
||||
}
|
||||
.piggy-info__free strong {
|
||||
@apply text-white;
|
||||
}
|
||||
.piggy-info__sheet {
|
||||
@apply relative z-10 -mt-4 rounded-t-3xl bg-gray-50 px-4 pt-5;
|
||||
}
|
||||
.piggy-info__actions {
|
||||
@apply mt-5 flex gap-3 px-4;
|
||||
@apply flex gap-2;
|
||||
}
|
||||
.piggy-info__tx {
|
||||
@apply mt-6 px-4;
|
||||
@apply mt-5;
|
||||
}
|
||||
.piggy-info__tx-title {
|
||||
@apply m-0 mb-3 text-base font-bold text-gray-900;
|
||||
}
|
||||
.piggy-info__tx-empty {
|
||||
@apply flex flex-col items-center rounded-2xl bg-white px-4 py-10 shadow-sm;
|
||||
}
|
||||
.piggy-info__tx-empty-img {
|
||||
@apply mb-3 h-24 w-24 object-contain opacity-80;
|
||||
@apply flex flex-col items-center rounded-2xl bg-white px-4 py-10 text-gray-400;
|
||||
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.05);
|
||||
}
|
||||
.piggy-info__tx-list {
|
||||
@apply flex flex-col gap-2;
|
||||
}
|
||||
.piggy-info__tx-row {
|
||||
@apply flex items-center justify-between gap-3 rounded-2xl bg-white p-3 shadow-sm;
|
||||
@apply flex items-center gap-3 rounded-2xl bg-white p-3;
|
||||
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.05);
|
||||
}
|
||||
.piggy-info__tx-dot {
|
||||
@apply h-2.5 w-2.5 shrink-0 rounded-full bg-primary;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,36 +1,59 @@
|
||||
<template>
|
||||
<div class="piggy-create">
|
||||
<header class="piggy-create__top">
|
||||
<button type="button" class="piggy-icon-btn" aria-label="بازگشت" @click="goBack">
|
||||
<header class="piggy-bar">
|
||||
<button type="button" class="piggy-bar__btn" aria-label="بازگشت" @click="goBack">
|
||||
<BasicIcon icon="vuesax-linear:arrow-right-1" size="22" />
|
||||
</button>
|
||||
<h1 class="piggy-create__title">{{ editId ? 'ویرایش قلک' : 'ساخت قلک' }}</h1>
|
||||
<h1 class="piggy-bar__title">{{ editId ? 'ویرایش قلک' : 'ساخت قلک' }}</h1>
|
||||
<span class="w-9" />
|
||||
</header>
|
||||
|
||||
<a-spin :spinning="busy">
|
||||
<div class="piggy-create__body">
|
||||
<div class="piggy-create__preview">
|
||||
<img :src="previewImg" alt="" class="piggy-create__preview-img" />
|
||||
</div>
|
||||
<section class="piggy-create__step">
|
||||
<div class="piggy-create__step-head">
|
||||
<span class="piggy-create__step-num">۱</span>
|
||||
<div>
|
||||
<h2 class="piggy-create__step-title">محصول</h2>
|
||||
<p class="piggy-create__step-desc">این قلک فقط برای همین محصول پر میشود</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="piggy-create__card">
|
||||
<label class="piggy-create__label">محصول قلک</label>
|
||||
<BasicSelect
|
||||
v-model:value="productId"
|
||||
class="!w-full"
|
||||
key-name="id"
|
||||
value-name="title"
|
||||
:items="products"
|
||||
:disabled="Boolean(editId)"
|
||||
placeholder="انتخاب محصول"
|
||||
/>
|
||||
<p class="piggy-create__hint">هر قلک فقط یک محصول دارد — طلا و سکه مخلوط نمیشوند</p>
|
||||
</div>
|
||||
<div v-if="editId" class="piggy-create__locked">
|
||||
<BasicIcon icon="vuesax-linear:box-1" size="18" />
|
||||
<span>{{ lockedProductTitle || 'محصول ثابت' }}</span>
|
||||
</div>
|
||||
<div v-else class="piggy-create__products">
|
||||
<button
|
||||
v-for="p in products"
|
||||
:key="p.id"
|
||||
type="button"
|
||||
class="piggy-create__product"
|
||||
:class="{ 'piggy-create__product--on': productId === p.id }"
|
||||
@click="productId = p.id"
|
||||
>
|
||||
<BasicIcon icon="vuesax-linear:box-1" size="18" />
|
||||
<span class="truncate">{{ p.title || `محصول ${p.id}` }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="piggy-create__step">
|
||||
<div class="piggy-create__step-head">
|
||||
<span class="piggy-create__step-num">۲</span>
|
||||
<div>
|
||||
<h2 class="piggy-create__step-title">نام قلک</h2>
|
||||
<p class="piggy-create__step-desc">مثلاً پسانداز، هدیه یا ازدواج</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="piggy-create__title-row">
|
||||
<div class="piggy-create__title-icon">
|
||||
<BasicIcon :icon="piggyIconName(title)" size="22" />
|
||||
</div>
|
||||
<BasicInput v-model:value="title" class="!flex-1" placeholder="عنوان قلک" maxlength="25" />
|
||||
</div>
|
||||
|
||||
<div class="piggy-create__card">
|
||||
<label class="piggy-create__label">عنوان قلک</label>
|
||||
<BasicInput v-model:value="title" placeholder="مثلاً پسانداز" maxlength="25" />
|
||||
<div class="piggy-create__presets">
|
||||
<button
|
||||
v-for="preset in presets"
|
||||
@@ -43,12 +66,10 @@
|
||||
{{ preset }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="piggy-create__footer">
|
||||
<BasicButton class="!w-full" type="primary" size="middle" block :disabled="busy" @click="submit">
|
||||
{{ editId ? 'ذخیره تغییرات' : 'ساخت قلک' }}
|
||||
{{ editId ? 'ذخیره' : 'ساخت قلک' }}
|
||||
</BasicButton>
|
||||
</div>
|
||||
</a-spin>
|
||||
@@ -64,7 +85,7 @@ import { useFundUserStore } from '@/store/fundStore'
|
||||
import { useSettingsStore } from '@/store/settingsStore'
|
||||
import { message } from 'ant-design-vue'
|
||||
import type { Product } from '@/models/products'
|
||||
import { PIGGY_PRESETS, piggyIconForTitle } from './piggyIcons'
|
||||
import { PIGGY_PRESETS, piggyIconName } from './piggyIcons'
|
||||
|
||||
const fundStore = useFundUserStore()
|
||||
const settingsStore = useSettingsStore()
|
||||
@@ -72,6 +93,7 @@ const busy = ref(false)
|
||||
const title = ref('پس انداز')
|
||||
const productId = ref<number | undefined>()
|
||||
const products = ref<Product[]>([])
|
||||
const lockedProductTitle = ref('')
|
||||
const presets = [...PIGGY_PRESETS]
|
||||
|
||||
const editId = computed(() => {
|
||||
@@ -80,8 +102,6 @@ const editId = computed(() => {
|
||||
return Number.isFinite(n) && n > 0 ? n : 0
|
||||
})
|
||||
|
||||
const previewImg = computed(() => piggyIconForTitle(title.value))
|
||||
|
||||
function goBack() {
|
||||
router.back()
|
||||
}
|
||||
@@ -127,6 +147,9 @@ onMounted(async () => {
|
||||
const fund = await getFundById(editId.value)
|
||||
title.value = fund?.title ?? ''
|
||||
productId.value = fund?.product_id
|
||||
lockedProductTitle.value = fund?.product?.title || ''
|
||||
} else if (products.value.length === 1) {
|
||||
productId.value = products.value[0]?.id
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
@@ -138,45 +161,62 @@ onMounted(async () => {
|
||||
|
||||
<style scoped>
|
||||
.piggy-create {
|
||||
@apply min-h-full bg-gray-50 pb-28;
|
||||
@apply min-h-full bg-gray-50 pb-24;
|
||||
}
|
||||
.piggy-create__top {
|
||||
@apply sticky top-0 z-20 flex items-center justify-between bg-white px-3 py-3 shadow-sm;
|
||||
.piggy-bar {
|
||||
@apply sticky top-0 z-20 flex items-center justify-between bg-white px-3 py-3;
|
||||
box-shadow: 0 1px 0 rgba(15, 23, 42, 0.06);
|
||||
}
|
||||
.piggy-create__title {
|
||||
.piggy-bar__title {
|
||||
@apply m-0 text-base font-bold text-gray-900;
|
||||
}
|
||||
.piggy-icon-btn {
|
||||
@apply flex h-9 w-9 items-center justify-center rounded-full border-none bg-gray-50 text-gray-800;
|
||||
.piggy-bar__btn {
|
||||
@apply flex h-9 w-9 items-center justify-center rounded-full border-none bg-gray-100 text-gray-800;
|
||||
}
|
||||
.piggy-create__body {
|
||||
@apply flex flex-col items-center gap-4 px-4 pt-6;
|
||||
@apply flex flex-col gap-4 px-4 pt-4;
|
||||
}
|
||||
.piggy-create__preview {
|
||||
@apply flex h-32 w-32 items-center justify-center rounded-2xl bg-sky-50;
|
||||
.piggy-create__step {
|
||||
@apply rounded-2xl bg-white p-4;
|
||||
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.05);
|
||||
}
|
||||
.piggy-create__preview-img {
|
||||
@apply h-24 w-24 object-contain;
|
||||
.piggy-create__step-head {
|
||||
@apply mb-4 flex items-start gap-3;
|
||||
}
|
||||
.piggy-create__card {
|
||||
@apply w-full rounded-2xl bg-white p-4 shadow-sm;
|
||||
.piggy-create__step-num {
|
||||
@apply flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-primary text-sm font-bold text-white;
|
||||
}
|
||||
.piggy-create__label {
|
||||
@apply mb-2 block text-sm font-bold text-gray-800;
|
||||
.piggy-create__step-title {
|
||||
@apply m-0 text-sm font-bold text-gray-900;
|
||||
}
|
||||
.piggy-create__hint {
|
||||
@apply m-0 mt-2 text-xs leading-5 text-gray-500;
|
||||
.piggy-create__step-desc {
|
||||
@apply m-0 mt-1 text-xs text-gray-500;
|
||||
}
|
||||
.piggy-create__products {
|
||||
@apply grid grid-cols-2 gap-2;
|
||||
}
|
||||
.piggy-create__product {
|
||||
@apply flex items-center gap-2 rounded-xl border-none bg-gray-50 px-3 py-3 text-right text-sm text-gray-700;
|
||||
}
|
||||
.piggy-create__product--on {
|
||||
@apply bg-primary text-white;
|
||||
}
|
||||
.piggy-create__locked {
|
||||
@apply flex items-center gap-2 rounded-xl bg-gray-50 px-3 py-3 text-sm font-bold text-gray-800;
|
||||
}
|
||||
.piggy-create__title-row {
|
||||
@apply flex items-center gap-2;
|
||||
}
|
||||
.piggy-create__title-icon {
|
||||
@apply flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-gray-50 text-primary;
|
||||
}
|
||||
.piggy-create__presets {
|
||||
@apply mt-3 flex gap-2 overflow-x-auto whitespace-nowrap pb-1;
|
||||
@apply mt-3 flex flex-wrap gap-2;
|
||||
}
|
||||
.piggy-create__chip {
|
||||
@apply shrink-0 rounded-full border-none bg-gray-100 px-3 py-2 text-xs text-gray-600;
|
||||
@apply rounded-full border-none bg-gray-100 px-3 py-2 text-xs text-gray-600;
|
||||
}
|
||||
.piggy-create__chip--on {
|
||||
@apply bg-primary text-white;
|
||||
}
|
||||
.piggy-create__footer {
|
||||
@apply fixed bottom-20 left-0 right-0 z-20 px-4;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
import homeImg from '@/assets/img/cash_home.svg'
|
||||
import giftImg from '@/assets/img/cash_gift.svg'
|
||||
import circulationImg from '@/assets/img/cash_circulation.svg'
|
||||
import loveImg from '@/assets/img/cash_love.svg'
|
||||
import repairsImg from '@/assets/img/cash_repairs.svg'
|
||||
import savingImg from '@/assets/img/cash_saving.svg'
|
||||
import type { Fund } from '@/models/fund'
|
||||
|
||||
export const PIGGY_PRESETS = [
|
||||
@@ -15,21 +9,22 @@ export const PIGGY_PRESETS = [
|
||||
'خرید لوازم منزل',
|
||||
] as const
|
||||
|
||||
export function piggyIconForTitle(title?: string | null): string {
|
||||
/** آیکون vuesax برای عنوان قلک — بدون تصویر سهبعدی گیجکننده */
|
||||
export function piggyIconName(title?: string | null): string {
|
||||
switch ((title ?? '').trim()) {
|
||||
case 'خرید لوازم منزل':
|
||||
return homeImg
|
||||
return 'vuesax-linear:house-2'
|
||||
case 'هدیه':
|
||||
return giftImg
|
||||
return 'vuesax-linear:gift'
|
||||
case 'گردش':
|
||||
return circulationImg
|
||||
return 'vuesax-linear:airplane'
|
||||
case 'ازدواج':
|
||||
return loveImg
|
||||
return 'vuesax-linear:heart'
|
||||
case 'تعمیرات':
|
||||
return repairsImg
|
||||
return 'vuesax-linear:setting-2'
|
||||
case 'پس انداز':
|
||||
default:
|
||||
return savingImg
|
||||
return 'vuesax-linear:safe-home'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,8 +39,35 @@ export function fundProductLabel(item?: Fund | null): string {
|
||||
return item.product?.title || (item as { product_title?: string }).product_title || '—'
|
||||
}
|
||||
|
||||
export function fundProductId(item?: Fund | null): number {
|
||||
return Number(item?.product_id ?? item?.product?.id ?? 0)
|
||||
}
|
||||
|
||||
export function formatPiggyAmount(value: number): string {
|
||||
const n = Number(value) || 0
|
||||
if (Math.abs(n) < 0.0005) return '۰'
|
||||
return n.toLocaleString('fa-IR', { maximumFractionDigits: 3 })
|
||||
}
|
||||
|
||||
export type PiggyProductGroup = {
|
||||
productId: number
|
||||
productTitle: string
|
||||
totalGold: number
|
||||
funds: Fund[]
|
||||
}
|
||||
|
||||
export function groupFundsByProduct(funds: Fund[]): PiggyProductGroup[] {
|
||||
const map = new Map<number, PiggyProductGroup>()
|
||||
for (const fund of funds) {
|
||||
const productId = fundProductId(fund) || -1
|
||||
const productTitle = fundProductLabel(fund)
|
||||
let group = map.get(productId)
|
||||
if (!group) {
|
||||
group = { productId, productTitle, totalGold: 0, funds: [] }
|
||||
map.set(productId, group)
|
||||
}
|
||||
group.funds.push(fund)
|
||||
group.totalGold += fundWalletGold(fund)
|
||||
}
|
||||
return [...map.values()].sort((a, b) => a.productTitle.localeCompare(b.productTitle, 'fa'))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user