다크모드 처리

This commit is contained in:
ckx6954 2024-12-21 14:43:00 +09:00
parent 1bc7c80fd0
commit d7cc575d2f
10 changed files with 197 additions and 51 deletions

View File

@ -31,7 +31,7 @@
/>
<link rel="stylesheet" href="/css/font.css" />
<link rel="stylesheet" href="/css/custom.css" />
<!-- Icons -->
<link rel="stylesheet" href="/vendor/fonts/boxicons.css" />
@ -39,8 +39,9 @@
<link rel="stylesheet" href="/vendor/fonts/flag-icons.css" />
<!-- Core CSS -->
<link rel="stylesheet" href="/vendor/css/rtl/core.css" class="template-customizer-core-css" />
<link rel="stylesheet" href="/vendor/css/rtl/theme-default.css" class="template-customizer-theme-css" />
<link rel="stylesheet" href="/css/custom.css" class="custom-css" />
<link rel="stylesheet" href="/vendor/css/rtl/core.css" class="core-css" />
<link rel="stylesheet" href="/vendor/css/rtl/theme-default.css" class="theme-css" />
<link rel="stylesheet" href="/css/demo.css" />
<!-- Vendors CSS -->

View File

@ -0,0 +1,6 @@
/* 여기에 dark css 작성 */
.display-block {
display: block !important;
}

View File

@ -1,4 +1,4 @@
/* 여기에 css 작성 */
/* 여기에 light css 작성 */
.display-block {

View File

@ -8,35 +8,6 @@
<script setup>
import { onMounted } from 'vue';
import NormalLayout from './layouts/NormalLayout.vue';
onMounted(() => {
const storedStyle = localStorage.getItem('templateCustomizer-vertical-menu-template--Style');
function switchToDarkMode() {
//
document.documentElement.classList.add('dark-style');
document.documentElement.classList.remove('light-style');
document.querySelector('.template-customizer-core-css').setAttribute('href', '/vendor/css/rtl/core-dark.css');
document.querySelector('.template-customizer-theme-css').setAttribute('href', '/vendor/css/rtl/theme-default-dark.css');
localStorage.setItem('templateCustomizer-vertical-menu-template--Style', 'dark'); //
}
function switchToLightMode() {
//
document.querySelector('.template-customizer-core-css').setAttribute('href', '/vendor/css/rtl/core.css');
document.querySelector('.template-customizer-theme-css').setAttribute('href', '/vendor/css/rtl/theme-default.css');
document.documentElement.classList.remove('dark-style');
document.documentElement.classList.add('light-style');
localStorage.setItem('templateCustomizer-vertical-menu-template--Style', 'light'); //
}
if (storedStyle === 'dark') {
switchToDarkMode();
} else {
switchToLightMode(); //
}
});
</script>
<style>
</style>

View File

@ -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;

View File

@ -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; /* 패딩 포함 계산 */
}
</style>

View File

@ -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);
}
}
});
</script>
<style>

View File

@ -158,6 +158,9 @@
</li>
<!-- Quick links -->
<button @click="switchToDarkMode">다크 모드</button>
<button @click="switchToLightMode">라이트 모드</button>
<!-- Notification -->
<li class="nav-item dropdown-notifications navbar-dropdown dropdown me-3 me-xl-2">
<a
@ -486,5 +489,18 @@
</div>
</nav>
</template>
<script setup></script>
<script setup>
import { useThemeStore } from '@s/darkmode';
import { onMounted } from 'vue';
const { isDarkMode, switchToDarkMode, switchToLightMode } = useThemeStore();
onMounted(() => {
if (isDarkMode) {
switchToDarkMode();
} else {
switchToLightMode();
}
});
</script>
<style></style>

View File

@ -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 막기
}

47
src/stores/darkmode.js Normal file
View File

@ -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
}
],
}
});