2026-10-18 18:49:49 +03:30
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
|
import { onMounted, ref } from 'vue';
|
|
|
|
|
|
import axios from 'axios';
|
2026-10-18 21:06:18 +03:30
|
|
|
|
import {
|
|
|
|
|
|
Alert as AAlert,
|
|
|
|
|
|
Button as AButton,
|
|
|
|
|
|
Card as ACard,
|
|
|
|
|
|
Col as ACol,
|
|
|
|
|
|
Divider as ADivider,
|
|
|
|
|
|
Empty as AEmpty,
|
|
|
|
|
|
Flex as AFlex,
|
|
|
|
|
|
Form as AForm,
|
|
|
|
|
|
FormItem as AFormItem,
|
|
|
|
|
|
Input as AInput,
|
|
|
|
|
|
List as AList,
|
|
|
|
|
|
Row as ARow,
|
|
|
|
|
|
Select as ASelect,
|
|
|
|
|
|
Typography,
|
|
|
|
|
|
} from 'ant-design-vue';
|
2026-10-18 18:49:49 +03:30
|
|
|
|
import { api } from '@/lib/api';
|
|
|
|
|
|
import TreasuryNav from '@/components/treasury/TreasuryNav.vue';
|
|
|
|
|
|
import { toman } from './treasury-format';
|
|
|
|
|
|
import { formatJalaliNumeric } from '@/lib/jalali';
|
|
|
|
|
|
|
2026-10-18 21:06:18 +03:30
|
|
|
|
const { Title: ATypographyTitle, Text: ATypographyText } = Typography;
|
|
|
|
|
|
const AListItem = AList.Item;
|
|
|
|
|
|
const AListItemMeta = AList.Item.Meta;
|
|
|
|
|
|
const ASelectOption = ASelect.Option;
|
|
|
|
|
|
|
2026-10-18 18:49:49 +03:30
|
|
|
|
type Row = { id: string; title: string; kind: string; amount: string; interval: string; nextDue: string; isActive: boolean };
|
|
|
|
|
|
const rows = ref<Row[]>([]);
|
|
|
|
|
|
const error = ref('');
|
|
|
|
|
|
const ok = ref('');
|
|
|
|
|
|
const form = ref({ title: '', kind: 'expense', amount: '', interval: 'monthly', startDate: '' });
|
|
|
|
|
|
|
|
|
|
|
|
async function load() {
|
|
|
|
|
|
const { data } = await api.get<Row[]>('/treasury/recurring');
|
|
|
|
|
|
rows.value = data;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function createItem() {
|
|
|
|
|
|
error.value = '';
|
|
|
|
|
|
try {
|
|
|
|
|
|
await api.post('/treasury/recurring', form.value);
|
|
|
|
|
|
ok.value = 'الگو ثبت شد. در سررسید فقط سند باز ساخته میشود.';
|
|
|
|
|
|
await load();
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
error.value = axios.isAxiosError(e)
|
|
|
|
|
|
? String((e.response?.data as { message?: string })?.message ?? 'ثبت ناموفق بود.')
|
|
|
|
|
|
: 'ثبت ناموفق بود.';
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
void load();
|
|
|
|
|
|
});
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<template>
|
2026-10-18 21:06:18 +03:30
|
|
|
|
<div>
|
|
|
|
|
|
<a-flex vertical :gap="16">
|
2026-10-18 18:49:49 +03:30
|
|
|
|
<div>
|
2026-10-18 21:06:18 +03:30
|
|
|
|
<a-typography-title :level="3" :style="{ margin: 0 }">درآمد و هزینه تکرارشونده</a-typography-title>
|
|
|
|
|
|
<a-typography-text type="secondary">
|
|
|
|
|
|
تا تأیید دریافت یا پرداخت، موجودی حساب عوض نمیشود.
|
|
|
|
|
|
</a-typography-text>
|
2026-10-18 18:49:49 +03:30
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-10-18 21:06:18 +03:30
|
|
|
|
<TreasuryNav />
|
|
|
|
|
|
|
|
|
|
|
|
<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="createItem">
|
|
|
|
|
|
<a-row :gutter="[16, 0]">
|
|
|
|
|
|
<a-col :xs="24" :md="12">
|
|
|
|
|
|
<a-form-item label="عنوان" name="title">
|
|
|
|
|
|
<a-input v-model:value="form.title" />
|
|
|
|
|
|
</a-form-item>
|
|
|
|
|
|
</a-col>
|
|
|
|
|
|
<a-col :xs="24" :md="8">
|
|
|
|
|
|
<a-form-item label="نوع" name="kind">
|
|
|
|
|
|
<a-select v-model:value="form.kind" style="width: 100%">
|
|
|
|
|
|
<a-select-option value="expense">هزینه</a-select-option>
|
|
|
|
|
|
<a-select-option value="income">درآمد</a-select-option>
|
|
|
|
|
|
</a-select>
|
|
|
|
|
|
</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>
|
|
|
|
|
|
</a-col>
|
|
|
|
|
|
<a-col :xs="24" :md="8">
|
|
|
|
|
|
<a-form-item label="دوره" name="interval">
|
|
|
|
|
|
<a-select v-model:value="form.interval" style="width: 100%">
|
|
|
|
|
|
<a-select-option value="monthly">ماهانه</a-select-option>
|
|
|
|
|
|
<a-select-option value="weekly">هفتگی</a-select-option>
|
|
|
|
|
|
<a-select-option value="yearly">سالانه</a-select-option>
|
|
|
|
|
|
</a-select>
|
|
|
|
|
|
</a-form-item>
|
|
|
|
|
|
</a-col>
|
|
|
|
|
|
<a-col :xs="24" :md="8">
|
|
|
|
|
|
<a-form-item label="شروع" name="startDate">
|
|
|
|
|
|
<a-input v-model:value="form.startDate" type="date" style="width: 100%" />
|
|
|
|
|
|
</a-form-item>
|
|
|
|
|
|
</a-col>
|
|
|
|
|
|
</a-row>
|
|
|
|
|
|
<a-button type="primary" html-type="submit">ثبت الگو</a-button>
|
|
|
|
|
|
</a-form>
|
|
|
|
|
|
|
|
|
|
|
|
<a-divider orientation="right">فهرست الگوها</a-divider>
|
|
|
|
|
|
<a-empty v-if="!rows.length" description="الگویی ثبت نشده است." />
|
|
|
|
|
|
<a-list v-else item-layout="horizontal" :data-source="rows">
|
|
|
|
|
|
<template #renderItem="{ item }">
|
|
|
|
|
|
<a-list-item>
|
|
|
|
|
|
<template #actions>
|
|
|
|
|
|
<a-typography-text strong>{{ toman(item.amount) }}</a-typography-text>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<a-list-item-meta>
|
|
|
|
|
|
<template #title>{{ item.title }}</template>
|
|
|
|
|
|
<template #description>
|
|
|
|
|
|
{{ item.kind === 'income' ? 'درآمد' : 'هزینه' }}
|
|
|
|
|
|
· {{ item.interval }}
|
|
|
|
|
|
· بعدی {{ formatJalaliNumeric(new Date(item.nextDue)) }}
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</a-list-item-meta>
|
|
|
|
|
|
</a-list-item>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</a-list>
|
|
|
|
|
|
</a-card>
|
|
|
|
|
|
</a-flex>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|