feat(tahesab): three-way mande decision UI including push to Tahesab
Clarify overwrite vs keep vs push-to-tahesab, warn when app balance is zero, and update colleague wait-callback help text. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,16 +1,24 @@
|
||||
<template>
|
||||
<BasicModal
|
||||
v-model:visible="visibleProxy"
|
||||
:width="isMobile ? '92vw' : '36rem'"
|
||||
:width="isMobile ? '92vw' : '40rem'"
|
||||
title="تصمیم برای مانده حساب"
|
||||
:custom-footer="true"
|
||||
cancel-text="بعداً"
|
||||
@close="close"
|
||||
>
|
||||
<div class="flex flex-col gap-3 text-sm" style="color: var(--admin-text)">
|
||||
<p class="m-0" style="color: var(--admin-text-secondary)">
|
||||
موجودی اپ با مانده {{ sourceLabel }} فرق دارد. دو مانده جمع نمیشوند؛ یکی را پس از بررسی سندها تأیید کنید.
|
||||
<p class="m-0 leading-6" style="color: var(--admin-text-secondary)">
|
||||
موجودی اپ با مانده {{ sourceLabel }} فرق دارد. دو مانده جمع نمیشوند — یکی از سه مسیر زیر را انتخاب کنید.
|
||||
</p>
|
||||
|
||||
<div
|
||||
v-if="appIsEmpty && allowPush"
|
||||
class="rounded-lg border border-amber-200 bg-amber-50 px-3 py-2 text-xs leading-5 text-amber-900"
|
||||
>
|
||||
موجودی اپ صفر است. اگر «اعمال مانده اپ روی تهحساب» را بزنید، مانده تهحساب با سند تعدیل همتراز صفر اپ میشود.
|
||||
</div>
|
||||
|
||||
<div class="rounded-lg border border-base p-3 space-y-2" style="background: var(--admin-bg-muted)">
|
||||
<div class="flex justify-between gap-2">
|
||||
<span style="color: var(--admin-text-secondary)">موجودی اپ (طلا)</span>
|
||||
@@ -84,12 +92,58 @@
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<a-button type="primary" block :loading="loading === 'overwrite'" :disabled="!!loading" @click="choose('overwrite')">
|
||||
اعمال مانده {{ sourceLabel }} روی کیف
|
||||
</a-button>
|
||||
<a-button block :loading="loading === 'keep'" :disabled="!!loading" @click="choose('keep')">
|
||||
نگه داشتن موجودی فعلی اپ
|
||||
</a-button>
|
||||
<button
|
||||
type="button"
|
||||
class="mande-choice mande-choice--primary"
|
||||
:disabled="!!loading"
|
||||
@click="choose('overwrite')"
|
||||
>
|
||||
<span class="mande-choice__title">
|
||||
<a-spin v-if="loading === 'overwrite'" size="small" class="me-1" />
|
||||
اعمال مانده {{ sourceLabel }} روی کیف اپ
|
||||
</span>
|
||||
<span class="mande-choice__hint">
|
||||
موجودی اپ مثل {{ sourceLabel }} میشود. سند تعدیل روی کیف اپ ثبت میشود.
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
v-if="allowPush"
|
||||
type="button"
|
||||
class="mande-choice"
|
||||
:class="appIsEmpty ? 'mande-choice--warn' : 'mande-choice--secondary'"
|
||||
:disabled="!!loading"
|
||||
@click="choose('push')"
|
||||
>
|
||||
<span class="mande-choice__title">
|
||||
<a-spin v-if="loading === 'push'" size="small" class="me-1" />
|
||||
اعمال مانده اپ روی {{ sourceLabel }}
|
||||
<span v-if="appIsEmpty" class="mande-choice__badge">اپ صفر</span>
|
||||
</span>
|
||||
<span class="mande-choice__hint">
|
||||
{{
|
||||
appIsEmpty
|
||||
? `مانده ${sourceLabel} با سند تعدیل همتراز صفر اپ میشود. کیف اپ تغییر نمیکند.`
|
||||
: `مانده ${sourceLabel} مثل موجودی اپ میشود. کیف اپ تغییر نمیکند.`
|
||||
}}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="mande-choice"
|
||||
:disabled="!!loading"
|
||||
@click="choose('keep')"
|
||||
>
|
||||
<span class="mande-choice__title">
|
||||
<a-spin v-if="loading === 'keep'" size="small" class="me-1" />
|
||||
نگه داشتن وضعیت فعلی
|
||||
</span>
|
||||
<span class="mande-choice__hint">
|
||||
نه کیف اپ عوض میشود نه {{ sourceLabel }}؛ اختلاف برای بعد میماند.
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<a-button type="link" block :disabled="!!loading" @click="close">بعداً</a-button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -103,27 +157,34 @@ import BasicModal from '@/components/BasicModal.vue'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
import type { TahesabPreviewDoc } from '@/api/tahesab'
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean
|
||||
appGold?: number | null
|
||||
appCash?: number | null
|
||||
tahesabGold?: number | null
|
||||
tahesabCash?: number | null
|
||||
sourceGold?: number | null
|
||||
sourceCash?: number | null
|
||||
goldDelta?: number | null
|
||||
rialDelta?: number | null
|
||||
sourceLabel?: string
|
||||
walletDocs?: TahesabPreviewDoc[]
|
||||
tahesabDocs?: TahesabPreviewDoc[]
|
||||
}>()
|
||||
export type MandeDecisionMode = 'overwrite' | 'keep' | 'push'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
visible: boolean
|
||||
appGold?: number | null
|
||||
appCash?: number | null
|
||||
tahesabGold?: number | null
|
||||
tahesabCash?: number | null
|
||||
sourceGold?: number | null
|
||||
sourceCash?: number | null
|
||||
goldDelta?: number | null
|
||||
rialDelta?: number | null
|
||||
sourceLabel?: string
|
||||
/** فقط تهحساب: اجازهٔ نوشتن مانده اپ روی حسابداری */
|
||||
allowPush?: boolean
|
||||
walletDocs?: TahesabPreviewDoc[]
|
||||
tahesabDocs?: TahesabPreviewDoc[]
|
||||
}>(),
|
||||
{ allowPush: false }
|
||||
)
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', value: boolean): void
|
||||
(e: 'decide', mode: 'overwrite' | 'keep'): void
|
||||
(e: 'decide', mode: MandeDecisionMode): void
|
||||
}>()
|
||||
|
||||
const loading = ref<'overwrite' | 'keep' | null>(null)
|
||||
const loading = ref<MandeDecisionMode | null>(null)
|
||||
const sourceLabel = computed(() => props.sourceLabel || 'تهحساب')
|
||||
const sourceGoldValue = computed(() => props.sourceGold ?? props.tahesabGold)
|
||||
const sourceCashValue = computed(() => props.sourceCash ?? props.tahesabCash)
|
||||
@@ -140,6 +201,11 @@ const sourceDocs = computed(() => props.tahesabDocs ?? [])
|
||||
const showDocs = computed(
|
||||
() => sourceLabel.value === 'تهحساب' || walletDocs.value.length > 0 || sourceDocs.value.length > 0
|
||||
)
|
||||
const appIsEmpty = computed(() => {
|
||||
const g = Math.abs(Number(props.appGold ?? 0))
|
||||
const c = Math.abs(Number(props.appCash ?? 0))
|
||||
return g < 0.0005 && c < 1
|
||||
})
|
||||
|
||||
const visibleProxy = computed({
|
||||
get: () => props.visible,
|
||||
@@ -186,8 +252,75 @@ function close() {
|
||||
visibleProxy.value = false
|
||||
}
|
||||
|
||||
function choose(mode: 'overwrite' | 'keep') {
|
||||
function choose(mode: MandeDecisionMode) {
|
||||
loading.value = mode
|
||||
emit('decide', mode)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.mande-choice {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 0.25rem;
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
border-radius: 0.75rem;
|
||||
border: 1px solid var(--admin-border, #e5e7eb);
|
||||
background: var(--admin-surface, #fff);
|
||||
padding: 0.75rem 0.875rem;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.15s ease, background 0.15s ease;
|
||||
}
|
||||
|
||||
.mande-choice:disabled {
|
||||
opacity: 0.65;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.mande-choice:not(:disabled):hover {
|
||||
border-color: var(--admin-primary, #10913a);
|
||||
}
|
||||
|
||||
.mande-choice--primary {
|
||||
border-color: var(--admin-primary, #10913a);
|
||||
background: color-mix(in srgb, var(--admin-primary, #10913a) 8%, white);
|
||||
}
|
||||
|
||||
.mande-choice--secondary:not(:disabled):hover {
|
||||
background: var(--admin-bg-muted, #f8fafc);
|
||||
}
|
||||
|
||||
.mande-choice--warn {
|
||||
border-color: #f59e0b;
|
||||
background: #fffbeb;
|
||||
}
|
||||
|
||||
.mande-choice__title {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 700;
|
||||
color: var(--admin-text, #111827);
|
||||
}
|
||||
|
||||
.mande-choice__hint {
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.5;
|
||||
color: var(--admin-text-secondary, #6b7280);
|
||||
}
|
||||
|
||||
.mande-choice__badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
border-radius: 999px;
|
||||
background: #fef3c7;
|
||||
color: #92400e;
|
||||
font-size: 0.6875rem;
|
||||
font-weight: 700;
|
||||
padding: 0.1rem 0.45rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user