progress in view invoices

This commit is contained in:
Hesabix 2025-03-30 22:35:53 +00:00
parent 6b19280c04
commit c26fcf6582
5 changed files with 299 additions and 267 deletions

View file

@ -1,9 +1,9 @@
<template> <template>
<div> <div>
<v-text-field v-model="displayDate" :label="label" prepend-inner-icon="mdi-calendar" persistent-placeholder <v-text-field v-model="displayDate" :label="label" prepend-inner-icon="mdi-calendar" persistent-placeholder
class="v-date-input" :rules="rules" @input="updateDateFromInput" @click:prepend="togglePicker"></v-text-field> :class="['v-date-input', `date-input-${uniqueId}`]" :rules="rules" @input="updateDateFromInput" @click:prepend="togglePicker"></v-text-field>
<date-picker v-model="displayDate" type="date" format="jYYYY/jMM/jDD" display-format="jYYYY/jMM/jDD" <date-picker v-model="displayDate" type="date" format="jYYYY/jMM/jDD" display-format="jYYYY/jMM/jDD"
:min="minDatePersian" :max="maxDatePersian" custom-input=".v-date-input" :input-mode="false" :min="minDatePersian" :max="maxDatePersian" :custom-input="`.date-input-${uniqueId}`" :input-mode="true"
:editable="pickerActive" @close="pickerActive = false"></date-picker> :editable="pickerActive" @close="pickerActive = false"></date-picker>
</div> </div>
</template> </template>
@ -14,7 +14,7 @@ import moment from 'jalali-moment';
export default { export default {
props: { props: {
value: { modelValue: {
type: String, type: String,
default: '', default: '',
}, },
@ -29,42 +29,52 @@ export default {
}, },
data() { data() {
return { return {
displayDate: '', // تاریخ به فرمت شمسی displayDate: this.modelValue, // مقداردهی اولیه از prop
pickerActive: false, // کنترل باز شدن تقویم pickerActive: false, // کنترل باز شدن تقویم
minDatePersian: '', // تاریخ شروع سال مالی (شمسی برای پکیج) minDatePersian: '', // تاریخ شروع سال مالی (شمسی برای پکیج)
maxDatePersian: '', // تاریخ پایان سال مالی (شمسی برای پکیج) maxDatePersian: '', // تاریخ پایان سال مالی (شمسی برای پکیج)
uniqueId: '', // شناسه یکتا برای هر نمونه
isInitialized: false, // فلگ برای کنترل مقداردهی اولیه
}; };
}, },
created() {
// ایجاد شناسه یکتا برای هر نمونه از کامپوننت
this.uniqueId = Math.random().toString(36).substring(2, 15);
},
watch: { watch: {
displayDate(newVal) { displayDate(newVal, oldVal) {
if (newVal) { if (newVal !== oldVal) {
this.$emit('input', newVal); // ارسال تاریخ شمسی به والد this.$emit('update:modelValue', newVal);
} else {
this.$emit('input', '');
} }
}, },
value(newVal) { modelValue: {
if (newVal) { immediate: true,
this.displayDate = newVal; handler(newVal) {
} else { if (newVal && newVal !== this.displayDate) {
this.displayDate = ''; this.displayDate = newVal;
}
} }
}, }
}, },
async mounted() { async mounted() {
await this.fetchYearData(); await this.fetchYearData();
if (!this.value && this.displayDate) {
this.$emit('input', this.displayDate);
}
}, },
methods: { methods: {
async fetchYearData() { async fetchYearData() {
try {
axios.get('/api/year/get').then((response) => { const response = await axios.get('/api/year/get');
this.minDatePersian = response.data.start; // فرمت YYYY/MM/DD شمسی this.minDatePersian = response.data.start;
this.maxDatePersian = response.data.end; // فرمت YYYY/MM/DD شمسی this.maxDatePersian = response.data.end;
this.displayDate = response.data.now; // تاریخ جاری شمسی
}); // فقط اگر مقدار اولیه نداریم، از تاریخ جاری استفاده کنیم
if (!this.modelValue && !this.isInitialized) {
this.displayDate = response.data.now;
this.$emit('update:modelValue', response.data.now);
this.isInitialized = true;
}
} catch (error) {
console.error('خطا در دریافت اطلاعات سال:', error);
}
}, },
updateDateFromInput(value) { updateDateFromInput(value) {
// بررسی و اعتبارسنجی تاریخ وارد شده توسط کاربر // بررسی و اعتبارسنجی تاریخ وارد شده توسط کاربر

View file

@ -1,11 +1,5 @@
<template> <template>
<v-btn <v-btn v-if="totalAmount > 0" icon color="error" class="ml-2" @click="dialog = true">
v-if="totalAmount > 0"
icon
color="error"
class="ml-2"
@click="dialog = true"
>
<v-icon>mdi-cash</v-icon> <v-icon>mdi-cash</v-icon>
<v-tooltip activator="parent" location="bottom">ثبت دریافت</v-tooltip> <v-tooltip activator="parent" location="bottom">ثبت دریافت</v-tooltip>
</v-btn> </v-btn>
@ -50,58 +44,26 @@
</v-toolbar> </v-toolbar>
<v-card-text> <v-card-text>
<v-alert
v-if="errorMessage"
type="error"
dismissible
@input="errorMessage = ''"
class="mb-4"
>
{{ errorMessage }}
</v-alert>
<v-row> <v-row>
<v-col cols="12" md="5"> <v-col cols="12" md="5">
<Hdatepicker v-model="date" label="تاریخ" /> <Hdatepicker v-model="date" label="تاریخ" />
</v-col> </v-col>
<v-col cols="12" md="7"> <v-col cols="12" md="7">
<v-text-field <v-text-field v-model="des" label="شرح" outlined clearable class="mb-4"></v-text-field>
v-model="des"
label="شرح"
outlined
clearable
class="mb-4"
></v-text-field>
</v-col> </v-col>
</v-row> </v-row>
<v-row> <v-row>
<v-col cols="12" md="6"> <v-col cols="12" md="6">
<v-text-field <v-text-field v-model="formattedTotalPays" label="مجموع" readonly outlined></v-text-field>
v-model="formattedTotalPays"
label="مجموع"
readonly
outlined
></v-text-field>
</v-col> </v-col>
<v-col cols="12" md="6"> <v-col cols="12" md="6">
<v-text-field <v-text-field v-model="formattedRemainingAmount" label="باقی مانده" readonly outlined></v-text-field>
v-model="formattedRemainingAmount"
label="باقی مانده"
readonly
outlined
></v-text-field>
</v-col> </v-col>
</v-row> </v-row>
<v-data-table <v-data-table :headers="headers" :items="items" :loading="loading" class="elevation-1 mt-2"
:headers="headers" :header-props="{ class: 'custom-header' }" :items-per-page="-1" hide-default-footer>
:items="items"
:loading="loading"
class="elevation-1 mt-2"
:header-props="{ class: 'custom-header' }"
:items-per-page="-1"
hide-default-footer
>
<template v-slot:item.type="{ item }"> <template v-slot:item.type="{ item }">
<v-icon v-if="item.type === 'bank'">mdi-bank</v-icon> <v-icon v-if="item.type === 'bank'">mdi-bank</v-icon>
<v-icon v-if="item.type === 'cashdesk'">mdi-cash-register</v-icon> <v-icon v-if="item.type === 'cashdesk'">mdi-cash-register</v-icon>
@ -109,105 +71,34 @@
<v-icon v-if="item.type === 'cheque'">mdi-checkbook</v-icon> <v-icon v-if="item.type === 'cheque'">mdi-checkbook</v-icon>
</template> </template>
<template v-slot:item.selection="{ item }"> <template v-slot:item.selection="{ item }">
<v-select <v-select v-if="item.type === 'bank'" v-model="item.bank" :items="listBanks" item-title="name" return-object
v-if="item.type === 'bank'" label="بانک"></v-select>
v-model="item.bank" <v-select v-if="item.type === 'cashdesk'" v-model="item.cashdesk" :items="listCashdesks" item-title="name"
:items="listBanks" return-object label="صندوق"></v-select>
item-title="name" <v-select v-if="item.type === 'salary'" v-model="item.salary" :items="listSalarys" item-title="name"
return-object return-object label="تنخواه گردان"></v-select>
label="بانک"
outlined
dense
></v-select>
<v-select
v-if="item.type === 'cashdesk'"
v-model="item.cashdesk"
:items="listCashdesks"
item-title="name"
return-object
label="صندوق"
outlined
dense
></v-select>
<v-select
v-if="item.type === 'salary'"
v-model="item.salary"
:items="listSalarys"
item-title="name"
return-object
label="تنخواه گردان"
outlined
dense
></v-select>
<template v-if="item.type === 'cheque'"> <template v-if="item.type === 'cheque'">
<v-text-field <v-text-field class="mb-2 mt-1" v-model="item.chequeNum" label="شماره چک" required></v-text-field>
v-model="item.chequeNum" <v-text-field class="mb-2" v-model="item.chequeSayadNum" label="شماره صیاد" dense required></v-text-field>
label="شماره چک" <v-text-field class="mb-2" v-model="item.chequeBank" label="بانک صادرکننده" required></v-text-field>
outlined <Hdatepicker class="mb-1" v-model="item.chequeDate" label="تاریخ چک" required />
dense
required
></v-text-field>
<v-text-field
v-model="item.chequeSayadNum"
label="شماره صیاد"
outlined
dense
required
></v-text-field>
<v-text-field
v-model="item.chequeBank"
label="بانک صادرکننده"
outlined
dense
required
></v-text-field>
<Hdatepicker
v-model="item.chequeDate"
label="تاریخ چک"
required
/>
</template> </template>
</template> </template>
<template v-slot:item.bd="{ item }"> <template v-slot:item.bd="{ item }">
<Hnumberinput <Hnumberinput v-model="item.bd" label="مبلغ" placeholder="0" @update:modelValue="calc" />
v-model="item.bd"
label="مبلغ"
outlined
dense
placeholder="0"
@update:modelValue="calc"
/>
</template> </template>
<template v-slot:item.referral="{ item }"> <template v-slot:item.referral="{ item }">
<v-text-field <v-text-field v-model="item.referral" label="ارجاع"></v-text-field>
v-model="item.referral"
label="ارجاع"
outlined
dense
></v-text-field>
</template> </template>
<template v-slot:item.des="{ item }"> <template v-slot:item.des="{ item }">
<v-text-field <v-text-field v-model="item.des" label="شرح"></v-text-field>
v-model="item.des"
label="شرح"
outlined
dense
></v-text-field>
</template> </template>
<template v-slot:item.actions="{ item, index }"> <template v-slot:item.actions="{ item, index }">
<v-btn <v-btn variant="plain" color="primary" @click="fillWithTotal(item)">
variant="plain"
color="primary"
@click="fillWithTotal(item)"
>
<v-icon>mdi-cash-100</v-icon> <v-icon>mdi-cash-100</v-icon>
<v-tooltip activator="parent" location="bottom">کل فاکتور</v-tooltip> <v-tooltip activator="parent" location="bottom">کل فاکتور</v-tooltip>
</v-btn> </v-btn>
<v-btn <v-btn variant="plain" color="error" @click="deleteItem(index)">
variant="plain"
color="error"
@click="deleteItem(index)"
>
<v-icon>mdi-trash-can</v-icon> <v-icon>mdi-trash-can</v-icon>
<v-tooltip activator="parent" location="bottom">حذف</v-tooltip> <v-tooltip activator="parent" location="bottom">حذف</v-tooltip>
</v-btn> </v-btn>
@ -225,6 +116,16 @@
</v-overlay> </v-overlay>
</v-card> </v-card>
</v-dialog> </v-dialog>
<v-dialog v-model="successDialog" max-width="400">
<v-card color="success">
<v-card-text class="text-center pa-4">
<v-icon size="large" color="white" class="mb-4">mdi-check-circle</v-icon>
<div class="text-h6 text-white mb-2">ثبت موفق</div>
<div class="text-white">{{ successMessage }}</div>
</v-card-text>
</v-card>
</v-dialog>
</template> </template>
<script lang="ts"> <script lang="ts">
@ -261,7 +162,9 @@ export default defineComponent({
listCashdesks: [], listCashdesks: [],
totalPays: 0, totalPays: 0,
loading: false, loading: false,
errorMessage: '' errorMessage: '',
successDialog: false,
successMessage: ''
}), }),
computed: { computed: {
headers() { headers() {
@ -278,17 +181,17 @@ export default defineComponent({
return this.totalAmount - this.totalPays return this.totalAmount - this.totalPays
}, },
formattedTotalPays() { formattedTotalPays() {
return this.formatNumber(this.totalPays) return this.formatNumber(this.totalPays) || '۰';
}, },
formattedRemainingAmount() { formattedRemainingAmount() {
return this.formatNumber(this.remainingAmount) return this.formatNumber(this.remainingAmount) || '۰';
} }
}, },
methods: { methods: {
formatNumber(value: number | string): string { formatNumber(value: number | string): string {
if (!value) return '' if (value === undefined || value === null || value === '') return '۰';
const num = parseInt(value.toString().replace(/[^\d]/g, '')) const num = parseInt(value.toString().replace(/[^\d]/g, ''));
return num.toLocaleString('fa-IR') return num.toLocaleString('fa-IR') || '۰';
}, },
fillWithTotal(pay) { fillWithTotal(pay) {
pay.bd = this.totalAmount - this.totalPays pay.bd = this.totalAmount - this.totalPays
@ -423,26 +326,42 @@ export default defineComponent({
related: this.originalDoc related: this.originalDoc
}) })
if (response.data.result === '1') { if (response.data.result === 1) {
this.submitedDoc = response.data.doc this.submitedDoc = response.data.doc
this.windowsState.submited = true
this.dialog = false if (this.windowsState) {
} else if (response.data.result === '4') { this.windowsState.submited = true
this.errorMessage = response.data.msg }
this.successMessage = 'سند دریافت با موفقیت ثبت شد'
this.successDialog = true
setTimeout(() => {
this.successDialog = false
this.dialog = false
}, 2000)
this.$emit('submit-success', response.data.doc)
} else {
this.errorMessage = response.data.msg || 'خطا در ثبت سند'
} }
} catch (error) { } catch (error) {
this.errorMessage = 'خطا در ثبت سند' this.errorMessage = error.response?.data?.message || 'خطا در ثبت سند'
} finally { } finally {
this.loading = false this.loading = false
} }
} }
}, },
mounted() { async mounted() {
this.loadData() await this.loadData()
const response = await axios.get('/api/year/get')
this.date = response.data.now // تاریخ جاری شمسی
} }
}) })
</script> </script>
<style scoped> <style scoped>
/* استایل‌های دلخواه */ .v-card.success {
background-color: #4caf50 !important;
}
</style> </style>

