hesabixCore/webUI/src/components/common/ApprovalManager.vue

269 lines
6.9 KiB
Vue
Raw Normal View History

2025-08-18 22:05:33 +03:30
<template>
<div class="approval-manager">
<!-- دکمه تأیید -->
<v-btn
v-if="showApprovalButton"
:color="approvalButtonColor"
size="small"
variant="outlined"
@click="showApprovalDialog = true"
:loading="processing"
:disabled="processing"
>
<v-icon size="small" class="me-1">
{{ approvalButtonIcon }}
</v-icon>
{{ approvalButtonText }}
</v-btn>
<!-- دکمه رد -->
<v-btn
v-if="showRejectButton"
color="error"
size="small"
variant="outlined"
@click="showRejectDialog = true"
:loading="processing"
:disabled="processing"
class="ms-2"
>
<v-icon size="small" class="me-1">mdi-close</v-icon>
رد سند
</v-btn>
<!-- دیالوگ تأیید -->
<v-dialog v-model="showApprovalDialog" max-width="500">
<v-card>
<v-card-title class="text-h6">
<v-icon color="success" class="me-2">mdi-check-circle</v-icon>
تأیید سند
</v-card-title>
<v-card-text>
<p>آیا از تأیید این سند اطمینان دارید؟</p>
<div class="mt-3">
<strong>نوع سند:</strong> {{ documentTypeText }}
</div>
<div class="mt-1">
<strong>شماره سند:</strong> {{ documentNumber }}
</div>
<div class="mt-1">
<strong>مبلغ:</strong> {{ formatCurrency(document.amount || 0) }}
</div>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="grey" @click="showApprovalDialog = false">انصراف</v-btn>
<v-btn
color="success"
@click="handleApproval"
:loading="processing"
>
تأیید
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- دیالوگ رد -->
<v-dialog v-model="showRejectDialog" max-width="500">
<v-card>
<v-card-title class="text-h6">
<v-icon color="error" class="me-2">mdi-close-circle</v-icon>
رد سند
</v-card-title>
<v-card-text>
<p>لطفاً دلیل رد این سند را وارد کنید:</p>
<v-textarea
v-model="rejectionReason"
label="دلیل رد"
variant="outlined"
rows="3"
:rules="[v => !!v || 'دلیل رد الزامی است']"
required
></v-textarea>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="grey" @click="showRejectDialog = false">انصراف</v-btn>
<v-btn
color="error"
@click="handleRejection"
:loading="processing"
:disabled="!rejectionReason"
>
رد سند
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- Snackbar برای نمایش پیامها -->
<v-snackbar
v-model="showSnackbar"
:color="snackbarColor"
:timeout="3000"
location="bottom"
>
{{ snackbarText }}
<template v-slot:actions>
<v-btn icon variant="text" @click="showSnackbar = false">
<v-icon>mdi-close</v-icon>
</v-btn>
</template>
</v-snackbar>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
import {
shouldShowApprovalButton,
getApprovalButtonText,
getDocumentType
} from '@/utils/approvalUtils'
const props = defineProps({
document: {
type: Object,
required: true
},
businessSettings: {
type: Object,
required: true
},
currentUserEmail: {
type: String,
required: true
},
isBusinessOwner: {
type: Boolean,
default: false
}
})
const emit = defineEmits(['approve', 'reject'])
const showApprovalDialog = ref(false)
const showRejectDialog = ref(false)
const rejectionReason = ref('')
const processing = ref(false)
const showSnackbar = ref(false)
const snackbarText = ref('')
const snackbarColor = ref('success')
// محاسبه نمایش دکمه‌ها
const showApprovalButton = computed(() => {
return shouldShowApprovalButton(
props.businessSettings,
props.document,
props.currentUserEmail,
props.isBusinessOwner
)
})
const showRejectButton = computed(() => {
// فقط مدیر کسب و کار می‌تواند سند را رد کند
return props.isBusinessOwner && props.document.approved !== false
})
// محاسبه متن و رنگ دکمه تأیید
const approvalButtonText = computed(() => getApprovalButtonText(props.document))
const approvalButtonColor = computed(() => {
if (props.document.approved === false) return 'warning'
return 'success'
})
const approvalButtonIcon = computed(() => {
if (props.document.approved === false) return 'mdi-refresh'
return 'mdi-check'
})
// محاسبه نوع سند
const documentType = computed(() => getDocumentType(props.document))
const documentTypeText = computed(() => {
switch (documentType.value) {
case 'invoice':
return 'فاکتور فروش'
case 'warehouse':
return 'حواله انبار'
case 'financial':
return 'سند مالی'
default:
return 'سند'
}
})
const documentNumber = computed(() => {
return props.document.invoiceNumber ||
props.document.warehouseNumber ||
props.document.documentNumber ||
props.document.id ||
'نامشخص'
})
// مدیریت تأیید
const handleApproval = async () => {
try {
processing.value = true
showApprovalDialog.value = false
await emit('approve', props.document)
showSnackbarText('سند با موفقیت تأیید شد', 'success')
} catch (error) {
console.error('خطا در تأیید سند:', error)
showSnackbarText('خطا در تأیید سند', 'error')
} finally {
processing.value = false
}
}
// مدیریت رد
const handleRejection = async () => {
try {
processing.value = true
showRejectDialog.value = false
await emit('reject', {
document: props.document,
reason: rejectionReason.value
})
rejectionReason.value = ''
showSnackbarText('سند با موفقیت رد شد', 'success')
} catch (error) {
console.error('خطا در رد سند:', error)
showSnackbarText('خطا در رد سند', 'error')
} finally {
processing.value = false
}
}
// نمایش پیام
const showSnackbarText = (text, color = 'success') => {
snackbarText.value = text
snackbarColor.value = color
showSnackbar.value = true
}
// فرمت مبلغ
const formatCurrency = (amount) => {
return new Intl.NumberFormat('fa-IR').format(amount) + ' ریال'
}
</script>
<style scoped>
.approval-manager {
display: flex;
align-items: center;
gap: 8px;
}
.approval-manager .v-btn {
text-transform: none;
}
</style>