123 lines
2.4 KiB
Vue
123 lines
2.4 KiB
Vue
<template>
|
|
<div id="app">
|
|
<router-view />
|
|
|
|
<!-- 全局通知系統 -->
|
|
<ToastContainer />
|
|
|
|
<!-- 全局彈窗系統 -->
|
|
<ModalContainer />
|
|
|
|
<!-- 全局載入指示器 -->
|
|
<q-ajax-bar
|
|
position="top"
|
|
color="primary-teal"
|
|
size="4px"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { onMounted, onUnmounted } from 'vue'
|
|
import { useAuthStore } from '@/stores/auth'
|
|
import { useUIStore } from '@/stores/ui'
|
|
import ToastContainer from '@/components/ui/ToastContainer.vue'
|
|
import ModalContainer from '@/components/ui/ModalContainer.vue'
|
|
|
|
const authStore = useAuthStore()
|
|
const uiStore = useUIStore()
|
|
|
|
onMounted(async () => {
|
|
// 初始化UI系統
|
|
uiStore.initializeUI()
|
|
|
|
// 初始化認證狀態
|
|
await authStore.initialize()
|
|
|
|
// 設定全局鍵盤事件監聽
|
|
document.addEventListener('keydown', handleGlobalKeydown)
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
// 清理資源
|
|
uiStore.cleanup()
|
|
document.removeEventListener('keydown', handleGlobalKeydown)
|
|
})
|
|
|
|
const handleGlobalKeydown = (event: KeyboardEvent) => {
|
|
// ESC 鍵關閉彈窗和選單
|
|
if (event.key === 'Escape') {
|
|
if (uiStore.currentModal) {
|
|
uiStore.hideModal()
|
|
}
|
|
if (uiStore.mobileMenuOpen) {
|
|
uiStore.closeMobileMenu()
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
/* 全局樣式重設和基礎設定 */
|
|
#app {
|
|
font-family: 'Inter', 'Noto Sans TC', -apple-system, BlinkMacSystemFont, sans-serif;
|
|
-webkit-font-smoothing: antialiased;
|
|
-moz-osx-font-smoothing: grayscale;
|
|
color: #2C3E50;
|
|
background: #F7F9FC;
|
|
min-height: 100vh;
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
|
|
/* 全局滾動條樣式 */
|
|
::-webkit-scrollbar {
|
|
width: 8px;
|
|
height: 8px;
|
|
}
|
|
|
|
::-webkit-scrollbar-track {
|
|
background: rgba(0,0,0, 0.1);
|
|
border-radius: 4px;
|
|
}
|
|
|
|
::-webkit-scrollbar-thumb {
|
|
background: rgba(0,0,0, 0.3);
|
|
border-radius: 4px;
|
|
}
|
|
|
|
::-webkit-scrollbar-thumb:hover {
|
|
background: rgba(0,0,0, 0.5);
|
|
}
|
|
|
|
/* 無障礙聚焦樣式 */
|
|
*:focus-visible {
|
|
outline: 2px solid #00E5CC;
|
|
outline-offset: 2px;
|
|
border-radius: 2px;
|
|
}
|
|
|
|
/* 全局動畫 */
|
|
.fade-enter-active,
|
|
.fade-leave-active {
|
|
transition: opacity 0.3s ease;
|
|
}
|
|
|
|
.fade-enter-from,
|
|
.fade-leave-to {
|
|
opacity: 0;
|
|
}
|
|
|
|
.slide-enter-active,
|
|
.slide-leave-active {
|
|
transition: transform 0.3s ease;
|
|
}
|
|
|
|
.slide-enter-from {
|
|
transform: translateX(-100%);
|
|
}
|
|
|
|
.slide-leave-to {
|
|
transform: translateX(100%);
|
|
}
|
|
</style> |