View file

@ -7,87 +7,175 @@ import axios from "axios";
export default defineComponent({ export default defineComponent({
name: "recList", name: "recList",
props: { props: {
items: Array, items: {
windowsState: Object type: Array,
}, required: true
data: () => { },
return { windowsState: {
searchValue: '', type: Object,
loading: ref(false), required: true
headers: [
{ text: "شماره سند", value: "code", sortable: true },
{ text: "تاریخ", value: "date", sortable: true },
{ text: "شرح", value: "des", sortable: true },
{ text: "مبلغ", value: "amount", sortable: true },
{ text: "نوع", value: "type", sortable: true },
{ text: "عملیات", value: "operation" },
]
} }
}, },
data: () => ({
dialog: false,
searchValue: '',
loading: false,
headers: [
{
title: 'شماره سند',
key: 'code',
sortable: true,
align: 'center'
},
{
title: 'تاریخ',
key: 'date',
sortable: true,
align: 'center'
},
{
title: 'شرح',
key: 'des',
sortable: true,
align: 'center'
},
{
title: 'مبلغ',
key: 'amount',
sortable: true,
align: 'center'
},
{
title: 'نوع',
key: 'type',
sortable: true,
align: 'center'
},
{
title: 'عملیات',
key: 'operation',
sortable: false,
align: 'center'
}
]
}),
methods: { methods: {
deleteItem(code) { async deleteItem(code: string) {
Swal.fire({ try {
text: 'آیا برای حذف این مورد مطمئن هستید؟', const result = await Swal.fire({
showCancelButton: true, text: 'آیا برای حذف این مورد مطمئن هستید؟',
confirmButtonText: 'بله', showCancelButton: true,
cancelButtonText: `خیر`, confirmButtonText: 'بله',
icon: 'warning' cancelButtonText: 'خیر',
}).then((result) => { icon: 'warning'
/* Read more about isConfirmed, isDenied below */ })
if (result.isConfirmed) { if (result.isConfirmed) {
axios.post('/api/accounting/remove', { const response = await axios.post('/api/accounting/remove', { code })
'code': code
if (response.data.result === 1) {
await Swal.fire({
text: 'سند دریافت فاکتور با موفقیت حذف شد.',
icon: 'success',
confirmButtonText: 'قبول'
})
this.windowsState.submited = true
}
if (response.data.result === 2) {
await Swal.fire({
text: response.data.message,
icon: 'success',
confirmButtonText: 'قبول'
})
} }
).then((response) => {
if (response.data.result == 1) {
Swal.fire({
text: 'سند دریافت فاکتور با موفقیت حذف شد.',
icon: 'success',
confirmButtonText: 'قبول'
}).then((result) => {
this.$props.windowsState.submited = true;
});
}
if (response.data.result == 2) {
Swal.fire({
text: response.data.message,
icon: 'success',
confirmButtonText: 'قبول'
});
}
})
} }
}) } catch (error) {
console.error('Error deleting item:', error)
await Swal.fire({
text: 'خطا در حذف سند',
icon: 'error',
confirmButtonText: 'قبول'
})
}
} }
} }
}) })
</script> </script>
<template> <template>
<div class="mb-1"> <!-- دکمه نمایش لیست -->
<div class="input-group input-group-sm"> <v-btn icon color="info" class="ml-2" @click="dialog = true">
<span class="input-group-text"><i class="fa fa-search"></i></span> <v-icon>mdi-format-list-bulleted</v-icon>
<input v-model="searchValue" class="form-control" type="text" placeholder="جست و جو ..."> <v-tooltip activator="parent" location="bottom">لیست دریافتها</v-tooltip>
</div> </v-btn>
</div>
<EasyDataTable table-class-name="customize-table" show-index alternating :search-value="searchValue" :headers="headers" :items="this.$props.items" <!-- دیالوگ نمایش لیست -->
theme-color="#1d90ff" header-text-direction="center" body-text-direction="center" rowsPerPageMessage="تعداد سطر" <v-dialog v-model="dialog" max-width="900" persistent>
emptyMessage="اطلاعاتی برای نمایش وجود ندارد" rowsOfPageSeparatorMessage="از" :loading="loading"> <v-card>
<template #item-operation="{ code, type }"> <v-toolbar color="grey-lighten-4" flat>
<span v-if="type == 'sell_receive' || type == 'buy_send'" class="text-danger px-1"> <v-toolbar-title>
<span class="text-danger px-1" @click="deleteItem(code)"> <v-icon color="info" class="ml-2">mdi-format-list-bulleted</v-icon>
<i class="fa fa-trash"></i> لیست دریافتها
</span> </v-toolbar-title>
</span> <v-spacer></v-spacer>
</template> <v-btn icon @click="dialog = false">
<template #item-type="{ type }"> <v-icon>mdi-close</v-icon>
<span v-if="type == 'sell_receive' || type == 'buy_send'" class="text-danger px-1">سند حسابداری</span> </v-btn>
<span v-else class="text-success px-1">پرداخت آنلاین</span> </v-toolbar>
</template>
<template #item-amount="{ amount }"> <v-card-text class="pa-0">
<span>{{ $filters.formatNumber(amount) }}</span> <v-data-table
</template> :headers="headers"
</EasyDataTable> :items="items"
:search="searchValue"
:loading="loading"
class="elevation-1"
:header-props="{ class: 'custom-header' }"
hover
>
<!-- ستون نوع -->
<template v-slot:item.type="{ item }">
<v-chip
:color="item.type === 'sell_receive' || item.type === 'buy_send' ? 'error' : 'success'"
size="small"
variant="flat"
>
{{ item.type === 'sell_receive' || item.type === 'buy_send' ? 'سند حسابداری' : 'پرداخت آنلاین' }}
</v-chip>
</template>
<!-- ستون مبلغ -->
<template v-slot:item.amount="{ item }">
{{ $filters.formatNumber(item.amount) }}
</template>
<!-- ستون عملیات -->
<template v-slot:item.operation="{ item }">
<v-btn
v-if="item.type === 'sell_receive' || item.type === 'buy_send'"
color="error"
size="default"
variant="plain"
@click="deleteItem(item.code)"
>
<v-icon>mdi-trash-can</v-icon>
<v-tooltip activator="parent" location="top">حذف</v-tooltip>
</v-btn>
</template>
<!-- نمایش پیام خالی بودن جدول -->
<template v-slot:no-data>
<v-alert type="info" variant="tonal" class="ma-2">
اطلاعاتی برای نمایش وجود ندارد
</v-alert>
</template>
</v-data-table>
</v-card-text>
</v-card>
</v-dialog>
</template> </template>
<style scoped></style> <style scoped>
</style>

