feat(app): migrate /app accounts to Vue SPA (stage 3)

This commit is contained in:
2025-12-13 23:56:47 +08:00
parent 324e0d614a
commit 9798ed52c3
26 changed files with 1098 additions and 69 deletions

View File

@@ -4,8 +4,11 @@ import { useRoute, useRouter } from 'vue-router'
import { ElMessageBox } from 'element-plus'
import { Calendar, Camera, User } from '@element-plus/icons-vue'
import { useUserStore } from '../stores/user'
const route = useRoute()
const router = useRouter()
const userStore = useUserStore()
const isMobile = ref(false)
const drawerOpen = ref(false)
@@ -20,6 +23,10 @@ onMounted(() => {
mediaQuery = window.matchMedia('(max-width: 768px)')
mediaQuery.addEventListener?.('change', syncIsMobile)
syncIsMobile()
userStore.refreshVipInfo().catch(() => {
window.location.href = '/login'
})
})
onBeforeUnmount(() => {
@@ -50,6 +57,7 @@ async function logout() {
return
}
await userStore.logout()
window.location.href = '/login'
}
</script>
@@ -80,6 +88,14 @@ async function logout() {
</div>
<div class="header-right">
<div class="user-meta">
<el-tag v-if="userStore.isVip" type="success" size="small" effect="light">VIP</el-tag>
<el-tag v-else type="info" size="small" effect="light">普通</el-tag>
<span class="user-name">{{ userStore.username || '用户' }}</span>
<span v-if="userStore.isVip && userStore.vipDaysLeft <= 7 && userStore.vipDaysLeft > 0" class="vip-warn">
({{ userStore.vipDaysLeft }}天后到期)
</span>
</div>
<el-button type="primary" plain @click="logout">退出</el-button>
</div>
</el-header>
@@ -94,12 +110,20 @@ async function logout() {
<div class="brand-title">知识管理平台</div>
<div class="brand-sub app-muted">用户中心</div>
</div>
<div class="drawer-user">
<el-tag v-if="userStore.isVip" type="success" size="small" effect="light">VIP</el-tag>
<el-tag v-else type="info" size="small" effect="light">普通</el-tag>
<span class="user-name">{{ userStore.username || '用户' }}</span>
</div>
<el-menu :default-active="activeMenu" class="aside-menu" router @select="go">
<el-menu-item v-for="item in menuItems" :key="item.path" :index="item.path">
<el-icon><component :is="item.icon" /></el-icon>
<span>{{ item.label }}</span>
</el-menu-item>
</el-menu>
<div class="drawer-actions">
<el-button type="primary" plain style="width: 100%" @click="logout">退出登录</el-button>
</div>
</el-drawer>
</el-container>
</template>
@@ -170,10 +194,44 @@ async function logout() {
gap: 12px;
}
.user-meta {
display: flex;
align-items: center;
gap: 8px;
min-width: 0;
}
.user-name {
font-size: 13px;
font-weight: 700;
max-width: 180px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.vip-warn {
font-size: 12px;
color: var(--app-muted);
white-space: nowrap;
}
.layout-main {
padding: 16px;
}
.drawer-user {
padding: 0 16px 10px;
display: flex;
align-items: center;
gap: 8px;
}
.drawer-actions {
padding: 12px 16px 4px;
border-top: 1px solid var(--app-border);
}
@media (max-width: 768px) {
.layout-header {
flex-wrap: wrap;
@@ -190,6 +248,9 @@ async function logout() {
.layout-main {
padding: 12px;
}
.user-name {
max-width: 120px;
}
}
</style>