some bug fixes

This commit is contained in:
Hesabix 2025-04-01 14:08:07 +00:00
parent ec17a2f4df
commit 2f3a47fc49
3 changed files with 301 additions and 107 deletions

View file

@ -59,6 +59,7 @@
previous: "",
operation: null,
activeButton: null,
waitingForPercent: false,
numberButtons: ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "."], // دکمههای اعداد
operatorButtons: ["+", "-", "*", "/", "%"], // دکمههای عملگر
actionButtons: ["C", "="], // دکمههای عملیاتی
@ -104,12 +105,25 @@
},
setOperation(op) {
if (this.current === "") return;
if (op === "%") {
if (this.operation === "+" || this.operation === "-") {
const base = parseFloat(this.previous);
const percentage = (base * parseFloat(this.current)) / 100;
this.current = percentage.toString();
this.calculate();
} else {
const curr = parseFloat(this.current);
this.current = (curr / 100).toString();
}
} else {
if (this.previous !== "") {
this.calculate();
}
this.operation = op;
this.previous = this.current;
this.current = "";
}
},
calculate() {
if (this.current === "" || this.previous === "") return;
@ -131,9 +145,10 @@
case "/":
result = curr !== 0 ? prev / curr : "خطا";
break;
case "%":
result = (prev * curr) / 100;
break;
}
if (typeof result === 'number' && !Number.isInteger(result)) {
result = parseFloat(result.toFixed(4));
}
this.display = result.toString();
@ -164,12 +179,11 @@
event.preventDefault();
}
},
// تعیین رنگ دکمهها بر اساس نوع
getButtonColor(btn) {
if (this.numberButtons.includes(btn)) return "blue darken-5"; // رنگ اعداد
if (this.operatorButtons.includes(btn)) return "gray darken-1"; // رنگ عملگرها
if (this.actionButtons.includes(btn)) return "red lighten-1"; // رنگ دکمههای عملیاتی
return "grey lighten-4"; // رنگ پیشفرض
if (this.numberButtons.includes(btn)) return "blue darken-5";
if (this.operatorButtons.includes(btn)) return "gray darken-1";
if (this.actionButtons.includes(btn)) return "red lighten-1";
return "grey lighten-4";
},
},
};
@ -186,9 +200,8 @@
box-shadow: none !important;
}
/* تغییر رنگ لحظه‌ای دکمه */
.active-btn {
background-color: #0288d1 !important; /* آبی تیره‌تر برای حالت فعال */
background-color: #0288d1 !important;
color: white !important;
transition: background-color 0.1s ease;
}

View file