View file

@ -34,7 +34,6 @@ export default defineComponent({
}, },
}, },
data: () => ({ data: () => ({
recListDialog: false,
activeTab: 'invoice-info', activeTab: 'invoice-info',
loading: ref(true), loading: ref(true),
shortlink_url: '', shortlink_url: '',
@ -159,6 +158,10 @@ export default defineComponent({
</v-tooltip> </v-tooltip>
</template> </template>
<v-spacer></v-spacer> <v-spacer></v-spacer>
<v-btn icon :to="`/acc/sell/mod/${$route.params.id}`">
<v-icon>mdi-pencil</v-icon>
<v-tooltip activator="parent" location="bottom">ویرایش</v-tooltip>
</v-btn>
<v-btn icon v-if="item.doc.id !== 0"> <v-btn icon v-if="item.doc.id !== 0">
<archive-upload :docid="item.doc.id" doctype="sell" cat="sell" /> <archive-upload :docid="item.doc.id" doctype="sell" cat="sell" />
<v-tooltip activator="parent" location="bottom">آرشیو</v-tooltip> <v-tooltip activator="parent" location="bottom">آرشیو</v-tooltip>
@ -166,10 +169,11 @@ export default defineComponent({
<notes :stat="notes" :code="$route.params.id" type-note="sell" /> <notes :stat="notes" :code="$route.params.id" type-note="sell" />
<rec v-if="parseInt(item.doc.amount) > parseInt(totalRec)" :windows-state="PayWindowsState" :person="person.id" :original-doc="item.doc.code" :total-amount="parseInt(item.doc.amount) - parseInt(totalRec)" /> <rec v-if="parseInt(item.doc.amount) > parseInt(totalRec)" :windows-state="PayWindowsState" :person="person.id" :original-doc="item.doc.code" :total-amount="parseInt(item.doc.amount) - parseInt(totalRec)" />
<v-btn icon color="info" class="ml-2" @click="recListDialog = true"> <rec-list
<v-icon>mdi-arrow-down-circle</v-icon> :windows-state="recListWindowsState"
<v-tooltip activator="parent" location="bottom">دریافتها</v-tooltip> :items="item.relatedDocs"
</v-btn> />
<share-options v-if="bid.shortlinks" :shortlink-url="shortlink_url" :mobile="person.mobile" :invoice-id="item.doc.id" /> <share-options v-if="bid.shortlinks" :shortlink-url="shortlink_url" :mobile="person.mobile" :invoice-id="item.doc.id" />
<print-options :invoice-id="$route.params.id" /> <print-options :invoice-id="$route.params.id" />
</v-toolbar> </v-toolbar>
@ -315,23 +319,33 @@ export default defineComponent({
</v-card-text> </v-card-text>
</v-window-item> </v-window-item>
</v-window> </v-window>
<v-dialog v-model="recListDialog" max-width="800">
<v-card>
<v-card-title>
<v-icon left>mdi-arrow-down-circle</v-icon>
دریافتها
</v-card-title>
<v-card-text>
<rec-list :windows-state="recListWindowsState" :items="item.relatedDocs" />
</v-card-text>
<v-card-actions>
<v-btn color="secondary" @click="recListDialog = false">بازگشت</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-container> </v-container>
</template> </template>
<style scoped> <style scoped>
/* استایل برای هدر جدول */
:deep(.custom-header) th {
text-align: center !important;
justify-content: center !important;
white-space: nowrap;
font-weight: bold;
}
/* استایل برای محتوای سلول‌های جدول */
:deep(.v-data-table-rows-item) td {
text-align: center !important;
justify-content: center !important;
}
/* برای اطمینان از وسط چین بودن محتوای سلول‌ها */
:deep(.v-data-table) .v-data-table__wrapper table tbody td {
text-align: center !important;
}
/* برای لینک‌ها و آیکون‌ها در جدول */
:deep(.v-data-table) .v-data-table__wrapper table tbody td a,
:deep(.v-data-table) .v-data-table__wrapper table tbody td .v-icon {
display: inline-flex;
justify-content: center;
}
</style> </style>

View file

@ -77,7 +77,7 @@ export default {
loading: false, loading: false,
captchaLoading: false, captchaLoading: false,
dialog: false, dialog: false,
siteName:'', siteName: '',
showCaptcha: false, showCaptcha: false,
errorMsg: self.$t('login.input_fail'), errorMsg: self.$t('login.input_fail'),
captchaImage: '', captchaImage: '',
@ -155,20 +155,21 @@ export default {
const response = await axios.post("/api/user/login", userData, { const response = await axios.post("/api/user/login", userData, {
withCredentials: true, withCredentials: true,
}); });
if (response.data.Success === true) { if (response.data.Success == true) {
this.setTokenAndRedirect(response); this.setTokenAndRedirect(response);
} }
} catch (error) { if (response.data?.data?.captcha_required == true) {
const errorData = (error as any).response?.data || {};
this.errorMsg = errorData.error || this.$t('login.input_fail');
this.dialog = true;
if (errorData.captcha_required) {
this.showCaptcha = true; this.showCaptcha = true;
await this.loadCaptcha(); await this.loadCaptcha();
} else { } else {
this.showCaptcha = false; this.showCaptcha = false;
} }
} catch (error) {
const errorData = (error as any).response?.data || {};
this.showCaptcha = true;
await this.loadCaptcha();
this.errorMsg = errorData.message || errorData.error || this.$t('login.input_fail');
this.dialog = true;
} finally { } finally {
this.loading = false; this.loading = false;
} }
@ -190,7 +191,7 @@ export default {
mounted() { mounted() {
// کپچا در ابتدا نمایش داده نمیشه // کپچا در ابتدا نمایش داده نمیشه
}, },
async created(){ async created() {
this.siteName = await getSiteName(); this.siteName = await getSiteName();
}, },
}; };