다크모드 처리
This commit is contained in:
parent
1bc7c80fd0
commit
d7cc575d2f
@ -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 -->
|
||||
|
||||
6
public/css/custom-dark.css
Normal file
6
public/css/custom-dark.css
Normal file
@ -0,0 +1,6 @@
|
||||
/* 여기에 dark css 작성 */
|
||||
|
||||
|
||||
.display-block {
|
||||
display: block !important;
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
/* 여기에 css 작성 */
|
||||
/* 여기에 light css 작성 */
|
||||
|
||||
|
||||
.display-block {
|
||||
|
||||
29
src/App.vue
29
src/App.vue
@ -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>
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
.pt-2.px-3 {
|
||||
position: relative; /* Flatpickr의 위치 기준이 될 부모 */
|
||||
}
|
||||
|
||||
.flatpickr-calendar {
|
||||
width: 100% !important;
|
||||
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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -13,3 +13,11 @@ app.use(router)
|
||||
.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
47
src/stores/darkmode.js
Normal 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
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user