You've already forked SmartisanNote.Remake
243 lines
5.5 KiB
Vue
243 lines
5.5 KiB
Vue
<template>
|
||
<div class="app-container">
|
||
<!-- 网络状态指示器 -->
|
||
<div v-if="!isOnline" class="network-status">
|
||
<div class="offline-indicator">
|
||
<span class="offline-icon">⚠️</span>
|
||
<span class="offline-text">离线模式</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 设置页面背景(列表页) -->
|
||
<NoteListPage v-show="showBackgroundPage" class="background-page" />
|
||
|
||
<!-- 普通页面过渡效果(不包括设置页面) -->
|
||
<template v-if="!isSettingsRoute">
|
||
<router-view v-slot="{ Component, route }">
|
||
<transition :name="transitionName">
|
||
<component :is="Component" :key="route.path" />
|
||
</transition>
|
||
</router-view>
|
||
</template>
|
||
|
||
<!-- 设置页面 -->
|
||
<transition name="settings-slide" v-show="isSettingsRoute" appear>
|
||
<SettingsPage class="setting-page" />
|
||
</transition>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, watch, computed, onMounted, onUnmounted } from 'vue'
|
||
import { useRoute } from 'vue-router'
|
||
import '@/common/base.css'
|
||
import { initModalService } from '@/utils/modalService'
|
||
import { addNetworkListener, removeNetworkListener, testOnline } from '@/utils/networkUtils'
|
||
|
||
// 导入页面组件
|
||
import NoteListPage from './pages/NoteListPage.vue'
|
||
import SettingsPage from './pages/SettingsPage.vue'
|
||
|
||
const route = useRoute()
|
||
const transitionName = ref('slide-left')
|
||
const modalRef = ref()
|
||
const isOnline = ref(navigator.onLine)
|
||
|
||
// 计算是否为设置页面路由
|
||
const isSettingsRoute = computed(() => {
|
||
return route.path === '/settings'
|
||
})
|
||
|
||
// 计算是否需要显示背景页面(仅在设置页面时显示)
|
||
const showBackgroundPage = computed(() => {
|
||
return route.path === '/settings'
|
||
})
|
||
|
||
// 网络状态变化回调
|
||
const handleOnline = () => {
|
||
isOnline.value = true
|
||
console.log('网络已连接')
|
||
// 可以在这里触发数据同步
|
||
}
|
||
|
||
const handleOffline = () => {
|
||
isOnline.value = false
|
||
console.log('网络已断开')
|
||
// 可以在这里显示离线提示
|
||
}
|
||
|
||
// 监听路由变化,动态设置过渡动画方向
|
||
watch(
|
||
() => route.path,
|
||
(toPath, fromPath) => {
|
||
// 特殊处理:从编辑页返回列表页的情况
|
||
if ((fromPath.startsWith('/editor') || fromPath.startsWith('/notes/')) && toPath === '/notes') {
|
||
transitionName.value = 'slide-right'
|
||
return
|
||
}
|
||
|
||
// 判断导航方向
|
||
const toDepth = toPath.split('/').length
|
||
const fromDepth = fromPath.split('/').length
|
||
|
||
// 如果是进入更深的页面(如从列表页进入编辑页),使用左滑动画
|
||
if (toDepth > fromDepth) {
|
||
transitionName.value = 'slide-left'
|
||
}
|
||
// 如果是返回上层页面(如从编辑页返回列表页),使用右滑动画
|
||
else if (toDepth < fromDepth) {
|
||
transitionName.value = 'slide-right'
|
||
}
|
||
// 同级页面切换使用默认的左滑动画
|
||
else {
|
||
transitionName.value = 'slide-left'
|
||
}
|
||
}
|
||
)
|
||
|
||
// 初始化弹框服务和网络监听
|
||
onMounted(() => {
|
||
initModalService()
|
||
addNetworkListener(handleOnline, handleOffline)
|
||
})
|
||
|
||
// 移除网络监听
|
||
onUnmounted(() => {
|
||
removeNetworkListener(handleOnline, handleOffline)
|
||
})
|
||
</script>
|
||
|
||
<style lang="less" scoped>
|
||
.app-container {
|
||
width: 100vw;
|
||
height: 100vh;
|
||
position: relative;
|
||
overflow: hidden;
|
||
background-color: #f5f5f5; // 设置默认背景色,防止闪烁
|
||
}
|
||
|
||
// 网络状态指示器
|
||
.network-status {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
z-index: 10000;
|
||
display: flex;
|
||
justify-content: center;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.offline-indicator {
|
||
background-color: #ff6b6b;
|
||
color: white;
|
||
padding: 8px 16px;
|
||
border-radius: 0 0 4px 4px;
|
||
font-size: 14px;
|
||
display: flex;
|
||
align-items: center;
|
||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||
pointer-events: auto;
|
||
}
|
||
|
||
.offline-icon {
|
||
margin-right: 8px;
|
||
}
|
||
|
||
.offline-text {
|
||
font-weight: 500;
|
||
}
|
||
|
||
// 背景页面样式
|
||
.background-page {
|
||
position: absolute;
|
||
width: 100%;
|
||
height: 100%;
|
||
top: 0;
|
||
left: 0;
|
||
z-index: 1;
|
||
}
|
||
|
||
.setting-page {
|
||
position: absolute;
|
||
width: 100%;
|
||
height: 100%;
|
||
top: 0;
|
||
left: 0;
|
||
z-index: 2;
|
||
}
|
||
|
||
// 左滑进入(从右到左)
|
||
.slide-left-enter-active,
|
||
.slide-left-leave-active {
|
||
transition: all 0.3s ease;
|
||
position: absolute;
|
||
width: 100%;
|
||
height: 100%;
|
||
top: 0;
|
||
left: 0;
|
||
}
|
||
|
||
.slide-left-enter-from {
|
||
transform: translateX(100%);
|
||
}
|
||
|
||
.slide-left-leave-to {
|
||
transform: translateX(-100%);
|
||
}
|
||
|
||
.slide-left-enter-to,
|
||
.slide-left-leave-from {
|
||
transform: translateX(0);
|
||
}
|
||
|
||
// 右滑进入(从左到右)
|
||
.slide-right-enter-active,
|
||
.slide-right-leave-active {
|
||
transition: all 0.3s ease;
|
||
position: absolute;
|
||
width: 100%;
|
||
height: 100%;
|
||
top: 0;
|
||
left: 0;
|
||
}
|
||
|
||
.slide-right-enter-from {
|
||
transform: translateX(-100%);
|
||
}
|
||
|
||
.slide-right-leave-to {
|
||
transform: translateX(100%);
|
||
}
|
||
|
||
.slide-right-enter-to,
|
||
.slide-right-leave-from {
|
||
transform: translateX(0);
|
||
}
|
||
|
||
// 设置页面滑入滑出效果
|
||
.settings-slide-enter-active,
|
||
.settings-slide-leave-active {
|
||
transition: transform 0.3s ease;
|
||
position: absolute;
|
||
width: 100%;
|
||
height: 100%;
|
||
top: 0;
|
||
left: 0;
|
||
z-index: 10;
|
||
}
|
||
|
||
.settings-slide-enter-from {
|
||
transform: translateY(100%);
|
||
}
|
||
|
||
.settings-slide-leave-to {
|
||
transform: translateY(100%);
|
||
}
|
||
|
||
.settings-slide-enter-to,
|
||
.settings-slide-leave-from {
|
||
transform: translateY(0);
|
||
}
|
||
</style>
|