first commit
This commit is contained in:
107
resources/js/Composables/useApiForm.js
Normal file
107
resources/js/Composables/useApiForm.js
Normal file
@@ -0,0 +1,107 @@
|
||||
// composables/useSmartApiForm.js
|
||||
import { ref, reactive, watch } from 'vue'
|
||||
import axios from 'axios'
|
||||
|
||||
export function useApiForm(initialData = {}) {
|
||||
const loading = ref(false)
|
||||
const errors = ref({})
|
||||
const progress = ref(0)
|
||||
|
||||
// Для обычных полей формы
|
||||
const formData = ref({ ...initialData })
|
||||
// Для файлов
|
||||
const files = ref({})
|
||||
|
||||
const setFile = (fieldName, file) => {
|
||||
files.value[fieldName] = file
|
||||
// Автоматически очищаем ошибку для этого поля
|
||||
clearError(fieldName)
|
||||
}
|
||||
|
||||
const clearError = (field) => {
|
||||
if (errors.value[field]) {
|
||||
const newErrors = { ...errors.value }
|
||||
delete newErrors[field]
|
||||
errors.value = newErrors
|
||||
}
|
||||
}
|
||||
|
||||
const clearAllErrors = () => {
|
||||
errors.value = {}
|
||||
}
|
||||
|
||||
const submit = async (url, method = 'post', config = {}) => {
|
||||
loading.value = true
|
||||
progress.value = 0
|
||||
clearAllErrors()
|
||||
|
||||
try {
|
||||
// Создаем FormData
|
||||
const formDataToSend = new FormData()
|
||||
|
||||
// Добавляем обычные поля формы
|
||||
Object.keys(formData.value).forEach(key => {
|
||||
if (formData.value[key] !== null && formData.value[key] !== undefined) {
|
||||
formDataToSend.append(key, formData.value[key])
|
||||
}
|
||||
})
|
||||
|
||||
// Добавляем файлы
|
||||
Object.keys(files.value).forEach(key => {
|
||||
if (files.value[key]) {
|
||||
formDataToSend.append(key, files.value[key])
|
||||
}
|
||||
})
|
||||
|
||||
const response = await axios({
|
||||
method,
|
||||
url,
|
||||
data: formDataToSend,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
...config.headers
|
||||
},
|
||||
onUploadProgress: (progressEvent) => {
|
||||
if (progressEvent.total) {
|
||||
progress.value = Math.round(
|
||||
(progressEvent.loaded * 100) / progressEvent.total
|
||||
)
|
||||
}
|
||||
},
|
||||
...config
|
||||
})
|
||||
|
||||
return response.data
|
||||
} catch (error) {
|
||||
if (error.response?.status === 422) {
|
||||
errors.value = error.response.data.errors || {}
|
||||
}
|
||||
throw error
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const reset = () => {
|
||||
// Сбрасываем обычные поля
|
||||
Object.keys(initialData).forEach(key => {
|
||||
formData.value[key] = initialData[key]
|
||||
})
|
||||
// Сбрасываем файлы
|
||||
files.value = {}
|
||||
clearAllErrors()
|
||||
}
|
||||
|
||||
return {
|
||||
formData,
|
||||
files,
|
||||
errors,
|
||||
loading,
|
||||
progress,
|
||||
submit,
|
||||
setFile,
|
||||
clearError,
|
||||
clearAllErrors,
|
||||
reset
|
||||
}
|
||||
}
|
||||
113
resources/js/Composables/useDynamicA4Layout.js
Normal file
113
resources/js/Composables/useDynamicA4Layout.js
Normal file
@@ -0,0 +1,113 @@
|
||||
import {
|
||||
ref,
|
||||
reactive,
|
||||
onMounted,
|
||||
onUpdated,
|
||||
nextTick
|
||||
} from "vue"
|
||||
import {useElementSize} from "@vueuse/core";
|
||||
|
||||
export function useDynamicA4Layout() {
|
||||
const A4_HEIGHT = 933
|
||||
const A4_WIDTH = 623
|
||||
const a4Pages = reactive([])
|
||||
const isCalculating = ref(false)
|
||||
const componentHeights = ref(new Map())
|
||||
|
||||
const waitForComponentRender = (componentRef, itemType) => {
|
||||
return new Promise((resolve) => {
|
||||
if (!componentRef) {
|
||||
resolve(0)
|
||||
return
|
||||
}
|
||||
|
||||
const checkRender = () => {
|
||||
requestAnimationFrame(() => {
|
||||
if (componentRef.offsetHeight > 0) {
|
||||
resolve(getComponentHeight(componentRef))
|
||||
} else {
|
||||
resolve(0)
|
||||
// setTimeout(checkRender, 1000)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
checkRender()
|
||||
})
|
||||
}
|
||||
|
||||
const getComponentHeight = (element) => {
|
||||
if (!element) return 0
|
||||
|
||||
|
||||
const styles = getComputedStyle(element)
|
||||
return element.offsetHeight
|
||||
+ parseInt(styles.marginTop)
|
||||
+ parseInt(styles.marginBottom)
|
||||
+ parseInt(styles.borderTopWidth)
|
||||
+ parseInt(styles.borderBottomWidth)
|
||||
}
|
||||
|
||||
const calculateDynamicLayout = async(items, getComponentRefs) => {
|
||||
if (isCalculating.value) return
|
||||
isCalculating.value = true
|
||||
|
||||
try {
|
||||
await nextTick()
|
||||
|
||||
const componentRefs = getComponentRefs()
|
||||
|
||||
a4Pages.splice(0, a4Pages.length)
|
||||
const currentPage = {items: [], totalHeight: 0, id: Date.now()}
|
||||
a4Pages.push(currentPage)
|
||||
|
||||
const heightPromises = items.map(async (item, index) => {
|
||||
const componentRef = componentRefs.get(item.id)
|
||||
const height = await waitForComponentRender(componentRef, item.type)
|
||||
componentHeights.value.set(item.id || index, height)
|
||||
return height
|
||||
})
|
||||
|
||||
const heights = await Promise.all(heightPromises)
|
||||
|
||||
let currentHeight = 0
|
||||
const pageMargin = 0
|
||||
|
||||
items.forEach((item, index) => {
|
||||
// console.log(item)
|
||||
const itemHeight = heights[index]
|
||||
const availableHeight = A4_HEIGHT - (pageMargin * 2) - currentHeight
|
||||
|
||||
console.log(currentHeight > 0 && availableHeight < itemHeight)
|
||||
|
||||
if (currentHeight > 0 && availableHeight < itemHeight) {
|
||||
const newPage = {
|
||||
items: [item],
|
||||
totalHeight: itemHeight,
|
||||
id: Date.now() + index
|
||||
}
|
||||
a4Pages.push(newPage)
|
||||
currentHeight = itemHeight
|
||||
} else {
|
||||
const page = a4Pages[a4Pages.length - 1]
|
||||
page.items.push(item)
|
||||
page.totalHeight += itemHeight
|
||||
currentHeight += itemHeight
|
||||
}
|
||||
})
|
||||
} catch(error) {
|
||||
console.error('Ошибка вычисления размера')
|
||||
} finally {
|
||||
isCalculating.value = false
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
a4Pages,
|
||||
A4_HEIGHT,
|
||||
A4_WIDTH,
|
||||
calculateDynamicLayout,
|
||||
isCalculating,
|
||||
componentHeights
|
||||
}
|
||||
}
|
||||
66
resources/js/Composables/useFileDownload.js
Normal file
66
resources/js/Composables/useFileDownload.js
Normal file
@@ -0,0 +1,66 @@
|
||||
// composables/useFileDownload.js
|
||||
export const useFileDownload = () => {
|
||||
const downloadFile = async (url, data, filename = 'file', method = 'post') => {
|
||||
try {
|
||||
const response = await axios({
|
||||
method,
|
||||
url,
|
||||
data: method === 'post' ? data : null,
|
||||
params: method === 'get' ? data : null,
|
||||
responseType: 'blob'
|
||||
});
|
||||
|
||||
// Проверяем, что это действительно файл, а не ошибка
|
||||
const contentType = response.headers['content-type'];
|
||||
if (contentType.includes('application/json')) {
|
||||
// Это JSON ошибка, а не файл
|
||||
const errorData = JSON.parse(await response.data.text());
|
||||
throw new Error(errorData.error || 'Download failed');
|
||||
}
|
||||
|
||||
// Создаем blob URL
|
||||
const blob = new Blob([response.data], {
|
||||
type: response.headers['content-type']
|
||||
});
|
||||
const downloadUrl = window.URL.createObjectURL(blob);
|
||||
|
||||
// Создаем временную ссылку для скачивания
|
||||
const link = document.createElement('a');
|
||||
link.href = downloadUrl;
|
||||
|
||||
link.download = filename;
|
||||
|
||||
// Имитируем клик для скачивания
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
|
||||
// Очищаем URL
|
||||
window.URL.revokeObjectURL(downloadUrl);
|
||||
|
||||
return true;
|
||||
|
||||
} catch (error) {
|
||||
console.error('Download error:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const getFileNameFromResponse = (response) => {
|
||||
const contentDisposition = response.headers['content-disposition'];
|
||||
let fileName = 'document.docx';
|
||||
|
||||
if (contentDisposition) {
|
||||
const filenameMatch = contentDisposition.match(/filename="?(.+)"?/);
|
||||
if (filenameMatch && filenameMatch[1]) {
|
||||
fileName = filenameMatch[1].replace(/"/g, '');
|
||||
}
|
||||
}
|
||||
|
||||
return fileName;
|
||||
};
|
||||
|
||||
return {
|
||||
downloadFile
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user