feat: Форма записи - Загрузка файлов
This commit is contained in:
parent
4954bd0a1c
commit
64b0a19408
@ -23,14 +23,16 @@ def start_app():
|
|||||||
|
|
||||||
api_app.add_middleware(
|
api_app.add_middleware(
|
||||||
CORSMiddleware,
|
CORSMiddleware,
|
||||||
allow_origins=['http://localhost:5173'],
|
allow_origins=['http://localhost:5173', 'http://localhost:5173'],
|
||||||
allow_credentials=True,
|
allow_credentials=True,
|
||||||
allow_methods=['*'],
|
allow_methods=['*'],
|
||||||
allow_headers=['*'],
|
allow_headers=['*'],
|
||||||
)
|
)
|
||||||
|
|
||||||
api_app.include_router(appointment_files_router, prefix=f'{settings.APP_PREFIX}/appointment_files', tags=['appointment_files'])
|
api_app.include_router(appointment_files_router, prefix=f'{settings.APP_PREFIX}/appointment_files',
|
||||||
api_app.include_router(appointments_types_router, prefix=f'{settings.APP_PREFIX}/appointment_types', tags=['appointment_types'])
|
tags=['appointment_files'])
|
||||||
|
api_app.include_router(appointments_types_router, prefix=f'{settings.APP_PREFIX}/appointment_types',
|
||||||
|
tags=['appointment_types'])
|
||||||
api_app.include_router(appointment_router, prefix=f'{settings.APP_PREFIX}/appointments', tags=['appointments'])
|
api_app.include_router(appointment_router, prefix=f'{settings.APP_PREFIX}/appointments', tags=['appointments'])
|
||||||
api_app.include_router(auth_router, prefix=settings.APP_PREFIX, tags=['auth'])
|
api_app.include_router(auth_router, prefix=settings.APP_PREFIX, tags=['auth'])
|
||||||
api_app.include_router(lens_issues_router, prefix=f'{settings.APP_PREFIX}/lens_issues', tags=['lens_issue'])
|
api_app.include_router(lens_issues_router, prefix=f'{settings.APP_PREFIX}/lens_issues', tags=['lens_issue'])
|
||||||
@ -39,7 +41,8 @@ def start_app():
|
|||||||
api_app.include_router(patients_router, prefix=f'{settings.APP_PREFIX}/patients', tags=['patients'])
|
api_app.include_router(patients_router, prefix=f'{settings.APP_PREFIX}/patients', tags=['patients'])
|
||||||
api_app.include_router(register_router, prefix=f'{settings.APP_PREFIX}/register', tags=['register'])
|
api_app.include_router(register_router, prefix=f'{settings.APP_PREFIX}/register', tags=['register'])
|
||||||
api_app.include_router(roles_router, prefix=f'{settings.APP_PREFIX}/roles', tags=['roles'])
|
api_app.include_router(roles_router, prefix=f'{settings.APP_PREFIX}/roles', tags=['roles'])
|
||||||
api_app.include_router(scheduled_appointments_router, prefix=f'{settings.APP_PREFIX}/scheduled_appointments', tags=['scheduled_appointments'])
|
api_app.include_router(scheduled_appointments_router, prefix=f'{settings.APP_PREFIX}/scheduled_appointments',
|
||||||
|
tags=['scheduled_appointments'])
|
||||||
api_app.include_router(set_content_router, prefix=f'{settings.APP_PREFIX}/set_content', tags=['set_content'])
|
api_app.include_router(set_content_router, prefix=f'{settings.APP_PREFIX}/set_content', tags=['set_content'])
|
||||||
api_app.include_router(sets_router, prefix=f'{settings.APP_PREFIX}/sets', tags=['sets'])
|
api_app.include_router(sets_router, prefix=f'{settings.APP_PREFIX}/sets', tags=['sets'])
|
||||||
api_app.include_router(users_router, prefix=f'{settings.APP_PREFIX}/users', tags=['users'])
|
api_app.include_router(users_router, prefix=f'{settings.APP_PREFIX}/users', tags=['users'])
|
||||||
|
|||||||
@ -1,65 +1,28 @@
|
|||||||
import {createApi} from "@reduxjs/toolkit/query/react";
|
import { createApi } from "@reduxjs/toolkit/query/react";
|
||||||
import {baseQueryWithAuth} from "./baseQuery.js";
|
import { baseQueryWithAuth } from "./baseQuery.js";
|
||||||
|
|
||||||
export const appointmentFilesApi = createApi({
|
export const appointmentFilesApi = createApi({
|
||||||
reducerPath: 'appointmentFilesApi',
|
reducerPath: 'appointmentFilesApi',
|
||||||
baseQuery: baseQueryWithAuth,
|
baseQuery: baseQueryWithAuth,
|
||||||
tagTypes: ['AppointmentFile'],
|
tagTypes: ['AppointmentFile'],
|
||||||
endpoints: (builder) => ({
|
endpoints: (builder) => ({
|
||||||
getAppointmentFiles: builder.query({
|
|
||||||
query: (appointmentId) => {
|
|
||||||
console.log(`Fetching files for appointment ID: ${appointmentId}`);
|
|
||||||
return `/appointment_files/${appointmentId}/`;
|
|
||||||
},
|
|
||||||
providesTags: ['AppointmentFile'],
|
|
||||||
refetchOnMountOrArgChange: 5,
|
|
||||||
}),
|
|
||||||
downloadAppointmentFile: builder.query({
|
|
||||||
query: (fileId) => ({
|
|
||||||
url: `/appointment_files/${fileId}/file/`,
|
|
||||||
responseHandler: async (response) => {
|
|
||||||
const blob = await response.blob();
|
|
||||||
const contentDisposition = response.headers.get('content-disposition');
|
|
||||||
const filename = contentDisposition
|
|
||||||
? contentDisposition.match(/filename="(.+)"/)?.[1] || `file_${fileId}`
|
|
||||||
: `file_${fileId}`;
|
|
||||||
return {blob, filename};
|
|
||||||
},
|
|
||||||
cache: 'no-cache',
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
uploadAppointmentFile: builder.mutation({
|
uploadAppointmentFile: builder.mutation({
|
||||||
query: ({appointmentId, file}) => {
|
query: ({ appointmentId, file }) => {
|
||||||
console.log(`File details:`, {
|
|
||||||
name: file.name,
|
|
||||||
size: file.size,
|
|
||||||
type: file.type,
|
|
||||||
isFile: file instanceof File,
|
|
||||||
});
|
|
||||||
if (!(file instanceof File)) {
|
if (!(file instanceof File)) {
|
||||||
console.error('Expected a File object, received:', file);
|
|
||||||
throw new Error('Invalid file object');
|
throw new Error('Invalid file object');
|
||||||
}
|
}
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('file', file);
|
formData.append('file', file);
|
||||||
for (let [key, value] of formData.entries()) {
|
|
||||||
console.log(`FormData entry: ${key}=${value.name || value}`);
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
url: `/appointment_files/${appointmentId}/upload/`,
|
url: `/appointment_files/${appointmentId}/upload/`,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
credentials: 'include',
|
||||||
|
formData: true,
|
||||||
body: formData,
|
body: formData,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
invalidatesTags: ['AppointmentFile'],
|
invalidatesTags: ['AppointmentFile'],
|
||||||
}),
|
}),
|
||||||
deleteAppointmentFile: builder.mutation({
|
|
||||||
query: (fileId) => ({
|
|
||||||
url: `/appointment_files/${fileId}/`,
|
|
||||||
method: 'DELETE',
|
|
||||||
}),
|
|
||||||
invalidatesTags: ['AppointmentFile'],
|
|
||||||
}),
|
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -1,24 +1,24 @@
|
|||||||
import {fetchBaseQuery} from '@reduxjs/toolkit/query/react';
|
import { fetchBaseQuery } from '@reduxjs/toolkit/query/react';
|
||||||
import {logout} from '../Redux/Slices/authSlice.js';
|
import { logout } from '../Redux/Slices/authSlice.js';
|
||||||
import CONFIG from "../Core/сonfig.js";
|
import CONFIG from "../Core/сonfig.js";
|
||||||
|
|
||||||
export const baseQuery = fetchBaseQuery({
|
export const baseQuery = fetchBaseQuery({
|
||||||
baseUrl: CONFIG.BASE_URL,
|
baseUrl: CONFIG.BASE_URL,
|
||||||
prepareHeaders: (headers, {getState}) => {
|
prepareHeaders: (headers, { getState, endpoint }) => {
|
||||||
const token = localStorage.getItem('access_token');
|
const token = localStorage.getItem('access_token');
|
||||||
if (token) {
|
if (token) {
|
||||||
headers.set('Authorization', `Bearer ${token}`);
|
headers.set('Authorization', `Bearer ${token}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const body = getState()?.api?.mutations?.[Object.keys(getState()?.api?.mutations || {})[0]]?.body;
|
if (endpoint === 'uploadAppointmentFile') {
|
||||||
if (!(body instanceof FormData)) {
|
const mutation = getState()?.api?.mutations?.[Object.keys(getState()?.api?.mutations || {})[0]];
|
||||||
headers.set('Content-Type', 'application/json');
|
if (mutation?.body instanceof FormData) {
|
||||||
} else {
|
headers.delete('Content-Type');
|
||||||
console.log('FormData request headers:', headers);
|
|
||||||
for (let [key, value] of body.entries()) {
|
|
||||||
console.log(`FormData entry: ${key}=${value.name || value}`);
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
headers.set('Content-Type', 'application/json');
|
||||||
}
|
}
|
||||||
|
|
||||||
return headers;
|
return headers;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -210,28 +210,6 @@ const AppointmentFormModal = () => {
|
|||||||
>
|
>
|
||||||
<Button icon={<UploadOutlined />}>Выбрать файлы</Button>
|
<Button icon={<UploadOutlined />}>Выбрать файлы</Button>
|
||||||
</Upload>
|
</Upload>
|
||||||
{appointmentFormModalUI.draftFiles.length > 0 && (
|
|
||||||
<List
|
|
||||||
style={{ marginTop: 16 }}
|
|
||||||
dataSource={appointmentFormModalUI.draftFiles}
|
|
||||||
renderItem={(file) => (
|
|
||||||
<List.Item
|
|
||||||
actions={[
|
|
||||||
<Button
|
|
||||||
key={file.uid}
|
|
||||||
type="link"
|
|
||||||
danger
|
|
||||||
onClick={() => appointmentFormModalUI.handleRemoveFile(file)}
|
|
||||||
>
|
|
||||||
Удалить
|
|
||||||
</Button>
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
{file.name} ({(file.size / 1024 / 1024).toFixed(2)} MB)
|
|
||||||
</List.Item>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -7,8 +7,6 @@ import {useGetAppointmentsQuery} from "../../../Api/appointmentsApi.js";
|
|||||||
import {Grid} from "antd";
|
import {Grid} from "antd";
|
||||||
import {useUploadAppointmentFileMutation} from "../../../Api/appointmentFilesApi.js";
|
import {useUploadAppointmentFileMutation} from "../../../Api/appointmentFilesApi.js";
|
||||||
import Compressor from 'compressorjs';
|
import Compressor from 'compressorjs';
|
||||||
import CONFIG from "../../../Core/сonfig.js";
|
|
||||||
import axios from "axios";
|
|
||||||
|
|
||||||
const {useBreakpoint} = Grid;
|
const {useBreakpoint} = Grid;
|
||||||
|
|
||||||
@ -299,36 +297,15 @@ const useAppointmentFormModalUI = (createAppointment, patients, cancelAppointmen
|
|||||||
|
|
||||||
for (const file of draftFiles) {
|
for (const file of draftFiles) {
|
||||||
try {
|
try {
|
||||||
console.log(`Uploading file: ${file.name}, size: ${file.size}, type: ${file.type}`);
|
await uploadAppointmentFile({
|
||||||
|
appointmentId: response.id,
|
||||||
// Формируем FormData
|
file: file,
|
||||||
const formData = new FormData();
|
}).unwrap();
|
||||||
formData.append("file", file);
|
|
||||||
|
|
||||||
// Логируем содержимое FormData
|
|
||||||
for (let [key, value] of formData.entries()) {
|
|
||||||
console.log(`FormData entry: ${key}=${value.name || value}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Отправляем запрос через Axios
|
|
||||||
const uploadResponse = await axios.post(
|
|
||||||
`${CONFIG.BASE_URL}/appointment_files/${response.id}/upload/`,
|
|
||||||
formData,
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${localStorage.getItem("access_token")}`,
|
|
||||||
// Content-Type не указываем, Axios автоматически установит multipart/form-data
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log(`Successfully uploaded file: ${file.name}`, uploadResponse.data);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error uploading file ${file.name}:`, error);
|
console.error(`Error uploading file ${file.name}:`, error);
|
||||||
// Улучшенная обработка ошибок
|
const errorMessage = error.data?.detail
|
||||||
const errorMessage = error.response?.data?.detail
|
? JSON.stringify(error.data.detail, null, 2)
|
||||||
? JSON.stringify(error.response.data.detail, null, 2)
|
: JSON.stringify(error.data || error.message || "Неизвестная ошибка", null, 2);
|
||||||
: error.message || "Неизвестная ошибка";
|
|
||||||
notification.error({
|
notification.error({
|
||||||
message: "Ошибка загрузки файла",
|
message: "Ошибка загрузки файла",
|
||||||
description: `Не удалось загрузить файл ${file.name}: ${errorMessage}`,
|
description: `Не удалось загрузить файл ${file.name}: ${errorMessage}`,
|
||||||
@ -353,8 +330,8 @@ const useAppointmentFormModalUI = (createAppointment, patients, cancelAppointmen
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error creating appointment:", error);
|
console.error("Error creating appointment:", error);
|
||||||
notification.error({
|
notification.error({
|
||||||
message: "Ошибка",
|
message: "Ошибка загрузки файла",
|
||||||
description: error.data?.message || "Не удалось создать прием.",
|
description: `Не удалось загрузить файл ${JSON.stringify(error.data?.detail || error.data || error.message || "Неизвестная ошибка", null, 2)}`,
|
||||||
placement: "topRight",
|
placement: "topRight",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user