다크모드 처리
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/font.css" />
|
||||||
<link rel="stylesheet" href="/css/custom.css" />
|
|
||||||
|
|
||||||
<!-- Icons -->
|
<!-- Icons -->
|
||||||
<link rel="stylesheet" href="/vendor/fonts/boxicons.css" />
|
<link rel="stylesheet" href="/vendor/fonts/boxicons.css" />
|
||||||
@ -39,8 +39,9 @@
|
|||||||
<link rel="stylesheet" href="/vendor/fonts/flag-icons.css" />
|
<link rel="stylesheet" href="/vendor/fonts/flag-icons.css" />
|
||||||
|
|
||||||
<!-- Core CSS -->
|
<!-- Core CSS -->
|
||||||
<link rel="stylesheet" href="/vendor/css/rtl/core.css" class="template-customizer-core-css" />
|
<link rel="stylesheet" href="/css/custom.css" class="custom-css" />
|
||||||
<link rel="stylesheet" href="/vendor/css/rtl/theme-default.css" class="template-customizer-theme-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" />
|
<link rel="stylesheet" href="/css/demo.css" />
|
||||||
|
|
||||||
<!-- Vendors 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 {
|
.display-block {
|
||||||
|
|||||||
29
src/App.vue
29
src/App.vue
@ -8,35 +8,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted } from 'vue';
|
import { onMounted } from 'vue';
|
||||||
import NormalLayout from './layouts/NormalLayout.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>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import router from "@/router/index";
|
import router from "@/router/index";
|
||||||
|
|
||||||
const $ = axios.create({
|
const $api = axios.create({
|
||||||
baseURL: import.meta.env.VITE_API_URL,
|
baseURL: import.meta.env.VITE_API_URL,
|
||||||
timeout: 300000,
|
timeout: 300000,
|
||||||
})
|
})
|
||||||
@ -10,7 +10,7 @@ const $ = axios.create({
|
|||||||
* Default Content-Type : json
|
* Default Content-Type : json
|
||||||
* form 사용 시 옵션에 { isFromDate : true } 추가
|
* form 사용 시 옵션에 { isFromDate : true } 추가
|
||||||
*/
|
*/
|
||||||
$.interceptors.request.use(
|
$api.interceptors.request.use(
|
||||||
function (config) {
|
function (config) {
|
||||||
|
|
||||||
let contentType = 'application/json';
|
let contentType = 'application/json';
|
||||||
@ -28,7 +28,7 @@ $.interceptors.request.use(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// 응답 인터셉터 추가하기
|
// 응답 인터셉터 추가하기
|
||||||
$.interceptors.response.use(
|
$api.interceptors.response.use(
|
||||||
function (response) {
|
function (response) {
|
||||||
// 2xx 범위에 있는 상태 코드는 이 함수를 트리거 합니다.
|
// 2xx 범위에 있는 상태 코드는 이 함수를 트리거 합니다.
|
||||||
// 응답 데이터가 있는 작업 수행
|
// 응답 데이터가 있는 작업 수행
|
||||||
@ -38,4 +38,4 @@ $.interceptors.response.use(
|
|||||||
// 응답 오류가 있는 작업 수행
|
// 응답 오류가 있는 작업 수행
|
||||||
return Promise.reject(error);
|
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 from 'vue-flatpickr-component';
|
||||||
import 'flatpickr/dist/flatpickr.min.css'
|
import 'flatpickr/dist/flatpickr.min.css'
|
||||||
import '@/assets/css/app-calendar.css'
|
import '@/assets/css/app-calendar.css'
|
||||||
// 기본만 넣었는데 기능 추가해야할듯
|
import { useThemeStore } from '@s/darkmode';
|
||||||
|
|
||||||
//dark모드 유무
|
const { isDarkMode } = useThemeStore();
|
||||||
const isDarkMode = ref(false);
|
|
||||||
|
// 기본만 넣었는데 기능 추가해야할듯
|
||||||
|
|
||||||
const key1 = 'vrkIY7jUy82WRHIozbBtz4n2L89jAqNDY%2B4DmNpmasTex%2FNDySN7FDYwBqPj1p%2BxXLg13BzYfgHPt6eipWhH8Q%3D%3D';
|
const key1 = 'vrkIY7jUy82WRHIozbBtz4n2L89jAqNDY%2B4DmNpmasTex%2FNDySN7FDYwBqPj1p%2BxXLg13BzYfgHPt6eipWhH8Q%3D%3D';
|
||||||
const key2 = 'vrkIY7jUy82WRHIozbBtz4n2L89jAqNDY+4DmNpmasTex/NDySN7FDYwBqPj1p+xXLg13BzYfgHPt6eipWhH8Q==';
|
const key2 = 'vrkIY7jUy82WRHIozbBtz4n2L89jAqNDY+4DmNpmasTex/NDySN7FDYwBqPj1p+xXLg13BzYfgHPt6eipWhH8Q==';
|
||||||
@ -256,7 +257,7 @@ const calendarOptions = reactive({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const loadFlatpickrTheme = async () => {
|
const loadFlatpickrTheme = async () => {
|
||||||
if (isDarkMode.value) {
|
if (isDarkMode) {
|
||||||
await import('flatpickr/dist/themes/dark.css');
|
await import('flatpickr/dist/themes/dark.css');
|
||||||
} else {
|
} else {
|
||||||
await import('flatpickr/dist/themes/light.css');
|
await import('flatpickr/dist/themes/light.css');
|
||||||
@ -265,10 +266,6 @@ const loadFlatpickrTheme = async () => {
|
|||||||
|
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const storedStyle = localStorage.getItem('templateCustomizer-vertical-menu-template--Style');
|
|
||||||
if (storedStyle === 'dark') {
|
|
||||||
isDarkMode.value = true;
|
|
||||||
}
|
|
||||||
await loadFlatpickrTheme();
|
await loadFlatpickrTheme();
|
||||||
fetchData();
|
fetchData();
|
||||||
});
|
});
|
||||||
@ -292,7 +289,22 @@ onMounted(async () => {
|
|||||||
display: none;
|
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>
|
</style>
|
||||||
|
|||||||
@ -53,6 +53,7 @@
|
|||||||
import Quill from 'quill';
|
import Quill from 'quill';
|
||||||
import 'quill/dist/quill.snow.css';
|
import 'quill/dist/quill.snow.css';
|
||||||
import { onMounted, ref, watch, defineEmits } from 'vue';
|
import { onMounted, ref, watch, defineEmits } from 'vue';
|
||||||
|
import $api from '@/common/axios-interceptor';
|
||||||
|
|
||||||
const editor = ref(null);
|
const editor = ref(null);
|
||||||
const font = ref('nanum-gothic');
|
const font = ref('nanum-gothic');
|
||||||
@ -90,7 +91,91 @@ onMounted(() => {
|
|||||||
quillInstance.format('font', font.value);
|
quillInstance.format('font', font.value);
|
||||||
quillInstance.format('size', fontSize.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>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@ -158,6 +158,9 @@
|
|||||||
</li>
|
</li>
|
||||||
<!-- Quick links -->
|
<!-- Quick links -->
|
||||||
|
|
||||||
|
<button @click="switchToDarkMode">다크 모드</button>
|
||||||
|
<button @click="switchToLightMode">라이트 모드</button>
|
||||||
|
|
||||||
<!-- Notification -->
|
<!-- Notification -->
|
||||||
<li class="nav-item dropdown-notifications navbar-dropdown dropdown me-3 me-xl-2">
|
<li class="nav-item dropdown-notifications navbar-dropdown dropdown me-3 me-xl-2">
|
||||||
<a
|
<a
|
||||||
@ -486,5 +489,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</template>
|
</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>
|
<style></style>
|
||||||
|
|||||||
14
src/main.js
14
src/main.js
@ -10,6 +10,14 @@ pinia.use(piniaPersist)
|
|||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
app.use(router)
|
app.use(router)
|
||||||
.use(pinia)
|
.use(pinia)
|
||||||
.use(dayjs)
|
.use(dayjs)
|
||||||
.mount('#app')
|
.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