Files
2026-07-15 16:13:27 +08:00

288 lines
7.0 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="select-page">
<!-- 顶栏 -->
<header class="select-topbar">
<div class="topbar-brand">
<svg class="brand-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/>
</svg>
<span class="brand-text">Mokee Gateway</span>
</div>
<div class="topbar-actions">
<el-button v-if="authStore.isAdmin" type="primary" size="default" @click="router.push('/admin')">
<el-icon><Setting /></el-icon>后台管理
</el-button>
<el-button text @click="handleLogout" style="color: var(--text-muted)">
<el-icon><SwitchButton /></el-icon>退出
</el-button>
</div>
</header>
<!-- 内容 -->
<div class="select-body">
<div class="select-hero">
<el-avatar :size="56" :src="authStore.userInfo?.avatar" class="hero-avatar">
{{ authStore.userInfo?.realName?.charAt(0) || 'U' }}
</el-avatar>
<h2 class="hero-greeting">
你好{{ authStore.userInfo?.realName || authStore.userInfo?.username || '用户' }}
</h2>
<p class="hero-sub">请选择要访问的业务系统</p>
</div>
<div class="system-grid" v-if="authStore.systemList.length > 0">
<div
v-for="(sys, i) in authStore.systemList"
:key="sys.systemId"
class="system-card"
@click="handleSelectSystem(sys)"
>
<div class="card-icon-box" :style="{ background: isIconUrl(sys.icon) ? 'transparent' : gradients[i % gradients.length] }">
<img v-if="isIconUrl(sys.icon)" :src="sys.icon" class="card-icon-img" />
<el-icon v-else :size="28"><component :is="getIcon(sys.icon)" /></el-icon>
</div>
<div class="card-info">
<div class="card-title">{{ sys.systemName }}</div>
<div class="card-code">{{ sys.systemCode }}</div>
<div class="card-desc">{{ sys.description || '暂无描述' }}</div>
</div>
<el-icon class="card-arrow" :size="16"><ArrowRight /></el-icon>
</div>
</div>
<el-empty
v-else
description="暂无可用系统,请联系管理员配置"
:image-size="140"
>
<el-button type="primary" @click="authStore.fetchSystemList()">刷新</el-button>
</el-empty>
</div>
</div>
</template>
<script setup lang="ts">
import { onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import { SwitchButton, Grid, Setting, ArrowRight } from '@element-plus/icons-vue'
import { getIconComponent } from '@/utils/icons'
import { useAuthStore, type SystemInfo } from '@/stores/auth'
const router = useRouter()
const authStore = useAuthStore()
const gradients = [
'linear-gradient(135deg, #6366f1, #818cf8)',
'linear-gradient(135deg, #10b981, #34d399)',
'linear-gradient(135deg, #f59e0b, #fbbf24)',
'linear-gradient(135deg, #ef4444, #f87171)',
'linear-gradient(135deg, #8b5cf6, #a78bfa)',
'linear-gradient(135deg, #06b6d4, #22d3ee)',
'linear-gradient(135deg, #ec4899, #f472b6)',
'linear-gradient(135deg, #3b82f6, #60a5fa)',
]
onMounted(async () => {
await authStore.fetchUserInfo()
await authStore.fetchSystemList()
})
function getIcon(icon: string) {
return getIconComponent(icon) || Grid
}
/** 图标值是 URL上传的文件图标还是 Element Plus 组件名 */
function isIconUrl(v: string) {
return v && (v.startsWith('http') || v.startsWith('/zgapi'))
}
async function handleSelectSystem(sys: SystemInfo) {
try {
await authStore.selectSystem(sys.systemId)
if (sys.systemCode === 'gateway') {
// 跳转到主应用域名,避免从 login 域名加载大 JS
const webUrl = import.meta.env.VITE_WEB_URL || window.location.origin
window.location.href = webUrl + '/admin'
return
}
if (sys.frontUrl) {
window.location.href = sys.frontUrl
} else {
ElMessage.warning('该系统未配置前端地址')
}
} catch {
ElMessage.error('切换系统失败')
}
}
async function handleLogout() {
try { await authStore.logout() } catch {}
router.push('/login')
}
</script>
<style scoped>
.select-page {
min-height: 100vh;
background: #f8fafc;
display: flex;
flex-direction: column;
}
/* ===== 顶栏 ===== */
.select-topbar {
display: flex;
align-items: center;
justify-content: space-between;
height: 56px;
background: #fff;
border-bottom: 1px solid var(--border-color);
padding: 0 24px;
position: sticky;
top: 0;
z-index: 100;
}
.topbar-brand {
display: flex;
align-items: center;
gap: 10px;
}
.brand-svg {
width: 26px;
height: 26px;
color: var(--color-primary);
}
.brand-text {
font-size: 16px;
font-weight: 700;
color: var(--text-primary);
letter-spacing: -0.3px;
}
.topbar-actions {
display: flex;
align-items: center;
gap: 8px;
}
/* ===== 内容 ===== */
.select-body {
flex: 1;
padding: 48px 40px 60px;
max-width: 960px;
width: 100%;
margin: 0 auto;
}
.select-hero {
text-align: center;
margin-bottom: 44px;
}
.hero-avatar {
margin-bottom: 16px;
box-shadow: 0 4px 16px rgba(99, 102, 241, 0.2);
}
.hero-greeting {
font-size: 24px;
font-weight: 700;
color: var(--text-primary);
margin-bottom: 6px;
letter-spacing: -0.3px;
}
.hero-sub {
font-size: 15px;
color: var(--text-muted);
}
/* ===== 系统卡片网格 ===== */
.system-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 16px;
}
.system-card {
display: flex;
align-items: center;
gap: 16px;
padding: 20px;
background: #fff;
border-radius: var(--radius-lg);
border: 1px solid var(--border-color);
cursor: pointer;
transition: all var(--transition-base);
box-shadow: var(--shadow-xs);
}
.system-card:hover {
transform: translateY(-3px);
box-shadow: var(--shadow-lg);
border-color: #cbd5e1;
}
.card-icon-box {
width: 52px;
height: 52px;
border-radius: var(--radius-md);
display: flex;
align-items: center;
justify-content: center;
color: #fff;
flex-shrink: 0;
}
.card-icon-img { width:40px; height:40px; object-fit:contain; }
.card-info {
flex: 1;
min-width: 0;
}
.card-title {
font-size: 15px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 2px;
}
.card-code {
font-size: 12px;
color: var(--text-muted);
font-family: 'SF Mono', 'Fira Code', monospace;
margin-bottom: 4px;
}
.card-desc {
font-size: 12px;
color: var(--text-muted);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.card-arrow {
color: var(--text-muted);
flex-shrink: 0;
transition: all var(--transition-fast);
}
.system-card:hover .card-arrow {
color: var(--color-primary);
transform: translateX(3px);
}
@media (max-width: 768px) {
.select-body { padding: 24px 16px 40px; }
.select-hero { margin-bottom: 28px; }
.hero-greeting { font-size: 20px; }
.system-grid { grid-template-columns: 1fr; gap: 12px; }
.select-topbar { padding: 0 12px; }
}
</style>