bug fix in cost controller list
This commit is contained in:
parent
dd101c8ee2
commit
65d45e73d1
|
@ -205,7 +205,9 @@ class CostController extends AbstractController
|
||||||
|
|
||||||
// اعمال فیلترها
|
// اعمال فیلترها
|
||||||
if (!empty($filters)) {
|
if (!empty($filters)) {
|
||||||
|
// جستجوی متنی
|
||||||
if (isset($filters['search'])) {
|
if (isset($filters['search'])) {
|
||||||
|
$searchValue = is_array($filters['search']) ? $filters['search']['value'] : $filters['search'];
|
||||||
$queryBuilder->leftJoin('d.hesabdariRows', 'r')
|
$queryBuilder->leftJoin('d.hesabdariRows', 'r')
|
||||||
->leftJoin('r.person', 'p')
|
->leftJoin('r.person', 'p')
|
||||||
->leftJoin('r.ref', 't')
|
->leftJoin('r.ref', 't')
|
||||||
|
@ -219,18 +221,42 @@ class CostController extends AbstractController
|
||||||
't.name LIKE :search'
|
't.name LIKE :search'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
->setParameter('search', "%{$filters['search']}%");
|
->setParameter('search', "%{$searchValue}%");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($filters['dateFrom'])) {
|
// فیلتر زمانی
|
||||||
$queryBuilder->andWhere('d.date >= :dateFrom')
|
if (isset($filters['timeFilter'])) {
|
||||||
->setParameter('dateFrom', $filters['dateFrom']);
|
$today = $jdate->jdate('Y/m/d', time());
|
||||||
}
|
switch ($filters['timeFilter']) {
|
||||||
|
case 'today':
|
||||||
if (isset($filters['dateTo'])) {
|
$queryBuilder->andWhere('d.date = :today')
|
||||||
$queryBuilder->andWhere('d.date <= :dateTo')
|
->setParameter('today', $today);
|
||||||
|
break;
|
||||||
|
case 'week':
|
||||||
|
$weekStart = $jdate->jdate('Y/m/d', strtotime('-6 days'));
|
||||||
|
$queryBuilder->andWhere('d.date BETWEEN :weekStart AND :today')
|
||||||
|
->setParameter('weekStart', $weekStart)
|
||||||
|
->setParameter('today', $today);
|
||||||
|
break;
|
||||||
|
case 'month':
|
||||||
|
$monthStart = $jdate->jdate('Y/m/01', time());
|
||||||
|
$queryBuilder->andWhere('d.date BETWEEN :monthStart AND :today')
|
||||||
|
->setParameter('monthStart', $monthStart)
|
||||||
|
->setParameter('today', $today);
|
||||||
|
break;
|
||||||
|
case 'custom':
|
||||||
|
if (isset($filters['dateFrom']) && isset($filters['dateTo'])) {
|
||||||
|
$queryBuilder->andWhere('d.date BETWEEN :dateFrom AND :dateTo')
|
||||||
|
->setParameter('dateFrom', $filters['dateFrom'])
|
||||||
->setParameter('dateTo', $filters['dateTo']);
|
->setParameter('dateTo', $filters['dateTo']);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case 'all':
|
||||||
|
default:
|
||||||
|
// بدون فیلتر زمانی اضافه
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($filters['amount'])) {
|
if (isset($filters['amount'])) {
|
||||||
$queryBuilder->andWhere('d.amount = :amount')
|
$queryBuilder->andWhere('d.amount = :amount')
|
||||||
|
@ -239,7 +265,7 @@ class CostController extends AbstractController
|
||||||
}
|
}
|
||||||
|
|
||||||
// اعمال مرتبسازی
|
// اعمال مرتبسازی
|
||||||
$sortField = $sort['sortBy'] ?? 'id';
|
$sortField = is_array($sort['sortBy']) ? ($sort['sortBy']['key'] ?? 'id') : ($sort['sortBy'] ?? 'id');
|
||||||
$sortDirection = ($sort['sortDesc'] ?? true) ? 'DESC' : 'ASC';
|
$sortDirection = ($sort['sortDesc'] ?? true) ? 'DESC' : 'ASC';
|
||||||
$queryBuilder->orderBy("d.$sortField", $sortDirection);
|
$queryBuilder->orderBy("d.$sortField", $sortDirection);
|
||||||
|
|
||||||
|
@ -265,7 +291,7 @@ class CostController extends AbstractController
|
||||||
'code' => $doc['code'],
|
'code' => $doc['code'],
|
||||||
'des' => $doc['des'],
|
'des' => $doc['des'],
|
||||||
'amount' => $doc['amount'],
|
'amount' => $doc['amount'],
|
||||||
'submitter' => $doc['submitter']
|
'submitter' => $doc['submitter'],
|
||||||
];
|
];
|
||||||
|
|
||||||
// دریافت اطلاعات مرکز هزینه و مبلغ
|
// دریافت اطلاعات مرکز هزینه و مبلغ
|
||||||
|
@ -282,7 +308,7 @@ class CostController extends AbstractController
|
||||||
$item['costCenters'] = array_map(function ($detail) {
|
$item['costCenters'] = array_map(function ($detail) {
|
||||||
return [
|
return [
|
||||||
'name' => $detail['center_name'],
|
'name' => $detail['center_name'],
|
||||||
'amount' => (int) $detail['amount']
|
'amount' => (int) $detail['amount'],
|
||||||
];
|
];
|
||||||
}, $costDetails);
|
}, $costDetails);
|
||||||
|
|
||||||
|
@ -301,7 +327,7 @@ class CostController extends AbstractController
|
||||||
$item['person'] = $personInfo ? [
|
$item['person'] = $personInfo ? [
|
||||||
'id' => $personInfo['id'],
|
'id' => $personInfo['id'],
|
||||||
'nikename' => $personInfo['nikename'],
|
'nikename' => $personInfo['nikename'],
|
||||||
'code' => $personInfo['code']
|
'code' => $personInfo['code'],
|
||||||
] : null;
|
] : null;
|
||||||
|
|
||||||
$dataTemp[] = $item;
|
$dataTemp[] = $item;
|
||||||
|
@ -311,7 +337,7 @@ class CostController extends AbstractController
|
||||||
'items' => $dataTemp,
|
'items' => $dataTemp,
|
||||||
'total' => (int) $totalItems,
|
'total' => (int) $totalItems,
|
||||||
'page' => $page,
|
'page' => $page,
|
||||||
'limit' => $limit
|
'limit' => $limit,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
"@ckeditor/ckeditor5-image": "^36.0.1",
|
"@ckeditor/ckeditor5-image": "^36.0.1",
|
||||||
"@ckeditor/ckeditor5-vue": "^4.0.1",
|
"@ckeditor/ckeditor5-vue": "^4.0.1",
|
||||||
"@ckeditor/vite-plugin-ckeditor5": "^0.1.1",
|
"@ckeditor/vite-plugin-ckeditor5": "^0.1.1",
|
||||||
|
"@date-io/date-fns-jalali": "^3.2.0",
|
||||||
"@mdi/font": "^7.4.47",
|
"@mdi/font": "^7.4.47",
|
||||||
"@syncfusion/ej2-vue-dropdowns": "^21.2.5",
|
"@syncfusion/ej2-vue-dropdowns": "^21.2.5",
|
||||||
"@vuelidate/core": "^2.0.0",
|
"@vuelidate/core": "^2.0.0",
|
||||||
|
@ -23,8 +24,11 @@
|
||||||
"animate.css": "^4.1.1",
|
"animate.css": "^4.1.1",
|
||||||
"apexcharts": "^4.4.0",
|
"apexcharts": "^4.4.0",
|
||||||
"axios": "^1.2.3",
|
"axios": "^1.2.3",
|
||||||
|
"date-fns": "^4.1.0",
|
||||||
|
"date-fns-jalali": "^3.2.0-0",
|
||||||
"downloadjs": "^1.4.7",
|
"downloadjs": "^1.4.7",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
|
"jalali-moment": "^3.3.11",
|
||||||
"libphonenumber-js": "^1.10.44",
|
"libphonenumber-js": "^1.10.44",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"maska": "^3.0.4",
|
"maska": "^3.0.4",
|
||||||
|
|
|
@ -1,53 +1,95 @@
|
||||||
<script lang="ts">
|
<template>
|
||||||
import {ref, watch, computed, defineComponent} from 'vue'
|
<div>
|
||||||
export default defineComponent({
|
<v-text-field v-model="displayDate" :label="label" prepend-inner-icon="mdi-calendar" persistent-placeholder
|
||||||
name:'Hdatepicker',
|
class="v-date-input" :rules="rules" @input="updateDateFromInput" @click:prepend="togglePicker"></v-text-field>
|
||||||
inheritAttrs: false,
|
<date-picker v-model="displayDate" type="date" format="jYYYY/jMM/jDD" display-format="jYYYY/jMM/jDD"
|
||||||
data:()=>{
|
:min="minDatePersian" :max="maxDatePersian" custom-input=".v-date-input" :input-mode="false"
|
||||||
const self = this;
|
:editable="pickerActive" @close="pickerActive = false"></date-picker>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import axios from 'axios';
|
||||||
|
import moment from 'jalali-moment';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
default: 'تاریخ',
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
return {
|
return {
|
||||||
isMenuOpen: ref(false),
|
displayDate: '', // تاریخ به فرمت شمسی
|
||||||
selectedDate: ref(),
|
pickerActive: false, // کنترل باز شدن تقویم
|
||||||
output:''
|
minDatePersian: '', // تاریخ شروع سال مالی (شمسی برای پکیج)
|
||||||
}
|
maxDatePersian: '', // تاریخ پایان سال مالی (شمسی برای پکیج)
|
||||||
|
};
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
selectedDate(){
|
displayDate(newVal) {
|
||||||
this.isMenuOpen = false
|
if (newVal) {
|
||||||
this.output = this.selectedDate.toLocaleString(
|
this.$emit('input', newVal); // ارسال تاریخ شمسی به والد
|
||||||
localStorage.getItem('UI_LANG'),
|
} else {
|
||||||
{year: 'numeric', month: 'numeric', day: 'numeric'}
|
this.$emit('input', '');
|
||||||
)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup(props, ctx) {
|
value(newVal) {
|
||||||
|
if (newVal) {
|
||||||
|
this.displayDate = newVal;
|
||||||
|
} else {
|
||||||
|
this.displayDate = '';
|
||||||
|
}
|
||||||
},
|
},
|
||||||
})
|
},
|
||||||
|
async mounted() {
|
||||||
|
await this.fetchYearData();
|
||||||
|
if (!this.value && this.displayDate) {
|
||||||
|
this.$emit('input', this.displayDate);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async fetchYearData() {
|
||||||
|
|
||||||
|
axios.get('/api/year/get').then((response) => {
|
||||||
|
this.minDatePersian = response.data.start; // فرمت YYYY/MM/DD شمسی
|
||||||
|
this.maxDatePersian = response.data.end; // فرمت YYYY/MM/DD شمسی
|
||||||
|
this.displayDate = response.data.now; // تاریخ جاری شمسی
|
||||||
|
});
|
||||||
|
},
|
||||||
|
updateDateFromInput(value) {
|
||||||
|
// بررسی و اعتبارسنجی تاریخ وارد شده توسط کاربر
|
||||||
|
if (value && moment(value, 'YYYY/MM/DD', 'fa', true).isValid()) {
|
||||||
|
const parsedDate = moment(value, 'YYYY/MM/DD').locale('fa');
|
||||||
|
if (
|
||||||
|
parsedDate.isSameOrAfter(moment(this.minDatePersian, 'YYYY/MM/DD')) &&
|
||||||
|
parsedDate.isSameOrBefore(moment(this.maxDatePersian, 'YYYY/MM/DD'))
|
||||||
|
) {
|
||||||
|
this.displayDate = value;
|
||||||
|
} else {
|
||||||
|
this.displayDate = ''; // یا خطا نمایش بدید
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
togglePicker() {
|
||||||
|
this.pickerActive = !this.pickerActive; // تغییر وضعیت تقویم
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
|
||||||
<v-menu v-model="isMenuOpen" :close-on-content-click="false">
|
|
||||||
<template v-slot:activator="{ props }">
|
|
||||||
<v-text-field
|
|
||||||
v-model="output"
|
|
||||||
readonly
|
|
||||||
v-bind="props"
|
|
||||||
:value="output"
|
|
||||||
outlined
|
|
||||||
color="primary"
|
|
||||||
@input="(v: any) => $emit('input', v)"
|
|
||||||
></v-text-field>
|
|
||||||
</template>
|
|
||||||
<!-- !!! hide-actions prop too !!! -->
|
|
||||||
<v-date-picker color="indigo" v-model="selectedDate">
|
|
||||||
<template v-slot:header></template>
|
|
||||||
</v-date-picker>
|
|
||||||
|
|
||||||
</v-menu>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
/* مطمئن شدن که تقویم فقط با آیکون فعال بشه */
|
||||||
|
.v-date-input {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
|
@ -1,4 +1,3 @@
|
||||||
import moment from 'moment'
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -13,7 +13,7 @@ if(activeLanguageCode == null || activeLanguageCode == undefined){
|
||||||
activeLanguageCode='fa';
|
activeLanguageCode='fa';
|
||||||
}
|
}
|
||||||
const i18n = createI18n({
|
const i18n = createI18n({
|
||||||
legacy: false, // Vuetify does not support the legacy mode of vue-i18n
|
legacy: false,
|
||||||
locale: activeLanguageCode,
|
locale: activeLanguageCode,
|
||||||
fallbackLocale: activeLanguageCode,
|
fallbackLocale: activeLanguageCode,
|
||||||
messages,
|
messages,
|
||||||
|
|
|
@ -6,13 +6,13 @@ import "./registerServiceWorker";
|
||||||
import { vMaska } from "maska/vue"
|
import { vMaska } from "maska/vue"
|
||||||
import VueApexCharts from "vue3-apexcharts";
|
import VueApexCharts from "vue3-apexcharts";
|
||||||
import Uploader from 'vue-media-upload';
|
import Uploader from 'vue-media-upload';
|
||||||
|
import DateFnsJalaliAdapter from '@date-io/date-fns-jalali';
|
||||||
|
import faIR from 'date-fns-jalali/locale/fa-IR';
|
||||||
|
|
||||||
//pinia
|
//pinia
|
||||||
import { createPinia } from 'pinia'
|
import { createPinia } from 'pinia'
|
||||||
const pinia = createPinia();
|
const pinia = createPinia();
|
||||||
|
|
||||||
import { VDateInput } from 'vuetify/labs/VDateInput'
|
|
||||||
|
|
||||||
import CKEditor from '@ckeditor/ckeditor5-vue';
|
import CKEditor from '@ckeditor/ckeditor5-vue';
|
||||||
// Import translations for the Persian language.
|
// Import translations for the Persian language.
|
||||||
import '@ckeditor/ckeditor5-build-classic/build/translations/fa';
|
import '@ckeditor/ckeditor5-build-classic/build/translations/fa';
|
||||||
|
@ -124,6 +124,12 @@ const vuetify = createVuetify({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
date: {
|
||||||
|
adapter: DateFnsJalaliAdapter,
|
||||||
|
locale: {
|
||||||
|
fa:faIR // تنظیم زبان فارسی
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
@ -146,7 +152,6 @@ import 'vue-select/dist/vue-select.css';
|
||||||
app.component('v-cob', vSelect)
|
app.component('v-cob', vSelect)
|
||||||
import Hdatepicker from "@/components/forms/Hdatepicker.vue";
|
import Hdatepicker from "@/components/forms/Hdatepicker.vue";
|
||||||
import calendarLocalConfig from "@/i18n/calendarLocalConfig";
|
import calendarLocalConfig from "@/i18n/calendarLocalConfig";
|
||||||
|
|
||||||
app.component('h-date-picker', Hdatepicker);
|
app.component('h-date-picker', Hdatepicker);
|
||||||
app.use(CKEditor)
|
app.use(CKEditor)
|
||||||
app.use(Vue3PersianDatetimePicker, {
|
app.use(Vue3PersianDatetimePicker, {
|
||||||
|
|
|
@ -647,14 +647,12 @@ export default {
|
||||||
{{ $t('drawer.accounting_docs') }}
|
{{ $t('drawer.accounting_docs') }}
|
||||||
<span v-if="isCtrlShiftPressed" class="shortcut-key">{{ getShortcutKey('/acc/accounting/list') }}</span>
|
<span v-if="isCtrlShiftPressed" class="shortcut-key">{{ getShortcutKey('/acc/accounting/list') }}</span>
|
||||||
</v-list-item-title>
|
</v-list-item-title>
|
||||||
<template v-slot:append>
|
<template v-slot:append v-if="isPluginActive('accpro') && 1==2">
|
||||||
<!--
|
|
||||||
<v-tooltip :text="$t('dialog.add_new')" location="end">
|
<v-tooltip :text="$t('dialog.add_new')" location="end">
|
||||||
<template v-slot:activator="{ props }">
|
<template v-slot:activator="{ props }">
|
||||||
<v-btn v-bind="props" icon="mdi-plus-box" variant="plain" to="/acc/accounting/mod/" />
|
<v-btn v-bind="props" icon="mdi-plus-box" variant="plain" to="/acc/accounting/mod/" />
|
||||||
</template>
|
</template>
|
||||||
</v-tooltip>
|
</v-tooltip>
|
||||||
-->
|
|
||||||
</template>
|
</template>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
<v-list-item v-if="permissions.accounting" to="/acc/accounting/open_balance">
|
<v-list-item v-if="permissions.accounting" to="/acc/accounting/open_balance">
|
||||||
|
|
|
@ -3,11 +3,16 @@
|
||||||
<v-form @submit.prevent="submitForm">
|
<v-form @submit.prevent="submitForm">
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col cols="12" md="6">
|
<v-col cols="12" md="6">
|
||||||
<v-text-field
|
<Hdatepicker
|
||||||
v-model="form.date"
|
v-model="form.date"
|
||||||
label="تاریخ (شمسی)"
|
|
||||||
placeholder="1403/02/28"
|
|
||||||
:rules="[v => !!v || 'تاریخ الزامی است']"
|
:rules="[v => !!v || 'تاریخ الزامی است']"
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" md="6">
|
||||||
|
<v-text-field
|
||||||
|
v-model="form.des"
|
||||||
|
label="توضیحات سند"
|
||||||
|
placeholder="توضیحات مربوط به سند"
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
|
@ -114,8 +119,12 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
import moment from 'jalali-moment';
|
||||||
|
import Hdatepicker from '@/components/forms/Hdatepicker.vue';
|
||||||
export default {
|
export default {
|
||||||
|
components: {
|
||||||
|
Hdatepicker,
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
docId: {
|
docId: {
|
||||||
type: Number,
|
type: Number,
|
||||||
|
@ -125,7 +134,8 @@
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
form: {
|
form: {
|
||||||
date: '',
|
date: '', // تاریخ به فرمت ISO (مثلاً 2025-03-24)
|
||||||
|
des: '',
|
||||||
rows: [
|
rows: [
|
||||||
{ ref: null, refName: '', bd: '0', bs: '0', des: '', selectedAccounts: [] },
|
{ ref: null, refName: '', bd: '0', bs: '0', des: '', selectedAccounts: [] },
|
||||||
],
|
],
|
||||||
|
@ -162,7 +172,9 @@
|
||||||
async fetchDoc() {
|
async fetchDoc() {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(`/api/hesabdari/doc/${this.docId}`);
|
const response = await axios.get(`/api/hesabdari/doc/${this.docId}`);
|
||||||
this.form.date = response.data.data.date;
|
const serverDate = response.data.data.date; // فرض: تاریخ شمسی از سرور
|
||||||
|
this.form.date = moment(serverDate, 'YYYY/MM/DD').format('YYYY-MM-DD');
|
||||||
|
this.form.des = response.data.data.des || '';
|
||||||
this.form.rows = response.data.data.rows.map(row => ({
|
this.form.rows = response.data.data.rows.map(row => ({
|
||||||
ref: row.ref.id,
|
ref: row.ref.id,
|
||||||
refName: row.ref.name,
|
refName: row.ref.name,
|
||||||
|
@ -206,7 +218,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
date: this.form.date,
|
date: moment(this.form.date, 'YYYY-MM-DD').locale('fa').format('YYYY/MM/DD'), // ارسال به فرمت شمسی
|
||||||
|
des: this.form.des,
|
||||||
rows: this.form.rows.map(row => ({
|
rows: this.form.rows.map(row => ({
|
||||||
ref: row.ref,
|
ref: row.ref,
|
||||||
bd: row.bd,
|
bd: row.bd,
|
||||||
|
|
|
@ -3,36 +3,30 @@
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
<v-tooltip :text="$t('dialog.back')" location="bottom">
|
<v-tooltip :text="$t('dialog.back')" location="bottom">
|
||||||
<template v-slot:activator="{ props }">
|
<template v-slot:activator="{ props }">
|
||||||
<v-btn v-bind="props" @click="$router.back()" class="d-none d-sm-flex" variant="text" icon="mdi-arrow-right" />
|
<v-btn v-bind="props" @click="$router.back()" class="d-none d-sm-flex" variant="text"
|
||||||
|
icon="mdi-arrow-right" />
|
||||||
</template>
|
</template>
|
||||||
</v-tooltip>
|
</v-tooltip>
|
||||||
</template>
|
</template>
|
||||||
<v-spacer />
|
<v-spacer />
|
||||||
|
|
||||||
<v-slide-group show-arrows>
|
|
||||||
<v-slide-group-item>
|
|
||||||
<v-tooltip :text="$t('dialog.add_new')" location="bottom">
|
<v-tooltip :text="$t('dialog.add_new')" location="bottom">
|
||||||
<template v-slot:activator="{ props }">
|
<template v-slot:activator="{ props }">
|
||||||
<v-btn v-bind="props" icon="mdi-plus" color="primary" to="/acc/costs/mod/" />
|
<v-btn v-bind="props" icon="mdi-plus" color="primary" to="/acc/costs/mod/" />
|
||||||
</template>
|
</template>
|
||||||
</v-tooltip>
|
</v-tooltip>
|
||||||
</v-slide-group-item>
|
|
||||||
|
|
||||||
<v-slide-group-item>
|
|
||||||
<v-menu>
|
<v-menu>
|
||||||
<template v-slot:activator="{ props }">
|
<template v-slot:activator="{ props }">
|
||||||
<v-btn
|
<v-btn v-bind="props" icon="" color="red">
|
||||||
v-bind="props"
|
|
||||||
icon=""
|
|
||||||
color="red"
|
|
||||||
>
|
|
||||||
<v-tooltip activator="parent" :text="$t('dialog.export_pdf')" location="bottom" />
|
<v-tooltip activator="parent" :text="$t('dialog.export_pdf')" location="bottom" />
|
||||||
<v-icon icon="mdi-file-pdf-box" />
|
<v-icon icon="mdi-file-pdf-box" />
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</template>
|
</template>
|
||||||
<v-list>
|
<v-list>
|
||||||
<v-list-subheader color="primary">{{ $t('dialog.export_pdf') }}</v-list-subheader>
|
<v-list-subheader color="primary">{{ $t('dialog.export_pdf') }}</v-list-subheader>
|
||||||
<v-list-item :disabled="!hasSelected" class="text-dark" :title="$t('dialog.selected')" @click="exportPDF(false)">
|
<v-list-item :disabled="!hasSelected" class="text-dark" :title="$t('dialog.selected')"
|
||||||
|
@click="exportPDF(false)">
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
<v-icon color="green-darken-4" icon="mdi-check" />
|
<v-icon color="green-darken-4" icon="mdi-check" />
|
||||||
</template>
|
</template>
|
||||||
|
@ -44,23 +38,18 @@
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
</v-list>
|
</v-list>
|
||||||
</v-menu>
|
</v-menu>
|
||||||
</v-slide-group-item>
|
|
||||||
|
|
||||||
<v-slide-group-item>
|
|
||||||
<v-menu>
|
<v-menu>
|
||||||
<template v-slot:activator="{ props }">
|
<template v-slot:activator="{ props }">
|
||||||
<v-btn
|
<v-btn v-bind="props" icon="" color="green">
|
||||||
v-bind="props"
|
|
||||||
icon=""
|
|
||||||
color="green"
|
|
||||||
>
|
|
||||||
<v-tooltip activator="parent" :text="$t('dialog.export_excel')" location="bottom" />
|
<v-tooltip activator="parent" :text="$t('dialog.export_excel')" location="bottom" />
|
||||||
<v-icon icon="mdi-file-excel-box" />
|
<v-icon icon="mdi-file-excel-box" />
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</template>
|
</template>
|
||||||
<v-list>
|
<v-list>
|
||||||
<v-list-subheader color="primary">{{ $t('dialog.export_excel') }}</v-list-subheader>
|
<v-list-subheader color="primary">{{ $t('dialog.export_excel') }}</v-list-subheader>
|
||||||
<v-list-item :disabled="!hasSelected" class="text-dark" :title="$t('dialog.selected')" @click="exportExcel(false)">
|
<v-list-item :disabled="!hasSelected" class="text-dark" :title="$t('dialog.selected')"
|
||||||
|
@click="exportExcel(false)">
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
<v-icon color="green-darken-4" icon="mdi-check" />
|
<v-icon color="green-darken-4" icon="mdi-check" />
|
||||||
</template>
|
</template>
|
||||||
|
@ -72,35 +61,16 @@
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
</v-list>
|
</v-list>
|
||||||
</v-menu>
|
</v-menu>
|
||||||
</v-slide-group-item>
|
|
||||||
|
|
||||||
<v-slide-group-item>
|
|
||||||
<v-tooltip :text="$t('dialog.delete')" location="bottom">
|
<v-tooltip :text="$t('dialog.delete')" location="bottom">
|
||||||
<template v-slot:activator="{ props }">
|
<template v-slot:activator="{ props }">
|
||||||
<v-btn
|
<v-btn v-bind="props" icon="mdi-trash-can" color="danger" @click="deleteGroup" :disabled="!hasSelected" />
|
||||||
v-bind="props"
|
|
||||||
icon="mdi-trash-can"
|
|
||||||
color="danger"
|
|
||||||
@click="deleteGroup"
|
|
||||||
:disabled="!hasSelected"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
</v-tooltip>
|
</v-tooltip>
|
||||||
</v-slide-group-item>
|
|
||||||
</v-slide-group>
|
|
||||||
</v-toolbar>
|
</v-toolbar>
|
||||||
|
|
||||||
<v-text-field
|
<v-text-field :loading="loading" color="green" class="mb-0 pt-0 rounded-0" hide-details="auto" density="compact"
|
||||||
:loading="loading"
|
:placeholder="$t('dialog.search_txt')" v-model="searchQuery" type="text" clearable>
|
||||||
color="green"
|
|
||||||
class="mb-0 pt-0 rounded-0"
|
|
||||||
hide-details="auto"
|
|
||||||
density="compact"
|
|
||||||
:placeholder="$t('dialog.search_txt')"
|
|
||||||
v-model="searchQuery"
|
|
||||||
type="text"
|
|
||||||
@input="debouncedSearch"
|
|
||||||
>
|
|
||||||
<template v-slot:prepend-inner>
|
<template v-slot:prepend-inner>
|
||||||
<v-tooltip location="bottom" :text="$t('dialog.search')">
|
<v-tooltip location="bottom" :text="$t('dialog.search')">
|
||||||
<template v-slot:activator="{ props }">
|
<template v-slot:activator="{ props }">
|
||||||
|
@ -108,35 +78,40 @@
|
||||||
</template>
|
</template>
|
||||||
</v-tooltip>
|
</v-tooltip>
|
||||||
</template>
|
</template>
|
||||||
|
<template v-slot:append-inner>
|
||||||
|
<v-menu :close-on-content-click="false">
|
||||||
|
<template v-slot:activator="{ props }">
|
||||||
|
<v-icon v-bind="props" size="sm" color="primary">
|
||||||
|
<v-icon>mdi-filter</v-icon>
|
||||||
|
<v-tooltip activator="parent" :text="$t('dialog.filters')" location="bottom" />
|
||||||
|
</v-icon>
|
||||||
|
</template>
|
||||||
|
<v-list>
|
||||||
|
<v-list-subheader color="primary">
|
||||||
|
<v-icon>mdi-filter</v-icon>
|
||||||
|
{{ $t('dialog.filters') }}
|
||||||
|
</v-list-subheader>
|
||||||
|
<v-list-item v-for="(filter, index) in timeFilters" :key="index" class="text-dark">
|
||||||
|
<template v-slot:title>
|
||||||
|
<v-checkbox v-model="filter.checked" :label="filter.label" @change="applyTimeFilter(filter.value)"
|
||||||
|
hide-details />
|
||||||
|
</template>
|
||||||
|
</v-list-item>
|
||||||
|
</v-list>
|
||||||
|
</v-menu>
|
||||||
|
</template>
|
||||||
</v-text-field>
|
</v-text-field>
|
||||||
|
|
||||||
<v-data-table-server
|
<v-data-table-server :headers="headers" :items="items" :loading="loading" :items-length="totalItems"
|
||||||
:headers="headers"
|
v-model:options="serverOptions" @update:options="fetchData" item-value="code" class="elevation-1 data-table-wrapper"
|
||||||
:items="items"
|
:header-props="{ class: 'custom-header' }">
|
||||||
:loading="loading"
|
|
||||||
:items-length="totalItems"
|
|
||||||
v-model:options="serverOptions"
|
|
||||||
@update:options="fetchData"
|
|
||||||
item-value="code"
|
|
||||||
class="elevation-1 data-table-wrapper"
|
|
||||||
:header-props="{ class: 'custom-header' }"
|
|
||||||
>
|
|
||||||
<template #header.checkbox>
|
<template #header.checkbox>
|
||||||
<v-checkbox
|
<v-checkbox :model-value="isAllSelected" @change="toggleSelectAll" hide-details density="compact" />
|
||||||
:model-value="isAllSelected"
|
|
||||||
@change="toggleSelectAll"
|
|
||||||
hide-details
|
|
||||||
density="compact"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #item.checkbox="{ item }">
|
<template #item.checkbox="{ item }">
|
||||||
<v-checkbox
|
<v-checkbox :model-value="selectedItems.has(item.code)" @change="toggleSelection(item.code)" hide-details
|
||||||
:model-value="selectedItems.has(item.code)"
|
density="compact" />
|
||||||
@change="toggleSelection(item.code)"
|
|
||||||
hide-details
|
|
||||||
density="compact"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #item.operation="{ item }">
|
<template #item.operation="{ item }">
|
||||||
|
@ -163,10 +138,30 @@
|
||||||
</v-list>
|
</v-list>
|
||||||
</v-menu>
|
</v-menu>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #item.amount="{ item }">
|
<template #item.amount="{ item }">
|
||||||
{{ $filters.formatNumber(item.amount) }}
|
{{ $filters.formatNumber(item.amount) }}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template #item.costCenter="{ item }">
|
||||||
|
{{item.costCenters.map(center => center.name).join(', ') || '—'}}
|
||||||
|
</template>
|
||||||
</v-data-table-server>
|
</v-data-table-server>
|
||||||
|
|
||||||
|
<v-row class="mt-4 pa-4">
|
||||||
|
<v-col cols="6">
|
||||||
|
<v-card flat>
|
||||||
|
<v-card-title>جمع کل هزینهها</v-card-title>
|
||||||
|
<v-card-text>{{ $filters.formatNumber(totalCost) }}</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="6">
|
||||||
|
<v-card flat>
|
||||||
|
<v-card-title>جمع موارد انتخابشده</v-card-title>
|
||||||
|
<v-card-text>{{ $filters.formatNumber(selectedCost) }}</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
@ -175,8 +170,8 @@ import axios from 'axios';
|
||||||
import Swal from 'sweetalert2';
|
import Swal from 'sweetalert2';
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
import { getApiUrl } from '/src/hesabixConfig';
|
import { getApiUrl } from '/src/hesabixConfig';
|
||||||
|
import moment from 'jalali-moment';
|
||||||
|
|
||||||
// تنظیم پایه URL از hesabixConfig
|
|
||||||
const apiUrl = getApiUrl();
|
const apiUrl = getApiUrl();
|
||||||
axios.defaults.baseURL = apiUrl;
|
axios.defaults.baseURL = apiUrl;
|
||||||
|
|
||||||
|
@ -186,28 +181,26 @@ const items = ref([]);
|
||||||
const selectedItems = ref(new Set());
|
const selectedItems = ref(new Set());
|
||||||
const totalItems = ref(0);
|
const totalItems = ref(0);
|
||||||
const searchQuery = ref('');
|
const searchQuery = ref('');
|
||||||
|
const timeFilter = ref('all');
|
||||||
|
|
||||||
|
// فیلترهای زمانی (بدون بازه دلخواه)
|
||||||
|
const timeFilters = ref([
|
||||||
|
{ label: 'امروز', value: 'today', checked: false },
|
||||||
|
{ label: 'این هفته', value: 'week', checked: false },
|
||||||
|
{ label: 'این ماه', value: 'month', checked: false },
|
||||||
|
{ label: 'همه', value: 'all', checked: true },
|
||||||
|
]);
|
||||||
|
|
||||||
// تعریف ستونهای جدول
|
// تعریف ستونهای جدول
|
||||||
const headers = ref([
|
const headers = ref([
|
||||||
{
|
{ title: '', key: 'checkbox', sortable: false, width: '50', align: 'center' },
|
||||||
title: '',
|
{ title: 'ردیف', key: 'index', align: 'center', sortable: false, width: '70' },
|
||||||
key: 'checkbox',
|
|
||||||
sortable: false,
|
|
||||||
width: '50',
|
|
||||||
align: 'center'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'ردیف',
|
|
||||||
key: 'index',
|
|
||||||
align: 'center',
|
|
||||||
sortable: false,
|
|
||||||
width: '70'
|
|
||||||
},
|
|
||||||
{ title: 'عملیات', key: 'operation', align: 'center', sortable: false, width: '100' },
|
{ title: 'عملیات', key: 'operation', align: 'center', sortable: false, width: '100' },
|
||||||
{ title: 'کد', key: 'code', align: 'center', sortable: true },
|
{ title: 'کد', key: 'code', align: 'center', sortable: true },
|
||||||
|
{ title: 'مرکز هزینه', key: 'costCenter', align: 'center', sortable: false },
|
||||||
|
{ title: 'مبلغ', key: 'amount', align: 'center', sortable: true },
|
||||||
{ title: 'تاریخ', key: 'date', align: 'center', sortable: true },
|
{ title: 'تاریخ', key: 'date', align: 'center', sortable: true },
|
||||||
{ title: 'شرح', key: 'des', align: 'center', sortable: true },
|
{ title: 'شرح', key: 'des', align: 'center', sortable: true },
|
||||||
{ title: 'مبلغ', key: 'amount', align: 'center', sortable: true },
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// تنظیمات سرور
|
// تنظیمات سرور
|
||||||
|
@ -218,10 +211,20 @@ const serverOptions = ref({
|
||||||
sortDesc: [],
|
sortDesc: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
// اضافه کردن computed property برای کنترل وضعیت دکمههای عملیات
|
// Computed properties
|
||||||
const hasSelected = computed(() => selectedItems.value.size > 0);
|
const hasSelected = computed(() => selectedItems.value.size > 0);
|
||||||
const isAllSelected = computed(() => selectedItems.value.size === items.value.length);
|
const isAllSelected = computed(() => selectedItems.value.size === items.value.length);
|
||||||
|
|
||||||
|
const totalCost = computed(() => {
|
||||||
|
return items.value.reduce((sum, item) => sum + Number(item.amount || 0), 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
const selectedCost = computed(() => {
|
||||||
|
return items.value
|
||||||
|
.filter((item) => selectedItems.value.has(item.code))
|
||||||
|
.reduce((sum, item) => sum + Number(item.amount || 0), 0);
|
||||||
|
});
|
||||||
|
|
||||||
// فچ کردن دادهها از سرور
|
// فچ کردن دادهها از سرور
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
try {
|
try {
|
||||||
|
@ -231,9 +234,33 @@ const fetchData = async () => {
|
||||||
if (searchQuery.value.trim()) {
|
if (searchQuery.value.trim()) {
|
||||||
filters.search = { value: searchQuery.value.trim() };
|
filters.search = { value: searchQuery.value.trim() };
|
||||||
}
|
}
|
||||||
|
if (timeFilter.value) {
|
||||||
|
filters.timeFilter = timeFilter.value;
|
||||||
|
|
||||||
const sortBy = serverOptions.value.sortBy?.[0] || 'code';
|
const today = moment().locale('fa').format('YYYY/MM/DD');
|
||||||
const sortDesc = serverOptions.value.sortDesc?.[0] ?? true;
|
switch (timeFilter.value) {
|
||||||
|
case 'today':
|
||||||
|
filters.dateFrom = today;
|
||||||
|
filters.dateTo = today;
|
||||||
|
break;
|
||||||
|
case 'week':
|
||||||
|
filters.dateFrom = moment().locale('fa').subtract(6, 'days').format('YYYY/MM/DD');
|
||||||
|
filters.dateTo = today;
|
||||||
|
break;
|
||||||
|
case 'month':
|
||||||
|
filters.dateFrom = moment().locale('fa').startOf('jMonth').format('YYYY/MM/DD');
|
||||||
|
filters.dateTo = today;
|
||||||
|
break;
|
||||||
|
case 'all':
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const sortByArray = Array.isArray(serverOptions.value.sortBy) ? serverOptions.value.sortBy : [];
|
||||||
|
const sortDescArray = Array.isArray(serverOptions.value.sortDesc) ? serverOptions.value.sortDesc : [];
|
||||||
|
const sortBy = sortByArray.length > 0 ? sortByArray[0].key : 'code';
|
||||||
|
const sortDesc = sortDescArray.length > 0 ? sortDescArray[0] : true;
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
filters,
|
filters,
|
||||||
|
@ -249,22 +276,20 @@ const fetchData = async () => {
|
||||||
|
|
||||||
const response = await axios.post('/api/cost/list/search', {
|
const response = await axios.post('/api/cost/list/search', {
|
||||||
type: 'cost',
|
type: 'cost',
|
||||||
...payload
|
...payload,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.data?.items) {
|
if (response.data?.items) {
|
||||||
// اضافه کردن شماره ردیف به هر آیتم
|
|
||||||
const startIndex = (serverOptions.value.page - 1) * serverOptions.value.itemsPerPage;
|
const startIndex = (serverOptions.value.page - 1) * serverOptions.value.itemsPerPage;
|
||||||
items.value = response.data.items.map((item, index) => ({
|
items.value = response.data.items.map((item, index) => ({
|
||||||
...item,
|
...item,
|
||||||
index: startIndex + index + 1
|
index: startIndex + index + 1,
|
||||||
}));
|
}));
|
||||||
totalItems.value = response.data.total; // استفاده از total از پاسخ سرور
|
totalItems.value = response.data.total;
|
||||||
} else {
|
} else {
|
||||||
items.value = [];
|
items.value = [];
|
||||||
totalItems.value = 0;
|
totalItems.value = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching data:', error);
|
console.error('Error fetching data:', error);
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
|
@ -280,6 +305,15 @@ const fetchData = async () => {
|
||||||
// دیبونس برای جستجو
|
// دیبونس برای جستجو
|
||||||
const debouncedSearch = debounce(() => fetchData(), 500);
|
const debouncedSearch = debounce(() => fetchData(), 500);
|
||||||
|
|
||||||
|
// اعمال فیلتر زمانی
|
||||||
|
const applyTimeFilter = (value) => {
|
||||||
|
timeFilters.value.forEach((filter) => {
|
||||||
|
filter.checked = filter.value === value;
|
||||||
|
});
|
||||||
|
timeFilter.value = value;
|
||||||
|
fetchData();
|
||||||
|
};
|
||||||
|
|
||||||
// حذف یک آیتم
|
// حذف یک آیتم
|
||||||
const deleteItem = async (code) => {
|
const deleteItem = async (code) => {
|
||||||
const result = await Swal.fire({
|
const result = await Swal.fire({
|
||||||
|
@ -315,11 +349,8 @@ const deleteItem = async (code) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// تابع toggleSelection را به این صورت تغییر میدهیم
|
// انتخاب و لغو انتخاب
|
||||||
const toggleSelection = (code) => {
|
const toggleSelection = (code) => {
|
||||||
const item = items.value.find(i => i.code === code);
|
|
||||||
if (!item) return;
|
|
||||||
|
|
||||||
if (selectedItems.value.has(code)) {
|
if (selectedItems.value.has(code)) {
|
||||||
selectedItems.value.delete(code);
|
selectedItems.value.delete(code);
|
||||||
} else {
|
} else {
|
||||||
|
@ -327,18 +358,15 @@ const toggleSelection = (code) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// تابع toggleSelectAll را به این صورت تغییر میدهیم
|
|
||||||
const toggleSelectAll = () => {
|
const toggleSelectAll = () => {
|
||||||
if (selectedItems.value.size === items.value.length) {
|
if (selectedItems.value.size === items.value.length) {
|
||||||
selectedItems.value.clear();
|
selectedItems.value.clear();
|
||||||
} else {
|
} else {
|
||||||
items.value.forEach(item => {
|
items.value.forEach((item) => selectedItems.value.add(item.code));
|
||||||
selectedItems.value.add(item.code);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// تغییر توابع export
|
// خروجی PDF
|
||||||
const exportPDF = async (all = false) => {
|
const exportPDF = async (all = false) => {
|
||||||
try {
|
try {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
|
@ -351,10 +379,9 @@ const exportPDF = async (all = false) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ایجاد آرایهای از آیتمهای انتخاب شده
|
|
||||||
const selectedItemsArray = all
|
const selectedItemsArray = all
|
||||||
? items.value
|
? items.value
|
||||||
: items.value.filter(item => selectedItems.value.has(item.code));
|
: items.value.filter((item) => selectedItems.value.has(item.code));
|
||||||
|
|
||||||
const payload = all ? { all: true } : { items: selectedItemsArray };
|
const payload = all ? { all: true } : { items: selectedItemsArray };
|
||||||
const response = await axios.post('/api/costs/list/print', payload);
|
const response = await axios.post('/api/costs/list/print', payload);
|
||||||
|
@ -372,6 +399,7 @@ const exportPDF = async (all = false) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// خروجی Excel
|
||||||
const exportExcel = async (all = false) => {
|
const exportExcel = async (all = false) => {
|
||||||
try {
|
try {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
|
@ -384,19 +412,20 @@ const exportExcel = async (all = false) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ایجاد آرایهای از آیتمهای انتخاب شده
|
|
||||||
const selectedItemsArray = all
|
const selectedItemsArray = all
|
||||||
? items.value
|
? items.value
|
||||||
: items.value.filter(item => selectedItems.value.has(item.code));
|
: items.value.filter((item) => selectedItems.value.has(item.code));
|
||||||
|
|
||||||
const payload = all ? { all: true } : { items: selectedItemsArray };
|
const payload = all ? { all: true } : { items: selectedItemsArray };
|
||||||
const response = await axios.post('/api/costs/list/excel', payload, {
|
const response = await axios.post('/api/costs/list/excel', payload, {
|
||||||
responseType: 'blob'
|
responseType: 'blob',
|
||||||
});
|
});
|
||||||
|
|
||||||
const url = window.URL.createObjectURL(new Blob([response.data], {
|
const url = window.URL.createObjectURL(
|
||||||
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
new Blob([response.data], {
|
||||||
}));
|
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||||
|
})
|
||||||
|
);
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
link.href = url;
|
link.href = url;
|
||||||
link.setAttribute('download', 'costs.xlsx');
|
link.setAttribute('download', 'costs.xlsx');
|
||||||
|
@ -416,7 +445,7 @@ const exportExcel = async (all = false) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// تغییر تابع deleteGroup
|
// حذف گروهی
|
||||||
const deleteGroup = async () => {
|
const deleteGroup = async () => {
|
||||||
if (selectedItems.value.size === 0) {
|
if (selectedItems.value.size === 0) {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
|
@ -439,7 +468,7 @@ const deleteGroup = async () => {
|
||||||
try {
|
try {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const selectedCodes = Array.from(selectedItems.value);
|
const selectedCodes = Array.from(selectedItems.value);
|
||||||
const promises = selectedCodes.map(code =>
|
const promises = selectedCodes.map((code) =>
|
||||||
axios.post('/api/accounting/remove', { code })
|
axios.post('/api/accounting/remove', { code })
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -466,10 +495,11 @@ const deleteGroup = async () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// اضافه کردن watch برای پاک کردن انتخابها هنگام تغییر صفحه
|
// Watchers
|
||||||
watch(() => serverOptions.value.page, () => {
|
watch(() => serverOptions.value.page, () => {
|
||||||
selectedItems.value.clear();
|
selectedItems.value.clear();
|
||||||
});
|
});
|
||||||
|
watch(searchQuery, () => debouncedSearch());
|
||||||
|
|
||||||
// OnMounted
|
// OnMounted
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|
Loading…
Reference in a new issue