Fix document upload

This commit is contained in:
Vova 2025-12-12 23:56:50 +03:00
parent 5e591eaea9
commit c3259b7e6c
11 changed files with 399 additions and 322 deletions

View File

@ -2,7 +2,7 @@ NODE_ENV=development
VITE_API_URL=https://joyworkdev.ru
VITE_LARAVEL_URL=https://llar.joyworkdev.ru
VITE_LARAVEL_API_URL=https://joyworkdev.ru
VITE_DEV_SESSION_ID=e140a9d56a24f47046b9ab0953e5a9ee
VITE_DEV_SESSION_ID=87ae8a661f903a5958e2a36aeaffb970
VITE_DEV_REQUISITION_ID=17539
#VITE_BASIC_AUTH_USER=adminlogin
#VITE_BASIC_AUTH_PASS=adminpassword

View File

@ -1,7 +1,8 @@
<template>
<div>
<h1>Сделка</h1>
<a href="#" @click.prevent="createDeal(17539)">Открыть тестовую сделку</a>
<div><a href="#" @click.prevent="createDeal(17539)">Создать новую сделку</a></div>
<div><a href="#" @click.prevent="editDeal(17539, 46)">Открыть тестовую сделку</a></div>
</div>
<DynamicDialog />
</template>
@ -36,11 +37,30 @@ const createDeal = (newDealReqId) => {
})
}
const editDeal = (id) => {
isOpen.value = true
dealId.value = id
newDealReqId.value = null
const editDeal = (newDealReqId, dealId) => {
console.log('[editDeal] Открываем DealModal через DynamicDialog', dealId)
dialog.open(DealModal, {
props: {
header: 'Редактирование сделки',
style: { width: '80vw' },
modal: true
},
data: {
newDealReqId: newDealReqId,
deal_id: dealId
},
onClose: (opt) => {
console.log('[DynamicDialog] Закрыто с данными:', opt?.data)
}
})
}
// const editDeal = (id) => {
// isOpen.value = true
// dealId.value = id
// newDealReqId.value = null
// }
const backToStep = () => {
isOpen.value = false

View File

@ -294,10 +294,10 @@
</div>
<div class="w-1/2">
<div class="form__item flex flex-col gap-1 w-full mb-3">
<label for="deal_sellers_count">Количество покупателей:</label>
<label for="deal_buyers_count">Количество покупателей:</label>
<InputNumber
v-model="deal.buyers_count"
id="deal_sellers_count"
id="deal_buyers_count"
/>
</div>
<div class="form__item flex flex-col gap-1 w-full mb-3">
@ -360,8 +360,14 @@
<h3>Документы</h3>
<div class="flex w-full flex-wrap">
<DocAddModal v-if="showModalDealDoc" :deal="deal" @close="closeModalDealDoc" @select="addDoc" />
<DocsCard v-for="doc in selectedDocs" :key="doc.id" :doc="doc" :urlLaravel="urlLaravel" @remove="removeDoc" />
<!-- <DocAddModal v-if="showModalDealDoc" :deal="deal" @close="closeModalDealDoc" @select="addDoc" />-->
<DocsCard
v-for="doc in selectedDocs"
:key="doc.id"
:doc="doc"
:urlLaravel="urlLaravel"
@remove="removeDoc"
/>
<div class="w-full sm:w-1/2 md:w-1/3 lg:w-1/4 p-2">
<button @click="openModalDealDoc" class="w-full h-full p-2 rounded flex flex-col items-center justify-center add-deal-doc-button">
<img src="@/shared/assets/icons/plus-green.svg" alt="Добавить документ" class="w-6 h-6">
@ -373,7 +379,6 @@
<h3>Составить договор</h3>
<div class="flex w-full flex-wrap">
<InnerDocAddModal v-if="showModalDealContract" :deal="deal" @close="closeModalDealContract" @created="handleNewInnerDoc" />
<button @click="openModalDealContract" class="w-full sm:w-1/2 md:w-1/3 lg:w-1/4 p-2 rounded items-center justify-center order-last add-deal-contract-button">Составить договор</button>
</div>
<div class="flex w-full flex-wrap">
@ -381,10 +386,6 @@
v-for="innerDoc in deal.inner_docs"
:key="innerDoc.id"
:innerDoc="innerDoc"
:deal-clients="{
side_one_clients: deal.side_one_clients,
side_two_clients: deal.side_two_clients
}"
@remove="removeInnerDoc"
class="w-full sm:w-1/2 md:w-1/3 lg:w-1/4 p-2"
/>
@ -409,12 +410,11 @@
<script setup>
import MyButton from "@/shared/UI/MyButton.vue";
import {ref, onMounted, watch, inject} from "vue";
import {ref, onMounted, watch, inject, toRaw, markRaw} from "vue";
import { useLaravelApi } from '@/composables/useLaravelApi';
import { useRouter } from "vue-router";
//import useDeal from '@/composables/useDeal';
import { useConfirm } from "primevue/useconfirm";
import { useToast } from "primevue/usetoast";
import { useDialog } from 'primevue/usedialog';
import ConfirmDialog from 'primevue/confirmdialog';
import Toast from "primevue/toast";
import InputText from "primevue/inputtext";
@ -444,11 +444,11 @@ const showError = (message) => {
const props = defineProps({
dealId: Number,
isOpen: Boolean,
onCloseModel: Function,
onDealAdded: Function
isOpen: Boolean
});
const dialog = useDialog();
const dialogRef = inject('dialogRef');
const newDealReqId = dialogRef.value.data?.newDealReqId;
@ -649,7 +649,7 @@ async function loadDealData(dealId) {
} catch (error) {
console.error('Ошибка загрузки сделки:', error);
showError(error.message);
showError(String(error?.message ?? 'Неизвестная ошибка'));
}
}
@ -692,7 +692,6 @@ function resetForm() {
const url = import.meta.env.VITE_API_URL;
const urlLaravel = import.meta.env.VITE_LARAVEL_URL
const titleForm = ref("Данные для сделки");
const deal = ref({
title: '',
side_one: '',
@ -805,6 +804,10 @@ const validateForm = () => {
};
onMounted(async () => {
console.log('[DealModal] dialogRef.data:', dialogRef.value.data)
dealId = dialogRef.value.data?.deal_id
try {
// Загрузка справочников
const [objects, clients, employees, requisitions] = await Promise.all([
@ -824,16 +827,12 @@ onMounted(async () => {
})) || [];
requisitionsList.value = reqList;
originalRequisitionsList.value = reqList; // Сохраняем полный список
originalRequisitionsList.value = reqList;
} catch (error) {
console.error('Ошибка загрузки:', error);
showError('Не удалось загрузить данные');
}
// if (import.meta.env.MODE === 'development') {
// loadRequisition(import.meta.env.VITE_DEV_REQUISITION_ID, 'sideOne');
// }
})
@ -849,7 +848,7 @@ watch(selectedRequisitionSideOneId, async (newId) => {
// Добавляем who_work только при создании сделки
if (newDealReqId.value && requisitionSideOneDetails.value.who_work) {
addResponsibleEmployee(requisitionSideOneDetails.value.who_work);
await addResponsibleEmployee(requisitionSideOneDetails.value.who_work);
}
}
@ -914,6 +913,7 @@ watch(selectedObjectSideOneId, async (newId) => {
selectedObjectSideOne.value = null; // Очищаем, если newId === null
}
});
// Получение деталей выбранного объекта 2 стороны
watch(selectedObjectSideTwoId, async (newId) => {
if (newId) {
@ -929,6 +929,7 @@ watch(selectedObjectSideTwoId, async (newId) => {
// Логика для управления модальным окном и выбранными сотрудниками
const showModalDealManager = ref(false);
const selectedEmployees = ref([]);
const openModalDealManager = () => {
@ -948,31 +949,53 @@ const removeEmployee = (employeeId) => {
selectedEmployees.value = selectedEmployees.value.filter(e => e.id !== employeeId);
};
const showModalDealDoc = ref(false);
const selectedDocs = ref([]);
// Открыть/закрыть модальное окно выбора документа
const openModalDealDoc = () => {
showModalDealDoc.value = true;
};
console.log('[openModalDealDoc] Открываем DocAddModal через DynamicDialog')
dialog.open(DocAddModal, {
props: {
header: 'Добавление документа',
style: { width: '50vw' },
modal: true
},
data: {
deal: deal.value
},
onClose: (opt) => {
console.log('[DynamicDialog] Закрыто с данными:', opt?.data)
if (opt?.data) {
console.log('opt', opt?.data)
addDoc(opt.data);
//selectedDocs.value.push(opt.data)
}
}
})
}
const closeModalDealDoc = () => {
showModalDealDoc.value = false;
};
const addDoc = (doc) => {
console.log('[DealModal] addDoc вызван с:', doc);
// Проверка, что doc объект и имеет id и name
if (!doc || typeof doc !== 'object' || !doc.id || !doc.name) {
console.error('[DealModal] addDoc: получен невалидный doc:', doc);
return;
}
// Проверяем, что объект документа содержит обязательные поля
if (doc && doc.id && doc.name && doc.file) {
// Проверяем, что документ еще не добавлен
const isDuplicate = selectedDocs.value.some((d) => d.id === doc.id);
if (!isDuplicate) {
selectedDocs.value.push(doc); // Добавляем документ в список
console.log('[DealModal] Документ добавлен в selectedDocs');
}
} else {
console.error('Документ не содержит обязательных полей:', doc);
}
closeModalDealDoc(); // Закрываем модальное окно
//closeModalDealDoc(); // Закрываем модальное окно
};
// Удалить документ
@ -1015,6 +1038,98 @@ const handleCloseDealObject = (side) => {
};
const openModalDealContract = async () => {
if (!deal.value.id) {
console.log('[DealModal] Сделка не сохранена, сохраняем перед открытием модального окна договора.');
try {
let currentDeal = deal.value
const savedDeal = await saveDeal();
if (savedDeal?.id) {
if (!savedDeal?.id) return
deal.value = savedDeal
currentDeal = savedDeal
const plainDeal = JSON.parse(JSON.stringify(toRaw(currentDeal)))
console.log('[DealModal] Сделка успешно сохранена, ID:', deal.value);
console.log('InnerDocAddModal:', InnerDocAddModal);
dialog.open(InnerDocAddModal, {
props: {
header: 'Составить договор',
modal: true,
},
data: {
deal: plainDeal,
},
onClose: (options) => {
console.log('InnerDocAddModal закрыт, options:', options)
if (options?.data) {
handleNewInnerDoc(options.data);
}
},
});
} else {
console.error('[DealModal] Сделка не была сохранена (id отсутствует после saveDeal)');
}
} catch (error) {
console.error('[DealModal] Ошибка при сохранении сделки перед составлением договора:', error);
}
} else {
console.log('[DealModal] Сделка уже сохранена (ID:', deal.value.id, '), открываем модальное окно.');
dialog.open(InnerDocAddModal, {
props: {
header: 'Составить договор',
modal: true,
},
data: {
deal: deal.value,
},
onClose: (options) => {
console.log('InnerDocAddModal закрыт, options:', options);
if (options?.data) {
handleNewInnerDoc(options.data);
}
},
});
}
};
// Обработка нового договора
const handleNewInnerDoc = (newInnerDoc) => {
console.log('Новый innerDoc получен:', newInnerDoc);
if (!newInnerDoc || typeof newInnerDoc !== 'object' || !newInnerDoc.id || !newInnerDoc.name) {
console.error('[DealModal] handleNewInnerDoc: получен невалидный newInnerDoc:', newInnerDoc);
return;
}
if (!deal.value.inner_docs) {
console.warn('Массив inner_docs не существует в deal. Создаём новый массив.');
deal.value.inner_docs = [];
}
console.log('Добавляем новый договор в deal.inner_docs');
deal.value.inner_docs.push(newInnerDoc);
console.log('Обновлённый список inner_docs:', deal.value.inner_docs);
};
// Удаление документа
const { fetchData: deleteData } = useLaravelApi();
const removeInnerDoc = async (docId) => {
console.log('[DealModal] deleteInnerDoc:', docId);
try {
await deleteData(`/inner-docs/${docId}`, {
method: 'DELETE'
});
// Удаляем документ из локального состояния
deal.value.inner_docs = deal.value.inner_docs.filter(doc => doc.id !== docId);
} catch (error) {
console.error('Ошибка при удалении документа:', error);
throw error;
}
};
// Сохранение сделки
const saveDeal = async () => {
@ -1026,7 +1141,7 @@ const saveDeal = async () => {
detail: 'Заполните все обязательные поля',
life: 3000,
});
return;
return null;
}
const { fetchData } = useLaravelApi();
@ -1060,14 +1175,16 @@ const saveDeal = async () => {
toast.add({
severity: 'success',
summary: 'Успех',
detail: isEditing.value ? 'Сделка успешно обновлена' : 'Сделка успешно создана',
detail: String(response.data.message || (isEditing.value ? 'Сделка успешно обновлена' : 'Сделка успешно создана')),
life: 3000,
});
// Вызываем onDealAdded при успешном сохранении
if (props.onDealAdded) {
props.onDealAdded();
}
// Обновляем локальное состояние сделки
// if (response.data.deal) {
// deal.value = response.data.deal;
// }
console.log('cat', response.data)
return response.data.deal;
} else {
new Error(response.data.message || 'Ошибка при сохранении сделки');
@ -1076,53 +1193,10 @@ const saveDeal = async () => {
toast.add({
severity: 'error',
summary: 'Ошибка',
detail: error.message,
detail: String(error?.message ?? 'Неизвестная ошибка'),
life: 3000,
});
}
};
const showModalDealContract = ref(false);
// Открыть/закрыть модальное окно составления договора
const openModalDealContract = () => {
showModalDealContract.value = true;
};
const closeModalDealContract = () => {
showModalDealContract.value = false;
};
// Обработка нового договора
const handleNewInnerDoc = (newInnerDoc) => {
console.log('Новый innerDoc получен:', newInnerDoc);
if (!deal.value.inner_docs) {
console.warn('Массив inner_docs не существует в deal. Создаём новый массив.');
deal.value.inner_docs = [];
}
console.log('Добавляем новый договор в deal.inner_docs');
deal.value.inner_docs.push(newInnerDoc);
console.log('Обновлённый список inner_docs:', deal.value.inner_docs);
};
// Удаление документа
const { fetchData: deleteData } = useLaravelApi();
const removeInnerDoc = async (docId) => {
try {
await deleteData(`/inner-docs/${docId}`, {
method: 'DELETE'
});
// Удаляем документ из локального состояния
deal.value.inner_docs = deal.value.inner_docs.filter(doc => doc.id !== docId);
} catch (error) {
console.error('Ошибка при удалении документа:', error);
throw error;
return null;
}
};
@ -1135,14 +1209,6 @@ watch(() => props.isOpen, (newVal) => {
isOpen.value = newVal;
});
// Обработчик закрытия
// function closeModel() {
// isOpen.value = false;
// if (props.onCloseModel) {
// props.onCloseModel(); // Это уже есть в вашем коде
// }
// }
const emit = defineEmits(['close', 'added']);
const closeModel = () => {
emit('close');
@ -1179,17 +1245,6 @@ async function addResponsibleEmployee(userId) {
];
}
//Старые
//const fieldsStore = useFieldsStore();
const confirm = useConfirm();
const router = useRouter();
defineExpose({ closeModel });
</script>
@ -1248,13 +1303,14 @@ defineExpose({ closeModel });
/* Убираем стрелочки вверх/вниз для Firefox */
input[type="number"] {
appearance: textfield;
-moz-appearance: textfield;
-webkit-appearance: textfield;
}
input[type="radio"] {
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
display: inline-block;
width: 21px;
height: 21px;

View File

@ -1,8 +1,8 @@
<template>
<div class="modal dealAddDoc">
<div class="dealAddDoc">
<div class="modal-content">
<span class="close" @click="closeModal">&times;</span>
<h3 class="mb-4">Добавление документа</h3>
<!-- <span class="close" @click="closeModal">&times;</span>-->
<!-- <h3 class="mb-4">Добавление документа</h3>-->
<div class="mb-4">
<label for="document-name" class="block text-sm font-medium text-gray-700">Название документа</label>
@ -35,19 +35,22 @@
</template>
<script setup>
import {ref} from 'vue';
import {inject, ref} from 'vue';
//import { useLaravelApi } from '@/composables/useLaravelApi';
import { dealsApi } from '@/shared/api/deals';
import InputText from "primevue/inputtext";
const props = defineProps({
deal: {
type: Object,
required: true
},
});
// const props = defineProps({
// deal: {
// type: Object,
// required: true
// },
// });
const emit = defineEmits(['close', 'select']);
const dialogRef = inject('dialogRef');
const props = dialogRef.value.data;
//const emit = defineEmits(['close', 'select']);
// Реактивные переменные для названия и файла
const documentName = ref('');
@ -59,7 +62,7 @@ const handleFileChange = (event) => {
};
const closeModal = () => {
emit('close');
//emit('close');
};
// Сохранение документа
@ -83,8 +86,9 @@ const saveDealDoc = async () => {
try {
const response = await dealsApi.uploadDocument(formData);
emit('select', response.data.data);
closeModal();
//emit('select', response.data.data);
dialogRef.value.close(response.data.data)
//closeModal();
} catch (error) {
console.error('Ошибка при загрузке документа:', error);
alert('Произошла ошибка при загрузке документа');

View File

@ -13,33 +13,32 @@
<!-- Название документа -->
<div class="ml-4 flex flex-col justify-between flex-grow">
<div class="font-bold text-gray-800">{{ doc.name }}</div>
<div class="font-bold text-gray-800">{{ doc.name || 'N/A' }}</div>
<div class="flex">
<img src="@/shared/assets/icons/doc.svg" alt="Договор" class="w-5 h-5">
<a :href="fileUrl" target="_blank" class="ml-2 underline document-file">{{ getFileName(doc.file) }}</a>
<a :href="fileUrl" target="_blank" class="ml-2 underline document-file">{{ getFileName(doc.file) || 'N/A' }}</a>
</div>
</div>
<button @click="confirmDelete" class="ml-auto mt-2 px-4 py-2 rounded">
<!-- <img src="@/shared/assets/icons/trash.svg" alt="Удалить" class="w-6 h-6">-->
<span class="simple-button delete" title="Удалить"><i class="jw-action-icon-delete"></i></span>
</button>
<!-- <button @click="confirmDelete" class="ml-auto mt-2 px-4 py-2 rounded">-->
<!--&lt;!&ndash; <img src="@/shared/assets/icons/trash.svg" alt="Удалить" class="w-6 h-6">&ndash;&gt;-->
<!-- <span class="simple-button delete" title="Удалить"><i class="jw-action-icon-delete"></i></span>-->
<!-- </button>-->
</div>
</div>
</template>
<script setup>
import { useConfirm } from 'primevue/useconfirm';
import { useToast } from 'primevue/usetoast';
// import { useConfirm } from 'primevue/useconfirm';
// import { useToast } from 'primevue/usetoast';
import { computed } from "vue";
import defaultIconUrl from '@/shared/assets/images/deal/deal-doc-icon.png';
const props = defineProps({
doc: {
type: Object,
required: true,
required: false,
validator: (doc) => doc.id && doc.name && doc.file
},
urlLaravel: {
@ -53,67 +52,64 @@ const fileUrl = computed(() => {
return `${props.urlLaravel}/storage/${props.doc.file}`;
});
// Проверка, является ли файл изображением
// // Проверка, является ли файл изображением
const isImage = computed(() => {
if (!props.doc.file) return false;
const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
return imageExtensions.includes(fileExtension.value);
});
// Расширение файла
// // Расширение файла
const fileExtension = computed(() => {
if (!props.doc.file) return false;
return props.doc.file.split('.').pop().toLowerCase();
});
const getFileName = (fullPath) => {
return fullPath.split('/').pop();
}
const emit = defineEmits(['remove']);
const confirm = useConfirm();
const toast = useToast();
const confirmDelete = (event) => {
confirm.require({
target: event.currentTarget,
message: `Удалить документ "${props.doc.name}"?`,
header: 'Подтверждение удаления',
acceptLabel: 'Удалить',
rejectLabel: 'Отмена',
acceptClass: 'p-button-danger',
accept: async () => {
try {
emit('remove', props.doc.id);
toast.add({
severity: 'success',
summary: 'Успешно',
detail: 'Документ удален',
life: 3000
});
} catch (error) {
toast.add({
severity: 'error',
summary: 'Ошибка',
detail: 'Не удалось удалить документ',
life: 3000
});
}
},
reject: () => {
toast.add({
severity: 'info',
summary: 'Отменено',
detail: 'Удаление отменено',
life: 2000
});
}
});
};
//
//
// const emit = defineEmits(['remove']);
//
// const confirm = useConfirm();
// const toast = useToast();
//
// const confirmDelete = (event) => {
// confirm.require({
// target: event.currentTarget,
// message: `Удалить документ "${props.doc.name}"?`,
// header: 'Подтверждение удаления',
// acceptLabel: 'Удалить',
// rejectLabel: 'Отмена',
// acceptClass: 'p-button-danger',
// accept: async () => {
// try {
// emit('remove', props.doc.id);
// toast.add({
// severity: 'success',
// summary: 'Успешно',
// detail: 'Документ удален',
// life: 3000
// });
// } catch (error) {
// toast.add({
// severity: 'error',
// summary: 'Ошибка',
// detail: 'Не удалось удалить документ',
// life: 3000
// });
// }
// },
// reject: () => {
// toast.add({
// severity: 'info',
// summary: 'Отменено',
// detail: 'Удаление отменено',
// life: 2000
// });
// }
// });
// };
</script>
<style scoped>

View File

@ -1,22 +1,22 @@
<template>
<div class="modal dealAddContract">
<div v-if="deal && deal.id" class="dealAddContract">
<div class="modal-content">
<span class="close" @click="closeModal">&times;</span>
<h3>Составить договор</h3>
<div class="mb-4 mt-4 add-contract-title">
<label class="mb-2">Бланк документа:</label>
<Dropdown
<Select
v-model="selectedInnerBlankId"
:options="innerBlanksList"
optionLabel="name"
optionValue="id"
class="w-full"
:key="innerBlanksList.length"
>
<template #dropdownicon>
<DropdownIcon />
</template>
</Dropdown>
<!-- <template #dropdownicon>-->
<!-- <DropdownIcon />-->
<!-- </template>-->
</Select>
</div>
<div class="mb-4 add-contract-title">
@ -41,19 +41,14 @@
</template>
<script setup>
import {defineEmits, onMounted, ref} from 'vue';
import {inject, onMounted, ref} from 'vue';
import { useLaravelApi } from '@/composables/useLaravelApi';
import DropdownIcon from "@/shared/assets/icons/dropdown-icon.vue";
//import DropdownIcon from "@/shared/assets/icons/dropdown-icon.vue";
import Textarea from "primevue/textarea";
import Select from 'primevue/select';
const props = defineProps({
deal: {
type: Object,
required: true
},
});
const emit = defineEmits(['close', 'select']);
const dialogRef = inject('dialogRef');
const deal = dialogRef.value?.data?.deal || {}
// Состояния формы
const selectedInnerBlankId = ref(null);
@ -65,23 +60,30 @@ const validationErrors = ref({});
const { data: innerBlanksData, error: innerBlanksError, fetchData: fetchInnerBlanksData } = useLaravelApi();
const innerBlanksList = ref([]);
console.log('[InnerDocAddModal] refs и useLaravelApi созданы');
onMounted(async () => {
console.log(props.deal);
// Загружаем список бланков
try {
await fetchInnerBlanksData('/inner-blanks');
if (innerBlanksData.value && innerBlanksData.value.success) {
innerBlanksList.value = innerBlanksData.value.data; // Сохраняем данные
innerBlanksList.value = innerBlanksData.value.data;
console.log('[InnerDocAddModal] Inner blanks list установлен:', innerBlanksList.value);
} else {
console.error('Ошибка при загрузке списка бланков:', innerBlanksError.value);
console.error('[InnerDocAddModal] Ошибка при загрузке списка бланков:', innerBlanksError.value);
}
} catch (err) {
console.error('[InnerDocAddModal] Исключение при загрузке бланков:', err);
}
});
const closeModal = () => {
emit('close');
};
const getTemplateData = () => {
if (!deal) {
console.warn('deal is undefined in getTemplateData');
return { sobstv: [], clients: [], object_id: null };
}
const sidesMap = {
'Продавец': 'sobstv',
'Арендодатель': 'sobstv',
@ -98,41 +100,41 @@ const getTemplateData = () => {
};
// Обработка стороны 1
if (props.deal.side_one) {
const roleType = sidesMap[props.deal.side_one];
if (deal.side_one) {
const roleType = sidesMap[deal.side_one];
if (roleType) {
// Основной клиент
if (props.deal.side_one_client_main) {
result[roleType].push(props.deal.side_one_client_main.id);
if (deal.side_one_client_main) {
result[roleType].push(deal.side_one_client_main.id);
}
// Дополнительные клиенты
if (props.deal.side_one_clients?.length) {
result[roleType].push(...props.deal.side_one_clients.map(c => c.id));
if (deal.side_one_clients?.length) {
result[roleType].push(...deal.side_one_clients.map(c => c.id));
}
}
}
// Обработка стороны 2
if (props.deal.side_two) {
const roleType = sidesMap[props.deal.side_two];
if (deal.side_two) {
const roleType = sidesMap[deal.side_two];
if (roleType) {
// Основной клиент
if (props.deal.side_two_client_main) {
result[roleType].push(props.deal.side_two_client_main.id);
if (deal.side_two_client_main) {
result[roleType].push(deal.side_two_client_main.id);
}
// Дополнительные клиенты
if (props.deal.side_two_clients?.length) {
result[roleType].push(...props.deal.side_two_clients.map(c => c.id));
if (deal.side_two_clients?.length) {
result[roleType].push(...deal.side_two_clients.map(c => c.id));
}
//result[roleType].push(...props.deal.side_two_clients.map(c => c.id));
//result[roleType].push(...deal.side_two_clients.map(c => c.id));
}
}
// Объект может быть только один либо из side_one, либо из side_two
if (props.deal.side_one_object_id) {
result.object_id = props.deal.side_one_object_id;
} else if (props.deal.side_two_object_id) {
result.object_id = props.deal.side_two_object_id;
if (deal.side_one_object_id) {
result.object_id = deal.side_one_object_id;
} else if (deal.side_two_object_id) {
result.object_id = deal.side_two_object_id;
}
return result;
@ -145,9 +147,7 @@ const saveDealInnerDoc = async () => {
try {
const { fetchData } = useLaravelApi();
console.log('Props перед payload:', props);
const dealId = props.deal.id ? Number(props.deal.id) : null;
const dealId = deal.id ? Number(deal.id) : null;
const templateData = getTemplateData();
@ -155,32 +155,32 @@ const saveDealInnerDoc = async () => {
template_data: {
...templateData,
blank_id: selectedInnerBlankId.value,
side_one_requisition_id: props.deal.side_one_requisition_id,
side_one_requisition_id: deal.side_one_requisition_id,
},
deal_id: dealId,
comment: commentDealInnerBlank.value || '',
side_one: [
// Основной клиент стороны 1
(props.deal.side_one_client_main ? {
client_id: props.deal.side_one_client_main.id,
role: props.deal.side_one
(deal.side_one_client_main ? {
client_id: deal.side_one_client_main.id,
role: deal.side_one
} : null),
// Дополнительные клиенты стороны 1
...(props.deal.side_one_clients?.map(client => ({
...(deal.side_one_clients?.map(client => ({
client_id: client.id,
role: props.deal.side_one
role: deal.side_one
})) || [])
].filter(Boolean), // Убираем null, если основного клиента нет
side_two: [
// Основной клиент стороны 2
(props.deal.side_two_client_main ? {
client_id: props.deal.side_two_client_main.id,
role: props.deal.side_two
(deal.side_two_client_main ? {
client_id: deal.side_two_client_main.id,
role: deal.side_two
} : null),
// Дополнительные клиенты стороны 2
...(props.deal.side_two_clients?.map(client => ({
...(deal.side_two_clients?.map(client => ({
client_id: client.id,
role: props.deal.side_two
role: deal.side_two
})) || [])
].filter(Boolean), // Убираем null, если основного клиента нет
};
@ -193,8 +193,12 @@ const saveDealInnerDoc = async () => {
});
if (response.data.success) {
emit('created', response.data.data);
closeModal();
console.log('[InnerDocAddModal] Doc сохранен:', response.data.data);
console.log('dialogRef.value:', dialogRef.value);
dialogRef.value.close(response.data.data);
//closeDialog(response.data.data);
} else {
// Обработка ошибок валидации
if (response.data.errors) {
@ -215,31 +219,31 @@ const saveDealInnerDoc = async () => {
</script>
<style>
.modal {
display: flex;
justify-content: center;
align-items: center;
position: fixed;
z-index: 1;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
}
.modal-content {
background-color: #fff;
padding: 20px;
border-radius: 10px;
width: 80%;
max-width: 500px;
}
.close {
position: absolute;
top: 10px;
right: 20px;
font-size: 24px;
cursor: pointer;
}
</style>
<!--<style>-->
<!--.modal {-->
<!-- display: flex;-->
<!-- justify-content: center;-->
<!-- align-items: center;-->
<!-- position: fixed;-->
<!-- z-index: 1;-->
<!-- left: 0;-->
<!-- top: 0;-->
<!-- width: 100%;-->
<!-- height: 100%;-->
<!-- background-color: rgba(0, 0, 0, 0.5);-->
<!--}-->
<!--.modal-content {-->
<!-- background-color: #fff;-->
<!-- padding: 20px;-->
<!-- border-radius: 10px;-->
<!-- width: 80%;-->
<!-- max-width: 500px;-->
<!--}-->
<!--.close {-->
<!-- position: absolute;-->
<!-- top: 10px;-->
<!-- right: 20px;-->
<!-- font-size: 24px;-->
<!-- cursor: pointer;-->
<!--}-->
<!--</style>-->

View File

@ -10,7 +10,7 @@
<!-- Сторона 1 -->
<div class="clients-inner-doc mt-4">
<h4 class="inner-doc-title">Сторона 1:</h4>
<div v-for="side in innerDoc.deals[0]?.side_one || []" :key="side.client_id" class="mt-2 flex items-baseline">
<div v-for="side in currentDeal[0]?.side_one || []" :key="side.client_id" class="mt-2 flex items-baseline">
<span style="margin-right: 10px;" class="font-medium min-w-[80px]">{{ side.role }}:</span>
<div class="ml-2 underline">
<span>{{ getClientName(side) }} ({{ side.phone }})</span>
@ -21,7 +21,7 @@
<!-- Сторона 2 -->
<div class="clients-inner-doc mt-4">
<h4 class="inner-doc-title">Сторона 2:</h4>
<div v-for="side in innerDoc.deals[0]?.side_two || []" :key="side.client_id" class="mt-2 flex items-baseline">
<div v-for="side in currentDeal[0]?.side_two || []" :key="side.client_id" class="mt-2 flex items-baseline">
<span style="margin-right: 10px;" class="font-medium min-w-[80px]">{{ side.role }}:</span>
<div class="ml-2 underline">
<span>{{ getClientName(side) }} ({{ side.phone }})</span>
@ -74,10 +74,12 @@
<script setup>
import { computed, ref } from 'vue';
import { computed } from 'vue';
import { useConfirm } from 'primevue/useconfirm';
import { useToast } from 'primevue/usetoast';
console.log('Запуск InnerDocCard');
const props = defineProps({
innerDoc: {
type: Object,
@ -94,6 +96,10 @@ const emit = defineEmits(['remove']);
const confirm = useConfirm();
const toast = useToast();
const currentDeal = computed(() => {
return props.innerDoc.deals?.[0];
});
// Получаем pivot данные для текущего документа
const currentPivot = computed(() => {
return props.innerDoc.deals?.[0]?.pivot;
@ -105,7 +111,7 @@ const getClientName = (client) => {
};
const handleEditInnerDoc = () => {
emit('edit', props.innerDoc); // Немедленно передаем документ на редактирование
emit('edit', props.innerDoc);
};
const handleRemoveDealDoc = (event) => {

View File

@ -1,8 +1,7 @@
import { ref } from 'vue';
import {
fetchDeal,
createDeal,
uploadDealDocument
createDeal
} from '@/shared/api/deals';
export default function useDeal() {

View File

@ -42,14 +42,14 @@ const fetchData = (endpoint, options = {}) => {
// Извлекаем путь из endpoint
const path = endpoint.replace(/^\//, '');
console.log('[laravelApiService] fetchData вызван с endpoint:', endpoint);
console.log('[laravelApiService] Тип data:', data && data.constructor.name);
console.log('[laravelApiService] data instanceof FormData:', data instanceof FormData);
//
// console.log('[laravelApiService] fetchData вызван с endpoint:', endpoint);
// console.log('[laravelApiService] Тип data:', data && data.constructor.name);
// console.log('[laravelApiService] data instanceof FormData:', data instanceof FormData);
const isFormData = data instanceof FormData;
if (isFormData) {
console.log('[laravelApiService] Используем directAxiosInstance для FormData');
//console.log('[laravelApiService] Используем directAxiosInstance для FormData');
const laravelApiUrl = 'http://llar.joyworkdev.ru/api';
@ -64,7 +64,7 @@ const fetchData = (endpoint, options = {}) => {
finalHeaders['X-Session-ID'] = sessionId;
}
logMessage("Sending FormData directly to Laravel API: " + laravelApiUrl + endpoint);
//logMessage("Sending FormData directly to Laravel API: " + laravelApiUrl + endpoint);
return directAxiosInstance({
url: endpoint,
method,
@ -74,8 +74,6 @@ const fetchData = (endpoint, options = {}) => {
});
}
console.log('[laravelApiService] Используем laravel_api_instance через deal/index.php');
const finalHeaders = { ...headers };
if (sessionId) {
finalHeaders['X-Session-ID'] = sessionId;
@ -94,10 +92,6 @@ const fetchData = (endpoint, options = {}) => {
});
};
function logMessage(message) {
console.log('[laravelApiService]', message);
}
export default {
fetchData,
};

View File

@ -3,7 +3,6 @@
fill="#4CAF50"
height="24px"
width="24px"
version="1.1"
id="Layer_1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"

View File

@ -4,12 +4,11 @@
width="20px"
height="20px"
viewBox="0 0 20 20"
version="1.1"
>
<g id="layer1">
<path
d="M 4 4.2910156 L 3.2910156 5 L 3.6464844 5.3535156 L 10 11.708984 L 16.353516 5.3535156 L 16.708984 5 L 16 4.2910156 L 15.646484 4.6464844 L 10 10.291016 L 4.3535156 4.6464844 L 4 4.2910156 z M 4 8.2910156 L 3.2910156 9 L 3.6464844 9.3535156 L 10 15.708984 L 16.353516 9.3535156 L 16.708984 9 L 16 8.2910156 L 15.646484 8.6464844 L 10 14.291016 L 4.3535156 8.6464844 L 4 8.2910156 z "
style="fill: #757575; fill-opacity: 1; stroke: none; stroke-width: 0px"
style="fill: #757575; fill-opacity: 1; stroke: none; stroke-width: 0"
/>
</g>
</svg>