fix(finance): shared treasury layout so tab switches stop jumping

Keep title and nav mounted in TreasuryLayout with stable header height, and finish Ant form chrome (MoneyInput, Jalali) on finance pages.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-21 21:47:00 +03:30
co-authored by Cursor
parent 23ae273232
commit cb5944e767
15 changed files with 907 additions and 476 deletions
@@ -16,13 +16,15 @@ import {
Row as ARow,
Select as ASelect,
Typography,
message,
} from 'ant-design-vue';
import { api } from '@/lib/api';
import type { FinancialAccountDto, FinancialCategoryDto, FinancialDocumentDto } from '@mento/shared';
import TreasuryNav from '@/components/treasury/TreasuryNav.vue';
import MoneyInput from '@/components/MoneyInput.vue';
import { toman } from './treasury-format';
import { parseMoneyNumber } from '@/lib/money';
const { Title: ATypographyTitle, Text: ATypographyText } = Typography;
const { Text: ATypographyText } = Typography;
const AListItem = AList.Item;
const AListItemMeta = AList.Item.Meta;
const ASelectOption = ASelect.Option;
@@ -47,7 +49,6 @@ const form = ref({
notes: '',
});
const title = 'پرداخت‌ها و هزینه‌ها';
const settleLabel = 'مبلغ پرداخت‌شده';
const statusLabel: Record<string, string> = {
open: 'پرداخت‌نشده',
@@ -81,19 +82,29 @@ async function load() {
accounts.value = acc.data.filter((row) => row.isActive);
}
function onFinishFailed() {
message.warning('لطفاً فیلدهای الزامی را کامل کنید.');
}
async function createDoc() {
saving.value = true;
error.value = '';
ok.value = '';
try {
const amount = parseMoneyNumber(String(form.value.amount ?? ''), 0);
if (amount == null || amount <= 0) {
error.value = 'مبلغ کل معتبر نیست.';
return;
}
const settleAmount = parseMoneyNumber(String(form.value.settleAmount ?? ''), 0) ?? 0;
await api.post('/treasury/documents', {
kind,
title: form.value.title,
amount: form.value.amount,
amount,
categoryId: form.value.categoryId || null,
partyName: form.value.partyName || null,
accountId: form.value.accountId || null,
settleAmount: form.value.settleAmount || 0,
settleAmount,
method: form.value.method,
reference: form.value.reference || null,
notes: form.value.notes || null,
@@ -136,43 +147,50 @@ onMounted(() => {
</script>
<template>
<div>
<a-flex vertical :gap="16">
<div>
<a-typography-title :level="3" :style="{ margin: 0 }">{{ title }}</a-typography-title>
<a-typography-text type="secondary">
ثبت سند، موجودی را عوض نمی‌کند مگر مبلغ دریافت یا پرداخت واقعی وارد شود.
</a-typography-text>
</div>
<TreasuryNav />
<a-flex vertical :gap="16">
<a-alert v-if="error" type="error" show-icon :message="error" />
<a-alert v-if="ok" type="success" show-icon :message="ok" />
<a-card :bordered="false">
<a-divider orientation="right">سند جدید</a-divider>
<a-form layout="vertical" @finish="createDoc">
<a-form
layout="vertical"
:model="form"
@finish="createDoc"
@finishFailed="onFinishFailed"
>
<a-row :gutter="[16, 0]">
<a-col :xs="24" :md="12">
<a-form-item label="عنوان" name="title">
<a-form-item
label="عنوان"
name="title"
:rules="[{ required: true, message: 'عنوان الزامی است' }]"
>
<a-input v-model:value="form.title" />
</a-form-item>
</a-col>
<a-col :xs="24" :md="8">
<a-form-item label="مبلغ کل" name="amount">
<a-input v-model:value="form.amount" />
<a-form-item
label="مبلغ کل"
name="amount"
:rules="[{ required: true, message: 'مبلغ الزامی است' }]"
>
<MoneyInput v-model="form.amount" :decimals="0" />
</a-form-item>
</a-col>
<a-col :xs="24" :md="8">
<a-form-item :label="settleLabel" name="settleAmount">
<a-input v-model:value="form.settleAmount" />
<MoneyInput v-model="form.settleAmount" :decimals="0" />
</a-form-item>
</a-col>
<a-col :xs="24" :md="8">
<a-form-item label="دسته‌بندی" name="categoryId">
<a-select v-model:value="form.categoryId" allow-clear style="width: 100%" placeholder="بدون دسته">
<a-select-option value="">بدون دسته</a-select-option>
<a-select
v-model:value="form.categoryId"
allow-clear
style="width: 100%"
placeholder="بدون دسته"
>
<a-select-option v-for="cat in categories" :key="cat.id" :value="cat.id">
{{ cat.name }}
</a-select-option>
@@ -186,8 +204,12 @@ onMounted(() => {
</a-col>
<a-col :xs="24" :md="8">
<a-form-item label="حساب" name="accountId">
<a-select v-model:value="form.accountId" allow-clear style="width: 100%" placeholder="انتخاب حساب">
<a-select-option value="">انتخاب حساب</a-select-option>
<a-select
v-model:value="form.accountId"
allow-clear
style="width: 100%"
placeholder="انتخاب حساب"
>
<a-select-option v-for="account in accounts" :key="account.id" :value="account.id">
{{ account.name }}
</a-select-option>
@@ -256,6 +278,5 @@ onMounted(() => {
</template>
</a-list>
</a-card>
</a-flex>
</div>
</a-flex>
</template>