@ -4,14 +4,21 @@
<v-tooltip activator="parent" location="bottom">ثبت دریافت</v-tooltip>
</v-btn>
<v-dialog v-model="dialog" max-width="950" persistent>
<v-card>
<v-dialog
v-model="dialog"
:fullscreen="$vuetify.display.mobile"
:max-width="$vuetify.display.mobile ? '' : '950'"
persistent
:class="$vuetify.display.mobile ? 'mobile-dialog' : ''"
>
<v-card :rounded="false" class="d-flex flex-column dialog-card">
<v-toolbar color="toolbar" flat>
<v-toolbar-title>
<v-icon color="error" left>mdi-cash</v-icon>
ثبت دریافت
</v-toolbar-title>
<v-spacer></v-spacer>
<v-menu bottom>
<template v-slot:activator="{ props }">
<v-btn icon color="success" v-bind="props" :disabled="loading">
@ -34,6 +41,7 @@
</v-list-item>
</v-list>
</v-menu>
<v-btn icon color="primary" @click="submit" :disabled="loading">
<v-icon>mdi-content-save</v-icon>
<v-tooltip activator="parent" location="bottom">ثبت</v-tooltip>
@ -43,7 +51,8 @@
</v-btn>
</v-toolbar>
<v-card-text>
<v-card-text class="flex-grow-1 overflow-y-auto pa-2">
<v-container class="pa-0">
<v-row>
<v-col cols="12" md="5">
<Hdatepicker v-model="date" label="تاریخ" />
@ -62,58 +71,123 @@
</v-col>
</v-row>
<v-data-table :headers="headers" :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 }">
<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 === 'salary'">mdi-wallet</v-icon>
<v-icon v-if="item.type === 'cheque'">mdi-checkbook</v-icon>
<div v-if="items.length === 0" class="text-center pa-4">
هیچ دریافتی ثبت نشده است.
</div>
<v-row v-else>
<v-col v-for="(item, index) in items" :key="index" cols="12">
<v-card :class="{
'bank-card': item.type === 'bank',
'cashdesk-card': item.type === 'cashdesk',
'salary-card': item.type === 'salary',
'cheque-card': item.type === 'cheque'
}" border="sm">
<v-card-item>
<template v-slot:prepend>
<v-icon :color="item.type === 'bank' ? 'blue' :
item.type === 'cashdesk' ? 'green' :
item.type === 'salary' ? 'orange' : 'purple'">
{{ item.type === 'bank' ? 'mdi-bank' :
item.type === 'cashdesk' ? 'mdi-cash-register' :
item.type === 'salary' ? 'mdi-wallet' : 'mdi-checkbook' }}
</v-icon>
</template>
<template v-slot:item.selection="{ item }">
<v-select v-if="item.type === 'bank'" v-model="item.bank" :items="listBanks" item-title="name" return-object
label="بانک"></v-select>
<v-select v-if="item.type === 'cashdesk'" v-model="item.cashdesk" :items="listCashdesks" item-title="name"
return-object label="صندوق"></v-select>
<v-select v-if="item.type === 'salary'" v-model="item.salary" :items="listSalarys" item-title="name"
return-object label="تنخواه گردان"></v-select>
<template v-if="item.type === 'cheque'">
<v-text-field class="mb-2 mt-1" v-model="item.chequeNum" label="شماره چک" required></v-text-field>
<v-text-field class="mb-2" v-model="item.chequeSayadNum" label="شماره صیاد" dense required></v-text-field>
<v-text-field class="mb-2" v-model="item.chequeBank" label="بانک صادرکننده" required></v-text-field>
<Hdatepicker class="mb-1" v-model="item.chequeDate" label="تاریخ چک" required />
</template>
</template>
<template v-slot:item.bd="{ item }">
<Hnumberinput v-model="item.bd" label="مبلغ" placeholder="0" @update:modelValue="calc" />
</template>
<template v-slot:item.referral="{ item }">
<v-text-field v-model="item.referral" label="ارجاع"></v-text-field>
</template>
<template v-slot:item.des="{ item }">
<v-text-field v-model="item.des" label="شرح"></v-text-field>
</template>
<template v-slot:item.actions="{ item, index }">
<v-btn variant="plain" color="primary" @click="fillWithTotal(item)">
<template v-slot:append>
<v-btn-group>
<v-btn variant="text" color="primary" density="comfortable" @click="fillWithTotal(item)">
<v-icon>mdi-cash-100</v-icon>
<v-tooltip activator="parent" location="bottom">کل فاکتور</v-tooltip>
</v-btn>
<v-btn variant="plain" color="error" @click="deleteItem(index)">
<v-btn variant="text" color="error" density="comfortable" @click="deleteItem(index)">
<v-icon>mdi-trash-can</v-icon>
<v-tooltip activator="parent" location="bottom">حذف</v-tooltip>
</v-btn>
</v-btn-group>
</template>
<template v-slot:no-data>
<v-card-text class="text-center">
هیچ دریافتی ثبت نشده است.
</v-card-text>
</template>
</v-data-table>
</v-card-item>
<v-card-text>
<v-row class="mt-2">
<v-col cols="12" sm="6">
<v-select
v-if="item.type === 'bank'"
v-model="item.bank"
:items="listBanks"
item-title="name"
return-object
label="بانک"
variant="outlined"
></v-select>
<v-select
v-if="item.type === 'cashdesk'"
v-model="item.cashdesk"
:items="listCashdesks"
item-title="name"
return-object
label="صندوق"
variant="outlined"
></v-select>
<v-select
v-if="item.type === 'salary'"
v-model="item.salary"
:items="listSalarys"
item-title="name"
return-object
label="تنخواه گردان"
variant="outlined"
></v-select>
</v-col>
<v-col cols="12" sm="6">
<Hnumberinput
v-model="item.bd"
label="مبلغ"
placeholder="0"
@update:modelValue="calc"
variant="outlined"
/>
</v-col>
<v-col cols="12" sm="6">
<v-text-field
v-model="item.referral"
label="ارجاع"
variant="outlined"
></v-text-field>
</v-col>
<v-col cols="12" sm="6">
<v-text-field
v-model="item.des"
label="شرح"
variant="outlined"
></v-text-field>
</v-col>
</v-row>
<v-row v-if="item.type === 'cheque'" class="mt-2">
<v-col cols="12" sm="6">
<v-text-field v-model="item.chequeNum" label="شماره چک" required variant="outlined"></v-text-field>
</v-col>
<v-col cols="12" sm="6">
<v-text-field v-model="item.chequeSayadNum" label="شماره صیاد" required variant="outlined"></v-text-field>
</v-col>
<v-col cols="12" sm="6">
<v-text-field v-model="item.chequeBank" label="بانک صادرکننده" required variant="outlined"></v-text-field>
</v-col>
<v-col cols="12" sm="6">
<Hdatepicker v-model="item.chequeDate" label="تاریخ چک" required />
</v-col>
</v-row>
</v-card-text>
</v-card>
</v-col>
</v-row>
<v-overlay :model-value="loading" contained class="align-center justify-center">
<v-progress-circular indeterminate size="64" color="primary"></v-progress-circular>
</v-overlay>
</v-container>
</v-card-text>
</v-card>
</v-dialog>
@ -126,6 +200,20 @@
</v-card-text>
</v-card>
</v-dialog>
<v-dialog v-model="errorDialog" max-width="400">
<v-card color="error">
<v-card-text class="text-center pa-4">
<v-icon size="large" color="white" class="mb-4">mdi-alert-circle</v-icon>
<div class="text-h6 text-white mb-2">خطا</div>
<div class="text-white" style="white-space: pre-line">{{ errorMessage }}</div>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="white" variant="text" @click="errorDialog = false">بستن</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script lang="ts">
@ -135,6 +223,23 @@ import { VDataTable } from 'vuetify/components'
import Hdatepicker from '@/components/forms/Hdatepicker.vue'
import Hnumberinput from '@/components/forms/Hnumberinput.vue'
interface Item {
id: string | number;
type: string;
bd: number;
bs: number;
table: number;
des: string;
bank?: any;
cashdesk?: any;
salary?: any;
chequeNum?: string;
chequeSayadNum?: string;
chequeBank?: string;
chequeDate?: string;
referral?: string;
}
export default defineComponent({
name: 'Rec',
components: {
@ -155,7 +260,7 @@ export default defineComponent({
data: () => ({
submitedDoc: {},
des: '',
items: [],
items: [] as Item[],
date: '',
listBanks: [],
listSalarys: [],
@ -164,7 +269,8 @@ export default defineComponent({
loading: false,
errorMessage: '',
successDialog: false,
successMessage: ''
successMessage: '',
errorDialog: false
}),
computed: {
headers() {
@ -265,12 +371,21 @@ export default defineComponent({
},
async submit() {
let errors = []
if (this.totalAmount < this.totalPays) {
if (this.items.length === 0) {
this.errorMessage = 'هیچ دریافتی ثبت نشده است.';
this.errorDialog = true;
return;
}
if (this.totalAmount && this.totalPays && this.totalAmount < this.totalPays) {
errors.push('مبالغ وارد شده بیشتر از مبلغ فاکتور است.')
}
this.items.forEach((element, index) => {
if (element.bd === 0) errors.push(`مبلغ صفر در ردیف ${index + 1} نا معتبر است.`)
this.items.forEach((element: any, index: number) => {
if (element.bd === 0 || element.bd === null || element.bd === undefined) {
errors.push(`مبلغ صفر در ردیف ${index + 1} نا معتبر است.`)
}
if (element.type === 'cheque' && (!element.chequeBank || !element.chequeNum || !element.chequeDate)) {
errors.push(`موارد الزامی ثبت چک در ردیف ${index + 1} وارد نشده است.`)
}
@ -285,13 +400,9 @@ export default defineComponent({
}
})
if (this.items.length === 0) {
this.errorMessage = 'هیچ دریافتی ثبت نشده است.'
return
}
if (errors.length > 0) {
this.errorMessage = errors.join('\n')
this.errorDialog = true
return
}
@ -305,16 +416,22 @@ export default defineComponent({
return element
})
rows.push({
if (!this.person) {
throw new Error('شخص انتخاب نشده است')
}
const personRow = {
id: this.person,
type: 'person',
bd: 0,
bs: this.totalPays,
table: 3,
des: `دریافت وجه فاکتور شماره ${this.formatNumber(this.originalDoc)}`
})
des: `دریافت وجه فاکتور شماره ${this.formatNumber(this.originalDoc || '')}`
}
this.des = this.des || `دریافت وجه فاکتور شماره ${this.formatNumber(this.originalDoc)}`
rows.push(personRow)
this.des = this.des || `دریافت وجه فاکتور شماره ${this.formatNumber(this.originalDoc || '')}`
try {
const response = await axios.post('/api/accounting/insert', {
@ -364,4 +481,68 @@ export default defineComponent({
.v-card.success {
background-color: #4caf50 !important;
}
/* استایل‌های جدید برای کارت‌ها */
.v-card {
transition: all 0.3s ease;
width: 100%;
max-width: 100%;
overflow-x: hidden;
}
.v-card-text {
overflow-x: hidden;
padding: 16px;
}
/* رنگ‌های متفاوت برای انواع مختلف کارت‌ها */
.v-card.bank-card {
border-right: 4px solid #1976d2;
}
.v-card.cashdesk-card {
border-right: 4px solid #4caf50;
}
.v-card.salary-card {
border-right: 4px solid #ff9800;
}
.v-card.cheque-card {
border-right: 4px solid #9c27b0;
}
.v-card-item {
background-color: rgba(0, 0, 0, 0.02);
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}
.v-btn-group .v-btn {
margin: 0 2px;
}
/* استایل‌های جدید برای رفع مشکل اسکرول */
.mobile-dialog {
overflow: hidden !important;
}
.mobile-dialog :deep(.v-card) {
height: 100vh !important;
max-height: 100vh !important;
}
.mobile-dialog :deep(.v-card-text) {
padding: 8px !important;
overflow-y: auto !important;
height: calc(100vh - 64px) !important; /* 64px ارتفاع toolbar */
}
.mobile-dialog :deep(.v-overlay__content) {
width: 100% !important;
height: 100% !important;
}
:deep(.v-card-text .container) {
max-width: 100% !important;
}
</style>

View file

@ -83,8 +83,8 @@
<v-col cols="12">
<v-card>
<v-card-title>{{ $t('marketing.recent_users') }}</v-card-title>
<v-data-table :header-props="{ class: 'custom-header' }" :headers="computedHeaders" :items="recentUsers" :loading="loading" hide-default-footer
class="elevation-1">
<v-data-table :header-props="{ class: 'custom-header' }" :headers="computedHeaders"
:items="recentUsers" :loading="loading" hide-default-footer class="elevation-1">
<!-- اسلات سفارشی برای ستون وضعیت -->
<template v-slot:item.active="{ item }">
<v-icon :color="item.active ? 'green' : 'red'" small>
@ -357,7 +357,7 @@ export default {
},
},
async mounted() {
try {
const res = await axios.post("/api/user/current/info");
this.user_email = res.data.email;
this.user_fullname = res.data.fullname;
@ -367,7 +367,7 @@ export default {
? `${window.location.origin + getBasePath()}ms/${this.referralCode}`
: "";
await this.fetchMarketingData();
try {} catch (error) {
} catch (error) {
this.errorMessage =
error.response?.data?.message || this.$t("dialog.error_unknown");
this.errorDialog = true;