CoolFace
Apppublic

zhemima/msmail

sourceHugging Faceupdated 1y agoView on Hugging Face
0likes
App.vue251 linesDownload Raw Back to src
1<script setup lang="ts">2import { useRoute, useRouter } from 'vue-router';3import { ref, onMounted } from 'vue';4import logo from './assets/logo.png'5import { ipApi } from './services/ipApi';6 7const router = useRouter();8const route = useRoute();9const menuVisible = ref(false);10const serverIP = ref('');11 12const menu = [13  {14    name: '收件',15    path: '/mail',16    icon: 'system-log'17  },18  {19    name: '账号',20    path: '/account',21    icon: 'user-list'22  },23  {24    name: '设置',25    path: '/setting',26    icon: 'setting-1'27  },28  {29    name: '调试',30    path: '/debug',31    icon: 'bug'32  }33];34 35const logout = () => {36  window.localStorage.removeItem('isAuthenticated');37  router.push('/login');38};39 40const toggleMenu = () => {41  menuVisible.value = !menuVisible.value;42};43 44// 获取服务器IP45const fetchServerIP = async () => {46  try {47    const serverInfo = await ipApi.getServerInfo();48    serverIP.value = serverInfo.serverIP;49  } catch (error) {50    console.error('获取服务器IP失败:', error);51    serverIP.value = '获取失败';52  }53};54 55// 组件挂载时获取服务器IP56onMounted(() => {57  if (route.path !== '/login') {58    fetchServerIP();59  }60});61 62//解析token63const parseToken = () => {64  try {65    const token = localStorage.getItem('token') as string;66    const [, payload] = token.split('.');67    const data = JSON.parse(atob(payload));68    return data;69  } catch (error) {70    return null;71  }72};73const jwtToken = parseToken();74 75</script>76 77<template>78  <template v-if="route.path === '/login'">79    <router-view />80  </template>81  <t-layout v-else class="h-screen">82    <!-- 移动端抽屉菜单 -->83    <t-drawer v-model:visible="menuVisible" placement="left" size="232" :footer="false" :header="false"84      :close-on-overlay-click="true" class="lg:hidden">85      <t-menu :value="route.path" theme="dark" class="h-full bg-transparent ">86        <template #logo>87          <router-link to="/" class="flex items-center gap-2 p-4">88            <img :src="logo" alt="logo" class="w-8 h-8" />89            <h1 class="text-xl font-bold text-white">MS MAIL</h1>90          </router-link>91        </template>92        <t-menu-item v-for="item in menu" :key="item.path" :value="item.path" :to="item.path"93          @click="menuVisible = false" class="menu-item mx-4 my-2 rounded-xl">94          <template #icon>95            <t-icon :name="item.icon" />96          </template>97          {{ item.name }}98        </t-menu-item>99      </t-menu>100    </t-drawer>101 102    <!-- 桌面端侧边栏 -->103    <t-aside class="sidebar backdrop-blur-lg hidden lg:block">104      <t-menu :value="route.path" theme="dark" class="bg-transparent">105        <template #logo>106          <router-link to="/" class="flex items-center gap-2">107            <img :src="logo" alt="logo" class="w-10 h-10 transition-transform hover:scale-110" />108            <h1 class="text-2xl font-bold text-white tracking-wider">MS MAIL</h1>109          </router-link>110        </template>111        <t-menu-item v-for="item in menu" :key="item.path" :value="item.path" :to="item.path"112          class="menu-item mx-4 my-2 rounded-xl">113          <template #icon>114            <t-icon :name="item.icon" class="text-xl" />115          </template>116          <span class="font-medium">{{ item.name }}</span>117        </t-menu-item>118      </t-menu>119    </t-aside>120 121 122    <t-layout class="w-full">123      <t-header class="header backdrop-blur-xl border-b border-gray-100">124        <div class="flex items-center justify-between h-full px-2 sm:px-4 lg:px-8">125          <!-- 移动端菜单按钮和标题 -->126          <div class="flex items-center gap-2 sm:gap-4 ">127            <div class="lg:hidden">128              <t-button theme="default" variant="text" class=" min-w-[40px]" @click="toggleMenu">129                <t-icon name="menu" size="20px" sm:size="24px" />130              </t-button>131            </div>132            <h1133              class="text-base sm:text-xl lg:text-2xl font-bold bg-gradient-to-r from-blue-600 to-indigo-600 bg-clip-text text-transparent truncate max-w-[150px] sm:max-w-full">134              {{ jwtToken?.sub }}135            </h1>136          </div>137          <div class="flex items-center gap-2 sm:gap-4">138            <t-button theme="danger" @click="logout" class="logout-btn text-sm sm:text-base py-1 px-2 sm:px-4">139              <template #icon>140                <t-icon name="logout" class="text-sm sm:text-base" />141              </template>142              <span>退出</span>143            </t-button>144          </div>145        </div>146      </t-header>147 148      <!-- 内容区域 -->149      <t-content class="content bg-gray-50/30 flex-1 overflow-y-auto w-full">150        <router-view v-slot="{ Component }">151          <transition name="fade-slide" mode="out-in">152            <component :is="Component" />153          </transition>154        </router-view>155      </t-content>156 157      <!-- 页脚 -->158      <t-footer class="footer backdrop-blur-sm py-4 text-center text-sm text-gray-600">159        <div class="flex flex-col items-center gap-1">160          <span class="opacity-75">© 微软邮箱管理系统</span>161          <span v-if="serverIP" class="text-xs opacity-60">服务器IP: {{ serverIP }}</span>162        </div>163      </t-footer>164    </t-layout>165 166  </t-layout>167</template>168 169<style scoped>170@reference "./assets/base.css";171 172.sidebar {173  @apply bg-gradient-to-br from-blue-600 via-blue-700 to-indigo-800;174  box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);175}176 177.header {178  @apply bg-white/80 sticky top-0 z-10;179  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);180}181 182.menu-item {183  @apply transition-all duration-300 hover:bg-white/15 active:scale-95;184  @apply flex items-center gap-3 px-4 py-3;185}186 187.logout-btn {188  @apply transition-transform hover:scale-105 active:scale-95;189}190 191.fade-slide-enter-active,192.fade-slide-leave-active {193  transition: all 0.3s ease;194}195 196.fade-slide-enter-from {197  opacity: 0;198  transform: translateY(20px);199}200 201.fade-slide-leave-to {202  opacity: 0;203  transform: translateY(-20px);204}205 206/* :deep(.t-default-menu.t-menu--dark){207  @apply bg-transparent;208} */209 210:deep(.t-drawer__body) {211  @apply p-0;212}213 214:deep(.t-drawer__body) {215  @apply p-0;216}217 218:deep(::-webkit-scrollbar) {219  @apply w-2;220}221 222:deep(::-webkit-scrollbar-thumb) {223  @apply bg-gray-400/30 rounded-full transition-colors hover:bg-gray-400/50;224}225 226:deep(::-webkit-scrollbar-track) {227  @apply bg-transparent;228}229 230.content {231  background-image: radial-gradient(circle at 50% 50%,232      rgba(255, 255, 255, 0.8) 0%,233      rgba(240, 240, 250, 0.6) 100%);234}235 236.footer {237  @apply bg-white/60;238}239 240/* 添加移动端响应式样式 */241@media (max-width: 1024px) {242  .sidebar {243    display: none;244  }245}246 247.drawer-menu {248  @apply bg-gradient-to-br from-blue-600 via-blue-700 to-indigo-800;249}250</style>251