유틸추가
This commit is contained in:
parent
9cefae6525
commit
4f7d21fdef
14
package-lock.json
generated
14
package-lock.json
generated
@ -20,9 +20,11 @@
|
||||
"dayjs": "^1.11.13",
|
||||
"flatpickr": "^4.6.13",
|
||||
"front": "file:",
|
||||
"heic2any": "^0.0.4",
|
||||
"pinia": "^2.2.6",
|
||||
"pinia-plugin-persist": "^1.0.0",
|
||||
"quill": "^2.0.3",
|
||||
"upload-images-converter": "^2.0.2",
|
||||
"vue": "^3.5.13",
|
||||
"vue-flatpickr-component": "^11.0.5",
|
||||
"vue-router": "^4.4.5"
|
||||
@ -2977,6 +2979,12 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/heic2any": {
|
||||
"version": "0.0.4",
|
||||
"resolved": "https://registry.npmjs.org/heic2any/-/heic2any-0.0.4.tgz",
|
||||
"integrity": "sha512-3lLnZiDELfabVH87htnRolZ2iehX9zwpRyGNz22GKXIu0fznlblf0/ftppXKNqS26dqFSeqfIBhAmAj/uSp0cA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/hookable": {
|
||||
"version": "5.5.3",
|
||||
"resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz",
|
||||
@ -4247,6 +4255,12 @@
|
||||
"browserslist": ">= 4.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/upload-images-converter": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/upload-images-converter/-/upload-images-converter-2.0.2.tgz",
|
||||
"integrity": "sha512-cltVwZ+RU70Qx8heZrSj8OpQhR9mFHZ6CXudosKkFlM91Iiznl8Oluw/x+9DTnSKkvipvkf1oCvyVw5UPIpZ1Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/uri-js": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
||||
|
||||
@ -23,9 +23,11 @@
|
||||
"dayjs": "^1.11.13",
|
||||
"flatpickr": "^4.6.13",
|
||||
"front": "file:",
|
||||
"heic2any": "^0.0.4",
|
||||
"pinia": "^2.2.6",
|
||||
"pinia-plugin-persist": "^1.0.0",
|
||||
"quill": "^2.0.3",
|
||||
"upload-images-converter": "^2.0.2",
|
||||
"vue": "^3.5.13",
|
||||
"vue-flatpickr-component": "^11.0.5",
|
||||
"vue-router": "^4.4.5"
|
||||
|
||||
@ -1 +1,104 @@
|
||||
.app-calendar-wrapper{position:relative;border-radius:.375rem}.app-calendar-wrapper .app-calendar-sidebar{position:absolute;overflow:hidden;flex-grow:0;flex-basis:18.75rem;left:calc(-18.75rem - 1.2rem);height:100%;width:18.75rem;transition:all .2s;z-index:4}.app-calendar-wrapper .app-calendar-sidebar.show{left:0}.app-calendar-wrapper .app-calendar-sidebar .flatpickr-calendar{box-shadow:none}.app-calendar-wrapper .app-calendar-sidebar .flatpickr-calendar .flatpickr-month,.app-calendar-wrapper .app-calendar-sidebar .flatpickr-calendar .flatpickr-weekday,.app-calendar-wrapper .app-calendar-sidebar .flatpickr-calendar .flatpickr-weekdays{background:rgba(0,0,0,0)}.app-calendar-wrapper .app-calendar-sidebar .flatpickr-calendar .flatpickr-days{border:0}.app-calendar-wrapper .app-calendar-sidebar .flatpickr-calendar:focus{outline:0}.app-calendar-wrapper .app-calendar-content{position:relative}.app-calendar-wrapper .fc-toolbar h2{font-size:1.5rem;line-height:2.375rem}@media(max-width: 767.98px){.app-calendar-wrapper .fc-toolbar h2{font-size:1rem}}.app-calendar-wrapper .fc-toolbar-chunk{overflow:auto}.app-calendar-wrapper table.fc-scrollgrid{border-left:0;border-right:0}.app-calendar-wrapper table.fc-scrollgrid th,.app-calendar-wrapper table.fc-scrollgrid td{border-right:0}.app-calendar-wrapper .fc-timeGridDay-view table.fc-scrollgrid tbody tr:not(.fc-scrollgrid-section:first-of-type) td,.app-calendar-wrapper .fc-timeGridWeek-view table.fc-scrollgrid tbody tr:not(.fc-scrollgrid-section:first-of-type) td{border-bottom:0}.app-calendar-wrapper .fc-dayGridMonth-view table.fc-scrollgrid td{border-bottom:0 !important}.app-calendar-wrapper .fc-header-toolbar{margin-bottom:1.5rem !important}.app-calendar-wrapper .fc-view-container{margin:0 -1.6rem}.app-calendar-wrapper .event-sidebar .ql-editor{min-height:5rem}.app-calendar-wrapper .event-sidebar .select2 .select2-selection__choice{display:flex}.app-calendar-wrapper .event-sidebar .select2 .select2-selection__choice .avatar{display:none}@media(min-width: 992px){.app-calendar-wrapper .app-calendar-sidebar{position:static;height:auto;background-color:rgba(0,0,0,0) !important}.app-calendar-wrapper .app-calendar-sidebar .flatpickr-days{background-color:rgba(0,0,0,0)}}[dir=rtl] .app-calendar-wrapper .fc .fc-toolbar .fc-sidebarToggle-button{order:1}[dir=rtl] .app-calendar-wrapper .app-calendar-sidebar{left:auto;right:calc(-18.75rem - 1.2rem)}[dir=rtl] .app-calendar-wrapper .app-calendar-sidebar.show{left:auto;right:0}.light-style .app-calendar-wrapper .app-calendar-sidebar{background-color:#fff}.dark-style .app-calendar-wrapper .app-calendar-sidebar{background-color:#2b2c40}
|
||||
.app-calendar-wrapper {
|
||||
position: relative;
|
||||
border-radius: 0.375rem;
|
||||
}
|
||||
.app-calendar-wrapper .app-calendar-sidebar {
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
flex-grow: 0;
|
||||
flex-basis: 18.75rem;
|
||||
left: calc(-18.75rem - 1.2rem);
|
||||
height: 100%;
|
||||
width: 18.75rem;
|
||||
transition: all 0.2s;
|
||||
z-index: 4;
|
||||
}
|
||||
.app-calendar-wrapper .app-calendar-sidebar.show {
|
||||
left: 0;
|
||||
}
|
||||
.app-calendar-wrapper .app-calendar-sidebar .flatpickr-calendar {
|
||||
box-shadow: none;
|
||||
}
|
||||
.app-calendar-wrapper .app-calendar-sidebar .flatpickr-calendar .flatpickr-month,
|
||||
.app-calendar-wrapper .app-calendar-sidebar .flatpickr-calendar .flatpickr-weekday,
|
||||
.app-calendar-wrapper .app-calendar-sidebar .flatpickr-calendar .flatpickr-weekdays {
|
||||
background: rgba(0, 0, 0, 0);
|
||||
}
|
||||
.app-calendar-wrapper .app-calendar-sidebar .flatpickr-calendar .flatpickr-days {
|
||||
border: 0;
|
||||
}
|
||||
.app-calendar-wrapper .app-calendar-sidebar .flatpickr-calendar:focus {
|
||||
outline: 0;
|
||||
}
|
||||
.app-calendar-wrapper .app-calendar-content {
|
||||
position: relative;
|
||||
}
|
||||
.app-calendar-wrapper .fc-toolbar h2 {
|
||||
font-size: 1.5rem;
|
||||
line-height: 2.375rem;
|
||||
}
|
||||
@media (max-width: 767.98px) {
|
||||
.app-calendar-wrapper .fc-toolbar h2 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
.app-calendar-wrapper .fc-toolbar-chunk {
|
||||
overflow: auto;
|
||||
}
|
||||
.app-calendar-wrapper table.fc-scrollgrid {
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
}
|
||||
.app-calendar-wrapper table.fc-scrollgrid th,
|
||||
.app-calendar-wrapper table.fc-scrollgrid td {
|
||||
border-right: 0;
|
||||
}
|
||||
.app-calendar-wrapper .fc-timeGridDay-view table.fc-scrollgrid tbody tr:not(.fc-scrollgrid-section:first-of-type) td,
|
||||
.app-calendar-wrapper .fc-timeGridWeek-view table.fc-scrollgrid tbody tr:not(.fc-scrollgrid-section:first-of-type) td {
|
||||
border-bottom: 0;
|
||||
}
|
||||
.app-calendar-wrapper .fc-dayGridMonth-view table.fc-scrollgrid td {
|
||||
border-bottom: 0 !important;
|
||||
}
|
||||
.app-calendar-wrapper .fc-header-toolbar {
|
||||
margin-bottom: 1.5rem !important;
|
||||
}
|
||||
.app-calendar-wrapper .fc-view-container {
|
||||
margin: 0 -1.6rem;
|
||||
}
|
||||
.app-calendar-wrapper .event-sidebar .ql-editor {
|
||||
min-height: 5rem;
|
||||
}
|
||||
.app-calendar-wrapper .event-sidebar .select2 .select2-selection__choice {
|
||||
display: flex;
|
||||
}
|
||||
.app-calendar-wrapper .event-sidebar .select2 .select2-selection__choice .avatar {
|
||||
display: none;
|
||||
}
|
||||
@media (min-width: 992px) {
|
||||
.app-calendar-wrapper .app-calendar-sidebar {
|
||||
position: static;
|
||||
height: auto;
|
||||
background-color: rgba(0, 0, 0, 0) !important;
|
||||
}
|
||||
.app-calendar-wrapper .app-calendar-sidebar .flatpickr-days {
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
[dir='rtl'] .app-calendar-wrapper .fc .fc-toolbar .fc-sidebarToggle-button {
|
||||
order: 1;
|
||||
}
|
||||
[dir='rtl'] .app-calendar-wrapper .app-calendar-sidebar {
|
||||
left: auto;
|
||||
right: calc(-18.75rem - 1.2rem);
|
||||
}
|
||||
[dir='rtl'] .app-calendar-wrapper .app-calendar-sidebar.show {
|
||||
left: auto;
|
||||
right: 0;
|
||||
}
|
||||
.light-style .app-calendar-wrapper .app-calendar-sidebar {
|
||||
background-color: #fff;
|
||||
}
|
||||
.dark-style .app-calendar-wrapper .app-calendar-sidebar {
|
||||
background-color: #2b2c40;
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import router from "@/router/index";
|
||||
const $api = axios.create({
|
||||
baseURL: import.meta.env.VITE_API_URL,
|
||||
timeout: 300000,
|
||||
withCredentials : true
|
||||
})
|
||||
|
||||
/**
|
||||
|
||||
129
src/common/convertImageToWebp.js
Normal file
129
src/common/convertImageToWebp.js
Normal file
@ -0,0 +1,129 @@
|
||||
/**
|
||||
image to webp
|
||||
@사용법
|
||||
const webpFile = await convertImageToWebp(this.attach);
|
||||
attach = webpFile[0];
|
||||
*/
|
||||
import heic2any from 'heic2any';
|
||||
import { imageConverter } from 'upload-images-converter';
|
||||
|
||||
const calculateAspectRatioSize = (originWidth, originHeight, maxWidthOrHeight) => {
|
||||
const maxSize = Math.max(originWidth, originHeight);
|
||||
|
||||
if (maxSize <= maxWidthOrHeight) {
|
||||
return { width: originWidth, height: originHeight };
|
||||
}
|
||||
|
||||
if (originWidth === maxSize) {
|
||||
const width = maxWidthOrHeight;
|
||||
const height = Number(((originHeight * maxWidthOrHeight) / originWidth).toFixed(1));
|
||||
|
||||
return { width, height };
|
||||
}
|
||||
|
||||
const width = Number(((originWidth * maxWidthOrHeight) / originHeight).toFixed(1));
|
||||
const height = maxWidthOrHeight;
|
||||
|
||||
return { width, height };
|
||||
};
|
||||
|
||||
const getImageSize = (imageFile) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!imageFile) {
|
||||
resolve({ originWidth: 100, originHeight: 100 }); // Default size
|
||||
return;
|
||||
}
|
||||
|
||||
const img = new Image();
|
||||
const reader = new FileReader();
|
||||
|
||||
img.onload = () => {
|
||||
const originWidth = img.naturalWidth;
|
||||
const originHeight = img.naturalHeight;
|
||||
|
||||
resolve({ originWidth, originHeight });
|
||||
};
|
||||
|
||||
reader.onload = () => {
|
||||
img.src = reader.result?.toString() ?? '';
|
||||
};
|
||||
|
||||
reader.readAsDataURL(imageFile);
|
||||
|
||||
img.onerror = error => {
|
||||
reject(error);
|
||||
};
|
||||
|
||||
reader.onerror = error => {
|
||||
reject(error);
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const convertImageToWebp = async (imageFile) => {
|
||||
if (typeof imageFile === 'string' || imageFile.name.split('.')[1].toLowerCase() === 'webp') {
|
||||
return imageFile;
|
||||
}
|
||||
|
||||
if (!imageFile || !(imageFile instanceof Blob)) {
|
||||
const placeholderBlob = new Blob([], { type: 'image/webp' });
|
||||
const placeholderFile = new File([placeholderBlob], `${Date.now().toString()}.webp`);
|
||||
const dataTransfer = new DataTransfer();
|
||||
dataTransfer.items.add(placeholderFile);
|
||||
return dataTransfer.files;
|
||||
}
|
||||
|
||||
const fileExtension = imageFile.name.split('.').pop().toLowerCase();
|
||||
|
||||
if (fileExtension === 'heic') {
|
||||
try {
|
||||
const webpBlob = await heic2any({
|
||||
blob: imageFile,
|
||||
toType: 'image/webp',
|
||||
quality: 0.8,
|
||||
});
|
||||
|
||||
const { originWidth, originHeight } = await getImageSize(webpBlob);
|
||||
const maxWidthOrHeight = 1200;
|
||||
|
||||
const { width, height } = calculateAspectRatioSize(originWidth, originHeight, maxWidthOrHeight);
|
||||
|
||||
const compressedBlob = await imageConverter({ files: [webpBlob], width, height });
|
||||
|
||||
const outputWebpFile = new File([compressedBlob[0]], `${Date.now().toString()}.webp`);
|
||||
|
||||
const dataTransfer = new DataTransfer();
|
||||
dataTransfer.items.add(outputWebpFile);
|
||||
|
||||
return dataTransfer.files;
|
||||
} catch (error) {
|
||||
console.error('HEIC 변환 오류:', error);
|
||||
throw error;
|
||||
} finally {
|
||||
// 로딩있으면 여기서 끝
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
const { originWidth, originHeight } = await getImageSize(imageFile);
|
||||
const maxWidthOrHeight = 1200;
|
||||
|
||||
const { width, height } = calculateAspectRatioSize(originWidth, originHeight, maxWidthOrHeight);
|
||||
|
||||
const compressedBlob = await imageConverter({ files: [imageFile], width, height });
|
||||
|
||||
const outputWebpFile = new File([compressedBlob[0]], `${Date.now().toString()}.webp`);
|
||||
|
||||
const dataTransfer = new DataTransfer();
|
||||
dataTransfer.items.add(outputWebpFile);
|
||||
|
||||
return dataTransfer.files;
|
||||
} catch (error) {
|
||||
console.error('HEIC 변환 오류:', error);
|
||||
throw error;
|
||||
} finally {
|
||||
// 로딩있으면 여기서 끝
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export { convertImageToWebp };
|
||||
@ -5,3 +5,97 @@ export const isEmpty = (value) => {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
export const isPC = () => {
|
||||
const varUA = navigator.userAgent.toLowerCase();
|
||||
const isMobile = /iphone|ipad|ipod|android|blackberry|iemobile|opera mini/i.test(varUA);
|
||||
return !isMobile;
|
||||
}
|
||||
|
||||
export const returnMimeTypes = (extension) => {
|
||||
const mimeTypes = {
|
||||
// Archive
|
||||
'7z': 'application/x-7z-compressed',
|
||||
'gz': 'application/gzip',
|
||||
'tar': 'application/x-tar',
|
||||
'zip': 'application/zip',
|
||||
|
||||
// Audio
|
||||
'aac': 'audio/aac',
|
||||
'mp3': 'audio/mpeg',
|
||||
'ogg': 'audio/ogg',
|
||||
'opus': 'audio/opus',
|
||||
'wav': 'audio/wav',
|
||||
'webm': 'audio/webm',
|
||||
|
||||
// Image
|
||||
'bmp': 'image/bmp',
|
||||
'gif': 'image/gif',
|
||||
'ico': 'image/x-icon',
|
||||
'jpeg': 'image/jpeg',
|
||||
'jpg': 'image/jpeg',
|
||||
'png': 'image/png',
|
||||
'svg': 'image/svg+xml',
|
||||
'tiff': 'image/tiff',
|
||||
'webp': 'image/webp',
|
||||
|
||||
// Video
|
||||
'avi': 'video/x-msvideo',
|
||||
'mp4': 'video/mp4',
|
||||
'mpeg': 'video/mpeg',
|
||||
'ogg': 'video/ogg',
|
||||
'webm': 'video/webm',
|
||||
'mov': 'video/quicktime',
|
||||
'wmv': 'video/x-ms-wmv',
|
||||
|
||||
// Document
|
||||
'doc': 'application/msword',
|
||||
'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'epub': 'application/epub+zip',
|
||||
'html': 'text/html',
|
||||
'htm': 'text/html',
|
||||
'odt': 'application/vnd.oasis.opendocument.text',
|
||||
'pdf': 'application/pdf',
|
||||
'ppt': 'application/vnd.ms-powerpoint',
|
||||
'pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||
'rtf': 'application/rtf',
|
||||
'txt': 'text/plain',
|
||||
'xls': 'application/vnd.ms-excel',
|
||||
'xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
'xml': 'application/xml',
|
||||
'json': 'application/json',
|
||||
'csv': 'text/csv',
|
||||
|
||||
// Code / Script
|
||||
'css': 'text/css',
|
||||
'js': 'text/javascript',
|
||||
'json': 'application/json',
|
||||
'php': 'application/x-httpd-php',
|
||||
'xml': 'application/xml',
|
||||
|
||||
// Font
|
||||
'woff': 'font/woff',
|
||||
'woff2': 'font/woff2',
|
||||
'ttf': 'font/ttf',
|
||||
'otf': 'font/otf',
|
||||
|
||||
// Web
|
||||
'jsonld': 'application/ld+json',
|
||||
'webmanifest': 'application/manifest+json',
|
||||
|
||||
// Others
|
||||
'bin': 'application/octet-stream',
|
||||
'exe': 'application/octet-stream',
|
||||
'apk': 'application/vnd.android.package-archive',
|
||||
'dll': 'application/x-msdownload',
|
||||
'iso': 'application/x-iso9660-image',
|
||||
'msi': 'application/x-msi',
|
||||
'torrent': 'application/x-bittorrent',
|
||||
|
||||
// Unknown
|
||||
'default': 'application/octet-stream'
|
||||
}
|
||||
|
||||
const ext = extension.trim().toLowerCase();
|
||||
return mimeTypes[ext] || mimeTypes['default'];
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user