fix some bugs
This commit is contained in:
+10
-1
@@ -1,10 +1,19 @@
|
|||||||
import type { TableInput } from '@core/type/baseTable'
|
import type { TableInput } from '@core/type/baseTable'
|
||||||
import useAxios from '@core/utils/useAxios'
|
import useAxios from '@core/utils/useAxios'
|
||||||
// import { Customer } from '@/model/customer'
|
// import { Customer } from '@/model/customer'
|
||||||
import type { Charge, Wallet, Withdraw, SendWeight } from '@/model/wallet'
|
import type { Charge, SendWeight, Wallet, WalletChargeTarget, Withdraw } from '@/model/wallet'
|
||||||
|
|
||||||
export const getWalletTable = async (params?: TableInput) =>
|
export const getWalletTable = async (params?: TableInput) =>
|
||||||
await useAxios.post<Array<Wallet>>('/admin/wallet/table', params)
|
await useAxios.post<Array<Wallet>>('/admin/wallet/table', params)
|
||||||
|
|
||||||
|
export const getWalletChargeTargets = async () =>
|
||||||
|
await useAxios.get<{ targets: WalletChargeTarget[] }>('/admin/wallet/charge-targets')
|
||||||
|
|
||||||
|
/** شارژ یکپارچه — نقدی یا محصول (product_id=null یعنی ریالی) */
|
||||||
|
export const chargeWalletUnified = async (params: Charge) =>
|
||||||
|
await useAxios.post<boolean>('/admin/wallet/charge_wallet', params)
|
||||||
|
|
||||||
|
/** @deprecated از chargeWalletUnified استفاده کنید */
|
||||||
export const chargeWallet = async (params: Charge, type: 'gold' | 'money') =>
|
export const chargeWallet = async (params: Charge, type: 'gold' | 'money') =>
|
||||||
type === 'money'
|
type === 'money'
|
||||||
? await useAxios.post<boolean>('/admin/wallet/charge', params)
|
? await useAxios.post<boolean>('/admin/wallet/charge', params)
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ export interface Customer {
|
|||||||
wallet: Wallet
|
wallet: Wallet
|
||||||
wallet_cash?: number
|
wallet_cash?: number
|
||||||
wallet_gold?: number
|
wallet_gold?: number
|
||||||
|
wallet_metal_gold?: Record<string, number>
|
||||||
n_code: string | number
|
n_code: string | number
|
||||||
last_seen_at:string
|
last_seen_at:string
|
||||||
profit_amount: number
|
profit_amount: number
|
||||||
|
|||||||
@@ -2,6 +2,11 @@ import type { Customer } from '@/model/customer'
|
|||||||
|
|
||||||
export interface Order {
|
export interface Order {
|
||||||
amount?: number
|
amount?: number
|
||||||
|
amount_display?: number
|
||||||
|
amount_unit?: string
|
||||||
|
order_metal_id?: number
|
||||||
|
order_metal_label?: string
|
||||||
|
metal_prices?: Record<string, number>
|
||||||
created_at?: string
|
created_at?: string
|
||||||
customer?: Customer
|
customer?: Customer
|
||||||
customer_id?: number
|
customer_id?: number
|
||||||
|
|||||||
@@ -58,6 +58,16 @@ export interface SetActiveTime {
|
|||||||
auto_close_at: string
|
auto_close_at: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface MetalBalanceItem {
|
||||||
|
metal_id: number
|
||||||
|
label: string
|
||||||
|
balance: number
|
||||||
|
goldBalance: number
|
||||||
|
cashBalance?: number
|
||||||
|
goldBalanceOfLiman?: number
|
||||||
|
goldBalanceOfKalhor?: number
|
||||||
|
}
|
||||||
|
|
||||||
export interface Balance {
|
export interface Balance {
|
||||||
cashBalance: number
|
cashBalance: number
|
||||||
cashBalanceOfKalhor: number
|
cashBalanceOfKalhor: number
|
||||||
@@ -65,6 +75,7 @@ export interface Balance {
|
|||||||
goldBalance: number
|
goldBalance: number
|
||||||
goldBalanceOfKalhor: number
|
goldBalanceOfKalhor: number
|
||||||
goldBalanceOfLiman: number
|
goldBalanceOfLiman: number
|
||||||
|
metalBalances?: MetalBalanceItem[]
|
||||||
mazanehBalance: number
|
mazanehBalance: number
|
||||||
mazanehBalanceOfKalhor: number
|
mazanehBalanceOfKalhor: number
|
||||||
income_money: number
|
income_money: number
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ export interface Transaction {
|
|||||||
hash: string
|
hash: string
|
||||||
id: number
|
id: number
|
||||||
note: number
|
note: number
|
||||||
|
metal_gold?: Record<string, number>
|
||||||
|
transaction_metal_id?: number
|
||||||
|
transaction_metal_label?: string
|
||||||
order_id: number
|
order_id: number
|
||||||
transaction_type: string
|
transaction_type: string
|
||||||
type: number
|
type: number
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
import type { User } from '@/model/user'
|
import type { User } from '@/model/user'
|
||||||
|
|
||||||
|
export interface WalletMetalBalance {
|
||||||
|
metal_id: number
|
||||||
|
label: string
|
||||||
|
gold: number
|
||||||
|
goldToCash: number
|
||||||
|
display_unit?: string
|
||||||
|
}
|
||||||
|
|
||||||
export interface Wallet {
|
export interface Wallet {
|
||||||
owner: number
|
owner: number
|
||||||
id: number
|
id: number
|
||||||
@@ -7,9 +15,18 @@ export interface Wallet {
|
|||||||
cash: number
|
cash: number
|
||||||
fund_id?: number | null
|
fund_id?: number | null
|
||||||
product_id?: number | null
|
product_id?: number | null
|
||||||
|
metal_gold?: Record<string, number>
|
||||||
customer: User
|
customer: User
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface WalletTableSums {
|
||||||
|
cash?: number
|
||||||
|
gold?: number
|
||||||
|
cashToGold?: number
|
||||||
|
goldToCash?: number
|
||||||
|
metalBalances?: WalletMetalBalance[]
|
||||||
|
}
|
||||||
|
|
||||||
export interface WithdrawTransaction {
|
export interface WithdrawTransaction {
|
||||||
title?: string
|
title?: string
|
||||||
gold?: number
|
gold?: number
|
||||||
@@ -37,6 +54,20 @@ export interface Withdraw {
|
|||||||
|
|
||||||
export type WalletChargeDirection = 'charge' | 'discharge'
|
export type WalletChargeDirection = 'charge' | 'discharge'
|
||||||
|
|
||||||
|
export type WalletChargeTargetKind = 'money' | 'metal'
|
||||||
|
|
||||||
|
export interface WalletChargeTarget {
|
||||||
|
key: string
|
||||||
|
kind: WalletChargeTargetKind
|
||||||
|
label: string
|
||||||
|
unit: string
|
||||||
|
product_id?: number | null
|
||||||
|
metal_id?: number
|
||||||
|
uses_main_wallet?: boolean
|
||||||
|
trade_by_quantity?: boolean
|
||||||
|
group_type?: string
|
||||||
|
}
|
||||||
|
|
||||||
export interface Charge {
|
export interface Charge {
|
||||||
value?: number
|
value?: number
|
||||||
note?: string
|
note?: string
|
||||||
@@ -44,6 +75,8 @@ export interface Charge {
|
|||||||
talabBedehi?: boolean
|
talabBedehi?: boolean
|
||||||
direction?: WalletChargeDirection
|
direction?: WalletChargeDirection
|
||||||
amountAbs?: number | string
|
amountAbs?: number | string
|
||||||
|
chargeTargetKey?: string | null
|
||||||
|
product_id?: number | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WithdrawStatus {
|
export interface WithdrawStatus {
|
||||||
|
|||||||
@@ -27,13 +27,27 @@
|
|||||||
un-checked-children="مشتری"
|
un-checked-children="مشتری"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mb-4 px-2" v-if="balance?.metalBalances?.length">
|
||||||
|
<div class="grid grid-cols-1 gap-2 sm:grid-cols-2">
|
||||||
|
<div
|
||||||
|
v-for="item in balance.metalBalances"
|
||||||
|
:key="item.metal_id"
|
||||||
|
class="rounded-lg border border-gray-200 bg-white px-3 py-2"
|
||||||
|
>
|
||||||
|
<p class="mb-1 text-xs text-gray-500">{{ metalBalanceTitleWithUnit(item.metal_id, item.label) }}</p>
|
||||||
|
<p class="mb-0 text-base font-bold">
|
||||||
|
{{ formatMetalWeightFromGrams(item.goldBalanceOfLiman ?? 0, item.metal_id) }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="px-2 col-span-2 grid grid-cols-4 gap-1">
|
<div class="px-2 col-span-2 grid grid-cols-4 gap-1">
|
||||||
<div class="bg-blue rounded text-white py-1 flex justify-center items-center">تراز ها</div>
|
<div class="bg-blue rounded text-white py-1 flex justify-center items-center">تراز ها</div>
|
||||||
<div class="bg-blue rounded text-white py-1 flex justify-center items-center">
|
<div class="bg-blue rounded text-white py-1 flex justify-center items-center">
|
||||||
تراز ریالی (ریال)
|
تراز ریالی (ریال)
|
||||||
</div>
|
</div>
|
||||||
<div class="bg-blue rounded text-white py-1 flex justify-center items-center">
|
<div class="bg-blue rounded text-white py-1 flex justify-center items-center">
|
||||||
تراز طلایی (گرم)
|
{{ m('تراز طلایی (گرم)') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="bg-blue rounded text-white py-1 flex justify-center items-center">
|
<div class="bg-blue rounded text-white py-1 flex justify-center items-center">
|
||||||
تراز مظنه (ریال)
|
تراز مظنه (ریال)
|
||||||
@@ -187,6 +201,9 @@ import { balance, getTaraz } from '@core/utils/price'
|
|||||||
import { usePrice } from '@core/utils/usePrice'
|
import { usePrice } from '@core/utils/usePrice'
|
||||||
import { onMounted, ref } from 'vue'
|
import { onMounted, ref } from 'vue'
|
||||||
import { getNow, toJalali } from '@core/utils/useDataTime'
|
import { getNow, toJalali } from '@core/utils/useDataTime'
|
||||||
|
import { m } from '@/utils/metalLabels'
|
||||||
|
import { metalBalanceTitleWithUnit } from '@/utils/adminMetalLabels'
|
||||||
|
import { formatMetalWeightFromGrams } from '@/utils/metalWeight'
|
||||||
const startTaraz = ref<string>('')
|
const startTaraz = ref<string>('')
|
||||||
const endTaraz = ref<string>('')
|
const endTaraz = ref<string>('')
|
||||||
const selectedItem = ref<object>({})
|
const selectedItem = ref<object>({})
|
||||||
|
|||||||
@@ -38,7 +38,7 @@
|
|||||||
</span>
|
</span>
|
||||||
<span class="dash-quick-sheet__featured-text">
|
<span class="dash-quick-sheet__featured-text">
|
||||||
<span class="dash-quick-sheet__featured-label">شارژ / دشارژ کیف پول</span>
|
<span class="dash-quick-sheet__featured-label">شارژ / دشارژ کیف پول</span>
|
||||||
<span class="dash-quick-sheet__featured-hint">مدیریت موجودی ریالی و طلایی</span>
|
<span class="dash-quick-sheet__featured-hint">{{ metalLabel.manageGoldCash() }}</span>
|
||||||
</span>
|
</span>
|
||||||
<BaseIcon icon="vuesax-linear:arrow-left-2" :size="16" class="dash-quick-sheet__featured-arrow" />
|
<BaseIcon icon="vuesax-linear:arrow-left-2" :size="16" class="dash-quick-sheet__featured-arrow" />
|
||||||
</button>
|
</button>
|
||||||
@@ -106,6 +106,7 @@ import type { RouteLocationRaw } from 'vue-router'
|
|||||||
import BasicActionSheet from '@/components/BasicActionSheet.vue'
|
import BasicActionSheet from '@/components/BasicActionSheet.vue'
|
||||||
import DashboardQuickActionBtn from '@/pages/dashboard/components/DashboardQuickActionBtn.vue'
|
import DashboardQuickActionBtn from '@/pages/dashboard/components/DashboardQuickActionBtn.vue'
|
||||||
import { isMobile } from '@core/utils/is'
|
import { isMobile } from '@core/utils/is'
|
||||||
|
import { metalLabel } from '@/utils/adminMetalLabels'
|
||||||
|
|
||||||
type ActionVariant = 'default' | 'accent' | 'danger' | 'primary'
|
type ActionVariant = 'default' | 'accent' | 'danger' | 'primary'
|
||||||
type ItemTone = 'slate' | 'emerald' | 'rose' | 'amber' | 'sky' | 'violet'
|
type ItemTone = 'slate' | 'emerald' | 'rose' | 'amber' | 'sky' | 'violet'
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<BasicActionSheet
|
<BasicActionSheet
|
||||||
v-model:visible="visibleProxy"
|
v-model:visible="visibleProxy"
|
||||||
:title="modalTitle"
|
:title="modalTitle"
|
||||||
modal-width="22rem"
|
modal-width="24rem"
|
||||||
drawer-height="auto"
|
drawer-height="auto"
|
||||||
compact
|
compact
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
@@ -24,33 +24,18 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div v-if="selectedCustomer" class="dashboard-wallet-modal__balances">
|
||||||
v-if="selectedCustomer"
|
|
||||||
class="dashboard-wallet-modal__balances"
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<span class="text-slate-500">طلایی</span>
|
|
||||||
<p class="font-semibold text-slate-800 dark:text-slate-100">{{ formatGold(selectedCustomer) }}</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
<div>
|
||||||
<span class="text-slate-500">ریالی</span>
|
<span class="text-slate-500">ریالی</span>
|
||||||
<p class="font-semibold text-slate-800 dark:text-slate-100">{{ formatCash(selectedCustomer) }}</p>
|
<p class="font-semibold text-slate-800 dark:text-slate-100">{{ formatCash(selectedCustomer) }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="dashboard-wallet-modal__section">
|
|
||||||
<label class="dashboard-wallet-modal__label">نوع کیف پول</label>
|
|
||||||
<a-radio-group v-model:value="walletVariant" button-style="solid" class="flex w-full">
|
|
||||||
<a-radio-button value="money" class="w-1/2 text-center">ریالی</a-radio-button>
|
|
||||||
<a-radio-button value="gold" class="w-1/2 text-center">طلایی</a-radio-button>
|
|
||||||
</a-radio-group>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<WalletChargeForm
|
<WalletChargeForm
|
||||||
ref="chargeFormRef"
|
ref="chargeFormRef"
|
||||||
class="dashboard-wallet-modal__form"
|
class="dashboard-wallet-modal__form"
|
||||||
:model-value="formCharge"
|
:model-value="formCharge"
|
||||||
:variant="walletVariant"
|
:targets="chargeTargets"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="dashboard-wallet-modal__footer">
|
<div class="dashboard-wallet-modal__footer">
|
||||||
@@ -78,16 +63,18 @@ import { message } from 'ant-design-vue'
|
|||||||
import BasicActionSheet from '@/components/BasicActionSheet.vue'
|
import BasicActionSheet from '@/components/BasicActionSheet.vue'
|
||||||
import WalletChargeForm from '@/pages/menuPages/customers/base/components/WalletChargeForm.vue'
|
import WalletChargeForm from '@/pages/menuPages/customers/base/components/WalletChargeForm.vue'
|
||||||
import {
|
import {
|
||||||
|
applyChargeTarget,
|
||||||
buildChargePayload,
|
buildChargePayload,
|
||||||
createEmptyChargeForm,
|
createEmptyChargeForm,
|
||||||
|
resolveChargeTarget,
|
||||||
walletChargeOkText,
|
walletChargeOkText,
|
||||||
walletChargeSuccessMessage,
|
walletChargeSuccessMessage,
|
||||||
walletChargeTitle,
|
walletChargeTitle,
|
||||||
} from '@/pages/menuPages/customers/base/utils/walletChargeForm'
|
} from '@/pages/menuPages/customers/base/utils/walletChargeForm'
|
||||||
import { getCustomersList } from '@/api/customer'
|
import { getCustomersList } from '@/api/customer'
|
||||||
import { chargeWallet } from '@/api/wallet'
|
import { chargeWalletUnified, getWalletChargeTargets } from '@/api/wallet'
|
||||||
import type { Customer } from '@/model/customer'
|
import type { Customer } from '@/model/customer'
|
||||||
import type { Charge, Wallet } from '@/model/wallet'
|
import type { Charge, Wallet, WalletChargeTarget } from '@/model/wallet'
|
||||||
import { usePrice } from '@core/utils/usePrice'
|
import { usePrice } from '@core/utils/usePrice'
|
||||||
|
|
||||||
const props = defineProps<{ visible: boolean }>()
|
const props = defineProps<{ visible: boolean }>()
|
||||||
@@ -101,7 +88,7 @@ const visibleProxy = computed({
|
|||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const submitting = ref(false)
|
const submitting = ref(false)
|
||||||
const customers = ref<Customer[]>([])
|
const customers = ref<Customer[]>([])
|
||||||
const walletVariant = ref<'money' | 'gold'>('money')
|
const chargeTargets = ref<WalletChargeTarget[]>([])
|
||||||
const formCharge = ref<Charge>(createEmptyChargeForm())
|
const formCharge = ref<Charge>(createEmptyChargeForm())
|
||||||
const chargeFormRef = ref<InstanceType<typeof WalletChargeForm>>()
|
const chargeFormRef = ref<InstanceType<typeof WalletChargeForm>>()
|
||||||
|
|
||||||
@@ -123,8 +110,12 @@ const selectedCustomer = computed(() =>
|
|||||||
customers.value.find((customer) => customer.id === formCharge.value.customer_id),
|
customers.value.find((customer) => customer.id === formCharge.value.customer_id),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const selectedTarget = computed(() =>
|
||||||
|
resolveChargeTarget(chargeTargets.value, formCharge.value.chargeTargetKey),
|
||||||
|
)
|
||||||
|
|
||||||
const modalTitle = computed(() =>
|
const modalTitle = computed(() =>
|
||||||
walletChargeTitle(walletVariant.value, formCharge.value.direction),
|
walletChargeTitle(selectedTarget.value, formCharge.value.direction),
|
||||||
)
|
)
|
||||||
|
|
||||||
const okText = computed(() => walletChargeOkText(formCharge.value.direction))
|
const okText = computed(() => walletChargeOkText(formCharge.value.direction))
|
||||||
@@ -138,20 +129,25 @@ function customerWallet(customer: Customer): Wallet | undefined {
|
|||||||
return Array.isArray(wallet) ? wallet[0] : wallet
|
return Array.isArray(wallet) ? wallet[0] : wallet
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatGold(customer: Customer) {
|
|
||||||
const gold = customerWallet(customer)?.gold ?? customer.wallet_gold ?? customer.gold
|
|
||||||
return `${Number(gold ?? 0).toLocaleString('fa-IR', { maximumFractionDigits: 3 })} گرم`
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatCash(customer: Customer) {
|
function formatCash(customer: Customer) {
|
||||||
const cash = customerWallet(customer)?.cash ?? customer.wallet_cash
|
const cash = customerWallet(customer)?.cash ?? customer.wallet_cash
|
||||||
return usePrice(Number(cash ?? 0), true, '')
|
return usePrice(Number(cash ?? 0), true, '')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function loadChargeTargets() {
|
||||||
|
const data = await getWalletChargeTargets()
|
||||||
|
chargeTargets.value = data?.targets ?? []
|
||||||
|
const firstTarget = chargeTargets.value[0]
|
||||||
|
if (firstTarget) {
|
||||||
|
Object.assign(formCharge.value, applyChargeTarget(formCharge.value, firstTarget))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function loadCustomers() {
|
async function loadCustomers() {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
customers.value = (await getCustomersList()) ?? []
|
const [customerList] = await Promise.all([getCustomersList(), loadChargeTargets()])
|
||||||
|
customers.value = customerList ?? []
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
message.error('خطا در بارگذاری لیست مشتریان')
|
message.error('خطا در بارگذاری لیست مشتریان')
|
||||||
@@ -161,8 +157,11 @@ async function loadCustomers() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function resetForm() {
|
function resetForm() {
|
||||||
walletVariant.value = 'money'
|
|
||||||
formCharge.value = createEmptyChargeForm()
|
formCharge.value = createEmptyChargeForm()
|
||||||
|
const firstTarget = chargeTargets.value[0]
|
||||||
|
if (firstTarget) {
|
||||||
|
Object.assign(formCharge.value, applyChargeTarget(formCharge.value, firstTarget))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleClose() {
|
function handleClose() {
|
||||||
@@ -188,7 +187,7 @@ async function submit() {
|
|||||||
|
|
||||||
submitting.value = true
|
submitting.value = true
|
||||||
try {
|
try {
|
||||||
await chargeWallet(buildChargePayload(formCharge.value), walletVariant.value)
|
await chargeWalletUnified(buildChargePayload(formCharge.value))
|
||||||
message.success(walletChargeSuccessMessage(formCharge.value.direction))
|
message.success(walletChargeSuccessMessage(formCharge.value.direction))
|
||||||
closeModal()
|
closeModal()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -231,7 +230,7 @@ watch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
.dashboard-wallet-modal__balances {
|
.dashboard-wallet-modal__balances {
|
||||||
@apply grid grid-cols-2 gap-3 rounded-lg border border-slate-200 bg-slate-50 px-3 py-2.5 text-xs dark:border-slate-600 dark:bg-slate-800/60;
|
@apply grid grid-cols-1 gap-3 rounded-lg border border-slate-200 bg-slate-50 px-3 py-2.5 text-xs dark:border-slate-600 dark:bg-slate-800/60;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dashboard-wallet-modal__footer {
|
.dashboard-wallet-modal__footer {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
toggleGateway,
|
toggleGateway,
|
||||||
} from '@/api/dashboard'
|
} from '@/api/dashboard'
|
||||||
import { getWalletTable } from '@/api/wallet'
|
import { getWalletTable } from '@/api/wallet'
|
||||||
|
import { formatMetalWeightFromGrams } from '@/utils/metalWeight'
|
||||||
import type {
|
import type {
|
||||||
DashboardFlowPoint,
|
DashboardFlowPoint,
|
||||||
DashboardFinance,
|
DashboardFinance,
|
||||||
@@ -23,6 +24,7 @@ import type { Order } from '@/model/order'
|
|||||||
import type { Balance } from '@/model/product'
|
import type { Balance } from '@/model/product'
|
||||||
import { usePrice } from '@core/utils/usePrice'
|
import { usePrice } from '@core/utils/usePrice'
|
||||||
import { toJalali } from '@core/utils/useDataTime'
|
import { toJalali } from '@core/utils/useDataTime'
|
||||||
|
import { metalLabel } from '@/utils/adminMetalLabels'
|
||||||
import { isMobile } from '@core/utils/is'
|
import { isMobile } from '@core/utils/is'
|
||||||
import { isStoreApp } from '@/utils/config'
|
import { isStoreApp } from '@/utils/config'
|
||||||
import { useAdminTheme } from '@core/composable/useAdminTheme'
|
import { useAdminTheme } from '@core/composable/useAdminTheme'
|
||||||
@@ -229,7 +231,10 @@ export function useDashboardPage() {
|
|||||||
master.value = await getDashboard()
|
master.value = await getDashboard()
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatGram(value?: number) {
|
function formatGram(value?: number, metalId?: number) {
|
||||||
|
if (metalId != null) {
|
||||||
|
return formatMetalWeightFromGrams(value ?? 0, metalId)
|
||||||
|
}
|
||||||
return `${Number(value ?? 0).toLocaleString('fa-IR', { maximumFractionDigits: 3 })} گرم`
|
return `${Number(value ?? 0).toLocaleString('fa-IR', { maximumFractionDigits: 3 })} گرم`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,7 +282,7 @@ export function useDashboardPage() {
|
|||||||
return {
|
return {
|
||||||
...chartSurfaceOptions(),
|
...chartSurfaceOptions(),
|
||||||
chart: { type: 'donut', fontFamily: 'inherit', offsetY: 4, foreColor: chartSurfaceOptions().chart.foreColor },
|
chart: { type: 'donut', fontFamily: 'inherit', offsetY: 4, foreColor: chartSurfaceOptions().chart.foreColor },
|
||||||
labels: ['سود معامله', 'کارمزد طلایی', 'هدایای طلا'],
|
labels: ['سود معامله', metalLabel.goldCommission(), metalLabel.goldGifts()],
|
||||||
colors: ['#10b981', '#f59e0b', '#8b5cf6'],
|
colors: ['#10b981', '#f59e0b', '#8b5cf6'],
|
||||||
legend: {
|
legend: {
|
||||||
position: 'bottom',
|
position: 'bottom',
|
||||||
@@ -576,6 +581,8 @@ export function useDashboardPage() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const metalBalanceItems = computed(() => balance.value?.metalBalances ?? [])
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
void refreshAll()
|
void refreshAll()
|
||||||
})
|
})
|
||||||
@@ -588,6 +595,7 @@ export function useDashboardPage() {
|
|||||||
master,
|
master,
|
||||||
finance,
|
finance,
|
||||||
balance,
|
balance,
|
||||||
|
metalBalanceItems,
|
||||||
walletSums,
|
walletSums,
|
||||||
recentOrders,
|
recentOrders,
|
||||||
customerStats,
|
customerStats,
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
/** توضیح و فرمول فارسی — برای tooltip آیکون راهنما */
|
/** توضیح و فرمول فارسی — برای tooltip آیکون راهنما */
|
||||||
|
|
||||||
|
import { m } from '@/utils/metalLabels'
|
||||||
|
|
||||||
export type DashboardHelp = {
|
export type DashboardHelp = {
|
||||||
description: string
|
description: string
|
||||||
formula: string
|
formula: string
|
||||||
@@ -7,133 +9,119 @@ export type DashboardHelp = {
|
|||||||
|
|
||||||
export const MESGHAL_FACTOR = '۴.۳۳۱۸۰۲'
|
export const MESGHAL_FACTOR = '۴.۳۳۱۸۰۲'
|
||||||
|
|
||||||
export const dashboardHero: DashboardHelp = {
|
function help(description: string, formula: string): DashboardHelp {
|
||||||
description: 'آخرین قیمت پایه هر گرم طلا در سیستم.',
|
return {
|
||||||
formula: `قیمت هر مثقال = قیمت هر گرم × ${MESGHAL_FACTOR}`,
|
description: m(description),
|
||||||
|
formula: m(formula),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const dashboardGateway: DashboardHelp = {
|
export const dashboardHero = help(
|
||||||
description: 'وقتی فعال باشد مشتری میتواند از درگاه بانکی واریز کند؛ در حالت غیرفعال فقط واریز دستی ممکن است.',
|
'آخرین قیمت پایه هر گرم طلا در سیستم.',
|
||||||
formula: 'وضعیت: فعال یا غیرفعال',
|
`قیمت هر مثقال = قیمت هر گرم × ${MESGHAL_FACTOR}`
|
||||||
}
|
)
|
||||||
|
|
||||||
|
export const dashboardGateway = help(
|
||||||
|
'وقتی فعال باشد مشتری میتواند از درگاه بانکی واریز کند؛ در حالت غیرفعال فقط واریز دستی ممکن است.',
|
||||||
|
'وضعیت: فعال یا غیرفعال'
|
||||||
|
)
|
||||||
|
|
||||||
export const dashboardKpi = {
|
export const dashboardKpi = {
|
||||||
profit: {
|
profit: help(
|
||||||
description: 'مجموع سود اسپرد (تفاوت قیمت خرید/فروش با مشتری) روی همه سفارشهای ثبتشده در بازه.',
|
'مجموع سود اسپرد (تفاوت قیمت خرید/فروش با مشتری) روی همه سفارشهای ثبتشده در بازه.',
|
||||||
formula: 'سود کل = جمع «سود هر سفارش»',
|
'سود کل = جمع «سود هر سفارش»'
|
||||||
},
|
),
|
||||||
commission: {
|
commission: help(
|
||||||
description: 'مجموع کارمزد طلایی محصول که روی همان سفارشها محاسبه شده.',
|
'مجموع کارمزد طلایی محصول که روی همان سفارشها محاسبه شده.',
|
||||||
formula: 'کارمزد کل = جمع «کارمزد هر سفارش»',
|
'کارمزد کل = جمع «کارمزد هر سفارش»'
|
||||||
},
|
),
|
||||||
orderCount: {
|
orderCount: help(
|
||||||
description: 'تعداد همه سفارشهایی که در بازه انتخابی ثبت شدهاند (هر وضعیتی).',
|
'تعداد همه سفارشهایی که در بازه انتخابی ثبت شدهاند (هر وضعیتی).',
|
||||||
formula: 'تعداد = شمارش سفارشها بین دو تاریخ',
|
'تعداد = شمارش سفارشها بین دو تاریخ'
|
||||||
},
|
),
|
||||||
goldBalance: {
|
goldBalance: help(
|
||||||
description: 'تراز وزنی طلا از سفارشات موفق؛ مثبت یعنی به مشتری بیشتر فروختهاید.',
|
'تراز وزنی طلا از سفارشات موفق؛ مثبت یعنی به مشتری بیشتر فروختهاید.',
|
||||||
formula: 'تراز طلا = گرم فروش − گرم خرید (سفارش موفق)',
|
'تراز طلا = گرم فروش − گرم خرید (سفارش موفق)'
|
||||||
},
|
),
|
||||||
cashBalance: {
|
cashBalance: help(
|
||||||
description: 'تراز ریالی همان سفارشات موفق؛ ورود و خروج نقد از معاملات.',
|
'تراز ریالی همان سفارشات موفق؛ ورود و خروج نقد از معاملات.',
|
||||||
formula: 'تراز ریال = مبلغ فروش − مبلغ خرید',
|
'تراز ریال = مبلغ فروش − مبلغ خرید'
|
||||||
},
|
),
|
||||||
mazanehBalance: {
|
mazanehBalance: help(
|
||||||
description: 'میانگین موزون قیمت معاملات، تبدیلشده به مثقال.',
|
'میانگین موزون قیمت معاملات، تبدیلشده به مثقال.',
|
||||||
formula: `(جمع مبلغ معاملات ÷ جمع گرم) × ${MESGHAL_FACTOR}`,
|
`(جمع مبلغ معاملات ÷ جمع گرم) × ${MESGHAL_FACTOR}`
|
||||||
},
|
),
|
||||||
} as const satisfies Record<string, DashboardHelp>
|
} as const satisfies Record<string, DashboardHelp>
|
||||||
|
|
||||||
export const dashboardDateRange: DashboardHelp = {
|
export const dashboardDateRange = help(
|
||||||
description: 'تاریخ شروع و پایان برای محاسبه کارتها، تراز و نمودارها.',
|
'تاریخ شروع و پایان برای محاسبه کارتها، تراز و نمودارها.',
|
||||||
formula: 'گزارش = از تاریخ … تا تاریخ …',
|
'گزارش = از تاریخ … تا تاریخ …'
|
||||||
}
|
)
|
||||||
|
|
||||||
export const dashboardDateMetrics = {
|
export const dashboardDateMetrics = {
|
||||||
bankDeposit: {
|
bankDeposit: help(
|
||||||
description: 'کل واریزهای موفق مشتریان از درگاه آنلاین در این بازه.',
|
'کل واریزهای موفق مشتریان از درگاه آنلاین در این بازه.',
|
||||||
formula: 'جمع مبلغ واریزهای تأییدشده از درگاه',
|
'جمع مبلغ واریزهای تأییدشده از درگاه'
|
||||||
},
|
),
|
||||||
netIncome: {
|
netIncome: help(
|
||||||
description: 'درآمد پس از کسر ارزش طلای هدیهدادهشده.',
|
'درآمد پس از کسر ارزش طلای هدیهدادهشده.',
|
||||||
formula: 'درآمد خالص = (اختلاف قیمت اصلی و پرداختی) − (گرم هدیه × مظنه)',
|
'درآمد خالص = (اختلاف قیمت اصلی و پرداختی) − (گرم هدیه × مظنه)'
|
||||||
},
|
),
|
||||||
gifts: {
|
gifts: help(
|
||||||
description: 'طلایی که ادمین به مشتریان هدیه داده در همین بازه.',
|
'طلایی که ادمین به مشتریان هدیه داده در همین بازه.',
|
||||||
formula: 'گرم هدیه = جمع طلای ثبتشده در تراکنشهای ادمین',
|
'گرم هدیه = جمع طلای ثبتشده در تراکنشهای ادمین'
|
||||||
},
|
),
|
||||||
} as const satisfies Record<string, DashboardHelp>
|
} as const satisfies Record<string, DashboardHelp>
|
||||||
|
|
||||||
export const dashboardCharts = {
|
export const dashboardCharts = {
|
||||||
incomeMix: {
|
incomeMix: help(
|
||||||
description: 'نشان میدهد سود معامله، کارمزد و هدایا چقدر از درآمد کل را تشکیل میدهند.',
|
'نشان میدهد سود معامله، کارمزد و هدایا چقدر از درآمد کل را تشکیل میدهند.',
|
||||||
formula: 'درآمد = سود معامله + کارمزد + (گرم هدیه × مظنه)',
|
'درآمد = سود معامله + کارمزد + (گرم هدیه × مظنه)'
|
||||||
},
|
),
|
||||||
buySellVolume: {
|
buySellVolume: help(
|
||||||
description: 'مقدار طلای خرید و فروش مشتری در هر روز (فقط سفارش موفق).',
|
'مقدار طلای خرید و فروش مشتری در هر روز (فقط سفارش موفق).',
|
||||||
formula: 'هر روز: گرم خرید مشتری و گرم فروش به مشتری',
|
'هر روز: گرم خرید مشتری و گرم فروش به مشتری'
|
||||||
},
|
),
|
||||||
priceChart: {
|
priceChart: help(
|
||||||
description: 'روند تغییر قیمت پایه هر گرم در ۲۴ ساعت، هفته یا ماه.',
|
'روند تغییر قیمت پایه هر گرم در ۲۴ ساعت، هفته یا ماه.',
|
||||||
formula: 'نمودار = قیمت پایه در هر لحظه',
|
'نمودار = قیمت پایه در هر لحظه'
|
||||||
},
|
),
|
||||||
productActivity: {
|
productActivity: help(
|
||||||
description: 'مقایسه حجم خرید و فروش هر محصول در بازه انتخابی.',
|
'مقایسه حجم خرید و فروش هر محصول در بازه انتخابی.',
|
||||||
formula: 'خرید = جمع گرم مثبت | فروش = جمع گرم منفی (معاملات همان بازه)',
|
'خرید = جمع گرم مثبت | فروش = جمع گرم منفی (معاملات همان بازه)'
|
||||||
},
|
),
|
||||||
} as const satisfies Record<string, DashboardHelp>
|
} as const satisfies Record<string, DashboardHelp>
|
||||||
|
|
||||||
export const dashboardOrders = {
|
export const dashboardOrders = {
|
||||||
peakHours: {
|
peakHours: help(
|
||||||
description: 'توزیع سفارشهای موفق مشتریان خانگی در ۲۴ ساعت شبانهروز؛ برای شناخت ساعات شلوغ.',
|
'توزیع سفارشهای موفق مشتریان خانگی در ۲۴ ساعت شبانهروز؛ برای شناخت ساعات شلوغ.',
|
||||||
formula: 'هر ستون = تعداد سفارش در آن ساعت (جمع چند روز اخیر)',
|
'هر ستون = تعداد سفارش در آن ساعت (جمع چند روز اخیر)'
|
||||||
},
|
),
|
||||||
recent: {
|
recent: help(
|
||||||
description: '۱۴ سفارش آخر با جزئیات مظنه و سود.',
|
'۱۴ سفارش آخر با جزئیات مظنه و سود.',
|
||||||
formula: 'مظنه = قیمت پایه | قیمت مشتری = مظنه ± اسپرد | سود/گرم = تفاوت اسپرد',
|
'مظنه = قیمت پایه | قیمت مشتری = مظنه ± اسپرد | سود/گرم = تفاوت اسپرد'
|
||||||
},
|
),
|
||||||
} as const satisfies Record<string, DashboardHelp>
|
} as const satisfies Record<string, DashboardHelp>
|
||||||
|
|
||||||
export const dashboardCustomers: DashboardHelp = {
|
export const dashboardCustomers = help(
|
||||||
description: 'آمار مشتریان خانگی ثبتشده در سیستم.',
|
'آمار مشتریان خانگی ثبتشده در سیستم.',
|
||||||
formula: 'فعال = دارای کیف پول | بدون احراز = هویت تأیید نشده',
|
'فعال = دارای کیف پول | بدون احراز = هویت تأیید نشده'
|
||||||
}
|
)
|
||||||
|
|
||||||
export const dashboardCustomerMetrics = {
|
export const dashboardCustomerMetrics = {
|
||||||
total: {
|
total: help('همه مشتریان خانگی ثبتشده.', 'شمارش کل مشتریان'),
|
||||||
description: 'همه مشتریان خانگی ثبتشده.',
|
new: help('مشتریانی که در ۳ ماه اخیر ثبتنام کردهاند.', 'ثبتنام در ۹۰ روز گذشته'),
|
||||||
formula: 'شمارش کل مشتریان',
|
active: help('مشتریانی که حداقل یک کیف پول دارند.', 'دارای موجودی در کیف'),
|
||||||
},
|
notVerified: help('مشتریانی که احراز هویت را تکمیل نکردهاند.', 'وضعیت احراز: ناقص'),
|
||||||
new: {
|
|
||||||
description: 'مشتریانی که در ۳ ماه اخیر ثبتنام کردهاند.',
|
|
||||||
formula: 'ثبتنام در ۹۰ روز گذشته',
|
|
||||||
},
|
|
||||||
active: {
|
|
||||||
description: 'مشتریانی که حداقل یک کیف پول دارند.',
|
|
||||||
formula: 'دارای موجودی در کیف',
|
|
||||||
},
|
|
||||||
notVerified: {
|
|
||||||
description: 'مشتریانی که احراز هویت را تکمیل نکردهاند.',
|
|
||||||
formula: 'وضعیت احراز: ناقص',
|
|
||||||
},
|
|
||||||
} as const satisfies Record<string, DashboardHelp>
|
} as const satisfies Record<string, DashboardHelp>
|
||||||
|
|
||||||
export const dashboardWallet: DashboardHelp = {
|
export const dashboardWallet = help(
|
||||||
description: 'مجموع موجودی طلا و ریال همه مشتریان.',
|
'مجموع موجودی طلا و ریال همه مشتریان.',
|
||||||
formula: 'ارزش طلا به ریال = جمع گرم × مظنه لحظهای',
|
'ارزش طلا به ریال = جمع گرم × مظنه لحظهای'
|
||||||
}
|
)
|
||||||
|
|
||||||
export const dashboardWalletMetrics = {
|
export const dashboardWalletMetrics = {
|
||||||
gold: {
|
gold: help('مجموع طلای موجود در کیف همه مشتریان.', 'جمع گرم طلا در کیفها'),
|
||||||
description: 'مجموع طلای موجود در کیف همه مشتریان.',
|
cash: help('مجموع ریال موجود در کیف همه مشتریان.', 'جمع ریال در کیفها'),
|
||||||
formula: 'جمع گرم طلا در کیفها',
|
goldToCash: help('ارزش ریالی کل طلای مشتریان با مظنه فعلی.', 'جمع گرم × قیمت هر گرم'),
|
||||||
},
|
|
||||||
cash: {
|
|
||||||
description: 'مجموع ریال موجود در کیف همه مشتریان.',
|
|
||||||
formula: 'جمع ریال در کیفها',
|
|
||||||
},
|
|
||||||
goldToCash: {
|
|
||||||
description: 'ارزش ریالی کل طلای مشتریان با مظنه فعلی.',
|
|
||||||
formula: 'جمع گرم × قیمت هر گرم',
|
|
||||||
},
|
|
||||||
} as const satisfies Record<string, DashboardHelp>
|
} as const satisfies Record<string, DashboardHelp>
|
||||||
|
|||||||
@@ -57,7 +57,7 @@
|
|||||||
<DashboardStatCard
|
<DashboardStatCard
|
||||||
tone="amber"
|
tone="amber"
|
||||||
icon="vuesax-linear:coin"
|
icon="vuesax-linear:coin"
|
||||||
title="کارمزد طلایی"
|
:title="metalLabel.goldCommission()"
|
||||||
:value="usePrice(finance?.fullPriceKarmozd ?? finance?.fullPriceKaermozd ?? 0, true, '')"
|
:value="usePrice(finance?.fullPriceKarmozd ?? finance?.fullPriceKaermozd ?? 0, true, '')"
|
||||||
:help="copy.kpi.commission"
|
:help="copy.kpi.commission"
|
||||||
/>
|
/>
|
||||||
@@ -68,10 +68,22 @@
|
|||||||
:value="String(finance?.orderCount ?? 0)"
|
:value="String(finance?.orderCount ?? 0)"
|
||||||
:help="copy.kpi.orderCount"
|
:help="copy.kpi.orderCount"
|
||||||
/>
|
/>
|
||||||
|
<template v-if="metalBalanceItems.length">
|
||||||
|
<DashboardStatCard
|
||||||
|
v-for="item in metalBalanceItems"
|
||||||
|
:key="item.metal_id"
|
||||||
|
tone="blue"
|
||||||
|
icon="vuesax-linear:empty-wallet"
|
||||||
|
:title="metalBalanceTitle(item.metal_id, item.label)"
|
||||||
|
:value="formatGram(item.goldBalance, item.metal_id)"
|
||||||
|
:help="copy.kpi.goldBalance"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
<DashboardStatCard
|
<DashboardStatCard
|
||||||
|
v-else
|
||||||
tone="blue"
|
tone="blue"
|
||||||
icon="vuesax-linear:empty-wallet"
|
icon="vuesax-linear:empty-wallet"
|
||||||
title="تراز طلایی"
|
:title="metalLabel.goldBalance()"
|
||||||
:value="formatGram(balance?.goldBalance)"
|
:value="formatGram(balance?.goldBalance)"
|
||||||
:help="copy.kpi.goldBalance"
|
:help="copy.kpi.goldBalance"
|
||||||
/>
|
/>
|
||||||
@@ -452,6 +464,7 @@ import {
|
|||||||
} from '@/pages/dashboard/dashboardSectionCopy'
|
} from '@/pages/dashboard/dashboardSectionCopy'
|
||||||
import { useDashboardPage } from '@/pages/dashboard/composables/useDashboardPage'
|
import { useDashboardPage } from '@/pages/dashboard/composables/useDashboardPage'
|
||||||
import { useDashboardQuickActions } from '@/pages/dashboard/composables/useDashboardQuickActions'
|
import { useDashboardQuickActions } from '@/pages/dashboard/composables/useDashboardQuickActions'
|
||||||
|
import { metalBalanceTitle, metalLabel } from '@/utils/adminMetalLabels'
|
||||||
|
|
||||||
const copy = {
|
const copy = {
|
||||||
hero: dashboardHero,
|
hero: dashboardHero,
|
||||||
@@ -472,6 +485,7 @@ const {
|
|||||||
master,
|
master,
|
||||||
finance,
|
finance,
|
||||||
balance,
|
balance,
|
||||||
|
metalBalanceItems,
|
||||||
walletSums,
|
walletSums,
|
||||||
recentOrders,
|
recentOrders,
|
||||||
peakHoursRange,
|
peakHoursRange,
|
||||||
@@ -573,7 +587,7 @@ const customerMetricItems = computed(() => [
|
|||||||
|
|
||||||
const walletMetricItems = computed(() => [
|
const walletMetricItems = computed(() => [
|
||||||
{
|
{
|
||||||
label: 'طلای مشتریان',
|
label: metalLabel.customersGold(),
|
||||||
value: formatGram(walletSums.value?.gold),
|
value: formatGram(walletSums.value?.gold),
|
||||||
help: dashboardWalletMetrics.gold,
|
help: dashboardWalletMetrics.gold,
|
||||||
variant: 'amber',
|
variant: 'amber',
|
||||||
@@ -585,7 +599,7 @@ const walletMetricItems = computed(() => [
|
|||||||
variant: 'sky',
|
variant: 'sky',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'ارزش طلا به ریال',
|
label: metalLabel.goldValueInRial(),
|
||||||
value: usePrice(walletSums.value?.goldToCash ?? 0, true, ''),
|
value: usePrice(walletSums.value?.goldToCash ?? 0, true, ''),
|
||||||
help: dashboardWalletMetrics.goldToCash,
|
help: dashboardWalletMetrics.goldToCash,
|
||||||
variant: 'violet',
|
variant: 'violet',
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<BaseIcon icon="vuesax-linear:empty-wallet" size="18" color="#15803d" />
|
<BaseIcon icon="vuesax-linear:empty-wallet" size="18" color="#15803d" />
|
||||||
</template>
|
</template>
|
||||||
</MobileStatCard>
|
</MobileStatCard>
|
||||||
<MobileStatCard bordered variant="success" label="موجودی طلایی" :value="`${row.balance_gold ?? 0} گرم`">
|
<MobileStatCard bordered variant="success" :label="metalLabel.goldInventory()" :value="`${row.balance_gold ?? 0} گرم`">
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<BaseIcon icon="vuesax-linear:coin" size="18" color="#15803d" />
|
<BaseIcon icon="vuesax-linear:coin" size="18" color="#15803d" />
|
||||||
</template>
|
</template>
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
<MobileDetailRow label="نام مشتری دعوت شده" :value="row.customer?.name || '-'" />
|
<MobileDetailRow label="نام مشتری دعوت شده" :value="row.customer?.name || '-'" />
|
||||||
<MobileDetailRow label="زمان ثبت" :value="row.created_at ? toJalali(row.created_at) : '-'" />
|
<MobileDetailRow label="زمان ثبت" :value="row.created_at ? toJalali(row.created_at) : '-'" />
|
||||||
<MobileDetailRow label="نقد (ریال)" :value="usePrice(row.cash ?? 0, true, '')" />
|
<MobileDetailRow label="نقد (ریال)" :value="usePrice(row.cash ?? 0, true, '')" />
|
||||||
<MobileDetailRow label="طلا (گرم)" :value="`${row.gold ?? 0}`" />
|
<MobileDetailRow :label="m('طلا (گرم)')" :value="`${row.gold ?? 0}`" />
|
||||||
<MobileDetailRow label="توضیحات" full-width :value="row.note || '-'" />
|
<MobileDetailRow label="توضیحات" full-width :value="row.note || '-'" />
|
||||||
</MobileInfoGrid>
|
</MobileInfoGrid>
|
||||||
</template>
|
</template>
|
||||||
@@ -44,6 +44,8 @@ import MobileSheetHeader from '@/components/mobile/MobileSheetHeader.vue'
|
|||||||
import { isMobile } from '@core/utils/is'
|
import { isMobile } from '@core/utils/is'
|
||||||
import { toJalali } from '@core/utils/useDataTime'
|
import { toJalali } from '@core/utils/useDataTime'
|
||||||
import { usePrice } from '@core/utils/usePrice'
|
import { usePrice } from '@core/utils/usePrice'
|
||||||
|
import { metalLabel } from '@/utils/adminMetalLabels'
|
||||||
|
import { m } from '@/utils/metalLabels'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
visible: boolean
|
visible: boolean
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<div class="text-xs text-gray-400">{{ createdAtLabel }}</div>
|
<div class="text-xs text-gray-400">{{ createdAtLabel }}</div>
|
||||||
</div>
|
</div>
|
||||||
<span class="text-xs text-gray-600">نقد: <span class="font-600 text-gray-900">{{ cashLabel }}</span></span>
|
<span class="text-xs text-gray-600">نقد: <span class="font-600 text-gray-900">{{ cashLabel }}</span></span>
|
||||||
<span class="text-xs text-gray-600">طلا: <span class="font-600 text-gray-900">{{ goldLabel }}</span></span>
|
<span class="text-xs text-gray-600">{{ metalLabel.goldUnit() }}: <span class="font-600 text-gray-900">{{ goldLabel }}</span></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -21,6 +21,7 @@ import type { TotalAccounting } from '@/model/accounting'
|
|||||||
import MobileCard from '@/components/mobile/MobileCard.vue'
|
import MobileCard from '@/components/mobile/MobileCard.vue'
|
||||||
import { toJalali } from '@core/utils/useDataTime'
|
import { toJalali } from '@core/utils/useDataTime'
|
||||||
import { usePrice } from '@core/utils/usePrice'
|
import { usePrice } from '@core/utils/usePrice'
|
||||||
|
import { metalLabel } from '@/utils/adminMetalLabels'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
item: TotalAccounting
|
item: TotalAccounting
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { getTotalAccountingTable } from '@/api/accounting'
|
import { getTotalAccountingTable } from '@/api/accounting'
|
||||||
import { cols } from '@/pages/menuPages/accounting/hooks/accountingColumns'
|
import { getAccountingColumns } from '@/pages/menuPages/accounting/hooks/accountingColumns'
|
||||||
import AccountingMobileCardList from '@/pages/menuPages/accounting/components/AccountingMobileCardList.vue'
|
import AccountingMobileCardList from '@/pages/menuPages/accounting/components/AccountingMobileCardList.vue'
|
||||||
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
||||||
import useBaseTable from '@core/components/BaseTable/hooks/useBaseTable'
|
import useBaseTable from '@core/components/BaseTable/hooks/useBaseTable'
|
||||||
@@ -51,7 +51,7 @@ const [submit, reload] = useBaseTable()
|
|||||||
const mobileListRef = ref<null | { fetch: () => Promise<void> }>(null)
|
const mobileListRef = ref<null | { fetch: () => Promise<void> }>(null)
|
||||||
|
|
||||||
const tblProps = ref({
|
const tblProps = ref({
|
||||||
columns: cols,
|
columns: getAccountingColumns(),
|
||||||
api: (params: TableInput) => getTotalAccountingTable(params)
|
api: (params: TableInput) => getTotalAccountingTable(params)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import type { BaseTableColumn } from '@core/type/baseTable'
|
|||||||
import { toJalali, toJalaliNoTime, toTime } from '@core/utils/useDataTime'
|
import { toJalali, toJalaliNoTime, toTime } from '@core/utils/useDataTime'
|
||||||
import CustomTag from '@/components/CustomTag.vue'
|
import CustomTag from '@/components/CustomTag.vue'
|
||||||
import { usePrice } from '@core/utils/usePrice'
|
import { usePrice } from '@core/utils/usePrice'
|
||||||
|
import { metalLabel } from '@/utils/adminMetalLabels'
|
||||||
/*import BudgetSelect from '@/components/BudgetSelect.vue'
|
/*import BudgetSelect from '@/components/BudgetSelect.vue'
|
||||||
import { useStaticApiListsStore } from '@/store/staticApisStore'
|
import { useStaticApiListsStore } from '@/store/staticApisStore'
|
||||||
import type { FilterSelect } from '@/model/filterSelect'
|
import type { FilterSelect } from '@/model/filterSelect'
|
||||||
@@ -21,7 +22,8 @@ const filteredDocTypes: FilterSelect = docTypes?.map((x: DocItem) => {
|
|||||||
}
|
}
|
||||||
})*/
|
})*/
|
||||||
|
|
||||||
export const cols: BaseTableColumn[] = [
|
export function getAccountingColumns(): BaseTableColumn[] {
|
||||||
|
return [
|
||||||
{
|
{
|
||||||
title: 'ردیف',
|
title: 'ردیف',
|
||||||
dataIndex: 'id',
|
dataIndex: 'id',
|
||||||
@@ -63,7 +65,7 @@ export const cols: BaseTableColumn[] = [
|
|||||||
// }
|
// }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'طلا',
|
title: metalLabel.goldUnit(),
|
||||||
dataIndex: 'gold',
|
dataIndex: 'gold',
|
||||||
key: 'gold',
|
key: 'gold',
|
||||||
type: 'Number',
|
type: 'Number',
|
||||||
@@ -81,4 +83,5 @@ export const cols: BaseTableColumn[] = [
|
|||||||
slot: 'action',
|
slot: 'action',
|
||||||
filterable: false
|
filterable: false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
<a-form :model="form" ref="formRef" autocomplete="off">
|
<a-form :model="form" ref="formRef" autocomplete="off">
|
||||||
<div class="grid md:grid-cols-1 gap-4">
|
<div class="grid md:grid-cols-1 gap-4">
|
||||||
<div class="bg-gray-50 px-10 py-3 flex justify-center gap-4">
|
<div class="bg-gray-50 px-10 py-3 flex justify-center gap-4">
|
||||||
<div>قیمت {{ form.direction === 1 ? 'خرید' : 'فروش' }} طلا:</div>
|
<div>{{ metalLabel.goldTradeBuySell(form.direction) }}</div>
|
||||||
<div class="font-bold">
|
<div class="font-bold">
|
||||||
{{ usePrice(form.direction === 1 ? dataLast?.buy ?? 0 : dataLast?.sell ?? 0) }}
|
{{ usePrice(form.direction === 1 ? dataLast?.buy ?? 0 : dataLast?.sell ?? 0) }}
|
||||||
</div>
|
</div>
|
||||||
@@ -65,7 +65,7 @@
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
<BasicInfoItem
|
<BasicInfoItem
|
||||||
is-line
|
is-line
|
||||||
title="کیف پول طلا"
|
:title="metalLabel.goldWalletTitle()"
|
||||||
class="mt-2"
|
class="mt-2"
|
||||||
:value="getGoldBalance(selectedCustomer?.wallet) || '_'"
|
:value="getGoldBalance(selectedCustomer?.wallet) || '_'"
|
||||||
/>
|
/>
|
||||||
@@ -95,7 +95,7 @@
|
|||||||
addon-before="گرم"
|
addon-before="گرم"
|
||||||
addon-bg-color="#0A1B37"
|
addon-bg-color="#0A1B37"
|
||||||
addon-text-color="white"
|
addon-text-color="white"
|
||||||
place-holder="مقدار طلا"
|
:place-holder="metalLabel.goldPlaceholder()"
|
||||||
type="tel"
|
type="tel"
|
||||||
@onchange="calculateCash"
|
@onchange="calculateCash"
|
||||||
/>
|
/>
|
||||||
@@ -143,6 +143,7 @@ import { getLastPrice } from '@/api/price'
|
|||||||
import type { last } from '@/model/price'
|
import type { last } from '@/model/price'
|
||||||
import { createOrder } from '@/api/order'
|
import { createOrder } from '@/api/order'
|
||||||
import { getCashBalance, getGoldBalance } from '@/components/mobile/utils/wallet'
|
import { getCashBalance, getGoldBalance } from '@/components/mobile/utils/wallet'
|
||||||
|
import { metalLabel } from '@/utils/adminMetalLabels'
|
||||||
|
|
||||||
//loading
|
//loading
|
||||||
const loading = ref<boolean>(false)
|
const loading = ref<boolean>(false)
|
||||||
@@ -248,7 +249,7 @@ function info(val: Order) {
|
|||||||
value: val.amount ?? '-'
|
value: val.amount ?? '-'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'قیمت هر گرم طلا برای مشتری (ریال)',
|
key: metalLabel.goldGramPriceCustomer(),
|
||||||
value: usePrice(val.price ?? 0) ?? '-'
|
value: usePrice(val.price ?? 0) ?? '-'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -256,7 +257,7 @@ function info(val: Order) {
|
|||||||
value: usePrice(val.total_price ?? 0) ?? '-'
|
value: usePrice(val.total_price ?? 0) ?? '-'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'قیمت هر گرم طلا بدون سود (ریال)',
|
key: metalLabel.goldGramPriceOriginal(),
|
||||||
value: usePrice(val.price_original ?? 0) ?? '-'
|
value: usePrice(val.price_original ?? 0) ?? '-'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -284,7 +285,7 @@ function info(val: Order) {
|
|||||||
value: usePrice(val.service_charge_price ?? 0)
|
value: usePrice(val.service_charge_price ?? 0)
|
||||||
},*/
|
},*/
|
||||||
{
|
{
|
||||||
key: 'کارمزد طلایی',
|
key: metalLabel.goldCommission(),
|
||||||
value: val.service_charge ?? '-' + ' گرم'
|
value: val.service_charge ?? '-' + ' گرم'
|
||||||
},
|
},
|
||||||
/* {
|
/* {
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ export const SYSTEM_SETTING_GROUPS: SettingGroupDef[] = [
|
|||||||
title: 'عمومی و برند',
|
title: 'عمومی و برند',
|
||||||
icon: 'vuesax-linear:home-2',
|
icon: 'vuesax-linear:home-2',
|
||||||
match: (k) =>
|
match: (k) =>
|
||||||
/^(name_fa|name_en|app_url|admin_url|apk_download_link|banner_enabled|bace_url|server_url|logo_|title_pwa|title_fav|metal_type|unit$|open_register|auto_active_customer|store_status|key$|is_store|group_sms|has_page_earn_money)/.test(
|
/^(name_fa|name_en|app_url|admin_url|apk_download_link|banner_enabled|bace_url|server_url|logo_|title_pwa|title_fav|metal_type|main_metal|unit$|open_register|auto_active_customer|store_status|key$|is_store|group_sms|has_page_earn_money)/.test(
|
||||||
k
|
k
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -123,9 +123,9 @@
|
|||||||
>
|
>
|
||||||
<BasicInfoItem
|
<BasicInfoItem
|
||||||
title="نام قلک"
|
title="نام قلک"
|
||||||
:value="item?.fund ? item?.fund?.title : 'کیف پول طلایی'"
|
:value="item?.fund ? item?.fund?.title : metalLabel.goldWallet()"
|
||||||
/>
|
/>
|
||||||
<BasicInfoItem title="مقدار طلا" :value="`${item?.gold} گرم`" />
|
<BasicInfoItem :title="metalLabel.goldAmount()" :value="`${item?.gold} گرم`" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -158,6 +158,7 @@ import type { FormInstance } from 'ant-design-vue'
|
|||||||
import BasicActionSheet from '@/components/BasicActionSheet.vue'
|
import BasicActionSheet from '@/components/BasicActionSheet.vue'
|
||||||
import BasicInfoModal from '@/components/BasicInfoModal/index.vue'
|
import BasicInfoModal from '@/components/BasicInfoModal/index.vue'
|
||||||
import { isMobile } from '@core/utils/is'
|
import { isMobile } from '@core/utils/is'
|
||||||
|
import { metalLabel } from '@/utils/adminMetalLabels'
|
||||||
import { message } from 'ant-design-vue/es'
|
import { message } from 'ant-design-vue/es'
|
||||||
import type { Info } from '@/components/BasicInfoModal/model/Info'
|
import type { Info } from '@/components/BasicInfoModal/model/Info'
|
||||||
import { toJalaliNoTime, toTime } from '@core/utils/useDataTime'
|
import { toJalaliNoTime, toTime } from '@core/utils/useDataTime'
|
||||||
|
|||||||
@@ -21,12 +21,12 @@
|
|||||||
<CustomerMobileDetailSheet
|
<CustomerMobileDetailSheet
|
||||||
v-model:visible="sheetVisible"
|
v-model:visible="sheetVisible"
|
||||||
:item="selectedItem"
|
:item="selectedItem"
|
||||||
|
:metals="catalogMetals"
|
||||||
@info="forwardAndClose('info', $event)"
|
@info="forwardAndClose('info', $event)"
|
||||||
@edit="forwardAndClose('edit', $event)"
|
@edit="forwardAndClose('edit', $event)"
|
||||||
@archive="forwardAndClose('archive', $event)"
|
@archive="forwardAndClose('archive', $event)"
|
||||||
@toggleActive="forward('toggleActive', $event)"
|
@toggleActive="forward('toggleActive', $event)"
|
||||||
@chargeMoney="forwardAndClose('chargeMoney', $event)"
|
@chargeWallet="forwardAndClose('chargeWallet', $event)"
|
||||||
@chargeGold="forwardAndClose('chargeGold', $event)"
|
|
||||||
@toggleTrade="forward('toggleTrade', $event)"
|
@toggleTrade="forward('toggleTrade', $event)"
|
||||||
@confirmUser="forwardAndClose('confirmUser', $event)"
|
@confirmUser="forwardAndClose('confirmUser', $event)"
|
||||||
/>
|
/>
|
||||||
@@ -40,14 +40,22 @@ import { useMobileDetailSheet } from '@/components/mobile/composables/useMobileD
|
|||||||
import { useMobileTableList } from '@/components/mobile/composables/useMobileTableList'
|
import { useMobileTableList } from '@/components/mobile/composables/useMobileTableList'
|
||||||
import CustomerMobileListItem from './CustomerMobileListItem.vue'
|
import CustomerMobileListItem from './CustomerMobileListItem.vue'
|
||||||
import CustomerMobileDetailSheet from './CustomerMobileDetailSheet.vue'
|
import CustomerMobileDetailSheet from './CustomerMobileDetailSheet.vue'
|
||||||
|
import type { CustomerMetalColumn } from '../hooks/customerColumns'
|
||||||
|
|
||||||
|
const catalogMetals = ref<CustomerMetalColumn[]>([])
|
||||||
|
|
||||||
|
async function fetchCustomers(params: Parameters<typeof getCustomerTable>[0]) {
|
||||||
|
const data = await getCustomerTable(params)
|
||||||
|
catalogMetals.value = data?.catalogMetals ?? []
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'info', item: Customer): void
|
(e: 'info', item: Customer): void
|
||||||
(e: 'edit', item: Customer): void
|
(e: 'edit', item: Customer): void
|
||||||
(e: 'archive', item: Customer): void
|
(e: 'archive', item: Customer): void
|
||||||
(e: 'toggleActive', item: Customer): void
|
(e: 'toggleActive', item: Customer): void
|
||||||
(e: 'chargeMoney', item: Customer): void
|
(e: 'chargeWallet', item: Customer): void
|
||||||
(e: 'chargeGold', item: Customer): void
|
|
||||||
(e: 'toggleTrade', item: Customer): void
|
(e: 'toggleTrade', item: Customer): void
|
||||||
(e: 'confirmUser', item: Customer): void
|
(e: 'confirmUser', item: Customer): void
|
||||||
}>()
|
}>()
|
||||||
@@ -59,8 +67,7 @@ type SheetAction =
|
|||||||
| 'edit'
|
| 'edit'
|
||||||
| 'archive'
|
| 'archive'
|
||||||
| 'toggleActive'
|
| 'toggleActive'
|
||||||
| 'chargeMoney'
|
| 'chargeWallet'
|
||||||
| 'chargeGold'
|
|
||||||
| 'toggleTrade'
|
| 'toggleTrade'
|
||||||
| 'confirmUser'
|
| 'confirmUser'
|
||||||
|
|
||||||
@@ -74,7 +81,7 @@ function forwardAndClose(action: SheetAction, item: Customer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { loading, search, items, hasMore, reload, loadMore, fetch } = useMobileTableList<Customer>({
|
const { loading, search, items, hasMore, reload, loadMore, fetch } = useMobileTableList<Customer>({
|
||||||
fetchApi: getCustomerTable,
|
fetchApi: fetchCustomers,
|
||||||
onFetched: (rows) => syncSelectedItem(rows)
|
onFetched: (rows) => syncSelectedItem(rows)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,15 @@
|
|||||||
<h2 class="text-base font-700 leading-relaxed text-gray-900">{{ row.name || '-' }}</h2>
|
<h2 class="text-base font-700 leading-relaxed text-gray-900">{{ row.name || '-' }}</h2>
|
||||||
<p v-if="row.mobile" class="text-sm text-gray-500" dir="ltr">{{ row.mobile }}</p>
|
<p v-if="row.mobile" class="text-sm text-gray-500" dir="ltr">{{ row.mobile }}</p>
|
||||||
|
|
||||||
<div class="grid grid-cols-2 gap-2">
|
<div class="grid grid-cols-1 gap-2">
|
||||||
<BaseButton class="w-full" size="middle" type="primary" icon="vuesax-linear:wallet-add" @click="closeAndEmit('chargeMoney', row)">
|
<BaseButton
|
||||||
شارژ ریالی
|
class="w-full"
|
||||||
</BaseButton>
|
size="middle"
|
||||||
<BaseButton class="w-full" size="middle" type="primary" icon="vuesax-linear:wallet-money" @click="closeAndEmit('chargeGold', row)">
|
type="primary"
|
||||||
شارژ طلایی
|
icon="vuesax-linear:wallet-add"
|
||||||
|
@click="closeAndEmit('chargeWallet', row)"
|
||||||
|
>
|
||||||
|
شارژ / دشارژ کیف پول
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
<BaseButton
|
<BaseButton
|
||||||
v-if="!row.is_legal"
|
v-if="!row.is_legal"
|
||||||
@@ -51,7 +54,14 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<MobileStatCard bordered variant="success" label="موجودی طلایی" :value="`${goldBalance(row)} گرم`">
|
<MobileStatCard
|
||||||
|
v-for="entry in metalBalanceEntries(row)"
|
||||||
|
:key="entry.metal_id"
|
||||||
|
bordered
|
||||||
|
variant="success"
|
||||||
|
:label="customerWalletMetalBalanceTitle(entry.metal_id, entry.label)"
|
||||||
|
:value="entry.value"
|
||||||
|
>
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<BaseIcon icon="vuesax-linear:coin" size="18" color="#15803d" />
|
<BaseIcon icon="vuesax-linear:coin" size="18" color="#15803d" />
|
||||||
</template>
|
</template>
|
||||||
@@ -83,7 +93,6 @@
|
|||||||
<MobileDetailRow label="نوع کاربر" :value="row.type_str || '-'" />
|
<MobileDetailRow label="نوع کاربر" :value="row.type_str || '-'" />
|
||||||
<MobileDetailRow label="کد معرف" :value="row.referral_id ?? '-'" />
|
<MobileDetailRow label="کد معرف" :value="row.referral_id ?? '-'" />
|
||||||
<MobileDetailRow label="نام معرف کاربر" :value="row.referral?.name ?? '-'" />
|
<MobileDetailRow label="نام معرف کاربر" :value="row.referral?.name ?? '-'" />
|
||||||
<MobileDetailRow label="گنج (گرم)" :value="row.gold ?? '-'" />
|
|
||||||
<MobileDetailRow label="آخرین بازدید" :value="lastSeen(row)" value-class="text-xs" />
|
<MobileDetailRow label="آخرین بازدید" :value="lastSeen(row)" value-class="text-xs" />
|
||||||
</MobileInfoGrid>
|
</MobileInfoGrid>
|
||||||
</template>
|
</template>
|
||||||
@@ -98,13 +107,17 @@ import MobileDetailRow from '@/components/mobile/MobileDetailRow.vue'
|
|||||||
import MobileStatCard from '@/components/mobile/MobileStatCard.vue'
|
import MobileStatCard from '@/components/mobile/MobileStatCard.vue'
|
||||||
import MobileDetailSheet from '@/components/mobile/MobileDetailSheet.vue'
|
import MobileDetailSheet from '@/components/mobile/MobileDetailSheet.vue'
|
||||||
import MobileInfoGrid from '@/components/mobile/MobileInfoGrid.vue'
|
import MobileInfoGrid from '@/components/mobile/MobileInfoGrid.vue'
|
||||||
import { getCashBalance, getGoldBalance } from '@/components/mobile/utils/wallet'
|
import { getCashBalance } from '@/components/mobile/utils/wallet'
|
||||||
import { toJalali } from '@core/utils/useDataTime'
|
import { toJalali } from '@core/utils/useDataTime'
|
||||||
import { usePrice } from '@core/utils/usePrice'
|
import { usePrice } from '@core/utils/usePrice'
|
||||||
|
import { customerWalletMetalBalanceTitle } from '@/utils/adminMetalLabels'
|
||||||
|
import type { CustomerMetalColumn } from '../hooks/customerColumns'
|
||||||
|
import { formatMetalDisplayValue, metalCellValue, metalDisplayUnit } from '@/utils/metalWeight'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
visible: boolean
|
visible: boolean
|
||||||
item: Customer | null
|
item: Customer | null
|
||||||
|
metals?: CustomerMetalColumn[]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
@@ -113,8 +126,7 @@ const emit = defineEmits<{
|
|||||||
(e: 'edit', item: Customer): void
|
(e: 'edit', item: Customer): void
|
||||||
(e: 'archive', item: Customer): void
|
(e: 'archive', item: Customer): void
|
||||||
(e: 'toggleActive', item: Customer): void
|
(e: 'toggleActive', item: Customer): void
|
||||||
(e: 'chargeMoney', item: Customer): void
|
(e: 'chargeWallet', item: Customer): void
|
||||||
(e: 'chargeGold', item: Customer): void
|
|
||||||
(e: 'toggleTrade', item: Customer): void
|
(e: 'toggleTrade', item: Customer): void
|
||||||
(e: 'confirmUser', item: Customer): void
|
(e: 'confirmUser', item: Customer): void
|
||||||
}>()
|
}>()
|
||||||
@@ -124,7 +136,7 @@ const visibleProxy = computed({
|
|||||||
set: (value: boolean) => emit('update:visible', value)
|
set: (value: boolean) => emit('update:visible', value)
|
||||||
})
|
})
|
||||||
|
|
||||||
type SheetEmit = 'archive' | 'chargeMoney' | 'chargeGold' | 'info' | 'confirmUser' | 'edit'
|
type SheetEmit = 'archive' | 'chargeWallet' | 'info' | 'confirmUser' | 'edit'
|
||||||
|
|
||||||
function closeAndEmit(event: SheetEmit, row: Customer) {
|
function closeAndEmit(event: SheetEmit, row: Customer) {
|
||||||
visibleProxy.value = false
|
visibleProxy.value = false
|
||||||
@@ -157,8 +169,24 @@ function cashBalance(row: Customer) {
|
|||||||
return getCashBalance(row.wallet)
|
return getCashBalance(row.wallet)
|
||||||
}
|
}
|
||||||
|
|
||||||
function goldBalance(row: Customer) {
|
function metalBalanceEntries(row: Customer) {
|
||||||
return getGoldBalance(row.wallet)
|
const metals = props.metals?.length
|
||||||
|
? props.metals
|
||||||
|
: [{ metal_id: 1, label: 'طلا' }]
|
||||||
|
|
||||||
|
return metals
|
||||||
|
.map((metal) => {
|
||||||
|
const value = metalCellValue(row as Record<string, unknown>, metal.metal_id, 'wallet_metal_gold')
|
||||||
|
return {
|
||||||
|
metal_id: metal.metal_id,
|
||||||
|
label: metal.label,
|
||||||
|
value: formatMetalDisplayValue(value, metal.metal_id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter((entry) => {
|
||||||
|
const numeric = metalCellValue(row as Record<string, unknown>, entry.metal_id, 'wallet_metal_gold')
|
||||||
|
return numeric !== 0
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function lastSeen(row: Customer) {
|
function lastSeen(row: Customer) {
|
||||||
|
|||||||
@@ -6,6 +6,18 @@
|
|||||||
class="wallet-charge-form"
|
class="wallet-charge-form"
|
||||||
:class="formClass"
|
:class="formClass"
|
||||||
>
|
>
|
||||||
|
<a-form-item label="کیف پول / محصول" name="chargeTargetKey" :rules="targetRules">
|
||||||
|
<a-select
|
||||||
|
v-model:value="modelValue.chargeTargetKey"
|
||||||
|
show-search
|
||||||
|
placeholder="انتخاب کنید"
|
||||||
|
class="w-full"
|
||||||
|
:options="targetOptions"
|
||||||
|
:filter-option="filterTarget"
|
||||||
|
@change="onTargetChange"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item label="نوع عملیات" name="direction">
|
<a-form-item label="نوع عملیات" name="direction">
|
||||||
<a-radio-group
|
<a-radio-group
|
||||||
v-model:value="modelValue.direction"
|
v-model:value="modelValue.direction"
|
||||||
@@ -36,10 +48,10 @@
|
|||||||
<BasicInput
|
<BasicInput
|
||||||
v-model:value="modelValue.amountAbs"
|
v-model:value="modelValue.amountAbs"
|
||||||
:addon-before="unit"
|
:addon-before="unit"
|
||||||
:type="variant === 'money' ? 'price' : 'number'"
|
:type="isMoney ? 'price' : 'number'"
|
||||||
:place-holder="amountPlaceholder"
|
:place-holder="amountPlaceholder"
|
||||||
/>
|
/>
|
||||||
<div v-if="variant === 'money' && Number(modelValue.amountAbs) > 10" class="wallet-charge-form__hint">
|
<div v-if="isMoney && Number(modelValue.amountAbs) > 10" class="wallet-charge-form__hint">
|
||||||
معادل {{ usePrice(Number(modelValue.amountAbs) / 10, true, 'تومان') }}
|
معادل {{ usePrice(Number(modelValue.amountAbs) / 10, true, 'تومان') }}
|
||||||
</div>
|
</div>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
@@ -51,22 +63,46 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref, watch } from 'vue'
|
||||||
import type { FormInstance } from 'ant-design-vue'
|
import type { FormInstance } from 'ant-design-vue'
|
||||||
import type { Charge } from '@/model/wallet'
|
import type { Charge, WalletChargeTarget } from '@/model/wallet'
|
||||||
import { inputRules } from '@/utils/inputRules'
|
import { inputRules } from '@/utils/inputRules'
|
||||||
import { usePrice } from '@core/utils/usePrice'
|
import { usePrice } from '@core/utils/usePrice'
|
||||||
|
import { applyChargeTarget, resolveChargeTarget, walletPreviewLabel } from '../utils/walletChargeForm'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
variant: 'money' | 'gold'
|
|
||||||
modelValue: Charge
|
modelValue: Charge
|
||||||
|
targets: WalletChargeTarget[]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const formRef = ref<FormInstance>()
|
const formRef = ref<FormInstance>()
|
||||||
|
|
||||||
const unit = computed(() => (props.variant === 'money' ? 'ریال' : 'گرم'))
|
const selectedTarget = computed(() =>
|
||||||
|
resolveChargeTarget(props.targets, props.modelValue.chargeTargetKey)
|
||||||
|
)
|
||||||
|
|
||||||
|
const isMoney = computed(() => selectedTarget.value?.kind === 'money')
|
||||||
|
const unit = computed(() => selectedTarget.value?.unit ?? 'ریال')
|
||||||
const isDischarge = computed(() => props.modelValue.direction === 'discharge')
|
const isDischarge = computed(() => props.modelValue.direction === 'discharge')
|
||||||
|
|
||||||
|
const targetOptions = computed(() =>
|
||||||
|
props.targets.map((target) => ({
|
||||||
|
value: target.key,
|
||||||
|
label: target.label
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
|
||||||
|
const targetRules = [
|
||||||
|
{
|
||||||
|
validator: (_rule: unknown, value: string | undefined) => {
|
||||||
|
if (!value || !resolveChargeTarget(props.targets, value)) {
|
||||||
|
return Promise.reject('کیف پول یا محصول را انتخاب کنید')
|
||||||
|
}
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
const formClass = computed(() =>
|
const formClass = computed(() =>
|
||||||
isDischarge.value ? 'wallet-charge-form--discharge' : 'wallet-charge-form--charge'
|
isDischarge.value ? 'wallet-charge-form--discharge' : 'wallet-charge-form--charge'
|
||||||
)
|
)
|
||||||
@@ -81,8 +117,7 @@ const previewSign = computed(() => (isDischarge.value ? '−' : '+'))
|
|||||||
|
|
||||||
const previewText = computed(() => {
|
const previewText = computed(() => {
|
||||||
const action = isDischarge.value ? 'کسر از' : 'افزایش'
|
const action = isDischarge.value ? 'کسر از' : 'افزایش'
|
||||||
const wallet = props.variant === 'money' ? 'موجودی ریالی' : 'موجودی طلایی'
|
return `${action} ${walletPreviewLabel(selectedTarget.value)}`
|
||||||
return `${action} ${wallet} کاربر`
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const amountLabel = computed(() =>
|
const amountLabel = computed(() =>
|
||||||
@@ -92,7 +127,7 @@ const amountLabel = computed(() =>
|
|||||||
const noteLabel = computed(() => (isDischarge.value ? 'علت دشارژ' : 'علت شارژ'))
|
const noteLabel = computed(() => (isDischarge.value ? 'علت دشارژ' : 'علت شارژ'))
|
||||||
|
|
||||||
const amountPlaceholder = computed(() =>
|
const amountPlaceholder = computed(() =>
|
||||||
props.variant === 'money' ? 'مثلاً ۱۰۰٬۰۰۰' : 'مثلاً ۰.۵'
|
isMoney.value ? 'مثلاً ۱۰۰٬۰۰۰' : 'مثلاً ۰.۵'
|
||||||
)
|
)
|
||||||
|
|
||||||
const notePlaceholder = computed(() =>
|
const notePlaceholder = computed(() =>
|
||||||
@@ -112,6 +147,28 @@ const amountRules = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
function filterTarget(input: string, option?: { label?: string }) {
|
||||||
|
return String(option?.label ?? '').toLowerCase().includes(input.toLowerCase())
|
||||||
|
}
|
||||||
|
|
||||||
|
function onTargetChange(key: string) {
|
||||||
|
const target = resolveChargeTarget(props.targets, key)
|
||||||
|
if (!target) return
|
||||||
|
Object.assign(props.modelValue, applyChargeTarget(props.modelValue, target))
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.targets,
|
||||||
|
(targets) => {
|
||||||
|
if (!targets.length) return
|
||||||
|
const current = resolveChargeTarget(targets, props.modelValue.chargeTargetKey)
|
||||||
|
if (!current) {
|
||||||
|
Object.assign(props.modelValue, applyChargeTarget(props.modelValue, targets[0]))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
validateFields: () => formRef.value?.validateFields()
|
validateFields: () => formRef.value?.validateFields()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -49,14 +49,10 @@
|
|||||||
:color="record.active ? '#54AD06' : '#ad0627'"
|
:color="record.active ? '#54AD06' : '#ad0627'"
|
||||||
/>
|
/>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-tooltip placement="bottom" class="cursor-pointer" @click="emits('chargeMoney', record)">
|
<a-tooltip placement="bottom" class="cursor-pointer" @click="emits('chargeWallet', record)">
|
||||||
<template #title><span>شارژ کیف پول ریالی</span></template>
|
<template #title><span>شارژ / دشارژ کیف پول</span></template>
|
||||||
<Icon icon="vuesax-linear:wallet-add" color="#9333ea" />
|
<Icon icon="vuesax-linear:wallet-add" color="#9333ea" />
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-tooltip placement="bottom" class="cursor-pointer" @click="emits('chargeGold', record)">
|
|
||||||
<template #title><span>شارژ کیف پول طلایی</span></template>
|
|
||||||
<Icon icon="vuesax-linear:wallet-money" color="#ea580c" />
|
|
||||||
</a-tooltip>
|
|
||||||
<a-tooltip placement="bottom" class="cursor-pointer" @click="emits('info', record)">
|
<a-tooltip placement="bottom" class="cursor-pointer" @click="emits('info', record)">
|
||||||
<template #title><span>مشخصات</span></template>
|
<template #title><span>مشخصات</span></template>
|
||||||
<Icon icon="vuesax-linear:stickynote" color="#10913A" />
|
<Icon icon="vuesax-linear:stickynote" color="#10913A" />
|
||||||
@@ -100,8 +96,7 @@
|
|||||||
@edit="emits('edit', $event)"
|
@edit="emits('edit', $event)"
|
||||||
@archive="handleArchive"
|
@archive="handleArchive"
|
||||||
@toggleActive="(item) => changeCustomerActivationFunction(item.id)"
|
@toggleActive="(item) => changeCustomerActivationFunction(item.id)"
|
||||||
@chargeMoney="emits('chargeMoney', $event)"
|
@chargeWallet="emits('chargeWallet', $event)"
|
||||||
@chargeGold="emits('chargeGold', $event)"
|
|
||||||
@toggleTrade="(item) => changeCustomerOrderActivationFunction(item.id)"
|
@toggleTrade="(item) => changeCustomerOrderActivationFunction(item.id)"
|
||||||
@confirmUser="openConfirmUser"
|
@confirmUser="openConfirmUser"
|
||||||
/>
|
/>
|
||||||
@@ -144,7 +139,7 @@ import {
|
|||||||
confirmUser,
|
confirmUser,
|
||||||
getCustomerTable
|
getCustomerTable
|
||||||
} from '@/api/customer'
|
} from '@/api/customer'
|
||||||
import { cols } from '@/pages/menuPages/customers/base/hooks/customerColumns'
|
import { buildCustomerColumns } from '@/pages/menuPages/customers/base/hooks/customerColumns'
|
||||||
import CustomerMobileCardList from '@/pages/menuPages/customers/base/components/CustomerMobileCardList.vue'
|
import CustomerMobileCardList from '@/pages/menuPages/customers/base/components/CustomerMobileCardList.vue'
|
||||||
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
||||||
import useBaseTable from '@core/components/BaseTable/hooks/useBaseTable'
|
import useBaseTable from '@core/components/BaseTable/hooks/useBaseTable'
|
||||||
@@ -155,7 +150,7 @@ import { MODAL_ABOVE_ACTION_SHEET_Z_INDEX } from '@/constants/overlayZIndex'
|
|||||||
import { isMobile } from '@core/utils/is'
|
import { isMobile } from '@core/utils/is'
|
||||||
|
|
||||||
const [submit, reload] = useBaseTable()
|
const [submit, reload] = useBaseTable()
|
||||||
const emits = defineEmits(['delete', 'edit', 'add', 'info', 'chargeMoney', 'chargeGold'])
|
const emits = defineEmits(['delete', 'edit', 'add', 'info', 'chargeWallet'])
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const confirmVisible = ref(false)
|
const confirmVisible = ref(false)
|
||||||
@@ -163,8 +158,13 @@ const confirmInfo = ref<Customer>()
|
|||||||
const mobileListRef = ref<null | { fetch: () => Promise<void> }>(null)
|
const mobileListRef = ref<null | { fetch: () => Promise<void> }>(null)
|
||||||
|
|
||||||
const tblProps = ref<any>({
|
const tblProps = ref<any>({
|
||||||
columns: cols,
|
columns: buildCustomerColumns(),
|
||||||
api: (params: TableInput) => getCustomerTable(params)
|
api: async (params: TableInput) => {
|
||||||
|
const data = await getCustomerTable(params)
|
||||||
|
const metals = data?.catalogMetals ?? []
|
||||||
|
tblProps.value.columns = buildCustomerColumns(metals)
|
||||||
|
return data
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const fetch = async () => {
|
const fetch = async () => {
|
||||||
|
|||||||
@@ -1,212 +1,164 @@
|
|||||||
import type { BaseTableColumn } from '@core/type/baseTable'
|
import type { BaseTableColumn } from '@core/type/baseTable'
|
||||||
import {toJalali, toJalaliNoTime, toTime} from '@core/utils/useDataTime'
|
import { toJalali } from '@core/utils/useDataTime'
|
||||||
import CustomTag from '@/components/CustomTag.vue'
|
import CustomTag from '@/components/CustomTag.vue'
|
||||||
import { usePrice } from '@core/utils/usePrice'
|
import { usePrice } from '@core/utils/usePrice'
|
||||||
import { getCashBalance, getGoldBalance } from '@/components/mobile/utils/wallet'
|
import { getCashBalance } from '@/components/mobile/utils/wallet'
|
||||||
/*import BudgetSelect from '@/components/BudgetSelect.vue'
|
import { customerWalletMetalBalanceTitle } from '@/utils/adminMetalLabels'
|
||||||
import { useStaticApiListsStore } from '@/store/staticApisStore'
|
import { formatMetalDisplayValue, metalCellValue } from '@/utils/metalWeight'
|
||||||
import type { FilterSelect } from '@/model/filterSelect'
|
|
||||||
import type { DocItem } from '@/model/docType'*/
|
|
||||||
|
|
||||||
//define store
|
export type CustomerMetalColumn = { metal_id: number; label: string; display_unit?: string }
|
||||||
//const store = useStaticApiListsStore()
|
|
||||||
//document types
|
|
||||||
/*let docTypes: DocItem[]*/
|
|
||||||
/*onMounted(async () => {
|
|
||||||
docTypes = await store.readyDocTypeList()
|
|
||||||
})
|
|
||||||
const filteredDocTypes: FilterSelect = docTypes?.map((x: DocItem) => {
|
|
||||||
return {
|
|
||||||
label: x.title,
|
|
||||||
value: x.id
|
|
||||||
}
|
|
||||||
})*/
|
|
||||||
|
|
||||||
export const cols: BaseTableColumn[] = [
|
export function buildCustomerColumns(metals: CustomerMetalColumn[] = []): BaseTableColumn[] {
|
||||||
{
|
const baseColumns: BaseTableColumn[] = [
|
||||||
title: 'ردیف',
|
{
|
||||||
dataIndex: 'id',
|
title: 'ردیف',
|
||||||
key: 'id',
|
dataIndex: 'id',
|
||||||
filterable: false,
|
key: 'id',
|
||||||
sortable: false,
|
|
||||||
customRender: (record: any) => {
|
|
||||||
return record.index + 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'کد کاربری',
|
|
||||||
dataIndex: 'id',
|
|
||||||
key: 'id',
|
|
||||||
type: 'Number'
|
|
||||||
// className: 'column-money'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'نام و نام خانوادگی',
|
|
||||||
dataIndex: 'name',
|
|
||||||
key: 'name',
|
|
||||||
type: 'Text'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'شماره موبایل',
|
|
||||||
dataIndex: 'mobile',
|
|
||||||
key: 'mobile',
|
|
||||||
type: 'Number'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'نوع کاربر',
|
|
||||||
dataIndex: 'type_str',
|
|
||||||
key: 'type_str',
|
|
||||||
type: 'Text'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'کد معرف',
|
|
||||||
dataIndex: 'referral_id',
|
|
||||||
key: 'referral_id',
|
|
||||||
type: 'Text'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'نام معرف کاربر',
|
|
||||||
dataIndex: 'referral.name',
|
|
||||||
key: 'referral.name',
|
|
||||||
type: 'Text'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'موجودی ریالی (ریال)',
|
|
||||||
dataIndex: 'wallet.cash',
|
|
||||||
key: 'wallet.cash',
|
|
||||||
type: 'Number',
|
|
||||||
customRender(data) {
|
|
||||||
return usePrice(getCashBalance(data.record?.wallet, { walletCash: data.record?.wallet_cash }), true, '')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'موجودی طلایی (گرم)',
|
|
||||||
dataIndex: 'wallet.gold',
|
|
||||||
key: 'wallet.gold',
|
|
||||||
type: 'Number',
|
|
||||||
filterable: false,
|
|
||||||
customRender(data) {
|
|
||||||
return getGoldBalance(data.record?.wallet, { walletGold: data.record?.wallet_gold })
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'اخرین بازدید',
|
|
||||||
dataIndex: 'last_seen_at',
|
|
||||||
key: 'last_seen_at',
|
|
||||||
type: 'DateTime',
|
|
||||||
customRender(data) {
|
|
||||||
return toJalali(data.record.last_seen_at)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'گنج(گرم)',
|
|
||||||
dataIndex: 'gold',
|
|
||||||
key: 'gold',
|
|
||||||
type: 'Number',
|
|
||||||
filterable: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'وضعیت کاربر',
|
|
||||||
dataIndex: 'active',
|
|
||||||
key: 'active',
|
|
||||||
type: 'Boolean',
|
|
||||||
customRender: ({ record }) => {
|
|
||||||
return record.active ? (
|
|
||||||
<div class="flex justify-center">
|
|
||||||
<CustomTag textColor="#25855A" bgColor="#E3F4E9" class={'font-bold px-6'}>
|
|
||||||
فعال
|
|
||||||
</CustomTag>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div class="flex justify-center">
|
|
||||||
<CustomTag textColor="#C53030" bgColor="#FECACA" class={'font-bold px-6'}>
|
|
||||||
غیر فعال
|
|
||||||
</CustomTag>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'وضعیت سفارش دهی',
|
|
||||||
dataIndex: 'trade_active',
|
|
||||||
key: 'trade_active',
|
|
||||||
sortable: false,
|
|
||||||
type: 'Boolean',
|
|
||||||
customRender: ({ record }) => {
|
|
||||||
return record.trade_active ? (
|
|
||||||
<div class="flex justify-center">
|
|
||||||
<CustomTag textColor="#25855A" bgColor="#E3F4E9" class={'font-bold px-6'}>
|
|
||||||
مجاز
|
|
||||||
</CustomTag>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div class="flex justify-center">
|
|
||||||
<CustomTag textColor="#C53030" bgColor="#FECACA" class={'font-bold px-6'}>
|
|
||||||
غیر مجاز
|
|
||||||
</CustomTag>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/*{
|
|
||||||
title: 'نقش',
|
|
||||||
dataIndex: 'user_type_record',
|
|
||||||
key: 'user_type_record',
|
|
||||||
type: 'Select',
|
|
||||||
filterable: false,
|
filterable: false,
|
||||||
sortable: false,
|
sortable: false,
|
||||||
filterComponentProps: {
|
customRender: (record: any) => {
|
||||||
multiple: true
|
return record.index + 1
|
||||||
},
|
|
||||||
filterField: 'user_type_obj.id',
|
|
||||||
filterComponent: ReadyUserTypeSelect,
|
|
||||||
/!* customRender: (record: any) => {
|
|
||||||
return record.record.user_type_record?.value
|
|
||||||
}*!/
|
|
||||||
customRender: ({ record }) => {
|
|
||||||
const items = record.is_admin ? (
|
|
||||||
<CustomTag textColor="#25855A" bgColor="#E3F4E9">
|
|
||||||
ادمین
|
|
||||||
</CustomTag>
|
|
||||||
) : (
|
|
||||||
record.user_type_obj.map((item: { title: any }, index: any) => {
|
|
||||||
if (item) {
|
|
||||||
return (
|
|
||||||
<CustomTag textColor="#25855A" bgColor="#E3F4E9">
|
|
||||||
{item?.title}
|
|
||||||
</CustomTag>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return <div class="flex justify-center items-center gap-2">{items}</div>
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'حوزه',
|
title: 'کد کاربری',
|
||||||
dataIndex: 'section',
|
dataIndex: 'id',
|
||||||
key: 'section'
|
key: 'id',
|
||||||
|
type: 'Number'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'نام و نام خانوادگی',
|
||||||
|
dataIndex: 'name',
|
||||||
|
key: 'name',
|
||||||
|
type: 'Text'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'شماره موبایل',
|
title: 'شماره موبایل',
|
||||||
dataIndex: 'mobile',
|
dataIndex: 'mobile',
|
||||||
type: 'Number',
|
key: 'mobile',
|
||||||
key: 'mobile'
|
type: 'Number'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'تاریخ ثبت',
|
title: 'نوع کاربر',
|
||||||
dataIndex: 'created_at',
|
dataIndex: 'type_str',
|
||||||
key: 'created_at',
|
key: 'type_str',
|
||||||
type: 'DateTime',
|
type: 'Text'
|
||||||
customRender: (record: any) => {
|
},
|
||||||
return toJalaliNoTime(record.record.created_at)
|
{
|
||||||
|
title: 'کد معرف',
|
||||||
|
dataIndex: 'referral_id',
|
||||||
|
key: 'referral_id',
|
||||||
|
type: 'Text'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'نام معرف کاربر',
|
||||||
|
dataIndex: 'referral.name',
|
||||||
|
key: 'referral.name',
|
||||||
|
type: 'Text'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'موجودی ریالی (ریال)',
|
||||||
|
dataIndex: 'wallet.cash',
|
||||||
|
key: 'wallet.cash',
|
||||||
|
type: 'Number',
|
||||||
|
customRender(data) {
|
||||||
|
return usePrice(getCashBalance(data.record?.wallet, { walletCash: data.record?.wallet_cash }), true, '')
|
||||||
}
|
}
|
||||||
},*/
|
}
|
||||||
{
|
]
|
||||||
title: 'عملیات',
|
|
||||||
dataIndex: 'action',
|
const metalColumns: BaseTableColumn[] =
|
||||||
key: 'action',
|
metals.length > 0
|
||||||
slot: 'action',
|
? metals.map((metal) => ({
|
||||||
filterable: false
|
title: customerWalletMetalBalanceTitle(metal.metal_id, metal.label),
|
||||||
}
|
dataIndex: `wallet_metal_gold_${metal.metal_id}`,
|
||||||
]
|
key: `wallet_metal_${metal.metal_id}`,
|
||||||
|
type: 'Number',
|
||||||
|
filterable: false,
|
||||||
|
customRender(data) {
|
||||||
|
const value = metalCellValue(data.record, metal.metal_id, 'wallet_metal_gold')
|
||||||
|
return formatMetalDisplayValue(value, metal.metal_id)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
: [
|
||||||
|
{
|
||||||
|
title: customerWalletMetalBalanceTitle(1, 'طلا'),
|
||||||
|
dataIndex: 'wallet.gold',
|
||||||
|
key: 'wallet.gold',
|
||||||
|
type: 'Number',
|
||||||
|
filterable: false,
|
||||||
|
customRender(data) {
|
||||||
|
const value = Number(data.record?.wallet_gold ?? 0)
|
||||||
|
return formatMetalDisplayValue(value, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
return [
|
||||||
|
...baseColumns,
|
||||||
|
...metalColumns,
|
||||||
|
{
|
||||||
|
title: 'اخرین بازدید',
|
||||||
|
dataIndex: 'last_seen_at',
|
||||||
|
key: 'last_seen_at',
|
||||||
|
type: 'DateTime',
|
||||||
|
customRender(data) {
|
||||||
|
return toJalali(data.record.last_seen_at)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'وضعیت کاربر',
|
||||||
|
dataIndex: 'active',
|
||||||
|
key: 'active',
|
||||||
|
type: 'Boolean',
|
||||||
|
customRender: ({ record }) => {
|
||||||
|
return record.active ? (
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<CustomTag textColor="#25855A" bgColor="#E3F4E9" class={'font-bold px-6'}>
|
||||||
|
فعال
|
||||||
|
</CustomTag>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<CustomTag textColor="#C53030" bgColor="#FECACA" class={'font-bold px-6'}>
|
||||||
|
غیر فعال
|
||||||
|
</CustomTag>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'وضعیت سفارش دهی',
|
||||||
|
dataIndex: 'trade_active',
|
||||||
|
key: 'trade_active',
|
||||||
|
sortable: false,
|
||||||
|
type: 'Boolean',
|
||||||
|
customRender: ({ record }) => {
|
||||||
|
return record.trade_active ? (
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<CustomTag textColor="#25855A" bgColor="#E3F4E9" class={'font-bold px-6'}>
|
||||||
|
مجاز
|
||||||
|
</CustomTag>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<CustomTag textColor="#C53030" bgColor="#FECACA" class={'font-bold px-6'}>
|
||||||
|
غیر مجاز
|
||||||
|
</CustomTag>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'عملیات',
|
||||||
|
dataIndex: 'action',
|
||||||
|
key: 'action',
|
||||||
|
slot: 'action',
|
||||||
|
filterable: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCustomerColumns(): BaseTableColumn[] {
|
||||||
|
return buildCustomerColumns()
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,8 +6,7 @@
|
|||||||
@info="info"
|
@info="info"
|
||||||
@edit="edit"
|
@edit="edit"
|
||||||
@add="add"
|
@add="add"
|
||||||
@chargeMoney="walletMoneyCharge"
|
@chargeWallet="walletCharge"
|
||||||
@chargeGold="walletGoldCharge"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- <BasicMenu :menu="menu" width="13rem" @onChange="tabSelected" />
|
<!-- <BasicMenu :menu="menu" width="13rem" @onChange="tabSelected" />
|
||||||
@@ -66,9 +65,6 @@
|
|||||||
<a-form-item label="میزان پاداش" name="profit_amount">
|
<a-form-item label="میزان پاداش" name="profit_amount">
|
||||||
<BasicInput v-model:value="form.profit_amount" />
|
<BasicInput v-model:value="form.profit_amount" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="گنج" name="gold">
|
|
||||||
<BasicInput v-model:value="form.gold" />
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label="حداکثر معرفی" name="add_user_limit">
|
<a-form-item label="حداکثر معرفی" name="add_user_limit">
|
||||||
<BasicInput v-model:value="form.add_user_limit" />
|
<BasicInput v-model:value="form.add_user_limit" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
@@ -191,9 +187,9 @@
|
|||||||
>
|
>
|
||||||
<BasicInfoItem
|
<BasicInfoItem
|
||||||
title="نام قلک"
|
title="نام قلک"
|
||||||
:value="item?.fund ? item?.fund?.title : 'کیف پول طلایی'"
|
:value="item?.fund ? item?.fund?.title : metalLabel.goldWallet()"
|
||||||
/>
|
/>
|
||||||
<BasicInfoItem title="مقدار طلا" :value="`${item?.gold} گرم`" />
|
<BasicInfoItem :title="metalLabel.goldAmount()" :value="`${item?.gold} گرم`" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -217,60 +213,39 @@
|
|||||||
</BasicActionSheet>
|
</BasicActionSheet>
|
||||||
<BasicActionSheet
|
<BasicActionSheet
|
||||||
v-if="isMobile"
|
v-if="isMobile"
|
||||||
v-model:visible="showMoneyWalletCharge"
|
v-model:visible="showWalletCharge"
|
||||||
:title="moneyChargeTitle"
|
:title="walletChargeModalTitle"
|
||||||
modal-width="24rem"
|
modal-width="26rem"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:show-button="true"
|
:show-button="true"
|
||||||
:show-cancel="true"
|
:show-cancel="true"
|
||||||
:show-ok="true"
|
:show-ok="true"
|
||||||
:ok-text="moneyChargeOkText"
|
:ok-text="walletChargeOkLabel"
|
||||||
cancel-text="انصراف"
|
cancel-text="انصراف"
|
||||||
@ok="chargeMoneyWallet"
|
@ok="submitWalletCharge"
|
||||||
@close="showMoneyWalletCharge = false"
|
@close="closeWalletCharge"
|
||||||
>
|
>
|
||||||
<WalletChargeForm ref="moneyChargeFormRef" :model-value="formCharge" variant="money" />
|
<WalletChargeForm
|
||||||
|
ref="walletChargeFormRef"
|
||||||
|
:model-value="formCharge"
|
||||||
|
:targets="chargeTargets"
|
||||||
|
/>
|
||||||
</BasicActionSheet>
|
</BasicActionSheet>
|
||||||
<BasicModal
|
<BasicModal
|
||||||
v-else
|
v-else
|
||||||
v-model:visible="showMoneyWalletCharge"
|
v-model:visible="showWalletCharge"
|
||||||
width="22rem"
|
width="24rem"
|
||||||
:title="moneyChargeTitle"
|
:title="walletChargeModalTitle"
|
||||||
:ok-text="moneyChargeOkText"
|
:ok-text="walletChargeOkLabel"
|
||||||
@ok="chargeMoneyWallet"
|
@ok="submitWalletCharge"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
@close="showMoneyWalletCharge = false"
|
@close="closeWalletCharge"
|
||||||
>
|
>
|
||||||
<WalletChargeForm ref="moneyChargeFormRef" :model-value="formCharge" variant="money" />
|
<WalletChargeForm
|
||||||
</BasicModal>
|
ref="walletChargeFormRef"
|
||||||
|
:model-value="formCharge"
|
||||||
<BasicActionSheet
|
:targets="chargeTargets"
|
||||||
v-if="isMobile"
|
/>
|
||||||
v-model:visible="showGoldWalletCharge"
|
|
||||||
:title="goldChargeTitle"
|
|
||||||
modal-width="24rem"
|
|
||||||
:loading="loading"
|
|
||||||
:show-button="true"
|
|
||||||
:show-cancel="true"
|
|
||||||
:show-ok="true"
|
|
||||||
:ok-text="goldChargeOkText"
|
|
||||||
cancel-text="انصراف"
|
|
||||||
@ok="chargeGoldWallet"
|
|
||||||
@close="showGoldWalletCharge = false"
|
|
||||||
>
|
|
||||||
<WalletChargeForm ref="goldChargeFormRef" :model-value="formCharge" variant="gold" />
|
|
||||||
</BasicActionSheet>
|
|
||||||
<BasicModal
|
|
||||||
v-else
|
|
||||||
v-model:visible="showGoldWalletCharge"
|
|
||||||
width="22rem"
|
|
||||||
:title="goldChargeTitle"
|
|
||||||
:ok-text="goldChargeOkText"
|
|
||||||
@ok="chargeGoldWallet"
|
|
||||||
:loading="loading"
|
|
||||||
@close="showGoldWalletCharge = false"
|
|
||||||
>
|
|
||||||
<WalletChargeForm ref="goldChargeFormRef" :model-value="formCharge" variant="gold" />
|
|
||||||
</BasicModal>
|
</BasicModal>
|
||||||
<BasicInfoModal
|
<BasicInfoModal
|
||||||
v-model:visible="userInfoOrderVisible"
|
v-model:visible="userInfoOrderVisible"
|
||||||
@@ -302,9 +277,9 @@ import { cloneDeep } from 'lodash'
|
|||||||
import type { Item } from '@/model/Item'
|
import type { Item } from '@/model/Item'
|
||||||
import type { Customer } from '@/model/customer'
|
import type { Customer } from '@/model/customer'
|
||||||
import { createCustomer, deleteCustomer, updateCustomer } from '@/api/customer'
|
import { createCustomer, deleteCustomer, updateCustomer } from '@/api/customer'
|
||||||
import type { Charge } from '@/model/wallet'
|
import type { Charge, WalletChargeTarget } from '@/model/wallet'
|
||||||
import customers from '@/router/routes/customers'
|
import customers from '@/router/routes/customers'
|
||||||
import { chargeWallet } from '@/api/wallet'
|
import { chargeWalletUnified, getWalletChargeTargets } from '@/api/wallet'
|
||||||
import { usePrice } from '@core/utils/usePrice'
|
import { usePrice } from '@core/utils/usePrice'
|
||||||
import jalaliday from 'jalaliday'
|
import jalaliday from 'jalaliday'
|
||||||
import ordersTbl from '@/pages/menuPages/orders/components/table.vue'
|
import ordersTbl from '@/pages/menuPages/orders/components/table.vue'
|
||||||
@@ -317,12 +292,15 @@ import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
|||||||
import { isMobile } from '@core/utils/is'
|
import { isMobile } from '@core/utils/is'
|
||||||
import WalletChargeForm from './components/WalletChargeForm.vue'
|
import WalletChargeForm from './components/WalletChargeForm.vue'
|
||||||
import {
|
import {
|
||||||
|
applyChargeTarget,
|
||||||
buildChargePayload,
|
buildChargePayload,
|
||||||
createEmptyChargeForm,
|
createEmptyChargeForm,
|
||||||
|
resolveChargeTarget,
|
||||||
walletChargeOkText,
|
walletChargeOkText,
|
||||||
walletChargeSuccessMessage,
|
walletChargeSuccessMessage,
|
||||||
walletChargeTitle
|
walletChargeTitle
|
||||||
} from './utils/walletChargeForm'
|
} from './utils/walletChargeForm'
|
||||||
|
import { metalLabel } from '@/utils/adminMetalLabels'
|
||||||
|
|
||||||
//tabs
|
//tabs
|
||||||
const activeKey = ref('اطلاعات جامع')
|
const activeKey = ref('اطلاعات جامع')
|
||||||
@@ -376,21 +354,23 @@ const customerItems: Array<Item> = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
//wallet
|
//wallet
|
||||||
const showMoneyWalletCharge = ref<boolean>(false)
|
const showWalletCharge = ref<boolean>(false)
|
||||||
const showGoldWalletCharge = ref<boolean>(false)
|
const chargeTargets = ref<WalletChargeTarget[]>([])
|
||||||
const userInfoOrderVisible = ref<boolean>(false)
|
const userInfoOrderVisible = ref<boolean>(false)
|
||||||
|
|
||||||
//form
|
//form
|
||||||
const form = ref<Partial<Customer>>({})
|
const form = ref<Partial<Customer>>({})
|
||||||
const formRef = ref<FormInstance>()
|
const formRef = ref<FormInstance>()
|
||||||
const formCharge = ref<Charge>({})
|
const formCharge = ref<Charge>({})
|
||||||
const moneyChargeFormRef = ref<InstanceType<typeof WalletChargeForm>>()
|
const walletChargeFormRef = ref<InstanceType<typeof WalletChargeForm>>()
|
||||||
const goldChargeFormRef = ref<InstanceType<typeof WalletChargeForm>>()
|
|
||||||
|
|
||||||
const moneyChargeTitle = computed(() => walletChargeTitle('money', formCharge.value.direction))
|
const selectedChargeTarget = computed(() =>
|
||||||
const goldChargeTitle = computed(() => walletChargeTitle('gold', formCharge.value.direction))
|
resolveChargeTarget(chargeTargets.value, formCharge.value.chargeTargetKey)
|
||||||
const moneyChargeOkText = computed(() => walletChargeOkText(formCharge.value.direction))
|
)
|
||||||
const goldChargeOkText = computed(() => walletChargeOkText(formCharge.value.direction))
|
const walletChargeModalTitle = computed(() =>
|
||||||
|
walletChargeTitle(selectedChargeTarget.value, formCharge.value.direction)
|
||||||
|
)
|
||||||
|
const walletChargeOkLabel = computed(() => walletChargeOkText(formCharge.value.direction))
|
||||||
|
|
||||||
//info
|
//info
|
||||||
let infoParams = reactive<Info>({})
|
let infoParams = reactive<Info>({})
|
||||||
@@ -407,7 +387,7 @@ function infoOrder(val: Order) {
|
|||||||
value: val.amount ?? '-'
|
value: val.amount ?? '-'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'قیمت هر گرم طلا برای مشتری (ریال)',
|
key: metalLabel.goldGramPriceCustomer(),
|
||||||
value: usePrice(val.price ?? 0) ?? '-'
|
value: usePrice(val.price ?? 0) ?? '-'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -415,7 +395,7 @@ function infoOrder(val: Order) {
|
|||||||
value: usePrice(val.total_price ?? 0) ?? '-'
|
value: usePrice(val.total_price ?? 0) ?? '-'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: '/قیمت هر گرم طلا بدون سود (ریال)',
|
key: metalLabel.goldGramPriceOriginalSlash(),
|
||||||
value: usePrice(val.price_original ?? 0) ?? '-'
|
value: usePrice(val.price_original ?? 0) ?? '-'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -443,7 +423,7 @@ function infoOrder(val: Order) {
|
|||||||
value: usePrice(val.service_charge_price ?? 0)
|
value: usePrice(val.service_charge_price ?? 0)
|
||||||
},*/
|
},*/
|
||||||
{
|
{
|
||||||
key: 'کارمزد طلایی',
|
key: metalLabel.goldCommission(),
|
||||||
value: val.service_charge ?? '-' + ' گرم'
|
value: val.service_charge ?? '-' + ' گرم'
|
||||||
},
|
},
|
||||||
/* {
|
/* {
|
||||||
@@ -511,40 +491,43 @@ async function add() {
|
|||||||
|
|
||||||
function resetFormCharge(customerId: number) {
|
function resetFormCharge(customerId: number) {
|
||||||
formCharge.value = createEmptyChargeForm(customerId)
|
formCharge.value = createEmptyChargeForm(customerId)
|
||||||
|
const firstTarget = chargeTargets.value[0]
|
||||||
|
if (firstTarget) {
|
||||||
|
Object.assign(formCharge.value, applyChargeTarget(formCharge.value, firstTarget))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function walletMoneyCharge(val: Customer) {
|
async function loadChargeTargets() {
|
||||||
resetFormCharge(val.id)
|
if (chargeTargets.value.length) return
|
||||||
showMoneyWalletCharge.value = true
|
const data = await getWalletChargeTargets()
|
||||||
|
chargeTargets.value = data?.targets ?? []
|
||||||
}
|
}
|
||||||
|
|
||||||
async function walletGoldCharge(val: Customer) {
|
async function walletCharge(val: Customer) {
|
||||||
resetFormCharge(val.id)
|
try {
|
||||||
showGoldWalletCharge.value = true
|
loading.value = true
|
||||||
|
await loadChargeTargets()
|
||||||
|
resetFormCharge(val.id)
|
||||||
|
showWalletCharge.value = true
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
message.error('خطا در بارگذاری گزینههای شارژ')
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function chargeMoneyWallet() {
|
function closeWalletCharge() {
|
||||||
moneyChargeFormRef.value?.validateFields()?.then(async () => {
|
showWalletCharge.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
async function submitWalletCharge() {
|
||||||
|
walletChargeFormRef.value?.validateFields()?.then(async () => {
|
||||||
try {
|
try {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
await chargeWallet(buildChargePayload(formCharge.value), 'money')
|
await chargeWalletUnified(buildChargePayload(formCharge.value))
|
||||||
message.success(walletChargeSuccessMessage(formCharge.value.direction))
|
message.success(walletChargeSuccessMessage(formCharge.value.direction))
|
||||||
showMoneyWalletCharge.value = false
|
showWalletCharge.value = false
|
||||||
} catch (e) {
|
|
||||||
console.log(e)
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async function chargeGoldWallet() {
|
|
||||||
goldChargeFormRef.value?.validateFields()?.then(async () => {
|
|
||||||
try {
|
|
||||||
loading.value = true
|
|
||||||
await chargeWallet(buildChargePayload(formCharge.value), 'gold')
|
|
||||||
message.success(walletChargeSuccessMessage(formCharge.value.direction))
|
|
||||||
showGoldWalletCharge.value = false
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e)
|
console.log(e)
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { Charge, WalletChargeDirection } from '@/model/wallet'
|
import type { Charge, WalletChargeDirection, WalletChargeTarget } from '@/model/wallet'
|
||||||
|
|
||||||
export function createEmptyChargeForm(customerId?: number): Charge {
|
export function createEmptyChargeForm(customerId?: number): Charge {
|
||||||
return {
|
return {
|
||||||
@@ -7,7 +7,9 @@ export function createEmptyChargeForm(customerId?: number): Charge {
|
|||||||
note: undefined,
|
note: undefined,
|
||||||
talabBedehi: false,
|
talabBedehi: false,
|
||||||
direction: 'charge',
|
direction: 'charge',
|
||||||
amountAbs: undefined
|
amountAbs: undefined,
|
||||||
|
chargeTargetKey: 'money',
|
||||||
|
product_id: null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,17 +20,35 @@ export function buildChargePayload(form: Charge): Charge {
|
|||||||
value: signed,
|
value: signed,
|
||||||
note: form.note,
|
note: form.note,
|
||||||
customer_id: form.customer_id,
|
customer_id: form.customer_id,
|
||||||
talabBedehi: form.talabBedehi === true
|
talabBedehi: form.talabBedehi === true,
|
||||||
|
product_id: form.product_id ?? null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveChargeTarget(
|
||||||
|
targets: WalletChargeTarget[],
|
||||||
|
key?: string | null
|
||||||
|
): WalletChargeTarget | undefined {
|
||||||
|
if (!key) return targets[0]
|
||||||
|
return targets.find((target) => target.key === key)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyChargeTarget(form: Charge, target?: WalletChargeTarget): Charge {
|
||||||
|
if (!target) return form
|
||||||
|
return {
|
||||||
|
...form,
|
||||||
|
chargeTargetKey: target.key,
|
||||||
|
product_id: target.kind === 'metal' ? target.product_id ?? null : null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function walletChargeTitle(
|
export function walletChargeTitle(
|
||||||
variant: 'money' | 'gold',
|
target: WalletChargeTarget | undefined,
|
||||||
direction: WalletChargeDirection = 'charge'
|
direction: WalletChargeDirection = 'charge'
|
||||||
) {
|
) {
|
||||||
const wallet = variant === 'money' ? 'ریالی' : 'طلایی'
|
|
||||||
const action = direction === 'discharge' ? 'دشارژ' : 'شارژ'
|
const action = direction === 'discharge' ? 'دشارژ' : 'شارژ'
|
||||||
return `${action} کیف پول ${wallet}`
|
const wallet = target?.label ?? 'کیف پول'
|
||||||
|
return `${action} ${wallet}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export function walletChargeOkText(direction: WalletChargeDirection = 'charge') {
|
export function walletChargeOkText(direction: WalletChargeDirection = 'charge') {
|
||||||
@@ -38,3 +58,10 @@ export function walletChargeOkText(direction: WalletChargeDirection = 'charge')
|
|||||||
export function walletChargeSuccessMessage(direction: WalletChargeDirection = 'charge') {
|
export function walletChargeSuccessMessage(direction: WalletChargeDirection = 'charge') {
|
||||||
return direction === 'discharge' ? 'دشارژ با موفقیت انجام شد' : 'شارژ با موفقیت انجام شد'
|
return direction === 'discharge' ? 'دشارژ با موفقیت انجام شد' : 'شارژ با موفقیت انجام شد'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function walletPreviewLabel(target: WalletChargeTarget | undefined): string {
|
||||||
|
if (!target) return 'موجودی کاربر'
|
||||||
|
if (target.kind === 'money') return 'موجودی ریالی'
|
||||||
|
if (target.uses_main_wallet) return `موجودی ${target.label.replace(/\s*\(کیف اصلی\)$/, '')} (کیف اصلی)`
|
||||||
|
return `موجودی ${target.label}`
|
||||||
|
}
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ import MobileSheetHeader from '@/components/mobile/MobileSheetHeader.vue'
|
|||||||
import MobileStatCard from '@/components/mobile/MobileStatCard.vue'
|
import MobileStatCard from '@/components/mobile/MobileStatCard.vue'
|
||||||
import { isMobile } from '@core/utils/is'
|
import { isMobile } from '@core/utils/is'
|
||||||
import { toJalali } from '@core/utils/useDataTime'
|
import { toJalali } from '@core/utils/useDataTime'
|
||||||
|
import { metalLabel } from '@/utils/adminMetalLabels'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
visible: boolean
|
visible: boolean
|
||||||
@@ -82,7 +83,7 @@ function statusClass(row: Giftcardcode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function unitLabel(row: Giftcardcode) {
|
function unitLabel(row: Giftcardcode) {
|
||||||
return row.unit === 2 ? 'طلایی' : 'ریالی'
|
return row.unit === 2 ? metalLabel.goldWalletShort() : 'ریالی'
|
||||||
}
|
}
|
||||||
|
|
||||||
function unitTagClass(row: Giftcardcode) {
|
function unitTagClass(row: Giftcardcode) {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Giftcardcode } from '@/model/giftCard'
|
import type { Giftcardcode } from '@/model/giftCard'
|
||||||
import MobileCard from '@/components/mobile/MobileCard.vue'
|
import MobileCard from '@/components/mobile/MobileCard.vue'
|
||||||
|
import { metalLabel } from '@/utils/adminMetalLabels'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
item: Giftcardcode
|
item: Giftcardcode
|
||||||
@@ -50,7 +51,7 @@ const statusClass = computed(() => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const unitLabel = computed(() => (props.item.unit === 2 ? 'طلایی' : 'ریالی'))
|
const unitLabel = computed(() => (props.item.unit === 2 ? metalLabel.goldWalletShort() : 'ریالی'))
|
||||||
const unitTagClass = computed(() =>
|
const unitTagClass = computed(() =>
|
||||||
props.item.unit === 2
|
props.item.unit === 2
|
||||||
? 'border-amber-300 bg-amber-50 text-amber-700'
|
? 'border-amber-300 bg-amber-50 text-amber-700'
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
<CustomTag v-else textColor="#C53030" bgColor="#FECACA">غیرفعال</CustomTag>
|
<CustomTag v-else textColor="#C53030" bgColor="#FECACA">غیرفعال</CustomTag>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="column.key === 'unit'">
|
<template v-else-if="column.key === 'unit'">
|
||||||
<CustomTag v-if="record.unit === 2" textColor="#C53030" bgColor="#FECACA">طلایی</CustomTag>
|
<CustomTag v-if="record.unit === 2" textColor="#C53030" bgColor="#FECACA">{{ metalLabel.goldWalletShort() }}</CustomTag>
|
||||||
<CustomTag v-else textColor="#25855A" bgColor="#E3F4E9">ریالی</CustomTag>
|
<CustomTag v-else textColor="#25855A" bgColor="#E3F4E9">ریالی</CustomTag>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="column.key === 'amount'">
|
<template v-else-if="column.key === 'amount'">
|
||||||
@@ -68,6 +68,7 @@ import BasicActionSheet from '@/components/BasicActionSheet.vue'
|
|||||||
import CustomTag from '@/components/CustomTag.vue'
|
import CustomTag from '@/components/CustomTag.vue'
|
||||||
import { useMobileDetailSheet } from '@/components/mobile/composables/useMobileDetailSheet'
|
import { useMobileDetailSheet } from '@/components/mobile/composables/useMobileDetailSheet'
|
||||||
import { isMobile } from '@core/utils/is'
|
import { isMobile } from '@core/utils/is'
|
||||||
|
import { metalLabel } from '@/utils/adminMetalLabels'
|
||||||
import { toJalali } from '@core/utils/useDataTime'
|
import { toJalali } from '@core/utils/useDataTime'
|
||||||
import GiftCardCodeMobileDetailSheet from './GiftCardCodeMobileDetailSheet.vue'
|
import GiftCardCodeMobileDetailSheet from './GiftCardCodeMobileDetailSheet.vue'
|
||||||
import GiftCardCodeMobileListItem from './GiftCardCodeMobileListItem.vue'
|
import GiftCardCodeMobileListItem from './GiftCardCodeMobileListItem.vue'
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<a-form-item label="نوع کارت هدیه" name="unit" :rules="inputRules()">
|
<a-form-item label="نوع کارت هدیه" name="unit" :rules="inputRules()">
|
||||||
<a-radio-group v-model:value="form.unit" button-style="solid">
|
<a-radio-group v-model:value="form.unit" button-style="solid">
|
||||||
<a-radio-button value="1">ریالی</a-radio-button>
|
<a-radio-button value="1">ریالی</a-radio-button>
|
||||||
<a-radio-button value="2">طلایی</a-radio-button>
|
<a-radio-button value="2">{{ metalLabel.goldWalletShort() }}</a-radio-button>
|
||||||
</a-radio-group>
|
</a-radio-group>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
@@ -48,6 +48,7 @@
|
|||||||
import type { FormInstance } from 'ant-design-vue'
|
import type { FormInstance } from 'ant-design-vue'
|
||||||
import type { GiftcardInput } from '@/model/giftCard'
|
import type { GiftcardInput } from '@/model/giftCard'
|
||||||
import { inputLenRules, inputRules, inputRulesNumber } from '@/utils/inputRules'
|
import { inputLenRules, inputRules, inputRulesNumber } from '@/utils/inputRules'
|
||||||
|
import { metalLabel } from '@/utils/adminMetalLabels'
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
form: GiftcardInput
|
form: GiftcardInput
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ import MobileStatCard from '@/components/mobile/MobileStatCard.vue'
|
|||||||
import { isMobile } from '@core/utils/is'
|
import { isMobile } from '@core/utils/is'
|
||||||
import { toJalali, toJalaliNoTime } from '@core/utils/useDataTime'
|
import { toJalali, toJalaliNoTime } from '@core/utils/useDataTime'
|
||||||
import { usePrice } from '@core/utils/usePrice'
|
import { usePrice } from '@core/utils/usePrice'
|
||||||
|
import { metalLabel } from '@/utils/adminMetalLabels'
|
||||||
|
|
||||||
type GiftCardRow = Giftcard & { customer?: { name?: string; mobile?: string } }
|
type GiftCardRow = Giftcard & { customer?: { name?: string; mobile?: string } }
|
||||||
|
|
||||||
@@ -83,7 +84,7 @@ const visibleProxy = computed({
|
|||||||
const sheetTitle = computed(() => props.item?.unique_code || `کارت هدیه #${props.item?.id ?? '-'}`)
|
const sheetTitle = computed(() => props.item?.unique_code || `کارت هدیه #${props.item?.id ?? '-'}`)
|
||||||
|
|
||||||
function unitLabel(row: GiftCardRow) {
|
function unitLabel(row: GiftCardRow) {
|
||||||
return row.unit === 2 ? 'طلایی' : 'ریالی'
|
return row.unit === 2 ? metalLabel.goldWalletShort() : 'ریالی'
|
||||||
}
|
}
|
||||||
|
|
||||||
function unitTagClass(row: GiftCardRow) {
|
function unitTagClass(row: GiftCardRow) {
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
import type { Giftcard } from '@/model/giftCard'
|
import type { Giftcard } from '@/model/giftCard'
|
||||||
import MobileCard from '@/components/mobile/MobileCard.vue'
|
import MobileCard from '@/components/mobile/MobileCard.vue'
|
||||||
import MobileIconButton from '@/components/mobile/MobileIconButton.vue'
|
import MobileIconButton from '@/components/mobile/MobileIconButton.vue'
|
||||||
|
import { metalLabel } from '@/utils/adminMetalLabels'
|
||||||
|
|
||||||
type GiftCardRow = Giftcard & { customer?: { name?: string; mobile?: string } }
|
type GiftCardRow = Giftcard & { customer?: { name?: string; mobile?: string } }
|
||||||
|
|
||||||
@@ -44,7 +45,7 @@ defineEmits<{
|
|||||||
(e: 'giftCardCodes', item: GiftCardRow): void
|
(e: 'giftCardCodes', item: GiftCardRow): void
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const unitLabel = computed(() => (props.item.unit === 2 ? 'طلایی' : 'ریالی'))
|
const unitLabel = computed(() => (props.item.unit === 2 ? metalLabel.goldWalletShort() : 'ریالی'))
|
||||||
const unitTagClass = computed(() =>
|
const unitTagClass = computed(() =>
|
||||||
props.item.unit === 2
|
props.item.unit === 2
|
||||||
? 'border-amber-300 bg-amber-50 text-amber-700'
|
? 'border-amber-300 bg-amber-50 text-amber-700'
|
||||||
|
|||||||
@@ -59,7 +59,7 @@
|
|||||||
import type { TableInput } from '@core/type/baseTable'
|
import type { TableInput } from '@core/type/baseTable'
|
||||||
import useBaseTable from '@core/components/BaseTableOld/hooks/useBaseTable'
|
import useBaseTable from '@core/components/BaseTableOld/hooks/useBaseTable'
|
||||||
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
||||||
import { cols } from '../hooks/orderColumns'
|
import { getGiftCardColumns } from '../hooks/orderColumns'
|
||||||
import { usePrefix } from '@core/composable/usePrefix'
|
import { usePrefix } from '@core/composable/usePrefix'
|
||||||
import { getGiftCardTable } from '@/api/giftCard'
|
import { getGiftCardTable } from '@/api/giftCard'
|
||||||
import GiftCardMobileCardList from './GiftCardMobileCardList.vue'
|
import GiftCardMobileCardList from './GiftCardMobileCardList.vue'
|
||||||
@@ -79,7 +79,7 @@ const mobileListRef = ref<null | { fetch: () => Promise<void> }>(null)
|
|||||||
const { prefixCls } = usePrefix('pophoverstatus')
|
const { prefixCls } = usePrefix('pophoverstatus')
|
||||||
|
|
||||||
const tblProps = ref<any>({
|
const tblProps = ref<any>({
|
||||||
columns: cols,
|
columns: getGiftCardColumns(),
|
||||||
api: (params: TableInput) => {
|
api: (params: TableInput) => {
|
||||||
if (props.customerId) {
|
if (props.customerId) {
|
||||||
params.filters = []
|
params.filters = []
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import type { BaseTableColumn } from '@core/type/baseTable'
|
|||||||
import { toJalali, toJalaliNoTime, toTime } from '@core/utils/useDataTime'
|
import { toJalali, toJalaliNoTime, toTime } from '@core/utils/useDataTime'
|
||||||
import CustomTag from '@/components/CustomTag.vue'
|
import CustomTag from '@/components/CustomTag.vue'
|
||||||
import { usePrice } from '@core/utils/usePrice'
|
import { usePrice } from '@core/utils/usePrice'
|
||||||
|
import { metalLabel } from '@/utils/adminMetalLabels'
|
||||||
/*import BudgetSelect from '@/components/BudgetSelect.vue'
|
/*import BudgetSelect from '@/components/BudgetSelect.vue'
|
||||||
import { useStaticApiListsStore } from '@/store/staticApisStore'
|
import { useStaticApiListsStore } from '@/store/staticApisStore'
|
||||||
import type { FilterSelect } from '@/model/filterSelect'
|
import type { FilterSelect } from '@/model/filterSelect'
|
||||||
@@ -21,7 +22,8 @@ const filteredDocTypes: FilterSelect = docTypes?.map((x: DocItem) => {
|
|||||||
}
|
}
|
||||||
})*/
|
})*/
|
||||||
|
|
||||||
export const cols: BaseTableColumn[] = [
|
export function getGiftCardColumns(): BaseTableColumn[] {
|
||||||
|
return [
|
||||||
{
|
{
|
||||||
title: 'ردیف',
|
title: 'ردیف',
|
||||||
dataIndex: 'id',
|
dataIndex: 'id',
|
||||||
@@ -50,7 +52,7 @@ export const cols: BaseTableColumn[] = [
|
|||||||
return (
|
return (
|
||||||
<div class="flex justify-center">
|
<div class="flex justify-center">
|
||||||
<CustomTag textColor="#C53030" bgColor="#FECACA">
|
<CustomTag textColor="#C53030" bgColor="#FECACA">
|
||||||
طلایی
|
{metalLabel.goldWalletShort()}
|
||||||
</CustomTag>
|
</CustomTag>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@@ -236,4 +238,5 @@ export const cols: BaseTableColumn[] = [
|
|||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
filterable: false
|
filterable: false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
}
|
||||||
|
|||||||
@@ -106,6 +106,7 @@ import type { Giftcard, Giftcardcode, GiftcardInput } from '@/model/giftCard'
|
|||||||
import { createGiftCard } from '@/api/giftCard'
|
import { createGiftCard } from '@/api/giftCard'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { isMobile } from '@core/utils/is'
|
import { isMobile } from '@core/utils/is'
|
||||||
|
import { metalLabel } from '@/utils/adminMetalLabels'
|
||||||
import { isStoreApp } from '@/utils/config'
|
import { isStoreApp } from '@/utils/config'
|
||||||
import { inputRules, inputRulesNumber } from '@/utils/inputRules'
|
import { inputRules, inputRulesNumber } from '@/utils/inputRules'
|
||||||
import BasicInput from '@/components/BasicInput.vue'
|
import BasicInput from '@/components/BasicInput.vue'
|
||||||
@@ -174,7 +175,7 @@ function info(val: Giftcard) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'نوع کارت هدیه',
|
key: 'نوع کارت هدیه',
|
||||||
value: val.unit === 3 ? 'تخفیف' : val.unit === 2 ? 'طلایی' : 'ریالی'
|
value: val.unit === 3 ? 'تخفیف' : val.unit === 2 ? metalLabel.goldWalletShort() : 'ریالی'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'تعداد کدها',
|
key: 'تعداد کدها',
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<a-form :model="form" ref="formRef" autocomplete="off">
|
<a-form :model="form" ref="formRef" autocomplete="off">
|
||||||
<div class="space-y-3">
|
<div class="space-y-3">
|
||||||
<div class="flex flex-col items-center gap-1 rounded-xl bg-gray-50 px-4 py-3 sm:flex-row sm:justify-center sm:gap-4">
|
<div class="flex flex-col items-center gap-1 rounded-xl bg-gray-50 px-4 py-3 sm:flex-row sm:justify-center sm:gap-4">
|
||||||
<span class="text-sm text-gray-600">قیمت {{ form.direction === 1 ? 'خرید' : 'فروش' }} طلا:</span>
|
<span class="text-sm text-gray-600">{{ metalLabel.goldTradeBuySell(form.direction) }}</span>
|
||||||
<span class="text-sm font-700 text-gray-900">{{ marketPriceLabel }}</span>
|
<span class="text-sm font-700 text-gray-900">{{ marketPriceLabel }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
@change="$emit('changeUser', $event)"
|
@change="$emit('changeUser', $event)"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<BasicInfoItem is-line title="کیف پول طلا" :value="selectedCustomer?.wallet?.[0]?.gold ?? '_'" />
|
<BasicInfoItem is-line :title="metalLabel.goldWalletTitle()" :value="selectedCustomer?.wallet?.[0]?.gold ?? '_'" />
|
||||||
<BasicInfoItem is-line title="کیف پول ریالی" :value="selectedCustomer?.wallet?.[0]?.cash ?? '_'" />
|
<BasicInfoItem is-line title="کیف پول ریالی" :value="selectedCustomer?.wallet?.[0]?.cash ?? '_'" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@
|
|||||||
addon-before="گرم"
|
addon-before="گرم"
|
||||||
addon-bg-color="#0A1B37"
|
addon-bg-color="#0A1B37"
|
||||||
addon-text-color="white"
|
addon-text-color="white"
|
||||||
place-holder="مقدار طلا"
|
:place-holder="metalLabel.goldPlaceholder()"
|
||||||
type="tel"
|
type="tel"
|
||||||
@onchange="$emit('calculateCash', $event)"
|
@onchange="$emit('calculateCash', $event)"
|
||||||
/>
|
/>
|
||||||
@@ -88,6 +88,7 @@ import type { Order } from '@/model/order'
|
|||||||
import type { last } from '@/model/price'
|
import type { last } from '@/model/price'
|
||||||
import { inputRules, inputRulesNumber } from '@/utils/inputRules'
|
import { inputRules, inputRulesNumber } from '@/utils/inputRules'
|
||||||
import { usePrice } from '@core/utils/usePrice'
|
import { usePrice } from '@core/utils/usePrice'
|
||||||
|
import { metalLabel } from '@/utils/adminMetalLabels'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
form: Order
|
form: Order
|
||||||
|
|||||||
@@ -26,23 +26,18 @@
|
|||||||
<BaseIcon icon="vuesax-linear:empty-wallet" size="18" color="#15803d" />
|
<BaseIcon icon="vuesax-linear:empty-wallet" size="18" color="#15803d" />
|
||||||
</template>
|
</template>
|
||||||
</MobileStatCard>
|
</MobileStatCard>
|
||||||
<MobileStatCard bordered variant="warning" label="مقدار (گرم)" :value="`${row.amount ?? 0} گرم`">
|
<MobileStatCard bordered variant="warning" label="مقدار" :value="amountLabel(row)" />
|
||||||
<template #prefix>
|
<MobileStatCard bordered label="قیمت زمان معامله (ریال)" :value="usePrice(row.price ?? 0, true, '')" />
|
||||||
<BaseIcon icon="vuesax-linear:coin" size="18" color="#b45309" />
|
|
||||||
</template>
|
|
||||||
</MobileStatCard>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<MobileStatCard bordered label="قیمت هر گرم طلا برای مشتری (ریال)" :value="usePrice(row.price ?? 0, true, '')" />
|
<MobileStatCard bordered label="کارمزد" :value="serviceChargeLabel(row)" />
|
||||||
<MobileStatCard bordered label="قیمت هر گرم طلا بدون سود (ریال)" :value="usePrice(row.price_original ?? 0, true, '')" />
|
|
||||||
<!-- <MobileStatCard bordered label="قیمت کل لیمان (ریال)" :value="usePrice(row.total_price_liman ?? 0, true, '')" />-->
|
|
||||||
<MobileStatCard bordered label="قیمت کل بدون سود (ریال)" :value="usePrice(row.total_price_original ?? 0, true, '')" />
|
<MobileStatCard bordered label="قیمت کل بدون سود (ریال)" :value="usePrice(row.total_price_original ?? 0, true, '')" />
|
||||||
<MobileStatCard bordered label="سود هر گرم (ریال)" :value="usePrice(row.fee ?? 0, true, '')" />
|
<MobileStatCard bordered label="سود واحد (ریال)" :value="usePrice(row.fee ?? 0, true, '')" />
|
||||||
<MobileStatCard bordered label="کارمزد طلایی" :value="serviceChargeLabel(row)" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<MobileInfoGrid>
|
<MobileInfoGrid>
|
||||||
|
<MobileDetailRow label="نوع فلز" :value="row.order_metal_label ?? '-'" />
|
||||||
<MobileDetailRow label="کد سفارش" :value="row.id ?? '-'" />
|
<MobileDetailRow label="کد سفارش" :value="row.id ?? '-'" />
|
||||||
<MobileDetailRow label="شماره موبایل" :value="row.customer?.mobile || '-'" />
|
<MobileDetailRow label="شماره موبایل" :value="row.customer?.mobile || '-'" />
|
||||||
<MobileDetailRow label="سود کل (ریال)" :value="usePrice(row.total_fee ?? 0, true, '')" />
|
<MobileDetailRow label="سود کل (ریال)" :value="usePrice(row.total_fee ?? 0, true, '')" />
|
||||||
@@ -66,6 +61,7 @@ import MobileStatCard from '@/components/mobile/MobileStatCard.vue'
|
|||||||
import { isMobile } from '@core/utils/is'
|
import { isMobile } from '@core/utils/is'
|
||||||
import { toJalali } from '@core/utils/useDataTime'
|
import { toJalali } from '@core/utils/useDataTime'
|
||||||
import { usePrice } from '@core/utils/usePrice'
|
import { usePrice } from '@core/utils/usePrice'
|
||||||
|
import { formatAmountWithUnit } from '@/utils/weightUnit'
|
||||||
|
|
||||||
type OrderRow = Order & { sts_str?: string; service_charge?: number | string }
|
type OrderRow = Order & { sts_str?: string; service_charge?: number | string }
|
||||||
|
|
||||||
@@ -113,10 +109,14 @@ function createdAtLabel(row: OrderRow) {
|
|||||||
return row.created_at ? toJalali(row.created_at) : '-'
|
return row.created_at ? toJalali(row.created_at) : '-'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function amountLabel(row: OrderRow) {
|
||||||
|
return formatAmountWithUnit(row.amount_display ?? row.amount, row.amount_unit)
|
||||||
|
}
|
||||||
|
|
||||||
function serviceChargeLabel(row: OrderRow) {
|
function serviceChargeLabel(row: OrderRow) {
|
||||||
if (row.service_charge == null || row.service_charge === '') return '-'
|
if (row.service_charge == null || row.service_charge === '') return '-'
|
||||||
const value = Number(row.service_charge)
|
const value = Number(row.service_charge)
|
||||||
if (Number.isNaN(value)) return `${row.service_charge} گرم`
|
if (Number.isNaN(value)) return String(row.service_charge)
|
||||||
return `${parseFloat(value.toFixed(5))} گرم`
|
return formatAmountWithUnit(value, row.amount_unit)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -6,6 +6,12 @@
|
|||||||
<span class="shrink-0 rounded-md border px-1.5 py-0.5 text-xs font-600" :class="directionClass">
|
<span class="shrink-0 rounded-md border px-1.5 py-0.5 text-xs font-600" :class="directionClass">
|
||||||
{{ directionLabel }}
|
{{ directionLabel }}
|
||||||
</span>
|
</span>
|
||||||
|
<span
|
||||||
|
v-if="item.order_metal_label"
|
||||||
|
class="shrink-0 rounded-md border border-amber-300 bg-amber-50 px-1.5 py-0.5 text-xs font-600 text-amber-700"
|
||||||
|
>
|
||||||
|
{{ item.order_metal_label }}
|
||||||
|
</span>
|
||||||
<span class="truncate text-sm font-700 text-gray-900">{{ customerName }}</span>
|
<span class="truncate text-sm font-700 text-gray-900">{{ customerName }}</span>
|
||||||
</div>
|
</div>
|
||||||
<span class="shrink-0 text-xs font-600" :class="statusValueClass">{{ statusLabel }}</span>
|
<span class="shrink-0 text-xs font-600" :class="statusValueClass">{{ statusLabel }}</span>
|
||||||
@@ -13,7 +19,7 @@
|
|||||||
|
|
||||||
<div class="flex items-center justify-between gap-2 text-xs text-gray-600">
|
<div class="flex items-center justify-between gap-2 text-xs text-gray-600">
|
||||||
<span>
|
<span>
|
||||||
<span class="text-gray-500">طلا:</span>
|
<span class="text-gray-500">مقدار:</span>
|
||||||
<span class="font-600 text-gray-900">{{ amountLabel }}</span>
|
<span class="font-600 text-gray-900">{{ amountLabel }}</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="font-700 text-gray-900">{{ totalPriceLabel }}</span>
|
<span class="font-700 text-gray-900">{{ totalPriceLabel }}</span>
|
||||||
@@ -26,6 +32,7 @@
|
|||||||
import type { Order } from '@/model/order'
|
import type { Order } from '@/model/order'
|
||||||
import MobileCard from '@/components/mobile/MobileCard.vue'
|
import MobileCard from '@/components/mobile/MobileCard.vue'
|
||||||
import { usePrice } from '@core/utils/usePrice'
|
import { usePrice } from '@core/utils/usePrice'
|
||||||
|
import { formatAmountWithUnit } from '@/utils/weightUnit'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
item: Order & { sts_str?: string; service_charge?: number }
|
item: Order & { sts_str?: string; service_charge?: number }
|
||||||
@@ -44,9 +51,8 @@ const directionClass = computed(() =>
|
|||||||
|
|
||||||
const customerName = computed(() => props.item.customer?.name || '-')
|
const customerName = computed(() => props.item.customer?.name || '-')
|
||||||
const amountLabel = computed(() => {
|
const amountLabel = computed(() => {
|
||||||
const amount = props.item.amount
|
const amount = props.item.amount_display ?? props.item.amount
|
||||||
if (amount == null) return '-'
|
return formatAmountWithUnit(amount, props.item.amount_unit)
|
||||||
return `${Number(amount.toFixed(3))} گرم`
|
|
||||||
})
|
})
|
||||||
const totalPriceLabel = computed(() => usePrice(props.item.total_price ?? 0, true, ''))
|
const totalPriceLabel = computed(() => usePrice(props.item.total_price ?? 0, true, ''))
|
||||||
const statusLabel = computed(() => props.item.sts_str || '-')
|
const statusLabel = computed(() => props.item.sts_str || '-')
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ import type { TableInput } from '@core/type/baseTable'
|
|||||||
import useBaseTable from '@core/components/BaseTableOld/hooks/useBaseTable'
|
import useBaseTable from '@core/components/BaseTableOld/hooks/useBaseTable'
|
||||||
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
||||||
import { changeCustomerActivation, getCustomerTable } from '@/api/customer'
|
import { changeCustomerActivation, getCustomerTable } from '@/api/customer'
|
||||||
import { cols } from '../hooks/orderColumns'
|
import { buildOrderColumns } from '../hooks/orderColumns'
|
||||||
import { useUserStore } from '@/store/userStore'
|
import { useUserStore } from '@/store/userStore'
|
||||||
import { usePrefix } from '@core/composable/usePrefix'
|
import { usePrefix } from '@core/composable/usePrefix'
|
||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
@@ -74,22 +74,22 @@ function checkPermission(temp: string) {
|
|||||||
const table = ref(null)
|
const table = ref(null)
|
||||||
|
|
||||||
const tblProps = ref<any>({
|
const tblProps = ref<any>({
|
||||||
columns: cols,
|
columns: buildOrderColumns(),
|
||||||
api: (params: TableInput) => {
|
api: async (params: TableInput) => {
|
||||||
if (props.customerId) {
|
if (props.customerId) {
|
||||||
params.filters = []
|
params.filters = []
|
||||||
params.filters?.push({
|
params.filters?.push({
|
||||||
field: "customer_id",
|
field: 'customer_id',
|
||||||
type: "text",
|
type: 'text',
|
||||||
value: props.customerId,
|
value: props.customerId,
|
||||||
cr: "and",
|
cr: 'and',
|
||||||
op: "eq",
|
op: 'eq',
|
||||||
hidden: false
|
hidden: false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return getOrderTable(params)
|
const data = await getOrderTable(params)
|
||||||
|
return data
|
||||||
}
|
}
|
||||||
//bordered: true
|
|
||||||
})
|
})
|
||||||
const loading = ref<boolean>(false)
|
const loading = ref<boolean>(false)
|
||||||
|
|
||||||
|
|||||||
@@ -1,239 +1,178 @@
|
|||||||
import type { BaseTableColumn } from '@core/type/baseTable'
|
import type { BaseTableColumn } from '@core/type/baseTable'
|
||||||
import { toJalali, toJalaliNoTime, toTime } from '@core/utils/useDataTime'
|
import { toJalali } from '@core/utils/useDataTime'
|
||||||
import CustomTag from '@/components/CustomTag.vue'
|
import CustomTag from '@/components/CustomTag.vue'
|
||||||
import { usePrice } from '@core/utils/usePrice'
|
import { usePrice } from '@core/utils/usePrice'
|
||||||
/*import BudgetSelect from '@/components/BudgetSelect.vue'
|
import { formatAmountWithUnit } from '@/utils/weightUnit'
|
||||||
import { useStaticApiListsStore } from '@/store/staticApisStore'
|
|
||||||
import type { FilterSelect } from '@/model/filterSelect'
|
|
||||||
import type { DocItem } from '@/model/docType'*/
|
|
||||||
|
|
||||||
//define store
|
export type OrderMetalColumn = { metal_id: number; label: string; display_unit?: string }
|
||||||
//const store = useStaticApiListsStore()
|
|
||||||
//document types
|
|
||||||
/*let docTypes: DocItem[]*/
|
|
||||||
/*onMounted(async () => {
|
|
||||||
docTypes = await store.readyDocTypeList()
|
|
||||||
})
|
|
||||||
const filteredDocTypes: FilterSelect = docTypes?.map((x: DocItem) => {
|
|
||||||
return {
|
|
||||||
label: x.title,
|
|
||||||
value: x.id
|
|
||||||
}
|
|
||||||
})*/
|
|
||||||
|
|
||||||
export const cols: BaseTableColumn[] = [
|
export function buildOrderColumns(_metals: OrderMetalColumn[] = []): BaseTableColumn[] {
|
||||||
{
|
return [
|
||||||
title: 'ردیف',
|
{
|
||||||
dataIndex: 'id',
|
title: 'ردیف',
|
||||||
key: 'id',
|
dataIndex: 'id',
|
||||||
filterable: false,
|
key: 'id',
|
||||||
sortable: false,
|
filterable: false,
|
||||||
customRender: (record: any) => {
|
sortable: false,
|
||||||
return record.index + 1
|
customRender: (record: any) => {
|
||||||
}
|
return record.index + 1
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'کد سفارش',
|
|
||||||
dataIndex: 'id',
|
|
||||||
key: 'id',
|
|
||||||
type: 'Number'
|
|
||||||
// className: 'column-money'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'نوع سفارش',
|
|
||||||
dataIndex: 'direction',
|
|
||||||
key: 'direction',
|
|
||||||
filterable: false,
|
|
||||||
customRender: ({ record }) => {
|
|
||||||
switch (record.direction) {
|
|
||||||
case 2:
|
|
||||||
return (
|
|
||||||
<div class="flex justify-center">
|
|
||||||
<CustomTag textColor="#C53030" bgColor="#FECACA">
|
|
||||||
فروش
|
|
||||||
</CustomTag>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
case 1:
|
|
||||||
return (
|
|
||||||
<div class="flex justify-center">
|
|
||||||
<CustomTag textColor="#25855A" bgColor="#E3F4E9">
|
|
||||||
خرید
|
|
||||||
</CustomTag>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
{
|
||||||
{
|
title: 'کد سفارش',
|
||||||
title: 'نام و نام خانوادگی',
|
dataIndex: 'id',
|
||||||
dataIndex: 'customer.name',
|
key: 'id',
|
||||||
key: 'customer.name',
|
type: 'Number'
|
||||||
type: 'Text'
|
},
|
||||||
},
|
{
|
||||||
{
|
title: 'نوع سفارش',
|
||||||
title: 'شماره موبایل',
|
dataIndex: 'direction',
|
||||||
dataIndex: 'customer.mobile',
|
key: 'direction',
|
||||||
key: 'customer.mobile',
|
filterable: false,
|
||||||
type: 'Number'
|
customRender: ({ record }) => {
|
||||||
},
|
switch (record.direction) {
|
||||||
{
|
case 2:
|
||||||
title: 'مقدار (گرم)',
|
return (
|
||||||
dataIndex: 'amount',
|
<div class="flex justify-center">
|
||||||
key: 'amount',
|
<CustomTag textColor="#C53030" bgColor="#FECACA">
|
||||||
type: 'Text'
|
فروش
|
||||||
},
|
</CustomTag>
|
||||||
/*{
|
</div>
|
||||||
title: 'کارمزد ریالی',
|
)
|
||||||
dataIndex: 'service_charge_price',
|
case 1:
|
||||||
key: 'service_charge_price',
|
return (
|
||||||
type: 'Number',
|
<div class="flex justify-center">
|
||||||
customRender(data) {
|
<CustomTag textColor="#25855A" bgColor="#E3F4E9">
|
||||||
return usePrice(data.record?.service_charge_price ?? 0, true, '')
|
خرید
|
||||||
}
|
</CustomTag>
|
||||||
// className: 'column-money'
|
</div>
|
||||||
},*/
|
)
|
||||||
{
|
}
|
||||||
title: 'کارمزد طلایی',
|
|
||||||
dataIndex: 'service_charge',
|
|
||||||
key: 'service_charge',
|
|
||||||
type: 'Number',
|
|
||||||
customRender(data) {
|
|
||||||
return data.record?.service_charge
|
|
||||||
}
|
|
||||||
// className: 'column-money'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'قیمت هر گرم طلا برای مشتری (ریال)',
|
|
||||||
dataIndex: 'price',
|
|
||||||
key: 'price',
|
|
||||||
type: 'Text',
|
|
||||||
customRender(data) {
|
|
||||||
return usePrice(data.record?.price ?? 0, true, '')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/* {
|
|
||||||
title: 'قیمت هر گرم طلا در لیمان',
|
|
||||||
dataIndex: 'limanPrice',
|
|
||||||
key: 'limanPrice',
|
|
||||||
type: 'Text',
|
|
||||||
customRender(data) {
|
|
||||||
return usePrice(data.record?.total_price_liman ?? 0, true, '')
|
|
||||||
}
|
|
||||||
},*/
|
|
||||||
{
|
|
||||||
title: 'قیمت کل برای مشتری (ریال)',
|
|
||||||
dataIndex: 'total_price',
|
|
||||||
key: 'total_price',
|
|
||||||
type: 'Text',
|
|
||||||
customRender(data) {
|
|
||||||
return usePrice(data.record?.total_price ?? 0, true, '')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
title: 'وضعیت',
|
|
||||||
dataIndex: 'sts',
|
|
||||||
key: 'sts',
|
|
||||||
filterable: false,
|
|
||||||
customRender: ({ record }) => {
|
|
||||||
switch (record.sts) {
|
|
||||||
case 1:
|
|
||||||
return (
|
|
||||||
<div class="flex justify-center">
|
|
||||||
<CustomTag textColor="#ca8a04" bgColor="#fef3c7">
|
|
||||||
{record.sts_str}
|
|
||||||
</CustomTag>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
case 2:
|
|
||||||
return (
|
|
||||||
<div class="flex justify-center">
|
|
||||||
<CustomTag textColor="#25855A" bgColor="#E3F4E9">
|
|
||||||
{record.sts_str}
|
|
||||||
</CustomTag>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
default:
|
|
||||||
return (
|
|
||||||
<div class="flex justify-center">
|
|
||||||
<CustomTag textColor="#C53030" bgColor="#FECACA">
|
|
||||||
{record.sts_str}
|
|
||||||
</CustomTag>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
{
|
||||||
{
|
title: 'نام و نام خانوادگی',
|
||||||
title: 'تاریخ ثبت',
|
dataIndex: 'customer.name',
|
||||||
dataIndex: 'created_at',
|
key: 'customer.name',
|
||||||
key: 'created_at',
|
type: 'Text'
|
||||||
type: 'DateTime',
|
},
|
||||||
customRender: (record: any) => {
|
{
|
||||||
return toJalali(record.record.created_at)
|
title: 'شماره موبایل',
|
||||||
}
|
dataIndex: 'customer.mobile',
|
||||||
},
|
key: 'customer.mobile',
|
||||||
/*{
|
type: 'Number'
|
||||||
title: 'نقش',
|
},
|
||||||
dataIndex: 'user_type_record',
|
{
|
||||||
key: 'user_type_record',
|
title: 'نوع فلز',
|
||||||
type: 'Select',
|
dataIndex: 'order_metal_label',
|
||||||
filterable: false,
|
key: 'order_metal_label',
|
||||||
sortable: false,
|
type: 'Text',
|
||||||
filterComponentProps: {
|
filterable: false,
|
||||||
multiple: true
|
customRender({ record }) {
|
||||||
},
|
return (
|
||||||
filterField: 'user_type_obj.id',
|
<div class="flex justify-center">
|
||||||
filterComponent: ReadyUserTypeSelect,
|
<CustomTag textColor="#ca8a04" bgColor="#fef3c7" class={'font-bold px-6'}>
|
||||||
/!* customRender: (record: any) => {
|
{record.order_metal_label ?? '-'}
|
||||||
return record.record.user_type_record?.value
|
|
||||||
}*!/
|
|
||||||
customRender: ({ record }) => {
|
|
||||||
const items = record.is_admin ? (
|
|
||||||
<CustomTag textColor="#25855A" bgColor="#E3F4E9">
|
|
||||||
ادمین
|
|
||||||
</CustomTag>
|
</CustomTag>
|
||||||
) : (
|
</div>
|
||||||
record.user_type_obj.map((item: { title: any }, index: any) => {
|
)
|
||||||
if (item) {
|
}
|
||||||
return (
|
},
|
||||||
<CustomTag textColor="#25855A" bgColor="#E3F4E9">
|
{
|
||||||
{item?.title}
|
title: 'مقدار',
|
||||||
</CustomTag>
|
dataIndex: 'amount',
|
||||||
)
|
key: 'amount',
|
||||||
}
|
type: 'Text',
|
||||||
})
|
customRender({ record }) {
|
||||||
)
|
const amount = record.amount_display ?? record.amount
|
||||||
return <div class="flex justify-center items-center gap-2">{items}</div>
|
return formatAmountWithUnit(amount, record.amount_unit)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'کارمزد',
|
||||||
|
dataIndex: 'service_charge',
|
||||||
|
key: 'service_charge',
|
||||||
|
type: 'Number',
|
||||||
|
customRender({ record }) {
|
||||||
|
const value = record.service_charge
|
||||||
|
if (value == null || value === '') {
|
||||||
|
return '-'
|
||||||
}
|
}
|
||||||
},
|
return formatAmountWithUnit(Number(value), record.amount_unit)
|
||||||
{
|
}
|
||||||
title: 'حوزه',
|
},
|
||||||
dataIndex: 'section',
|
{
|
||||||
key: 'section'
|
title: 'قیمت زمان معامله (ریال)',
|
||||||
},
|
dataIndex: 'price',
|
||||||
{
|
key: 'price',
|
||||||
title: 'شماره موبایل',
|
type: 'Text',
|
||||||
dataIndex: 'mobile',
|
customRender({ record }) {
|
||||||
type: 'Number',
|
return usePrice(record.price ?? 0, true, '')
|
||||||
key: 'mobile'
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'تاریخ ثبت',
|
title: 'قیمت کل برای مشتری (ریال)',
|
||||||
dataIndex: 'created_at',
|
dataIndex: 'total_price',
|
||||||
key: 'created_at',
|
key: 'total_price',
|
||||||
type: 'DateTime',
|
type: 'Text',
|
||||||
customRender: (record: any) => {
|
customRender(data) {
|
||||||
return toJalaliNoTime(record.record.created_at)
|
return usePrice(data.record?.total_price ?? 0, true, '')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'وضعیت',
|
||||||
|
dataIndex: 'sts',
|
||||||
|
key: 'sts',
|
||||||
|
filterable: false,
|
||||||
|
customRender: ({ record }) => {
|
||||||
|
switch (record.sts) {
|
||||||
|
case 1:
|
||||||
|
return (
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<CustomTag textColor="#ca8a04" bgColor="#fef3c7">
|
||||||
|
{record.sts_str}
|
||||||
|
</CustomTag>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
case 2:
|
||||||
|
return (
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<CustomTag textColor="#25855A" bgColor="#E3F4E9">
|
||||||
|
{record.sts_str}
|
||||||
|
</CustomTag>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
default:
|
||||||
|
return (
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<CustomTag textColor="#C53030" bgColor="#FECACA">
|
||||||
|
{record.sts_str}
|
||||||
|
</CustomTag>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
},*/
|
}
|
||||||
{
|
},
|
||||||
title: 'عملیات',
|
{
|
||||||
dataIndex: 'action',
|
title: 'تاریخ ثبت',
|
||||||
key: 'action',
|
dataIndex: 'created_at',
|
||||||
slot: 'action',
|
key: 'created_at',
|
||||||
fixed: 'right',
|
type: 'DateTime',
|
||||||
filterable: false
|
customRender: (record: any) => {
|
||||||
}
|
return toJalali(record.record.created_at)
|
||||||
]
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'عملیات',
|
||||||
|
dataIndex: 'action',
|
||||||
|
key: 'action',
|
||||||
|
slot: 'action',
|
||||||
|
fixed: 'right',
|
||||||
|
filterable: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getOrderColumns(): BaseTableColumn[] {
|
||||||
|
return buildOrderColumns()
|
||||||
|
}
|
||||||
|
|||||||
@@ -80,6 +80,8 @@ import { getLastPrice } from '@/api/price'
|
|||||||
import type { last } from '@/model/price'
|
import type { last } from '@/model/price'
|
||||||
import { createOrder } from '@/api/order'
|
import { createOrder } from '@/api/order'
|
||||||
import { isMobile } from '@core/utils/is'
|
import { isMobile } from '@core/utils/is'
|
||||||
|
import { metalLabel } from '@/utils/adminMetalLabels'
|
||||||
|
import { formatAmountWithUnit } from '@/utils/weightUnit'
|
||||||
|
|
||||||
//loading
|
//loading
|
||||||
const loading = ref<boolean>(false)
|
const loading = ref<boolean>(false)
|
||||||
@@ -182,11 +184,15 @@ function info(val: Order) {
|
|||||||
infoParams.itemsTitle = 'اطلاعات کاربری'
|
infoParams.itemsTitle = 'اطلاعات کاربری'
|
||||||
infoParams.items = [
|
infoParams.items = [
|
||||||
{
|
{
|
||||||
key: 'مقدار (گرم)',
|
key: 'مقدار',
|
||||||
value: val.amount ?? '-'
|
value: formatAmountWithUnit(val.amount_display ?? val.amount, val.amount_unit)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'قیمت هر گرم طلا برای مشتری (ریال)',
|
key: 'نوع فلز',
|
||||||
|
value: val.order_metal_label ?? '-'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'قیمت زمان معامله (ریال)',
|
||||||
value: usePrice(val.price ?? 0) ?? '-'
|
value: usePrice(val.price ?? 0) ?? '-'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -194,7 +200,7 @@ function info(val: Order) {
|
|||||||
value: usePrice(val.total_price ?? 0) ?? '-'
|
value: usePrice(val.total_price ?? 0) ?? '-'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'قیمت هر گرم طلا بدون سود (ریال)',
|
key: metalLabel.goldGramPriceOriginal(),
|
||||||
value: usePrice(val.price_original ?? 0) ?? '-'
|
value: usePrice(val.price_original ?? 0) ?? '-'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -226,8 +232,11 @@ function info(val: Order) {
|
|||||||
value: usePrice(val.service_charge_price ?? 0)
|
value: usePrice(val.service_charge_price ?? 0)
|
||||||
},*/
|
},*/
|
||||||
{
|
{
|
||||||
key: 'کارمزد طلایی',
|
key: 'کارمزد',
|
||||||
value: val.service_charge ?? '-' + ' گرم'
|
value:
|
||||||
|
val.service_charge != null && val.service_charge !== ''
|
||||||
|
? formatAmountWithUnit(Number(val.service_charge), val.amount_unit)
|
||||||
|
: '-'
|
||||||
},
|
},
|
||||||
/* {
|
/* {
|
||||||
key: 'تاریخ تولد',
|
key: 'تاریخ تولد',
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<MobileSheetHeader :title="sheetTitle" :subtitle="dateTimeLabel(row)" :show-title="isMobile" />
|
<MobileSheetHeader :title="sheetTitle" :subtitle="dateTimeLabel(row)" :show-title="isMobile" />
|
||||||
|
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<MobileStatCard bordered variant="success" label="قیمت هر گرم طلا برای مشتری (ریال)" :value="usePrice(row.price ?? 0, true, '')">
|
<MobileStatCard bordered variant="success" :label="metalLabel.goldGramPriceCustomer()" :value="usePrice(row.price ?? 0, true, '')">
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<BaseIcon icon="vuesax-linear:empty-wallet" size="18" color="#15803d" />
|
<BaseIcon icon="vuesax-linear:empty-wallet" size="18" color="#15803d" />
|
||||||
</template>
|
</template>
|
||||||
@@ -32,6 +32,7 @@ import MobileStatCard from '@/components/mobile/MobileStatCard.vue'
|
|||||||
import { isMobile } from '@core/utils/is'
|
import { isMobile } from '@core/utils/is'
|
||||||
import { toJalaliNoTime } from '@core/utils/useDataTime'
|
import { toJalaliNoTime } from '@core/utils/useDataTime'
|
||||||
import { usePrice } from '@core/utils/usePrice'
|
import { usePrice } from '@core/utils/usePrice'
|
||||||
|
import { metalLabel } from '@/utils/adminMetalLabels'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
visible: boolean
|
visible: boolean
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import type { BaseTableColumn } from '@core/type/baseTable'
|
|||||||
import { toJalaliNoTime, toTime } from '@core/utils/useDataTime'
|
import { toJalaliNoTime, toTime } from '@core/utils/useDataTime'
|
||||||
import CustomTag from '@/components/CustomTag.vue'
|
import CustomTag from '@/components/CustomTag.vue'
|
||||||
import { usePrice } from '@core/utils/usePrice'
|
import { usePrice } from '@core/utils/usePrice'
|
||||||
|
import { metalLabel } from '@/utils/adminMetalLabels'
|
||||||
/*import BudgetSelect from '@/components/BudgetSelect.vue'
|
/*import BudgetSelect from '@/components/BudgetSelect.vue'
|
||||||
import { useStaticApiListsStore } from '@/store/staticApisStore'
|
import { useStaticApiListsStore } from '@/store/staticApisStore'
|
||||||
import type { FilterSelect } from '@/model/filterSelect'
|
import type { FilterSelect } from '@/model/filterSelect'
|
||||||
@@ -21,7 +22,8 @@ const filteredDocTypes: FilterSelect = docTypes?.map((x: DocItem) => {
|
|||||||
}
|
}
|
||||||
})*/
|
})*/
|
||||||
|
|
||||||
export const cols: BaseTableColumn[] = [
|
export function getPriceColumns(): BaseTableColumn[] {
|
||||||
|
return [
|
||||||
{
|
{
|
||||||
title: 'ردیف',
|
title: 'ردیف',
|
||||||
dataIndex: 'id',
|
dataIndex: 'id',
|
||||||
@@ -53,7 +55,7 @@ export const cols: BaseTableColumn[] = [
|
|||||||
type: 'Text'
|
type: 'Text'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'قیمت هر گرم طلا برای مشتری (ریال)',
|
title: metalLabel.goldGramPriceCustomer(),
|
||||||
dataIndex: 'price',
|
dataIndex: 'price',
|
||||||
key: 'price',
|
key: 'price',
|
||||||
type: 'Text',
|
type: 'Text',
|
||||||
@@ -157,4 +159,5 @@ export const cols: BaseTableColumn[] = [
|
|||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
filterable: false
|
filterable: false
|
||||||
}*/
|
}*/
|
||||||
]
|
]
|
||||||
|
}
|
||||||
|
|||||||
@@ -118,9 +118,9 @@
|
|||||||
>
|
>
|
||||||
<BasicInfoItem
|
<BasicInfoItem
|
||||||
title="نام قلک"
|
title="نام قلک"
|
||||||
:value="item?.fund ? item?.fund?.title : 'کیف پول طلایی'"
|
:value="item?.fund ? item?.fund?.title : metalLabel.goldWallet()"
|
||||||
/>
|
/>
|
||||||
<BasicInfoItem title="مقدار طلا" :value="`${item?.gold} گرم`" />
|
<BasicInfoItem :title="metalLabel.goldAmount()" :value="`${item?.gold} گرم`" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -162,6 +162,7 @@ import { createCustomer, deleteCustomer, updateCustomer } from '@/api/customer'
|
|||||||
import type { Charge } from '@/model/wallet'
|
import type { Charge } from '@/model/wallet'
|
||||||
import customers from '@/router/routes/customers'
|
import customers from '@/router/routes/customers'
|
||||||
import { chargeWallet } from '@/api/wallet'
|
import { chargeWallet } from '@/api/wallet'
|
||||||
|
import { metalLabel } from '@/utils/adminMetalLabels'
|
||||||
|
|
||||||
//loading
|
//loading
|
||||||
const loading = ref<boolean>(false)
|
const loading = ref<boolean>(false)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex h-full min-h-0 flex-col gap-4 p-4">
|
<div class="category-page flex h-full min-h-0 flex-col gap-4 p-4">
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
<div class="flex shrink-0 flex-wrap items-center gap-2">
|
||||||
<BaseButton :loading="syncing" @click="handleSync">
|
<BaseButton :loading="syncing" @click="handleSync">
|
||||||
همگامسازی از محصولات
|
همگامسازی از محصولات
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
@@ -9,47 +9,58 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a-spin :spinning="loading">
|
<div
|
||||||
<a-table
|
ref="tableWrapRef"
|
||||||
:columns="columns"
|
class="category-page__table min-h-0 flex-1 overflow-hidden"
|
||||||
:data-source="items"
|
:class="{ 'category-page__table--dragging': dragFromId !== null }"
|
||||||
:pagination="false"
|
>
|
||||||
row-key="id"
|
<a-spin :spinning="loading" class="category-page__spin">
|
||||||
size="middle"
|
<a-table
|
||||||
class="min-h-0 flex-1"
|
:columns="columns"
|
||||||
>
|
:data-source="items"
|
||||||
<template #bodyCell="{ column, record }">
|
:pagination="false"
|
||||||
<template v-if="column.key === 'active'">
|
:scroll="tableScrollY ? { y: tableScrollY } : undefined"
|
||||||
<a-switch
|
row-key="id"
|
||||||
:checked="asBool(record.active)"
|
size="middle"
|
||||||
checked-children="فعال"
|
>
|
||||||
un-checked-children="مخفی"
|
<template #bodyCell="{ column, record }">
|
||||||
@change="(checked: boolean) => toggleField(record, 'active', checked)"
|
<template v-if="column.key === 'drag'">
|
||||||
/>
|
<span
|
||||||
|
class="category-page__drag-handle"
|
||||||
|
title="کشیدن برای تغییر ترتیب"
|
||||||
|
@pointerdown="onHandlePointerDown(record.id, $event)"
|
||||||
|
>
|
||||||
|
<BaseIcon icon="vuesax-linear:menu" size="18" />
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="column.key === 'active'">
|
||||||
|
<a-switch
|
||||||
|
:checked="asBool(record.active)"
|
||||||
|
checked-children="فعال"
|
||||||
|
un-checked-children="مخفی"
|
||||||
|
@change="(checked: boolean) => toggleField(record, 'active', checked)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="column.key === 'show_in_select'">
|
||||||
|
<a-switch
|
||||||
|
:checked="asBool(record.show_in_select)"
|
||||||
|
@change="(checked: boolean) => toggleField(record, 'show_in_select', checked)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="column.key === 'sort_order'">
|
||||||
|
<span class="text-gray-400">{{ record.sort_order }}</span>
|
||||||
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="column.key === 'show_in_select'">
|
</a-table>
|
||||||
<a-switch
|
</a-spin>
|
||||||
:checked="asBool(record.show_in_select)"
|
</div>
|
||||||
@change="(checked: boolean) => toggleField(record, 'show_in_select', checked)"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template v-else-if="column.key === 'sort_order'">
|
|
||||||
<BasicInput
|
|
||||||
class="!w-16"
|
|
||||||
type="number"
|
|
||||||
v-model:value="record.sort_order"
|
|
||||||
@onchange="() => scheduleSortOrder(record)"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
</a-table>
|
|
||||||
</a-spin>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
import type { TableColumnType } from 'ant-design-vue'
|
import type { TableColumnType } from 'ant-design-vue'
|
||||||
|
import { useResizeObserver } from '@vueuse/core'
|
||||||
import { debounce } from 'lodash'
|
import { debounce } from 'lodash'
|
||||||
import { onMounted, onUnmounted, ref } from 'vue'
|
import { onMounted, onUnmounted, ref } from 'vue'
|
||||||
import { getCategories, syncCategoriesFromProducts, updateCategory } from '@/api/category'
|
import { getCategories, syncCategoriesFromProducts, updateCategory } from '@/api/category'
|
||||||
@@ -57,7 +68,17 @@ import type { Category } from '@/model/category'
|
|||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const syncing = ref(false)
|
const syncing = ref(false)
|
||||||
|
const savingOrder = ref(false)
|
||||||
const items = ref<Category[]>([])
|
const items = ref<Category[]>([])
|
||||||
|
const tableWrapRef = ref<HTMLElement | null>(null)
|
||||||
|
const tableScrollY = ref<number>()
|
||||||
|
const dragFromId = ref<number | null>(null)
|
||||||
|
let highlightedRow: HTMLTableRowElement | null = null
|
||||||
|
let draggingRow: HTMLTableRowElement | null = null
|
||||||
|
let pointerDragActive = false
|
||||||
|
let dragOverId: number | null = null
|
||||||
|
let activePointerId: number | null = null
|
||||||
|
let saveOrderGeneration = 0
|
||||||
|
|
||||||
type CategoryField = 'active' | 'show_in_select' | 'sort_order'
|
type CategoryField = 'active' | 'show_in_select' | 'sort_order'
|
||||||
|
|
||||||
@@ -66,32 +87,216 @@ const debouncedFlush = new Map<number, ReturnType<typeof debounce>>()
|
|||||||
let syncInFlight: Promise<void> | null = null
|
let syncInFlight: Promise<void> | null = null
|
||||||
|
|
||||||
const columns: TableColumnType<Category>[] = [
|
const columns: TableColumnType<Category>[] = [
|
||||||
|
{
|
||||||
|
title: '',
|
||||||
|
key: 'drag',
|
||||||
|
width: 44,
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
{ title: 'نام دسته', dataIndex: 'name', key: 'name' },
|
{ title: 'نام دسته', dataIndex: 'name', key: 'name' },
|
||||||
{
|
{
|
||||||
title: 'نمایش محصولات در اپ',
|
title: 'نمایش محصولات در اپ',
|
||||||
key: 'active',
|
key: 'active',
|
||||||
width: 160,
|
width: 160,
|
||||||
align: 'center'
|
align: 'center',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'سلکت افزودن محصول',
|
title: 'سلکت افزودن محصول',
|
||||||
key: 'show_in_select',
|
key: 'show_in_select',
|
||||||
width: 150,
|
width: 150,
|
||||||
align: 'center'
|
align: 'center',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'ترتیب',
|
title: 'ترتیب',
|
||||||
key: 'sort_order',
|
key: 'sort_order',
|
||||||
width: 90,
|
width: 72,
|
||||||
align: 'center'
|
align: 'center',
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
function updateTableScrollY() {
|
||||||
|
const height = tableWrapRef.value?.clientHeight ?? 0
|
||||||
|
tableScrollY.value = height > 120 ? height - 56 : undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
useResizeObserver(tableWrapRef, () => {
|
||||||
|
updateTableScrollY()
|
||||||
|
})
|
||||||
|
|
||||||
/** پیشفرض: روشن — فقط صفر یا false یعنی خاموش */
|
/** پیشفرض: روشن — فقط صفر یا false یعنی خاموش */
|
||||||
function asBool(value: boolean | number | undefined | null) {
|
function asBool(value: boolean | number | undefined | null) {
|
||||||
return value !== false && value !== 0
|
return value !== false && value !== 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getTableBodyEl(): HTMLElement | null {
|
||||||
|
return tableWrapRef.value?.querySelector('.ant-table-body') as HTMLElement | null
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseRowId(row: HTMLTableRowElement): number | null {
|
||||||
|
const key = row.getAttribute('data-row-key')
|
||||||
|
if (!key) return null
|
||||||
|
const id = Number(key)
|
||||||
|
return Number.isFinite(id) ? id : null
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRowFromPoint(clientX: number, clientY: number): HTMLTableRowElement | null {
|
||||||
|
const wrap = tableWrapRef.value
|
||||||
|
if (!wrap) return null
|
||||||
|
|
||||||
|
if (typeof document.elementsFromPoint === 'function') {
|
||||||
|
for (const element of document.elementsFromPoint(clientX, clientY)) {
|
||||||
|
if (!(element instanceof Element) || !wrap.contains(element)) continue
|
||||||
|
const row = element.closest('tr.ant-table-row')
|
||||||
|
if (row instanceof HTMLTableRowElement) return row
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const rows = wrap.querySelectorAll('tr.ant-table-row')
|
||||||
|
for (const row of rows) {
|
||||||
|
const rect = row.getBoundingClientRect()
|
||||||
|
if (
|
||||||
|
clientY >= rect.top &&
|
||||||
|
clientY <= rect.bottom &&
|
||||||
|
clientX >= rect.left &&
|
||||||
|
clientX <= rect.right
|
||||||
|
) {
|
||||||
|
return row as HTMLTableRowElement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
function setDragHighlight(row: HTMLTableRowElement | null) {
|
||||||
|
if (highlightedRow && highlightedRow !== row) {
|
||||||
|
highlightedRow.classList.remove('category-page__row--over')
|
||||||
|
}
|
||||||
|
highlightedRow = row
|
||||||
|
row?.classList.add('category-page__row--over')
|
||||||
|
}
|
||||||
|
|
||||||
|
function autoScrollAtPointer(clientY: number) {
|
||||||
|
const body = getTableBodyEl()
|
||||||
|
if (!body) return
|
||||||
|
|
||||||
|
const rect = body.getBoundingClientRect()
|
||||||
|
const edge = 56
|
||||||
|
if (clientY < rect.top + edge) {
|
||||||
|
body.scrollTop -= Math.max(8, (rect.top + edge - clientY) / 3)
|
||||||
|
} else if (clientY > rect.bottom - edge) {
|
||||||
|
body.scrollTop += Math.max(8, (clientY - (rect.bottom - edge)) / 3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onHandlePointerDown(id: number, event: PointerEvent) {
|
||||||
|
if (event.button !== 0) return
|
||||||
|
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
|
|
||||||
|
const handle = event.currentTarget as HTMLElement
|
||||||
|
handle.setPointerCapture(event.pointerId)
|
||||||
|
|
||||||
|
activePointerId = event.pointerId
|
||||||
|
pointerDragActive = true
|
||||||
|
dragFromId.value = id
|
||||||
|
dragOverId = id
|
||||||
|
|
||||||
|
draggingRow = handle.closest('tr.ant-table-row')
|
||||||
|
draggingRow?.classList.add('category-page__row--dragging')
|
||||||
|
|
||||||
|
window.addEventListener('pointermove', onPointerMove)
|
||||||
|
window.addEventListener('pointerup', onPointerUp)
|
||||||
|
window.addEventListener('pointercancel', onPointerUp)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onPointerMove(event: PointerEvent) {
|
||||||
|
if (!pointerDragActive || activePointerId !== event.pointerId) return
|
||||||
|
|
||||||
|
event.preventDefault()
|
||||||
|
autoScrollAtPointer(event.clientY)
|
||||||
|
|
||||||
|
const row = getRowFromPoint(event.clientX, event.clientY)
|
||||||
|
setDragHighlight(row)
|
||||||
|
dragOverId = row ? parseRowId(row) : null
|
||||||
|
}
|
||||||
|
|
||||||
|
function onPointerUp(event: PointerEvent) {
|
||||||
|
if (!pointerDragActive || activePointerId !== event.pointerId) return
|
||||||
|
|
||||||
|
const fromId = dragFromId.value
|
||||||
|
const toId =
|
||||||
|
dragOverId ?? (() => {
|
||||||
|
const row = getRowFromPoint(event.clientX, event.clientY)
|
||||||
|
return row ? parseRowId(row) : null
|
||||||
|
})()
|
||||||
|
|
||||||
|
if (fromId !== null && toId !== null && fromId !== toId) {
|
||||||
|
const fromIndex = items.value.findIndex((item) => item.id === fromId)
|
||||||
|
const toIndex = items.value.findIndex((item) => item.id === toId)
|
||||||
|
if (fromIndex >= 0 && toIndex >= 0) {
|
||||||
|
void reorderByDrag(fromIndex, toIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanupPointerDrag()
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanupPointerDrag() {
|
||||||
|
pointerDragActive = false
|
||||||
|
activePointerId = null
|
||||||
|
dragOverId = null
|
||||||
|
dragFromId.value = null
|
||||||
|
|
||||||
|
draggingRow?.classList.remove('category-page__row--dragging')
|
||||||
|
draggingRow = null
|
||||||
|
setDragHighlight(null)
|
||||||
|
|
||||||
|
window.removeEventListener('pointermove', onPointerMove)
|
||||||
|
window.removeEventListener('pointerup', onPointerUp)
|
||||||
|
window.removeEventListener('pointercancel', onPointerUp)
|
||||||
|
}
|
||||||
|
|
||||||
|
function applySortOrders(list: Category[]) {
|
||||||
|
list.forEach((item, index) => {
|
||||||
|
item.sort_order = index + 1
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function reorderByDrag(fromIndex: number, toIndex: number) {
|
||||||
|
if (fromIndex === toIndex) return
|
||||||
|
|
||||||
|
const list = [...items.value]
|
||||||
|
const [moved] = list.splice(fromIndex, 1)
|
||||||
|
list.splice(toIndex, 0, moved)
|
||||||
|
|
||||||
|
const previousOrders = new Map(items.value.map((item) => [item.id, item.sort_order]))
|
||||||
|
applySortOrders(list)
|
||||||
|
items.value = list
|
||||||
|
|
||||||
|
const updates = list.filter((item) => previousOrders.get(item.id) !== item.sort_order)
|
||||||
|
if (!updates.length) return
|
||||||
|
|
||||||
|
const generation = ++saveOrderGeneration
|
||||||
|
savingOrder.value = true
|
||||||
|
try {
|
||||||
|
await Promise.all(
|
||||||
|
updates.map((item) => updateCategory(item.id, { sort_order: item.sort_order }))
|
||||||
|
)
|
||||||
|
if (generation !== saveOrderGeneration) return
|
||||||
|
message.success('ترتیب ذخیره شد')
|
||||||
|
} catch (e) {
|
||||||
|
if (generation !== saveOrderGeneration) return
|
||||||
|
console.error(e)
|
||||||
|
message.error('خطا در ذخیره ترتیب')
|
||||||
|
await fetchList()
|
||||||
|
} finally {
|
||||||
|
if (generation === saveOrderGeneration) {
|
||||||
|
savingOrder.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function fetchList() {
|
async function fetchList() {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
@@ -101,6 +306,7 @@ async function fetchList() {
|
|||||||
message.error('خطا در دریافت دستهبندیها')
|
message.error('خطا در دریافت دستهبندیها')
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
|
updateTableScrollY()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,7 +335,7 @@ async function flushSave(id: number) {
|
|||||||
const snapshot = {
|
const snapshot = {
|
||||||
active: record.active,
|
active: record.active,
|
||||||
show_in_select: record.show_in_select,
|
show_in_select: record.show_in_select,
|
||||||
sort_order: record.sort_order
|
sort_order: record.sort_order,
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -154,12 +360,6 @@ function toggleField(
|
|||||||
scheduleSave(record, { [field]: next })
|
scheduleSave(record, { [field]: next })
|
||||||
}
|
}
|
||||||
|
|
||||||
function scheduleSortOrder(record: Category) {
|
|
||||||
const order = Number(record.sort_order)
|
|
||||||
if (Number.isNaN(order)) return
|
|
||||||
scheduleSave(record, { sort_order: order })
|
|
||||||
}
|
|
||||||
|
|
||||||
async function syncList(showMessage = false) {
|
async function syncList(showMessage = false) {
|
||||||
if (syncInFlight) {
|
if (syncInFlight) {
|
||||||
return syncInFlight
|
return syncInFlight
|
||||||
@@ -183,6 +383,7 @@ async function syncList(showMessage = false) {
|
|||||||
} finally {
|
} finally {
|
||||||
syncing.value = false
|
syncing.value = false
|
||||||
syncInFlight = null
|
syncInFlight = null
|
||||||
|
updateTableScrollY()
|
||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
|
|
||||||
@@ -194,12 +395,66 @@ async function handleSync() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
void syncList()
|
void fetchList()
|
||||||
|
updateTableScrollY()
|
||||||
})
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
cleanupPointerDrag()
|
||||||
debouncedFlush.forEach((fn) => fn.cancel())
|
debouncedFlush.forEach((fn) => fn.cancel())
|
||||||
debouncedFlush.clear()
|
debouncedFlush.clear()
|
||||||
pendingPatches.clear()
|
pendingPatches.clear()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.category-page__spin {
|
||||||
|
display: block;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
:deep(.ant-spin-container) {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-page__drag-handle {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: grab;
|
||||||
|
color: #9ca3af;
|
||||||
|
padding: 4px;
|
||||||
|
border-radius: 6px;
|
||||||
|
user-select: none;
|
||||||
|
touch-action: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #6b7280;
|
||||||
|
background: rgba(148, 163, 184, 0.16);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
cursor: grabbing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-page__table--dragging {
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
:deep(.ant-table-tbody > tr > td) {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.category-page__drag-handle) {
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.category-page__row--dragging > td) {
|
||||||
|
opacity: 0.45;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.category-page__row--over > td) {
|
||||||
|
background: rgba(59, 130, 246, 0.08) !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -20,7 +20,11 @@
|
|||||||
/>
|
/>
|
||||||
</MobileCardList>
|
</MobileCardList>
|
||||||
|
|
||||||
<TransactionMobileDetailSheet v-model:visible="sheetVisible" :item="selectedItem" />
|
<TransactionMobileDetailSheet
|
||||||
|
v-model:visible="sheetVisible"
|
||||||
|
:item="selectedItem"
|
||||||
|
:metals="catalogMetals"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@@ -32,6 +36,9 @@ import { useMobileTableList } from '@/components/mobile/composables/useMobileTab
|
|||||||
import type { TableInput } from '@core/type/baseTable'
|
import type { TableInput } from '@core/type/baseTable'
|
||||||
import TransactionMobileDetailSheet from './TransactionMobileDetailSheet.vue'
|
import TransactionMobileDetailSheet from './TransactionMobileDetailSheet.vue'
|
||||||
import TransactionMobileListItem from './TransactionMobileListItem.vue'
|
import TransactionMobileListItem from './TransactionMobileListItem.vue'
|
||||||
|
import type { TransactionMetalColumn } from '../hooks/transactionsColumns'
|
||||||
|
|
||||||
|
const catalogMetals = ref<TransactionMetalColumn[]>([])
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
customerId?: number
|
customerId?: number
|
||||||
@@ -51,7 +58,10 @@ function fetchTransactions(params: TableInput) {
|
|||||||
hidden: false
|
hidden: false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return getTransactionTable(params)
|
return getTransactionTable(params).then((data) => {
|
||||||
|
catalogMetals.value = data?.catalogMetals ?? []
|
||||||
|
return data
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const { loading, search, items, hasMore, reload, loadMore, fetch } = useMobileTableList<Transaction>({
|
const { loading, search, items, hasMore, reload, loadMore, fetch } = useMobileTableList<Transaction>({
|
||||||
|
|||||||
@@ -3,21 +3,29 @@
|
|||||||
<template #default="{ item: row }">
|
<template #default="{ item: row }">
|
||||||
<MobileSheetHeader :title="sheetTitle" :subtitle="dateLabel(row)" :show-title="isMobile" />
|
<MobileSheetHeader :title="sheetTitle" :subtitle="dateLabel(row)" :show-title="isMobile" />
|
||||||
|
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
<span class="rounded-lg border px-2 py-0.5 text-xs font-600" :class="typeTagClass(row)">
|
<span class="rounded-lg border px-2 py-0.5 text-xs font-600" :class="typeTagClass(row)">
|
||||||
{{ typeLabel(row) }}
|
{{ typeLabel(row) }}
|
||||||
</span>
|
</span>
|
||||||
|
<span
|
||||||
|
v-if="!isMoneyType(row) && row.transaction_metal_label"
|
||||||
|
class="rounded-lg border border-amber-300 bg-amber-50 px-2 py-0.5 text-xs font-600 text-amber-700"
|
||||||
|
>
|
||||||
|
{{ row.transaction_metal_label }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<MobileStatCard
|
<MobileStatCard
|
||||||
|
v-for="entry in metalEntries(row)"
|
||||||
|
:key="entry.metal_id"
|
||||||
bordered
|
bordered
|
||||||
:variant="(row.gold ?? 0) > 0 ? 'success' : 'danger'"
|
:variant="entry.value > 0 ? 'success' : 'danger'"
|
||||||
label="مقدار طلا (گرم)"
|
:label="transactionMetalAmountTitle(entry.metal_id, entry.label)"
|
||||||
:value="goldLabel(row)"
|
:value="entry.display"
|
||||||
>
|
>
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<BaseIcon icon="vuesax-linear:coin" size="18" :color="(row.gold ?? 0) > 0 ? '#15803d' : '#dc2626'" />
|
<BaseIcon icon="vuesax-linear:coin" size="18" :color="entry.value > 0 ? '#15803d' : '#dc2626'" />
|
||||||
</template>
|
</template>
|
||||||
</MobileStatCard>
|
</MobileStatCard>
|
||||||
<MobileStatCard
|
<MobileStatCard
|
||||||
@@ -33,6 +41,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<MobileInfoGrid>
|
<MobileInfoGrid>
|
||||||
|
<MobileDetailRow label="نوع فلز" :value="isMoneyType(row) ? '-' : (row.transaction_metal_label ?? '-')" />
|
||||||
<MobileDetailRow label="کد تراکنش" :value="row.id ?? '-'" />
|
<MobileDetailRow label="کد تراکنش" :value="row.id ?? '-'" />
|
||||||
<MobileDetailRow label="نام تراکنش" :value="row.type_str || '-'" />
|
<MobileDetailRow label="نام تراکنش" :value="row.type_str || '-'" />
|
||||||
<MobileDetailRow label="علت تراکنش" full-width :value="noteLabel(row)" />
|
<MobileDetailRow label="علت تراکنش" full-width :value="noteLabel(row)" />
|
||||||
@@ -53,10 +62,14 @@ import MobileStatCard from '@/components/mobile/MobileStatCard.vue'
|
|||||||
import { isMobile } from '@core/utils/is'
|
import { isMobile } from '@core/utils/is'
|
||||||
import { toJalaliNoTime } from '@core/utils/useDataTime'
|
import { toJalaliNoTime } from '@core/utils/useDataTime'
|
||||||
import { usePrice } from '@core/utils/usePrice'
|
import { usePrice } from '@core/utils/usePrice'
|
||||||
|
import { metalLabel, transactionMetalAmountTitle } from '@/utils/adminMetalLabels'
|
||||||
|
import { formatMetalDisplayValue, metalCellValue } from '@/utils/metalWeight'
|
||||||
|
import type { TransactionMetalColumn } from '../hooks/transactionsColumns'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
visible: boolean
|
visible: boolean
|
||||||
item: Transaction | null
|
item: Transaction | null
|
||||||
|
metals?: TransactionMetalColumn[]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
@@ -75,7 +88,7 @@ function isMoneyType(row: Transaction) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function typeLabel(row: Transaction) {
|
function typeLabel(row: Transaction) {
|
||||||
return isMoneyType(row) ? 'پول' : 'طلا'
|
return isMoneyType(row) ? 'پول' : 'فلز'
|
||||||
}
|
}
|
||||||
|
|
||||||
function typeTagClass(row: Transaction) {
|
function typeTagClass(row: Transaction) {
|
||||||
@@ -88,14 +101,26 @@ function dateLabel(row: Transaction) {
|
|||||||
return row.created_at ? toJalaliNoTime(row.created_at) : '-'
|
return row.created_at ? toJalaliNoTime(row.created_at) : '-'
|
||||||
}
|
}
|
||||||
|
|
||||||
function goldLabel(row: Transaction) {
|
|
||||||
const gold = row.gold
|
|
||||||
if (gold == null) return '-'
|
|
||||||
return `${Number(Number(gold).toFixed(3))} گرم`
|
|
||||||
}
|
|
||||||
|
|
||||||
function noteLabel(row: Transaction) {
|
function noteLabel(row: Transaction) {
|
||||||
if (row.note == null) return '-'
|
if (row.note == null) return '-'
|
||||||
return String(row.note)
|
return String(row.note)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function metalEntries(row: Transaction) {
|
||||||
|
const metals = props.metals?.length
|
||||||
|
? props.metals
|
||||||
|
: [{ metal_id: row.transaction_metal_id ?? 1, label: row.transaction_metal_label ?? 'طلا' }]
|
||||||
|
|
||||||
|
return metals
|
||||||
|
.map((metal) => {
|
||||||
|
const value = metalCellValue(row as Record<string, unknown>, metal.metal_id)
|
||||||
|
return {
|
||||||
|
metal_id: metal.metal_id,
|
||||||
|
label: metal.label,
|
||||||
|
value,
|
||||||
|
display: value ? formatMetalDisplayValue(value, metal.metal_id) : '-'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter((entry) => entry.value !== 0)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -6,6 +6,12 @@
|
|||||||
<span class="shrink-0 rounded-md border px-1.5 py-0.5 text-xs font-600" :class="typeTagClass">
|
<span class="shrink-0 rounded-md border px-1.5 py-0.5 text-xs font-600" :class="typeTagClass">
|
||||||
{{ typeLabel }}
|
{{ typeLabel }}
|
||||||
</span>
|
</span>
|
||||||
|
<span
|
||||||
|
v-if="!isMoneyType && item.transaction_metal_label"
|
||||||
|
class="shrink-0 rounded-md border border-amber-300 bg-amber-50 px-1.5 py-0.5 text-xs font-600 text-amber-700"
|
||||||
|
>
|
||||||
|
{{ item.transaction_metal_label }}
|
||||||
|
</span>
|
||||||
<span class="truncate text-sm font-700 text-gray-900">{{ item.type_str || '-' }}</span>
|
<span class="truncate text-sm font-700 text-gray-900">{{ item.type_str || '-' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<span class="shrink-0 text-xs text-gray-400">{{ dateLabel }}</span>
|
<span class="shrink-0 text-xs text-gray-400">{{ dateLabel }}</span>
|
||||||
@@ -31,7 +37,7 @@ defineEmits<{
|
|||||||
}>()
|
}>()
|
||||||
|
|
||||||
const isMoneyType = computed(() => props.item.type === 4 || props.item.type === 3)
|
const isMoneyType = computed(() => props.item.type === 4 || props.item.type === 3)
|
||||||
const typeLabel = computed(() => (isMoneyType.value ? 'پول' : 'طلا'))
|
const typeLabel = computed(() => (isMoneyType.value ? 'پول' : 'فلز'))
|
||||||
const typeTagClass = computed(() =>
|
const typeTagClass = computed(() =>
|
||||||
isMoneyType.value
|
isMoneyType.value
|
||||||
? 'border-green-300 bg-green-50 text-green-700'
|
? 'border-green-300 bg-green-50 text-green-700'
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ import type {TableInput} from '@core/type/baseTable'
|
|||||||
import useBaseTable from '@core/components/BaseTableOld/hooks/useBaseTable'
|
import useBaseTable from '@core/components/BaseTableOld/hooks/useBaseTable'
|
||||||
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
||||||
import {changeCustomerActivation, getCustomerTable} from '@/api/customer'
|
import {changeCustomerActivation, getCustomerTable} from '@/api/customer'
|
||||||
import {cols} from '../hooks/transactionsColumns'
|
import { buildTransactionsColumns } from '../hooks/transactionsColumns'
|
||||||
import {useUserStore} from '@/store/userStore'
|
import {useUserStore} from '@/store/userStore'
|
||||||
import {usePrefix} from '@core/composable/usePrefix'
|
import {usePrefix} from '@core/composable/usePrefix'
|
||||||
import {message} from 'ant-design-vue'
|
import {message} from 'ant-design-vue'
|
||||||
@@ -102,23 +102,24 @@ const {prefixCls} = usePrefix('pophoverstatus')
|
|||||||
const table = ref(null)
|
const table = ref(null)
|
||||||
|
|
||||||
const tblProps = ref<any>({
|
const tblProps = ref<any>({
|
||||||
columns: cols,
|
columns: buildTransactionsColumns(),
|
||||||
api: (params: TableInput) => {
|
api: async (params: TableInput) => {
|
||||||
// console.log(props.customerId)
|
|
||||||
if (props.customerId) {
|
if (props.customerId) {
|
||||||
params.filters = []
|
params.filters = []
|
||||||
params.filters?.push({
|
params.filters?.push({
|
||||||
field: "customer_id",
|
field: 'customer_id',
|
||||||
type: "text",
|
type: 'text',
|
||||||
value: props.customerId,
|
value: props.customerId,
|
||||||
cr: "and",
|
cr: 'and',
|
||||||
op: "eq",
|
op: 'eq',
|
||||||
hidden: false
|
hidden: false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return getTransactionTable(params)
|
const data = await getTransactionTable(params)
|
||||||
|
const metals = data?.catalogMetals ?? []
|
||||||
|
tblProps.value.columns = buildTransactionsColumns(metals)
|
||||||
|
return data
|
||||||
}
|
}
|
||||||
//bordered: true
|
|
||||||
})
|
})
|
||||||
const loading = ref<boolean>(false)
|
const loading = ref<boolean>(false)
|
||||||
/*async function changeCustomerActivationFunction(id: number) {
|
/*async function changeCustomerActivationFunction(id: number) {
|
||||||
|
|||||||
@@ -1,203 +1,165 @@
|
|||||||
import type {BaseTableColumn} from '@core/type/baseTable'
|
import type { BaseTableColumn } from '@core/type/baseTable'
|
||||||
import {toJalaliNoTime, toTime} from '@core/utils/useDataTime'
|
import { toJalaliNoTime } from '@core/utils/useDataTime'
|
||||||
import CustomTag from '@/components/CustomTag.vue'
|
import CustomTag from '@/components/CustomTag.vue'
|
||||||
import {usePrice} from '@core/utils/usePrice'
|
import { usePrice } from '@core/utils/usePrice'
|
||||||
/*import BudgetSelect from '@/components/BudgetSelect.vue'
|
import { metalLabel, transactionMetalAmountTitle } from '@/utils/adminMetalLabels'
|
||||||
import { useStaticApiListsStore } from '@/store/staticApisStore'
|
import { formatMetalDisplayValuePlain, metalCellValue } from '@/utils/metalWeight'
|
||||||
import type { FilterSelect } from '@/model/filterSelect'
|
|
||||||
import type { DocItem } from '@/model/docType'*/
|
|
||||||
|
|
||||||
//define store
|
export type TransactionMetalColumn = { metal_id: number; label: string; display_unit?: string }
|
||||||
//const store = useStaticApiListsStore()
|
|
||||||
//document types
|
function renderSignedMetalCell(value: number, metalId: number) {
|
||||||
/*let docTypes: DocItem[]*/
|
if (!value) {
|
||||||
/*onMounted(async () => {
|
return (
|
||||||
docTypes = await store.readyDocTypeList()
|
<div class="flex justify-center">
|
||||||
})
|
<div class="px-6">0</div>
|
||||||
const filteredDocTypes: FilterSelect = docTypes?.map((x: DocItem) => {
|
</div>
|
||||||
return {
|
)
|
||||||
label: x.title,
|
|
||||||
value: x.id
|
|
||||||
}
|
}
|
||||||
})*/
|
|
||||||
|
|
||||||
export const cols: BaseTableColumn[] = [
|
const cls = value > 0 ? 'text-green' : 'text-red'
|
||||||
{
|
const formatted = formatMetalDisplayValuePlain(Math.abs(value), metalId)
|
||||||
title: 'ردیف',
|
|
||||||
dataIndex: 'id',
|
|
||||||
key: 'id',
|
|
||||||
filterable: false,
|
|
||||||
sortable: false,
|
|
||||||
customRender: (record: any) => {
|
|
||||||
return record.index + 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'کد تراکنش',
|
|
||||||
dataIndex: 'id',
|
|
||||||
key: 'id',
|
|
||||||
type: 'Number'
|
|
||||||
// className: 'column-money'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'نام تراکنش',
|
|
||||||
dataIndex: 'type_str',
|
|
||||||
key: 'type_str',
|
|
||||||
type: 'Text'
|
|
||||||
// className: 'column-money'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'علت تراکنش',
|
|
||||||
dataIndex: 'note',
|
|
||||||
key: 'note',
|
|
||||||
type: 'Text',
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<div class={`font-bold px-6 ${cls}`}>{value > 0 ? formatted : `-${formatted}`}</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// className: 'column-money'
|
function isMoneyTransaction(record: { type?: number }) {
|
||||||
|
return record.type === 4 || record.type === 3
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildTransactionsColumns(metals: TransactionMetalColumn[] = []): BaseTableColumn[] {
|
||||||
|
const baseColumns: BaseTableColumn[] = [
|
||||||
|
{
|
||||||
|
title: 'ردیف',
|
||||||
|
dataIndex: 'id',
|
||||||
|
key: 'id',
|
||||||
|
filterable: false,
|
||||||
|
sortable: false,
|
||||||
|
customRender: (record: any) => {
|
||||||
|
return record.index + 1
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'نوع تراکنش',
|
title: 'کد تراکنش',
|
||||||
dataIndex: 'active',
|
dataIndex: 'id',
|
||||||
key: 'active',
|
key: 'id',
|
||||||
filterable: false,
|
|
||||||
sortable: false,
|
|
||||||
customRender: ({record}) => {
|
|
||||||
return record.type === 4 || record.type === 3 ? (
|
|
||||||
<div class="flex justify-center">
|
|
||||||
<CustomTag textColor="#25855A" bgColor="#E3F4E9" class={'font-bold px-6'}>
|
|
||||||
پول
|
|
||||||
</CustomTag>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div class="flex justify-center">
|
|
||||||
{/*<CustomTag textColor="rgb(255 120 0)" bgColor="rgb(255 221 0)">*/}
|
|
||||||
<CustomTag textColor="#ca8a04" bgColor="#fef3c7" class={'font-bold px-6'}>
|
|
||||||
طلا
|
|
||||||
</CustomTag>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'نام مشتری',
|
|
||||||
dataIndex: 'customer.name',
|
|
||||||
key: 'customer.name',
|
|
||||||
type: 'Text'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'مقدار طلا طی تراکنش (گرم)',
|
|
||||||
dataIndex: 'gold',
|
|
||||||
key: 'gold',
|
|
||||||
type: 'Number',
|
|
||||||
customRender: ({record}) => {
|
|
||||||
return record.gold > 0 ? (
|
|
||||||
<div class="flex justify-center">
|
|
||||||
<div class={'font-bold px-6 text-green'}>{record.gold}</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div class="flex justify-center">
|
|
||||||
{/*<CustomTag textColor="rgb(255 120 0)" bgColor="rgb(255 221 0)">*/}
|
|
||||||
<div class={'font-bold px-6 text-red'}>{record.gold}</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'مقدار پول طی تراکنش (ریال)',
|
|
||||||
dataIndex: 'cash',
|
|
||||||
key: 'cash',
|
|
||||||
type: 'Number',
|
|
||||||
customRender: ({record}) => {
|
|
||||||
return record.cash > 0 ? (
|
|
||||||
<div class="flex justify-center">
|
|
||||||
<div class={'font-bold px-6 text-green'}>{usePrice(record?.cash ?? 0, true, '')}</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div class="flex justify-center">
|
|
||||||
{/*<CustomTag textColor="rgb(255 120 0)" bgColor="rgb(255 221 0)">*/}
|
|
||||||
<div class={'font-bold px-6 text-red'}>{usePrice(record?.cash ?? 0, true, '')}</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/* {
|
|
||||||
title: 'شماره سفارش',
|
|
||||||
dataIndex: 'order_id',
|
|
||||||
key: 'order_id',
|
|
||||||
type: 'Text'
|
|
||||||
},*/
|
|
||||||
/* {
|
|
||||||
title: 'شماره موبایل',
|
|
||||||
dataIndex: 'mobile',
|
|
||||||
key: 'mobile',
|
|
||||||
type: 'Number'
|
type: 'Number'
|
||||||
},*/
|
},
|
||||||
/* {
|
|
||||||
title: 'نام فروشگاه',
|
|
||||||
dataIndex: 'shop_name',
|
|
||||||
key: 'shop_name',
|
|
||||||
type: 'Text'
|
|
||||||
},*/
|
|
||||||
{
|
{
|
||||||
title: 'تاریخ تراکنش',
|
title: 'نام تراکنش',
|
||||||
dataIndex: 'created_at',
|
dataIndex: 'type_str',
|
||||||
key: 'created_at',
|
key: 'type_str',
|
||||||
type: 'DateTime',
|
type: 'Text'
|
||||||
customRender: (record: any) => {
|
},
|
||||||
return toJalaliNoTime(record.record.created_at)
|
{
|
||||||
}
|
title: 'علت تراکنش',
|
||||||
}
|
dataIndex: 'note',
|
||||||
/*{
|
key: 'note',
|
||||||
title: 'نقش',
|
type: 'Text'
|
||||||
dataIndex: 'user_type_record',
|
},
|
||||||
key: 'user_type_record',
|
{
|
||||||
type: 'Select',
|
title: 'نوع تراکنش',
|
||||||
filterable: false,
|
dataIndex: 'active',
|
||||||
sortable: false,
|
key: 'active',
|
||||||
filterComponentProps: {
|
filterable: false,
|
||||||
multiple: true
|
sortable: false,
|
||||||
},
|
customRender: ({ record }) => {
|
||||||
filterField: 'user_type_obj.id',
|
return isMoneyTransaction(record) ? (
|
||||||
filterComponent: ReadyUserTypeSelect,
|
<div class="flex justify-center">
|
||||||
/!* customRender: (record: any) => {
|
<CustomTag textColor="#25855A" bgColor="#E3F4E9" class={'font-bold px-6'}>
|
||||||
return record.record.user_type_record?.value
|
پول
|
||||||
}*!/
|
|
||||||
customRender: ({ record }) => {
|
|
||||||
const items = record.is_admin ? (
|
|
||||||
<CustomTag textColor="#25855A" bgColor="#E3F4E9">
|
|
||||||
ادمین
|
|
||||||
</CustomTag>
|
</CustomTag>
|
||||||
) : (
|
</div>
|
||||||
record.user_type_obj.map((item: { title: any }, index: any) => {
|
) : (
|
||||||
if (item) {
|
<div class="flex justify-center">
|
||||||
return (
|
<CustomTag textColor="#ca8a04" bgColor="#fef3c7" class={'font-bold px-6'}>
|
||||||
<CustomTag textColor="#25855A" bgColor="#E3F4E9">
|
فلز
|
||||||
{item?.title}
|
</CustomTag>
|
||||||
</CustomTag>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
)
|
{
|
||||||
return <div class="flex justify-center items-center gap-2">{items}</div>
|
title: 'نوع فلز',
|
||||||
|
dataIndex: 'transaction_metal_label',
|
||||||
|
key: 'transaction_metal_label',
|
||||||
|
filterable: false,
|
||||||
|
sortable: false,
|
||||||
|
customRender: ({ record }) => {
|
||||||
|
if (isMoneyTransaction(record)) {
|
||||||
|
return '-'
|
||||||
}
|
}
|
||||||
},
|
return (
|
||||||
{
|
<div class="flex justify-center">
|
||||||
title: 'حوزه',
|
<CustomTag textColor="#ca8a04" bgColor="#fef3c7" class={'font-bold px-6'}>
|
||||||
dataIndex: 'section',
|
{record.transaction_metal_label ?? metalLabel.goldUnit()}
|
||||||
key: 'section'
|
</CustomTag>
|
||||||
},
|
</div>
|
||||||
{
|
)
|
||||||
title: 'شماره موبایل',
|
}
|
||||||
dataIndex: 'mobile',
|
},
|
||||||
type: 'Number',
|
{
|
||||||
key: 'mobile'
|
title: 'نام مشتری',
|
||||||
},*/
|
dataIndex: 'customer.name',
|
||||||
/* {
|
key: 'customer.name',
|
||||||
title: 'عملیات',
|
type: 'Text'
|
||||||
dataIndex: 'action',
|
}
|
||||||
key: 'action',
|
]
|
||||||
slot: 'action',
|
|
||||||
fixed: 'right',
|
const metalColumns: BaseTableColumn[] =
|
||||||
filterable: false
|
metals.length > 0
|
||||||
}*/
|
? metals.map((metal) => ({
|
||||||
]
|
title: transactionMetalAmountTitle(metal.metal_id, metal.label),
|
||||||
|
dataIndex: `metal_gold_${metal.metal_id}`,
|
||||||
|
key: `metal_${metal.metal_id}`,
|
||||||
|
type: 'Number',
|
||||||
|
customRender: ({ record }) => renderSignedMetalCell(metalCellValue(record, metal.metal_id), metal.metal_id)
|
||||||
|
}))
|
||||||
|
: [
|
||||||
|
{
|
||||||
|
title: metalLabel.goldInTransaction(),
|
||||||
|
dataIndex: 'gold',
|
||||||
|
key: 'gold',
|
||||||
|
type: 'Number',
|
||||||
|
customRender: ({ record }) => renderSignedMetalCell(Number(record.gold ?? 0), 1)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
return [
|
||||||
|
...baseColumns,
|
||||||
|
...metalColumns,
|
||||||
|
{
|
||||||
|
title: 'مقدار پول طی تراکنش (ریال)',
|
||||||
|
dataIndex: 'cash',
|
||||||
|
key: 'cash',
|
||||||
|
type: 'Number',
|
||||||
|
customRender: ({ record }) => {
|
||||||
|
return record.cash > 0 ? (
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<div class={'font-bold px-6 text-green'}>{usePrice(record?.cash ?? 0, true, '')}</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<div class={'font-bold px-6 text-red'}>{usePrice(record?.cash ?? 0, true, '')}</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'تاریخ تراکنش',
|
||||||
|
dataIndex: 'created_at',
|
||||||
|
key: 'created_at',
|
||||||
|
type: 'DateTime',
|
||||||
|
customRender: (record: any) => {
|
||||||
|
return toJalaliNoTime(record.record.created_at)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTransactionsColumns(): BaseTableColumn[] {
|
||||||
|
return buildTransactionsColumns()
|
||||||
|
}
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ import type { Item } from '@/model/Item'
|
|||||||
import type { Customer } from '@/model/customer'
|
import type { Customer } from '@/model/customer'
|
||||||
import { createCustomer, deleteCustomer, updateCustomer } from '@/api/customer'
|
import { createCustomer, deleteCustomer, updateCustomer } from '@/api/customer'
|
||||||
import type { Transaction } from '@/model/transaction'
|
import type { Transaction } from '@/model/transaction'
|
||||||
|
import { metalLabel } from '@/utils/adminMetalLabels'
|
||||||
|
|
||||||
//loading
|
//loading
|
||||||
const loading = ref<boolean>(false)
|
const loading = ref<boolean>(false)
|
||||||
@@ -127,7 +128,7 @@ let infoParams = reactive<Info>({})
|
|||||||
|
|
||||||
function info(val: Transaction) {
|
function info(val: Transaction) {
|
||||||
infoParams.name = val.customer.name
|
infoParams.name = val.customer.name
|
||||||
infoParams.type = val.transaction_type === '1' ? 'نقدی' : 'طلا'
|
infoParams.type = val.transaction_type === '1' ? 'نقدی' : metalLabel.goldUnit()
|
||||||
infoParams.date = toJalaliNoTime(val.created_at)
|
infoParams.date = toJalaliNoTime(val.created_at)
|
||||||
infoParams.time = toTime(val.created_at)
|
infoParams.time = toTime(val.created_at)
|
||||||
infoParams.itemsTitle = 'اطلاعات کاربری'
|
infoParams.itemsTitle = 'اطلاعات کاربری'
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
v-for="item in items"
|
v-for="item in items"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
:item="item"
|
:item="item"
|
||||||
|
:metals="catalogMetals"
|
||||||
@open="openSheet"
|
@open="openSheet"
|
||||||
/>
|
/>
|
||||||
</MobileCardList>
|
</MobileCardList>
|
||||||
@@ -22,36 +23,39 @@
|
|||||||
<WalletMobileDetailSheet
|
<WalletMobileDetailSheet
|
||||||
v-model:visible="sheetVisible"
|
v-model:visible="sheetVisible"
|
||||||
:item="selectedItem"
|
:item="selectedItem"
|
||||||
|
:metals="catalogMetals"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { getWalletTable } from '@/api/wallet'
|
import { getWalletTable } from '@/api/wallet'
|
||||||
import type { Wallet } from '@/model/wallet'
|
import type { Wallet, WalletTableSums } from '@/model/wallet'
|
||||||
import MobileCardList from '@/components/mobile/MobileCardList.vue'
|
import MobileCardList from '@/components/mobile/MobileCardList.vue'
|
||||||
import { useMobileDetailSheet } from '@/components/mobile/composables/useMobileDetailSheet'
|
import { useMobileDetailSheet } from '@/components/mobile/composables/useMobileDetailSheet'
|
||||||
import { useMobileTableList } from '@/components/mobile/composables/useMobileTableList'
|
import { useMobileTableList } from '@/components/mobile/composables/useMobileTableList'
|
||||||
import type { TableInput, TableOutput } from '@core/type/baseTable'
|
import type { TableInput, TableOutput } from '@core/type/baseTable'
|
||||||
|
import type { WalletMetalColumn } from '../hooks/walletColumns'
|
||||||
import WalletMobileDetailSheet from './WalletMobileDetailSheet.vue'
|
import WalletMobileDetailSheet from './WalletMobileDetailSheet.vue'
|
||||||
import WalletMobileListItem from './WalletMobileListItem.vue'
|
import WalletMobileListItem from './WalletMobileListItem.vue'
|
||||||
|
|
||||||
type WalletTableResponse = TableOutput<Wallet> & {
|
type WalletTableResponse = TableOutput<Wallet> & {
|
||||||
sums?: {
|
sums?: WalletTableSums
|
||||||
cash?: number
|
|
||||||
gold?: number
|
|
||||||
cashToGold?: number
|
|
||||||
goldToCash?: number
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'sums', sums: WalletTableResponse['sums']): void
|
(e: 'sums', sums: WalletTableResponse['sums']): void
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
const catalogMetals = ref<WalletMetalColumn[]>([])
|
||||||
const { sheetVisible, selectedItem, openSheet, syncSelectedItem } = useMobileDetailSheet<Wallet>()
|
const { sheetVisible, selectedItem, openSheet, syncSelectedItem } = useMobileDetailSheet<Wallet>()
|
||||||
|
|
||||||
async function fetchWallet(params: TableInput) {
|
async function fetchWallet(params: TableInput) {
|
||||||
const data = (await getWalletTable(params)) as WalletTableResponse
|
const data = (await getWalletTable(params)) as WalletTableResponse
|
||||||
|
catalogMetals.value =
|
||||||
|
data?.sums?.metalBalances?.map((metal) => ({
|
||||||
|
metal_id: metal.metal_id,
|
||||||
|
label: metal.label
|
||||||
|
})) ?? []
|
||||||
emit('sums', data?.sums)
|
emit('sums', data?.sums)
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,15 @@
|
|||||||
<BaseIcon icon="vuesax-linear:empty-wallet" size="18" color="#15803d" />
|
<BaseIcon icon="vuesax-linear:empty-wallet" size="18" color="#15803d" />
|
||||||
</template>
|
</template>
|
||||||
</MobileStatCard>
|
</MobileStatCard>
|
||||||
<MobileStatCard bordered variant="warning" label="موجودی طلایی" :value="goldLabel(row)">
|
|
||||||
|
<MobileStatCard
|
||||||
|
v-for="entry in metalEntries(row)"
|
||||||
|
:key="entry.metal_id"
|
||||||
|
bordered
|
||||||
|
variant="warning"
|
||||||
|
:label="walletMetalBalanceTitle(entry.metal_id, entry.label)"
|
||||||
|
:value="entry.display"
|
||||||
|
>
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<BaseIcon icon="vuesax-linear:coin" size="18" color="#b45309" />
|
<BaseIcon icon="vuesax-linear:coin" size="18" color="#b45309" />
|
||||||
</template>
|
</template>
|
||||||
@@ -34,10 +42,14 @@ import MobileSheetHeader from '@/components/mobile/MobileSheetHeader.vue'
|
|||||||
import MobileStatCard from '@/components/mobile/MobileStatCard.vue'
|
import MobileStatCard from '@/components/mobile/MobileStatCard.vue'
|
||||||
import { isMobile } from '@core/utils/is'
|
import { isMobile } from '@core/utils/is'
|
||||||
import { usePrice } from '@core/utils/usePrice'
|
import { usePrice } from '@core/utils/usePrice'
|
||||||
|
import { walletMetalBalanceTitle } from '@/utils/adminMetalLabels'
|
||||||
|
import { formatMetalDisplayValue, metalCellValue } from '@/utils/metalWeight'
|
||||||
|
import type { WalletMetalColumn } from '../hooks/walletColumns'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
visible: boolean
|
visible: boolean
|
||||||
item: Wallet | null
|
item: Wallet | null
|
||||||
|
metals?: WalletMetalColumn[]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
@@ -51,9 +63,20 @@ const visibleProxy = computed({
|
|||||||
|
|
||||||
const sheetTitle = computed(() => props.item?.customer?.name || `کیف پول #${props.item?.id ?? '-'}`)
|
const sheetTitle = computed(() => props.item?.customer?.name || `کیف پول #${props.item?.id ?? '-'}`)
|
||||||
|
|
||||||
function goldLabel(row: Wallet) {
|
function metalEntries(row: Wallet) {
|
||||||
const gold = row.gold
|
const metals = props.metals?.length
|
||||||
if (gold == null) return '-'
|
? props.metals
|
||||||
return `${Number(Number(gold).toFixed(3))} گرم`
|
: [{ metal_id: 1, label: 'طلا' }]
|
||||||
|
|
||||||
|
return metals
|
||||||
|
.map((metal) => {
|
||||||
|
const value = metalCellValue(row as Record<string, unknown>, metal.metal_id)
|
||||||
|
return {
|
||||||
|
metal_id: metal.metal_id,
|
||||||
|
label: metal.label,
|
||||||
|
display: formatMetalDisplayValue(value, metal.metal_id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter((entry) => metalCellValue(row as Record<string, unknown>, entry.metal_id) !== 0)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -11,9 +11,19 @@
|
|||||||
<span class="text-gray-500">ریال:</span>
|
<span class="text-gray-500">ریال:</span>
|
||||||
<span class="font-600 text-gray-900">{{ cashLabel }}</span>
|
<span class="font-600 text-gray-900">{{ cashLabel }}</span>
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span v-if="metalEntries.length === 1">
|
||||||
<span class="text-gray-500">طلا:</span>
|
<span class="text-gray-500">{{ metalEntries[0].label }}:</span>
|
||||||
<span class="font-700 text-gray-900">{{ goldLabel }}</span>
|
<span class="font-700 text-gray-900">{{ metalEntries[0].display }}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="metalEntries.length > 1"
|
||||||
|
class="flex flex-wrap gap-x-3 gap-y-1 text-xs text-gray-600"
|
||||||
|
>
|
||||||
|
<span v-for="entry in metalEntries" :key="entry.metal_id">
|
||||||
|
<span class="text-gray-500">{{ entry.label }}:</span>
|
||||||
|
<span class="font-700 text-gray-900">{{ entry.display }}</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -24,9 +34,12 @@
|
|||||||
import type { Wallet } from '@/model/wallet'
|
import type { Wallet } from '@/model/wallet'
|
||||||
import MobileCard from '@/components/mobile/MobileCard.vue'
|
import MobileCard from '@/components/mobile/MobileCard.vue'
|
||||||
import { usePrice } from '@core/utils/usePrice'
|
import { usePrice } from '@core/utils/usePrice'
|
||||||
|
import { formatMetalDisplayValue, metalCellValue } from '@/utils/metalWeight'
|
||||||
|
import type { WalletMetalColumn } from '../hooks/walletColumns'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
item: Wallet
|
item: Wallet
|
||||||
|
metals?: WalletMetalColumn[]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
defineEmits<{
|
defineEmits<{
|
||||||
@@ -35,9 +48,25 @@ defineEmits<{
|
|||||||
|
|
||||||
const customerName = computed(() => props.item.customer?.name || '-')
|
const customerName = computed(() => props.item.customer?.name || '-')
|
||||||
const cashLabel = computed(() => usePrice(props.item.cash ?? 0, true, ''))
|
const cashLabel = computed(() => usePrice(props.item.cash ?? 0, true, ''))
|
||||||
const goldLabel = computed(() => {
|
|
||||||
const gold = props.item.gold
|
function metalValue(metalId: number): number {
|
||||||
if (gold == null) return '-'
|
return metalCellValue(props.item as Record<string, unknown>, metalId)
|
||||||
return `${Number(Number(gold).toFixed(3))} گرم`
|
}
|
||||||
|
|
||||||
|
const metalEntries = computed(() => {
|
||||||
|
const metals = props.metals?.length
|
||||||
|
? props.metals
|
||||||
|
: [{ metal_id: 1, label: 'طلا' }]
|
||||||
|
|
||||||
|
return metals
|
||||||
|
.map((metal) => {
|
||||||
|
const value = metalValue(metal.metal_id)
|
||||||
|
return {
|
||||||
|
metal_id: metal.metal_id,
|
||||||
|
label: metal.label,
|
||||||
|
display: formatMetalDisplayValue(value, metal.metal_id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter((entry) => metalValue(entry.metal_id) !== 0)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div class="h-full min-h-0">
|
<div class="h-full min-h-0">
|
||||||
<BasicShowContext title="لیست کیف پول">
|
<BasicShowContext title="لیست کیف پول">
|
||||||
<div class="flex h-full min-h-0 flex-col gap-3">
|
<div class="flex h-full min-h-0 flex-col gap-3">
|
||||||
<div class="hidden shrink-0 md:grid md:grid-cols-1 md:gap-3 lg:grid-cols-2">
|
<div class="hidden shrink-0 md:grid md:grid-cols-1 md:gap-3 lg:grid-cols-2 xl:grid-cols-3">
|
||||||
<div
|
<div
|
||||||
class="rounded-lg p-2.5 text-sm"
|
class="rounded-lg p-2.5 text-sm"
|
||||||
:class="walletSumTone(sums?.cash)"
|
:class="walletSumTone(sums?.cash)"
|
||||||
@@ -23,20 +23,22 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
v-for="metal in metalBalanceCards"
|
||||||
|
:key="metal.metal_id"
|
||||||
class="rounded-lg p-2.5 text-sm"
|
class="rounded-lg p-2.5 text-sm"
|
||||||
:class="walletSumTone(sums?.gold)"
|
:class="walletSumTone(metal.gold)"
|
||||||
>
|
>
|
||||||
<div class="mb-2 font-medium">مجموع موجودی کیف طلا</div>
|
<div class="mb-2 font-medium">{{ totalMetalWalletTitle(metal.metal_id, metal.label) }}</div>
|
||||||
<div class="grid grid-cols-2 gap-2">
|
<div class="grid grid-cols-2 gap-2">
|
||||||
<div class="stat-inner rounded-lg px-2 py-1.5 text-center">
|
<div class="stat-inner rounded-lg px-2 py-1.5 text-center">
|
||||||
<div class="text-xs text-gray-500">گرم</div>
|
<div class="text-xs text-gray-500">{{ metal.display_unit ?? metalDisplayUnit(metal.metal_id) }}</div>
|
||||||
<div class="mt-1 font-bold">
|
<div class="mt-1 font-bold">
|
||||||
{{ usePrice(sums?.gold ?? 0, true, 'گرم') }}
|
{{ formatMetalDisplayValue(metal.gold ?? 0, metal.metal_id) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-inner rounded-lg px-2 py-1.5 text-center">
|
<div class="stat-inner rounded-lg px-2 py-1.5 text-center">
|
||||||
<div class="text-xs text-gray-500">ریال</div>
|
<div class="text-xs text-gray-500">ریال</div>
|
||||||
<div class="mt-1 font-bold break-all">{{ usePrice(sums?.goldToCash ?? 0) }}</div>
|
<div class="mt-1 font-bold break-all">{{ usePrice(metal.goldToCash ?? 0) }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -77,13 +79,16 @@ import type { TableInput } from '@core/type/baseTable'
|
|||||||
import useBaseTable from '@core/components/BaseTableOld/hooks/useBaseTable'
|
import useBaseTable from '@core/components/BaseTableOld/hooks/useBaseTable'
|
||||||
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
||||||
import { changeCustomerActivation } from '@/api/customer'
|
import { changeCustomerActivation } from '@/api/customer'
|
||||||
import { cols } from '../hooks/walletColumns'
|
import { buildWalletColumns } from '../hooks/walletColumns'
|
||||||
import { usePrefix } from '@core/composable/usePrefix'
|
import { usePrefix } from '@core/composable/usePrefix'
|
||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
import { getWalletTable } from '@/api/wallet'
|
import { getWalletTable } from '@/api/wallet'
|
||||||
import { usePrice } from '../../../../core/utils/usePrice'
|
import { usePrice } from '../../../../core/utils/usePrice'
|
||||||
import WalletMobileCardList from './WalletMobileCardList.vue'
|
import WalletMobileCardList from './WalletMobileCardList.vue'
|
||||||
import { isMobile } from '@core/utils/is'
|
import { isMobile } from '@core/utils/is'
|
||||||
|
import { totalMetalWalletTitle } from '@/utils/adminMetalLabels'
|
||||||
|
import type { WalletTableSums } from '@/model/wallet'
|
||||||
|
import { formatMetalDisplayValue, metalDisplayUnit } from '@/utils/metalWeight'
|
||||||
|
|
||||||
const [submit, reload] = useBaseTable()
|
const [submit, reload] = useBaseTable()
|
||||||
const mobileListRef = ref<null | { fetch: () => Promise<void> }>(null)
|
const mobileListRef = ref<null | { fetch: () => Promise<void> }>(null)
|
||||||
@@ -92,13 +97,38 @@ const emits = defineEmits(['delete', 'edit', 'add', 'info', 'sums'])
|
|||||||
const { prefixCls } = usePrefix('pophoverstatus')
|
const { prefixCls } = usePrefix('pophoverstatus')
|
||||||
|
|
||||||
const table = ref(null)
|
const table = ref(null)
|
||||||
const sums = ref<any>()
|
const sums = ref<WalletTableSums>()
|
||||||
|
|
||||||
|
const metalBalanceCards = computed(() => {
|
||||||
|
if (sums.value?.metalBalances?.length) {
|
||||||
|
return sums.value.metalBalances
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sums.value?.gold != null || sums.value?.goldToCash != null) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
metal_id: 1,
|
||||||
|
label: 'طلا',
|
||||||
|
gold: sums.value.gold ?? 0,
|
||||||
|
goldToCash: sums.value.goldToCash ?? 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
return []
|
||||||
|
})
|
||||||
|
|
||||||
const tblProps = ref<any>({
|
const tblProps = ref<any>({
|
||||||
columns: cols,
|
columns: buildWalletColumns(),
|
||||||
api: async (params: TableInput) => {
|
api: async (params: TableInput) => {
|
||||||
const data = await getWalletTable(params)
|
const data = await getWalletTable(params)
|
||||||
sums.value = data?.sums
|
sums.value = data?.sums
|
||||||
|
const metals =
|
||||||
|
data?.sums?.metalBalances?.map((metal) => ({
|
||||||
|
metal_id: metal.metal_id,
|
||||||
|
label: metal.label
|
||||||
|
})) ?? []
|
||||||
|
tblProps.value.columns = buildWalletColumns(metals)
|
||||||
emits('sums', data?.sums)
|
emits('sums', data?.sums)
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,157 +1,75 @@
|
|||||||
import type { BaseTableColumn } from '@core/type/baseTable'
|
import type { BaseTableColumn } from '@core/type/baseTable'
|
||||||
import { toJalaliNoTime, toTime } from '@core/utils/useDataTime'
|
|
||||||
import CustomTag from '@/components/CustomTag.vue'
|
|
||||||
import { usePrice } from '@core/utils/usePrice'
|
import { usePrice } from '@core/utils/usePrice'
|
||||||
/*import BudgetSelect from '@/components/BudgetSelect.vue'
|
import { metalLabel, walletMetalBalanceTitle } from '@/utils/adminMetalLabels'
|
||||||
import { useStaticApiListsStore } from '@/store/staticApisStore'
|
import { formatMetalDisplayValue, metalCellValue } from '@/utils/metalWeight'
|
||||||
import type { FilterSelect } from '@/model/filterSelect'
|
|
||||||
import type { DocItem } from '@/model/docType'*/
|
|
||||||
|
|
||||||
//define store
|
export type WalletMetalColumn = { metal_id: number; label: string; display_unit?: string }
|
||||||
//const store = useStaticApiListsStore()
|
|
||||||
//document types
|
|
||||||
/*let docTypes: DocItem[]*/
|
|
||||||
/*onMounted(async () => {
|
|
||||||
docTypes = await store.readyDocTypeList()
|
|
||||||
})
|
|
||||||
const filteredDocTypes: FilterSelect = docTypes?.map((x: DocItem) => {
|
|
||||||
return {
|
|
||||||
label: x.title,
|
|
||||||
value: x.id
|
|
||||||
}
|
|
||||||
})*/
|
|
||||||
|
|
||||||
export const cols: BaseTableColumn[] = [
|
export function buildWalletColumns(metals: WalletMetalColumn[] = []): BaseTableColumn[] {
|
||||||
{
|
const baseColumns: BaseTableColumn[] = [
|
||||||
title: 'ردیف',
|
{
|
||||||
dataIndex: 'id',
|
title: 'ردیف',
|
||||||
key: 'id',
|
dataIndex: 'id',
|
||||||
filterable: false,
|
key: 'id',
|
||||||
sortable: false,
|
|
||||||
customRender: (record: any) => {
|
|
||||||
return record.index + 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'کد کیف پول',
|
|
||||||
dataIndex: 'id',
|
|
||||||
key: 'id',
|
|
||||||
type: 'Number'
|
|
||||||
// className: 'column-money'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'نام مشتری',
|
|
||||||
dataIndex: 'customer.name',
|
|
||||||
key: 'customer.name',
|
|
||||||
type: 'Text'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'موجودی کیف پول (ریال)',
|
|
||||||
dataIndex: 'cash',
|
|
||||||
key: 'cash',
|
|
||||||
type: 'Text',
|
|
||||||
customRender(data) {
|
|
||||||
return usePrice(data.record.cash, true, '')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'موجودی کیف طلا (گرم)',
|
|
||||||
dataIndex: 'gold',
|
|
||||||
key: 'gold',
|
|
||||||
type: 'Text'
|
|
||||||
}
|
|
||||||
/* {
|
|
||||||
title: 'شماره موبایل',
|
|
||||||
dataIndex: 'mobile',
|
|
||||||
key: 'mobile',
|
|
||||||
type: 'Number'
|
|
||||||
},*/
|
|
||||||
/* {
|
|
||||||
title: 'نام فروشگاه',
|
|
||||||
dataIndex: 'shop_name',
|
|
||||||
key: 'shop_name',
|
|
||||||
type: 'Text'
|
|
||||||
},*/
|
|
||||||
/* {
|
|
||||||
title: 'وضعیت',
|
|
||||||
dataIndex: 'active',
|
|
||||||
key: 'active',
|
|
||||||
customRender: ({ record }) => {
|
|
||||||
return record.active ? (
|
|
||||||
<div class="flex justify-center">
|
|
||||||
<CustomTag textColor="#25855A" bgColor="#E3F4E9">
|
|
||||||
فعال
|
|
||||||
</CustomTag>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div class="flex justify-center">
|
|
||||||
<CustomTag textColor="#C53030" bgColor="#FECACA">
|
|
||||||
غیر فعال
|
|
||||||
</CustomTag>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},*/
|
|
||||||
/*{
|
|
||||||
title: 'نقش',
|
|
||||||
dataIndex: 'user_type_record',
|
|
||||||
key: 'user_type_record',
|
|
||||||
type: 'Select',
|
|
||||||
filterable: false,
|
filterable: false,
|
||||||
sortable: false,
|
sortable: false,
|
||||||
filterComponentProps: {
|
|
||||||
multiple: true
|
|
||||||
},
|
|
||||||
filterField: 'user_type_obj.id',
|
|
||||||
filterComponent: ReadyUserTypeSelect,
|
|
||||||
/!* customRender: (record: any) => {
|
|
||||||
return record.record.user_type_record?.value
|
|
||||||
}*!/
|
|
||||||
customRender: ({ record }) => {
|
|
||||||
const items = record.is_admin ? (
|
|
||||||
<CustomTag textColor="#25855A" bgColor="#E3F4E9">
|
|
||||||
ادمین
|
|
||||||
</CustomTag>
|
|
||||||
) : (
|
|
||||||
record.user_type_obj.map((item: { title: any }, index: any) => {
|
|
||||||
if (item) {
|
|
||||||
return (
|
|
||||||
<CustomTag textColor="#25855A" bgColor="#E3F4E9">
|
|
||||||
{item?.title}
|
|
||||||
</CustomTag>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return <div class="flex justify-center items-center gap-2">{items}</div>
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'حوزه',
|
|
||||||
dataIndex: 'section',
|
|
||||||
key: 'section'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'شماره موبایل',
|
|
||||||
dataIndex: 'mobile',
|
|
||||||
type: 'Number',
|
|
||||||
key: 'mobile'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'تاریخ ثبت',
|
|
||||||
dataIndex: 'created_at',
|
|
||||||
key: 'created_at',
|
|
||||||
type: 'DateTime',
|
|
||||||
customRender: (record: any) => {
|
customRender: (record: any) => {
|
||||||
return toJalaliNoTime(record.record.created_at)
|
return record.index + 1
|
||||||
}
|
}
|
||||||
},*/
|
},
|
||||||
/* {
|
{
|
||||||
title: 'عملیات',
|
title: 'کد کیف پول',
|
||||||
dataIndex: 'action',
|
dataIndex: 'id',
|
||||||
key: 'action',
|
key: 'id',
|
||||||
slot: 'action',
|
type: 'Number'
|
||||||
fixed: 'right',
|
},
|
||||||
filterable: false
|
{
|
||||||
}*/
|
title: 'نام مشتری',
|
||||||
]
|
dataIndex: 'customer.name',
|
||||||
|
key: 'customer.name',
|
||||||
|
type: 'Text'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'موجودی کیف پول (ریال)',
|
||||||
|
dataIndex: 'cash',
|
||||||
|
key: 'cash',
|
||||||
|
type: 'Text',
|
||||||
|
customRender(data) {
|
||||||
|
return usePrice(data.record.cash, true, '')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
if (metals.length === 0) {
|
||||||
|
return [
|
||||||
|
...baseColumns,
|
||||||
|
{
|
||||||
|
title: metalLabel.goldWalletBalanceGram(),
|
||||||
|
dataIndex: 'gold',
|
||||||
|
key: 'gold',
|
||||||
|
type: 'Text',
|
||||||
|
customRender(data) {
|
||||||
|
const value = Number(data.record?.gold ?? 0)
|
||||||
|
return formatMetalDisplayValue(value, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
const metalColumns: BaseTableColumn[] = metals.map((metal) => ({
|
||||||
|
title: walletMetalBalanceTitle(metal.metal_id, metal.label),
|
||||||
|
dataIndex: `metal_gold_${metal.metal_id}`,
|
||||||
|
key: `metal_${metal.metal_id}`,
|
||||||
|
type: 'Text',
|
||||||
|
customRender(data) {
|
||||||
|
const value = metalCellValue(data.record, metal.metal_id)
|
||||||
|
return formatMetalDisplayValue(value, metal.metal_id)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
return [...baseColumns, ...metalColumns]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getWalletColumns(): BaseTableColumn[] {
|
||||||
|
return buildWalletColumns()
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,57 +15,45 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="gap-5 text-xs">
|
<div class="gap-5 text-xs">
|
||||||
<div>مجموع موجودی کیف طلا:</div>
|
<div>{{ metalLabel.totalGoldWallet() }}:</div>
|
||||||
<div class="font-bold">{{ usePrice(sums?.gold ?? 0, true, 'گرم') }}</div>
|
<div class="font-bold">{{ usePrice(sums?.gold ?? 0, true, 'گرم') }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="gap-5 text-xs">
|
<div class="gap-5 text-xs">
|
||||||
<div>مجموع موجودی کیف طلا:</div>
|
<div>{{ metalLabel.totalGoldWallet() }}:</div>
|
||||||
<div class="font-bold">{{ usePrice(sums?.goldToCash ?? 0) }}</div>
|
<div class="font-bold">{{ usePrice(sums?.goldToCash ?? 0) }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>-->
|
</div>-->
|
||||||
<div class="grid grid-cols-3 gap-1 text-xs">
|
<div class="grid grid-cols-3 gap-1 text-xs">
|
||||||
<div class="bg-blue-600 text-white rounded p-1">مجموع</div>
|
<div class="bg-blue-600 text-white rounded p-1">مجموع</div>
|
||||||
<div class="bg-blue-600 text-white rounded p-1">ریال</div>
|
<div class="bg-blue-600 text-white rounded p-1">ریال</div>
|
||||||
<div class="bg-blue-600 text-white rounded p-1">گرم</div>
|
<div class="bg-blue-600 text-white rounded p-1">وزن</div>
|
||||||
<div class="bg-blue-600 text-white rounded p-1">کیف طلا</div>
|
|
||||||
<div
|
<template v-for="metal in mobileMetalRows" :key="`metal-${metal.metal_id}`">
|
||||||
class="text-white rounded p-1"
|
<div class="bg-blue-600 text-white rounded p-1">{{ metal.label }}</div>
|
||||||
:class="
|
<div
|
||||||
sums?.goldToCash >= 0
|
class="text-white rounded p-1"
|
||||||
? sums?.goldToCash === 0
|
:class="sumToneClass(metal.goldToCash)"
|
||||||
? 'bg-amber-600'
|
>
|
||||||
: 'bg-green-600'
|
{{ usePrice(metal.goldToCash ?? 0, true, '') }}
|
||||||
: 'bg-red-600'
|
</div>
|
||||||
"
|
<div
|
||||||
>
|
class="text-white rounded p-1"
|
||||||
{{ usePrice(sums?.goldToCash ?? 0, true, '') }}
|
:class="sumToneClass(metal.gold)"
|
||||||
</div>
|
>
|
||||||
<div
|
{{ formatMetalDisplayValue(metal.gold ?? 0, metal.metal_id) }}
|
||||||
class="text-white rounded p-1"
|
</div>
|
||||||
:class="
|
</template>
|
||||||
sums?.gold >= 0 ? (sums?.gold === 0 ? 'bg-amber-600' : 'bg-green-600') : 'bg-red-600'
|
|
||||||
"
|
|
||||||
>
|
|
||||||
{{ usePrice(sums?.gold ?? 0, true, '') }}
|
|
||||||
</div>
|
|
||||||
<div class="bg-blue-600 text-white rounded p-1">کیف پول</div>
|
<div class="bg-blue-600 text-white rounded p-1">کیف پول</div>
|
||||||
<div
|
<div
|
||||||
class="text-white rounded p-1"
|
class="text-white rounded p-1"
|
||||||
:class="
|
:class="sumToneClass(sums?.cash)"
|
||||||
sums?.cash >= 0 ? (sums?.cash === 0 ? 'bg-amber-600' : 'bg-green-600') : 'bg-red-600'
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
{{ usePrice(sums?.cash ?? 0, true, '') }}
|
{{ usePrice(sums?.cash ?? 0, true, '') }}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="text-white rounded p-1"
|
class="text-white rounded p-1"
|
||||||
:class="
|
:class="sumToneClass(sums?.cashToGold)"
|
||||||
sums?.cashToGold >= 0
|
|
||||||
? sums?.cashToGold === 0
|
|
||||||
? 'bg-amber-600'
|
|
||||||
: 'bg-green-600'
|
|
||||||
: 'bg-red-600'
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
{{ usePrice(sums?.cashToGold ?? 0, true, '') }}
|
{{ usePrice(sums?.cashToGold ?? 0, true, '') }}
|
||||||
</div>
|
</div>
|
||||||
@@ -165,6 +153,8 @@ import type { Item } from '@/model/Item'
|
|||||||
import type { Customer } from '@/model/customer'
|
import type { Customer } from '@/model/customer'
|
||||||
import { createCustomer, deleteCustomer, updateCustomer } from '@/api/customer'
|
import { createCustomer, deleteCustomer, updateCustomer } from '@/api/customer'
|
||||||
import { usePrice } from '@core/utils/usePrice'
|
import { usePrice } from '@core/utils/usePrice'
|
||||||
|
import type { WalletTableSums } from '@/model/wallet'
|
||||||
|
import { formatMetalDisplayValue } from '@/utils/metalWeight'
|
||||||
|
|
||||||
//loading
|
//loading
|
||||||
const loading = ref<boolean>(false)
|
const loading = ref<boolean>(false)
|
||||||
@@ -172,7 +162,38 @@ const loading = ref<boolean>(false)
|
|||||||
//define store
|
//define store
|
||||||
const store = useStaticApiListsStore()
|
const store = useStaticApiListsStore()
|
||||||
|
|
||||||
const sums = ref()
|
const sums = ref<WalletTableSums>()
|
||||||
|
|
||||||
|
const mobileMetalRows = computed(() => {
|
||||||
|
if (sums.value?.metalBalances?.length) {
|
||||||
|
return sums.value.metalBalances.map((metal) => ({
|
||||||
|
metal_id: metal.metal_id,
|
||||||
|
label: metal.label,
|
||||||
|
gold: metal.gold ?? 0,
|
||||||
|
goldToCash: metal.goldToCash ?? 0
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sums.value?.gold != null || sums.value?.goldToCash != null) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
metal_id: 1,
|
||||||
|
label: 'طلا',
|
||||||
|
gold: sums.value.gold ?? 0,
|
||||||
|
goldToCash: sums.value.goldToCash ?? 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
return []
|
||||||
|
})
|
||||||
|
|
||||||
|
function sumToneClass(value?: number) {
|
||||||
|
if (value == null || value >= 0) {
|
||||||
|
return value === 0 ? 'bg-amber-600' : 'bg-green-600'
|
||||||
|
}
|
||||||
|
return 'bg-red-600'
|
||||||
|
}
|
||||||
|
|
||||||
//define tbl
|
//define tbl
|
||||||
const table = ref(null)
|
const table = ref(null)
|
||||||
|
|||||||
@@ -68,7 +68,7 @@
|
|||||||
<BaseIcon icon="vuesax-linear:empty-wallet" size="18" color="#dc2626" />
|
<BaseIcon icon="vuesax-linear:empty-wallet" size="18" color="#dc2626" />
|
||||||
</template>
|
</template>
|
||||||
</MobileStatCard>
|
</MobileStatCard>
|
||||||
<MobileStatCard bordered variant="warning" label="طلا" :value="goldLabel(row)">
|
<MobileStatCard bordered variant="warning" :label="metalLabel.goldUnit()" :value="goldLabel(row)">
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<BaseIcon icon="vuesax-linear:coin" size="18" color="#b45309" />
|
<BaseIcon icon="vuesax-linear:coin" size="18" color="#b45309" />
|
||||||
</template>
|
</template>
|
||||||
@@ -107,6 +107,7 @@ import { status } from '@/pages/menuPages/withdraw/information/hooks/status'
|
|||||||
import { isMobile } from '@core/utils/is'
|
import { isMobile } from '@core/utils/is'
|
||||||
import { toJalali } from '@core/utils/useDataTime'
|
import { toJalali } from '@core/utils/useDataTime'
|
||||||
import { usePrice } from '@core/utils/usePrice'
|
import { usePrice } from '@core/utils/usePrice'
|
||||||
|
import { metalLabel } from '@/utils/adminMetalLabels'
|
||||||
import { toNumber } from 'lodash'
|
import { toNumber } from 'lodash'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ import type { TableInput } from '@core/type/baseTable'
|
|||||||
import useBaseTable from '@core/components/BaseTableOld/hooks/useBaseTable'
|
import useBaseTable from '@core/components/BaseTableOld/hooks/useBaseTable'
|
||||||
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
||||||
import { changeCustomerActivation, getCustomerTable } from '@/api/customer'
|
import { changeCustomerActivation, getCustomerTable } from '@/api/customer'
|
||||||
import { cols } from '../hooks/withdrawColumns'
|
import { getWithdrawColumns } from '../hooks/withdrawColumns'
|
||||||
import { useUserStore } from '@/store/userStore'
|
import { useUserStore } from '@/store/userStore'
|
||||||
import { usePrefix } from '@core/composable/usePrefix'
|
import { usePrefix } from '@core/composable/usePrefix'
|
||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
@@ -133,7 +133,7 @@ const confirmButtonLabel = computed(() =>
|
|||||||
)
|
)
|
||||||
|
|
||||||
const tblProps = computed(() => ({
|
const tblProps = computed(() => ({
|
||||||
columns: cols,
|
columns: getWithdrawColumns(),
|
||||||
api: async (params: TableInput) => {
|
api: async (params: TableInput) => {
|
||||||
const payload = { ...params, flow_type: props.flowType }
|
const payload = { ...params, flow_type: props.flowType }
|
||||||
lastTableParams.value = payload
|
lastTableParams.value = payload
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { toJalali, toJalaliNoTime, toTime } from '@core/utils/useDataTime'
|
|||||||
import CustomTag from '@/components/CustomTag.vue'
|
import CustomTag from '@/components/CustomTag.vue'
|
||||||
import { usePrice } from '@core/utils/usePrice'
|
import { usePrice } from '@core/utils/usePrice'
|
||||||
import WithdrawStatus from "@/pages/menuPages/withdraw/components/WithdrawStatus.vue";
|
import WithdrawStatus from "@/pages/menuPages/withdraw/components/WithdrawStatus.vue";
|
||||||
|
import { metalLabel } from '@/utils/adminMetalLabels'
|
||||||
/*import BudgetSelect from '@/components/BudgetSelect.vue'
|
/*import BudgetSelect from '@/components/BudgetSelect.vue'
|
||||||
import { useStaticApiListsStore } from '@/store/staticApisStore'
|
import { useStaticApiListsStore } from '@/store/staticApisStore'
|
||||||
import type { FilterSelect } from '@/model/filterSelect'
|
import type { FilterSelect } from '@/model/filterSelect'
|
||||||
@@ -22,7 +23,8 @@ const filteredDocTypes: FilterSelect = docTypes?.map((x: DocItem) => {
|
|||||||
}
|
}
|
||||||
})*/
|
})*/
|
||||||
|
|
||||||
export const cols: BaseTableColumn[] = [
|
export function getWithdrawColumns(): BaseTableColumn[] {
|
||||||
|
return [
|
||||||
{
|
{
|
||||||
title: 'ردیف',
|
title: 'ردیف',
|
||||||
dataIndex: 'id',
|
dataIndex: 'id',
|
||||||
@@ -82,7 +84,7 @@ export const cols: BaseTableColumn[] = [
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'طلا',
|
title: metalLabel.goldUnit(),
|
||||||
dataIndex: 'gold',
|
dataIndex: 'gold',
|
||||||
key: 'gold',
|
key: 'gold',
|
||||||
type: 'Number',
|
type: 'Number',
|
||||||
@@ -232,4 +234,5 @@ export const cols: BaseTableColumn[] = [
|
|||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
filterable: false
|
filterable: false
|
||||||
}*/
|
}*/
|
||||||
]
|
]
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,35 +1,13 @@
|
|||||||
import type { RouteRecordRaw } from 'vue-router'
|
import type { RouteRecordRaw } from 'vue-router'
|
||||||
|
|
||||||
const Orders: RouteRecordRaw = {
|
/** ریدایرکت آدرس قدیمی — منو زیر پیکربندی است */
|
||||||
|
const Accounting: RouteRecordRaw = {
|
||||||
name: 'accounting',
|
name: 'accounting',
|
||||||
path: '/accounting',
|
path: '/accounting',
|
||||||
component: () => import('@core/layouts/BasicLayout.vue'),
|
|
||||||
redirect: { name: 'accountingMain' },
|
redirect: { name: 'accountingMain' },
|
||||||
meta: {
|
meta: {
|
||||||
icon: 'vuesax-linear:calculator',
|
hideMenu: true
|
||||||
permissionsKey: 'accounting',
|
}
|
||||||
title: 'حسابداری',
|
|
||||||
disabled: false,
|
|
||||||
hideMenu: false,
|
|
||||||
hideChildren: true,
|
|
||||||
order: 7
|
|
||||||
},
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
name: 'accountingMain',
|
|
||||||
path: '',
|
|
||||||
component: () => import('@/pages/menuPages/accounting/index.vue'),
|
|
||||||
meta: {
|
|
||||||
permissionsKey: 'project',
|
|
||||||
icon: 'vuesax-linear:empty-wallet-time',
|
|
||||||
order: 3,
|
|
||||||
title: 'حسابداری',
|
|
||||||
disabled: false,
|
|
||||||
hideMenu: false
|
|
||||||
//hideChildren: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Orders
|
export default Accounting
|
||||||
|
|||||||
@@ -136,6 +136,17 @@ const Setting: RouteRecordRaw = {
|
|||||||
title: 'مدیران',
|
title: 'مدیران',
|
||||||
order: 9
|
order: 9
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'accountingMain',
|
||||||
|
path: 'accounting',
|
||||||
|
component: () => import('@/pages/menuPages/accounting/index.vue'),
|
||||||
|
meta: {
|
||||||
|
permissionsKey: 'accounting',
|
||||||
|
icon: 'vuesax-linear:calculator',
|
||||||
|
title: 'حسابداری',
|
||||||
|
order: 10
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,131 @@
|
|||||||
|
import { m } from '@/utils/metalLabels'
|
||||||
|
import { metalDisplayUnit } from '@/utils/metalWeight'
|
||||||
|
|
||||||
|
/** برچسبهای پرتکرار پنل ادمین که «طلا» در متن دارند */
|
||||||
|
export const metalLabel = {
|
||||||
|
goldBalance: () => m('تراز طلایی'),
|
||||||
|
goldCommission: () => m('کارمزد طلایی'),
|
||||||
|
goldWallet: () => m('کیف پول طلایی'),
|
||||||
|
goldWalletShort: () => m('طلایی'),
|
||||||
|
goldAmount: () => m('مقدار طلا'),
|
||||||
|
goldInventory: () => m('موجودی طلایی'),
|
||||||
|
goldGramPriceCustomer: () => m('قیمت هر گرم طلا برای مشتری (ریال)'),
|
||||||
|
goldGramPriceOriginal: () => m('قیمت هر گرم طلا بدون سود (ریال)'),
|
||||||
|
goldGramPriceOriginalSlash: () => m('/قیمت هر گرم طلا بدون سود (ریال)'),
|
||||||
|
customersGold: () => m('طلای مشتریان'),
|
||||||
|
goldValueInRial: () => m('ارزش طلا به ریال'),
|
||||||
|
goldWalletCharge: () => m('شارژ کیف پول طلایی'),
|
||||||
|
goldWalletBalance: () => m('موجودی طلایی (گرم)'),
|
||||||
|
goldWalletBalanceGram: () => m('موجودی کیف طلا (گرم)'),
|
||||||
|
goldInTransaction: () => m('مقدار طلا طی تراکنش (گرم)'),
|
||||||
|
goldWalletTitle: () => m('کیف پول طلا'),
|
||||||
|
goldPlaceholder: () => m('مقدار طلا'),
|
||||||
|
goldUnit: () => m('طلا'),
|
||||||
|
goldChargeShort: () => m('شارژ طلایی'),
|
||||||
|
manageGoldCash: () => m('مدیریت موجودی ریالی و طلایی'),
|
||||||
|
goldGifts: () => m('هدایای طلا'),
|
||||||
|
goldTradeBuySell: (direction: 1 | 2 | number) =>
|
||||||
|
m(`قیمت ${direction === 1 ? 'خرید' : 'فروش'} طلا:`),
|
||||||
|
totalGoldWallet: () => m('مجموع موجودی کیف طلا'),
|
||||||
|
goldWalletTab: () => m('کیف طلا'),
|
||||||
|
}
|
||||||
|
|
||||||
|
export function walletMetalBalanceTitle(metalId: number, label?: string) {
|
||||||
|
const unit = metalDisplayUnit(metalId)
|
||||||
|
switch (metalId) {
|
||||||
|
case 1:
|
||||||
|
return m(`موجودی کیف طلا (${unit})`)
|
||||||
|
case 2:
|
||||||
|
return m(`موجودی کیف نقره (${unit})`)
|
||||||
|
case 3:
|
||||||
|
return m(`موجودی کیف مس (${unit})`)
|
||||||
|
default:
|
||||||
|
return label ? `موجودی کیف ${label} (${unit})` : metalLabel.goldWalletBalanceGram()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function customerWalletMetalBalanceTitle(metalId: number, label?: string) {
|
||||||
|
const unit = metalDisplayUnit(metalId)
|
||||||
|
switch (metalId) {
|
||||||
|
case 1:
|
||||||
|
return m(`موجودی طلایی (${unit})`)
|
||||||
|
case 2:
|
||||||
|
return m(`موجودی نقره (${unit})`)
|
||||||
|
case 3:
|
||||||
|
return m(`موجودی مس (${unit})`)
|
||||||
|
default:
|
||||||
|
return label ? `موجودی ${label} (${unit})` : metalLabel.goldWalletBalance()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function totalMetalWalletTitle(metalId: number, label?: string) {
|
||||||
|
const unit = metalDisplayUnit(metalId)
|
||||||
|
switch (metalId) {
|
||||||
|
case 1:
|
||||||
|
return m(`مجموع موجودی کیف طلا`)
|
||||||
|
case 2:
|
||||||
|
return m(`مجموع موجودی کیف نقره`)
|
||||||
|
case 3:
|
||||||
|
return m(`مجموع موجودی کیف مس`)
|
||||||
|
default:
|
||||||
|
return label ? `مجموع موجودی کیف ${label}` : metalLabel.totalGoldWallet()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function metalBalanceTitle(metalId: number, label?: string) {
|
||||||
|
switch (metalId) {
|
||||||
|
case 1:
|
||||||
|
return metalLabel.goldBalance()
|
||||||
|
case 2:
|
||||||
|
return m('تراز نقره')
|
||||||
|
case 3:
|
||||||
|
return m('تراز مس')
|
||||||
|
default:
|
||||||
|
return label ? `تراز ${label}` : metalLabel.goldBalance()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function metalBalanceTitleWithUnit(metalId: number, label?: string) {
|
||||||
|
const unit = metalDisplayUnit(metalId)
|
||||||
|
return `${metalBalanceTitle(metalId, label)} (${unit})`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function transactionMetalAmountTitle(metalId: number, label?: string) {
|
||||||
|
const unit = metalDisplayUnit(metalId)
|
||||||
|
switch (metalId) {
|
||||||
|
case 1:
|
||||||
|
return m(`مقدار طلا طی تراکنش (${unit})`)
|
||||||
|
case 2:
|
||||||
|
return m(`مقدار نقره طی تراکنش (${unit})`)
|
||||||
|
case 3:
|
||||||
|
return m(`مقدار مس طی تراکنش (${unit})`)
|
||||||
|
default:
|
||||||
|
return label ? `مقدار ${label} طی تراکنش (${unit})` : metalLabel.goldInTransaction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function dealPriceAtTransactionTitle(metalId: number, label?: string) {
|
||||||
|
switch (metalId) {
|
||||||
|
case 1:
|
||||||
|
return m('قیمت زمان معامله طلا (ریال)')
|
||||||
|
case 2:
|
||||||
|
return m('قیمت زمان معامله نقره (ریال)')
|
||||||
|
case 3:
|
||||||
|
return m('قیمت زمان معامله مس (ریال)')
|
||||||
|
default:
|
||||||
|
return label ? `قیمت زمان معامله ${label} (ریال)` : 'قیمت زمان معامله (ریال)'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function metalCommissionTitle(metalId: number, label?: string) {
|
||||||
|
switch (metalId) {
|
||||||
|
case 1:
|
||||||
|
return metalLabel.goldCommission()
|
||||||
|
case 2:
|
||||||
|
return m('کارمزد نقره')
|
||||||
|
case 3:
|
||||||
|
return m('کارمزد مس')
|
||||||
|
default:
|
||||||
|
return label ? `کارمزد ${label}` : metalLabel.goldCommission()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,7 +17,7 @@ export function mapCategoriesToSelectItems(
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
/** همان منبع جدول دستهبندیها — قبل از خواندن، همگامسازی میکند */
|
/** همان منبع جدول دستهبندیها — دستههای جدید از محصولات اضافه میشوند؛ ترتیب دستی حفظ میشود */
|
||||||
export async function syncAndLoadCategories(options?: {
|
export async function syncAndLoadCategories(options?: {
|
||||||
selectOnly?: boolean
|
selectOnly?: boolean
|
||||||
metalHint?: string
|
metalHint?: string
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { setCachedMainMetal } from '@/utils/metalLabels'
|
||||||
|
|
||||||
export function getConfigApp(key: string) {
|
export function getConfigApp(key: string) {
|
||||||
const config = window.__APP_CONFIG__ ? window.__APP_CONFIG__.user : {}
|
const config = window.__APP_CONFIG__ ? window.__APP_CONFIG__.user : {}
|
||||||
|
|
||||||
@@ -29,11 +31,14 @@ async function bootstrapSettingsFromApi(): Promise<void> {
|
|||||||
const list = Array.isArray(rows) ? rows : []
|
const list = Array.isArray(rows) ? rows : []
|
||||||
const isStoreRow = list.find((item) => item.key === 'is_store')
|
const isStoreRow = list.find((item) => item.key === 'is_store')
|
||||||
const mainWalletRow = list.find((item) => item.key === 'main_wallet_products')
|
const mainWalletRow = list.find((item) => item.key === 'main_wallet_products')
|
||||||
|
const mainMetalRow = list.find((item) => item.key === 'main_metal')
|
||||||
storeModeFromSettings = isStoreRow ? isSwitchOn(isStoreRow.value) : false
|
storeModeFromSettings = isStoreRow ? isSwitchOn(isStoreRow.value) : false
|
||||||
|
setCachedMainMetal(mainMetalRow?.value)
|
||||||
const { setCachedMainWalletProductIds } = await import('@/components/mobile/utils/wallet')
|
const { setCachedMainWalletProductIds } = await import('@/components/mobile/utils/wallet')
|
||||||
setCachedMainWalletProductIds(parseMainWalletProductIds(mainWalletRow?.value))
|
setCachedMainWalletProductIds(parseMainWalletProductIds(mainWalletRow?.value))
|
||||||
} catch {
|
} catch {
|
||||||
storeModeFromSettings = false
|
storeModeFromSettings = false
|
||||||
|
setCachedMainMetal(null)
|
||||||
const { setCachedMainWalletProductIds } = await import('@/components/mobile/utils/wallet')
|
const { setCachedMainWalletProductIds } = await import('@/components/mobile/utils/wallet')
|
||||||
setCachedMainWalletProductIds([])
|
setCachedMainWalletProductIds([])
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
export const DEFAULT_MAIN_METAL = 'طلا'
|
||||||
|
|
||||||
|
const mainMetalRef = ref(DEFAULT_MAIN_METAL)
|
||||||
|
|
||||||
|
export function resolveMainMetal(value?: string | number | null): string {
|
||||||
|
const normalized = value?.toString().trim()
|
||||||
|
return normalized || DEFAULT_MAIN_METAL
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setCachedMainMetal(value?: string | number | null): void {
|
||||||
|
mainMetalRef.value = resolveMainMetal(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMainMetal(): string {
|
||||||
|
return mainMetalRef.value
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useMainMetalRef() {
|
||||||
|
return mainMetalRef
|
||||||
|
}
|
||||||
|
|
||||||
|
/** جایگزینی طلایی/طلای/طلا با نام فلز — بدون صفت مسی/طلایی */
|
||||||
|
export function applyMainMetalText(text: string, mainMetal = getMainMetal()): string {
|
||||||
|
if (!text || mainMetal === DEFAULT_MAIN_METAL) return text
|
||||||
|
|
||||||
|
return text
|
||||||
|
.replace(/طلایی/g, mainMetal)
|
||||||
|
.replace(/طلای/g, mainMetal)
|
||||||
|
.replace(/طلا/g, mainMetal)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function m(text: string): string {
|
||||||
|
return applyMainMetalText(text)
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
import { TRADE_WEIGHT_UNIT_GRAM, TRADE_WEIGHT_UNIT_KILOGRAM } from '@/utils/weightUnit'
|
||||||
|
|
||||||
|
export const METAL_ID_GOLD = 1
|
||||||
|
export const METAL_ID_SILVER = 2
|
||||||
|
export const METAL_ID_COPPER = 3
|
||||||
|
|
||||||
|
/** واحد نمایش موجودی/تراز هر فلز — مس کیلوگرم، بقیه گرم */
|
||||||
|
export function metalDisplayUnit(metalId: number): string {
|
||||||
|
return metalId === METAL_ID_COPPER ? TRADE_WEIGHT_UNIT_KILOGRAM : TRADE_WEIGHT_UNIT_GRAM
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isMetalKilogram(metalId: number): boolean {
|
||||||
|
return metalId === METAL_ID_COPPER
|
||||||
|
}
|
||||||
|
|
||||||
|
/** تبدیل مقدار ذخیرهشده به گرم (برای محاسبات ریالی) */
|
||||||
|
export function gramsToMetalDisplay(grams: number, metalId: number): number {
|
||||||
|
return isMetalKilogram(metalId) ? grams / 1000 : grams
|
||||||
|
}
|
||||||
|
|
||||||
|
/** مقدار نمایشی از گرم */
|
||||||
|
export function formatMetalWeightFromGrams(
|
||||||
|
grams: number,
|
||||||
|
metalId: number,
|
||||||
|
decimals = 3
|
||||||
|
): string {
|
||||||
|
const value = gramsToMetalDisplay(grams, metalId)
|
||||||
|
return formatMetalDisplayValue(value, metalId, decimals)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** مقدار از قبل در واحد نمایشی فلز */
|
||||||
|
export function formatMetalDisplayValue(
|
||||||
|
value: number,
|
||||||
|
metalId: number,
|
||||||
|
decimals = 3
|
||||||
|
): string {
|
||||||
|
const unit = metalDisplayUnit(metalId)
|
||||||
|
return `${Number(Number(value).toFixed(decimals))} ${unit}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatMetalDisplayValuePlain(
|
||||||
|
value: number,
|
||||||
|
metalId: number,
|
||||||
|
decimals = 3
|
||||||
|
): string {
|
||||||
|
return Number(Number(value).toFixed(decimals)).toLocaleString('fa-IR', {
|
||||||
|
maximumFractionDigits: decimals
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function metalCellValue(
|
||||||
|
record: Record<string, unknown>,
|
||||||
|
metalId: number,
|
||||||
|
prefix = 'metal_gold'
|
||||||
|
): number {
|
||||||
|
const flatKey = `${prefix}_${metalId}`
|
||||||
|
if (record[flatKey] != null) {
|
||||||
|
return Number(record[flatKey])
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapKey = prefix === 'wallet_metal_gold' ? 'wallet_metal_gold' : 'metal_gold'
|
||||||
|
const map = record[mapKey] as Record<string, number> | undefined
|
||||||
|
if (!map) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return Number(map[String(metalId)] ?? 0)
|
||||||
|
}
|
||||||
@@ -13,3 +13,17 @@ export function resolveTradeWeightUnit(settingValue?: string | number | null) {
|
|||||||
export function weightFieldLabelForUnit(unit?: string | null) {
|
export function weightFieldLabelForUnit(unit?: string | null) {
|
||||||
return isKilogramUnit(unit) ? 'وزن به کیلوگرم' : 'وزن به گرم'
|
return isKilogramUnit(unit) ? 'وزن به کیلوگرم' : 'وزن به گرم'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function formatAmountWithUnit(
|
||||||
|
value: number | null | undefined,
|
||||||
|
unit?: string | null,
|
||||||
|
decimals = 3
|
||||||
|
): string {
|
||||||
|
if (value == null || Number.isNaN(Number(value))) {
|
||||||
|
return '-'
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolvedUnit = (unit ?? TRADE_WEIGHT_UNIT_GRAM).trim() || TRADE_WEIGHT_UNIT_GRAM
|
||||||
|
|
||||||
|
return `${Number(Number(value).toFixed(decimals))} ${resolvedUnit}`
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user