diff --git a/index.html b/index.html
index 5bd3857..12e3b00 100644
--- a/index.html
+++ b/index.html
@@ -31,7 +31,7 @@
/>
-
+
@@ -39,8 +39,9 @@
-
-
+
+
+
diff --git a/public/css/custom-dark.css b/public/css/custom-dark.css
new file mode 100644
index 0000000..5be91ed
--- /dev/null
+++ b/public/css/custom-dark.css
@@ -0,0 +1,6 @@
+/* 여기에 dark css 작성 */
+
+
+.display-block {
+ display: block !important;
+}
\ No newline at end of file
diff --git a/public/css/custom.css b/public/css/custom.css
index 568171a..9f01857 100644
--- a/public/css/custom.css
+++ b/public/css/custom.css
@@ -1,4 +1,4 @@
-/* 여기에 css 작성 */
+/* 여기에 light css 작성 */
.display-block {
diff --git a/src/App.vue b/src/App.vue
index f8850a3..1d5f928 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -8,35 +8,6 @@
diff --git a/src/common/axios-interceptor.js b/src/common/axios-interceptor.js
index b0bd0a0..c286882 100644
--- a/src/common/axios-interceptor.js
+++ b/src/common/axios-interceptor.js
@@ -1,7 +1,7 @@
import axios from "axios";
import router from "@/router/index";
-const $ = axios.create({
+const $api = axios.create({
baseURL: import.meta.env.VITE_API_URL,
timeout: 300000,
})
@@ -10,7 +10,7 @@ const $ = axios.create({
* Default Content-Type : json
* form 사용 시 옵션에 { isFromDate : true } 추가
*/
-$.interceptors.request.use(
+$api.interceptors.request.use(
function (config) {
let contentType = 'application/json';
@@ -28,7 +28,7 @@ $.interceptors.request.use(
);
// 응답 인터셉터 추가하기
-$.interceptors.response.use(
+$api.interceptors.response.use(
function (response) {
// 2xx 범위에 있는 상태 코드는 이 함수를 트리거 합니다.
// 응답 데이터가 있는 작업 수행
@@ -38,4 +38,4 @@ $.interceptors.response.use(
// 응답 오류가 있는 작업 수행
return Promise.reject(error);
});
-export default $;
+export default $api;
diff --git a/src/components/calendar/SampleCalendar.vue b/src/components/calendar/SampleCalendar.vue
index 71d68eb..f3362f3 100644
--- a/src/components/calendar/SampleCalendar.vue
+++ b/src/components/calendar/SampleCalendar.vue
@@ -73,10 +73,11 @@ import FormInput from '../input/FormInput.vue';
import FlatPickr from 'vue-flatpickr-component';
import 'flatpickr/dist/flatpickr.min.css'
import '@/assets/css/app-calendar.css'
-// 기본만 넣었는데 기능 추가해야할듯
+import { useThemeStore } from '@s/darkmode';
-//dark모드 유무
-const isDarkMode = ref(false);
+const { isDarkMode } = useThemeStore();
+
+// 기본만 넣었는데 기능 추가해야할듯
const key1 = 'vrkIY7jUy82WRHIozbBtz4n2L89jAqNDY%2B4DmNpmasTex%2FNDySN7FDYwBqPj1p%2BxXLg13BzYfgHPt6eipWhH8Q%3D%3D';
const key2 = 'vrkIY7jUy82WRHIozbBtz4n2L89jAqNDY+4DmNpmasTex/NDySN7FDYwBqPj1p+xXLg13BzYfgHPt6eipWhH8Q==';
@@ -256,7 +257,7 @@ const calendarOptions = reactive({
});
const loadFlatpickrTheme = async () => {
- if (isDarkMode.value) {
+ if (isDarkMode) {
await import('flatpickr/dist/themes/dark.css');
} else {
await import('flatpickr/dist/themes/light.css');
@@ -265,10 +266,6 @@ const loadFlatpickrTheme = async () => {
onMounted(async () => {
- const storedStyle = localStorage.getItem('templateCustomizer-vertical-menu-template--Style');
- if (storedStyle === 'dark') {
- isDarkMode.value = true;
- }
await loadFlatpickrTheme();
fetchData();
});
@@ -292,7 +289,22 @@ onMounted(async () => {
display: none;
}
-.flatpickr-calendar{
- width: 100% !important;
+
+.pt-2.px-3 {
+ position: relative; /* Flatpickr의 위치 기준이 될 부모 */
+}
+
+.flatpickr-calendar {
+ position: relative !important; /* 부모 내부에 맞게 그리기 */
+ display: block !important;
+ width: 100% !important; /* 부모 크기에 맞게 확장 */
+ height: auto !important; /* 내용에 따라 높이 조정 */
+ z-index: 1; /* 부모 위에만 표시 */
+}
+
+.flatpickr-rContainer,.dayContainer,.flatpickr-days{
+ display: inline-block !important;
+ width: 100% !important; /* 부모 안에서 전체 너비 차지 */
+ box-sizing: border-box !important; /* 패딩 포함 계산 */
}
diff --git a/src/components/editor/QEditor.vue b/src/components/editor/QEditor.vue
index c141fea..405f621 100644
--- a/src/components/editor/QEditor.vue
+++ b/src/components/editor/QEditor.vue
@@ -53,6 +53,7 @@
import Quill from 'quill';
import 'quill/dist/quill.snow.css';
import { onMounted, ref, watch, defineEmits } from 'vue';
+import $api from '@/common/axios-interceptor';
const editor = ref(null);
const font = ref('nanum-gothic');
@@ -90,7 +91,91 @@ onMounted(() => {
quillInstance.format('font', font.value);
quillInstance.format('size', fontSize.value);
});
+
+ // 이미지 업로드 및 삭제 감지 로직
+ // 아직 서버에 실험 안해봄 ***********처리부탁***********
+ let imageUrls = new Set();
+
+ quillInstance.getModule('toolbar').addHandler('image', () => {
+ selectLocalImage();
+ });
+
+ quillInstance.on('text-change', (delta, oldDelta, source) => {
+ emit('update:data', quillInstance.root.innerHTML);
+ delta.ops.forEach(op => {
+ if (op.insert && typeof op.insert === 'object' && op.insert.image) {
+ const imageUrl = op.insert.image;
+ imageUrls.add(imageUrl);
+ } else if (op.delete) {
+ checkForDeletedImages();
+ }
+ });
+ });
+
+ function selectLocalImage() {
+ const input = document.createElement('input');
+ input.setAttribute('type', 'file');
+ input.setAttribute('accept', 'image/*');
+ input.click();
+
+ input.onchange = () => {
+ const file = input.files[0];
+ if (file) {
+ const reader = new FileReader();
+ reader.onload = async (e) => {
+ const range = quillInstance.getSelection();
+ const base64Image = e.target.result;
+
+ try {
+ const serverImageUrl = await uploadImageToServer(base64Image);
+ quillInstance.insertEmbed(range.index, 'image', serverImageUrl);
+ imageUrls.add(serverImageUrl);
+ } catch (error) {
+ console.error('이미지 업로드 중 오류 발생:', error);
+ }
+ };
+ reader.readAsDataURL(file);
+ }
+ };
+ }
+
+
+ function checkForDeletedImages() {
+ const editorImages = document.querySelectorAll('#editor img');
+ const currentImages = new Set(Array.from(editorImages).map(img => img.src));
+
+ imageUrls.forEach(url => {
+ if (!currentImages.has(url)) {
+ imageUrls.delete(url);
+ removeImageFromServer(url);
+ }
+ });
+ }
+
+ async function uploadImageToServer(base64Image) {
+ try {
+ const response = await $api.post('/img/upload', {
+ image: base64Image,
+ });
+ return response.data.url; // 서버에서 반환한 이미지 URL
+ } catch (error) {
+ console.error('서버 업로드 중 오류 발생:', error);
+ throw error;
+ }
+ }
+
+ async function removeImageFromServer(imageUrl) {
+ try {
+ await $api.delete('/img/delete', {
+ data: { url: imageUrl },
+ });
+ console.log(`서버에서 이미지 삭제: ${imageUrl}`);
+ } catch (error) {
+ console.error('서버 이미지 삭제 중 오류 발생:', error);
+ }
+ }
});
+
diff --git a/src/main.js b/src/main.js
index b546ff8..14f4f3b 100644
--- a/src/main.js
+++ b/src/main.js
@@ -10,6 +10,14 @@ pinia.use(piniaPersist)
const app = createApp(App)
app.use(router)
-.use(pinia)
-.use(dayjs)
-.mount('#app')
+ .use(pinia)
+ .use(dayjs)
+ .mount('#app')
+
+
+if (import.meta.env.MODE === "prod") {
+ const console = window.console || {};
+ console.log = function no_console() { }; // console log 막기
+ console.warn = function no_console() { }; // console warning 막기
+ console.error = function () { }; // console error 막기
+}
diff --git a/src/stores/darkmode.js b/src/stores/darkmode.js
new file mode 100644
index 0000000..c33546d
--- /dev/null
+++ b/src/stores/darkmode.js
@@ -0,0 +1,47 @@
+import { ref, computed } from 'vue';
+import { defineStore } from 'pinia';
+
+export const useThemeStore = defineStore('theme', () => {
+ const theme = ref('light'); // 초기 상태는 light
+
+ const isDarkMode = computed(() => theme.value === 'dark');
+
+ const customClass = '.custom-css'; // custom.css 경로
+ const coreClass = '.core-css';
+ const themeClass = '.theme-css';
+
+ const switchToDarkMode = () => {
+ theme.value = 'dark';
+ document.documentElement.classList.add('dark-style');
+ document.documentElement.classList.remove('light-style');
+ document.querySelector(customClass).setAttribute('href', '/css/custom-dark.css');
+ document.querySelector(coreClass).setAttribute('href', '/vendor/css/rtl/core-dark.css');
+ document.querySelector(themeClass).setAttribute('href', '/vendor/css/rtl/theme-default-dark.css');
+ };
+
+ const switchToLightMode = () => {
+ theme.value = 'light';
+ document.documentElement.classList.remove('dark-style');
+ document.documentElement.classList.add('light-style');
+ document.querySelector(customClass).setAttribute('href', '/css/custom.css');
+ document.querySelector(coreClass).setAttribute('href', '/vendor/css/rtl/core.css');
+ document.querySelector(themeClass).setAttribute('href', '/vendor/css/rtl/theme-default.css');
+ };
+
+ return {
+ theme,
+ isDarkMode,
+ switchToDarkMode,
+ switchToLightMode,
+ };
+}, {
+ persist: {
+ enabled: true,
+ strategies: [
+ {
+ storage: localStorage
+ }
+ ],
+ }
+
+});