初始化

This commit is contained in:
zg
2026-07-15 16:13:27 +08:00
parent 6d6f559793
commit c4023c1623
276 changed files with 42622 additions and 0 deletions

View File

@@ -0,0 +1,287 @@
<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>