308 lines
7.7 KiB
Vue
308 lines
7.7 KiB
Vue
<template>
|
||
<div class="mobile-home">
|
||
<!-- 顶部Banner -->
|
||
<div class="mobile-home__banner">
|
||
<div class="mobile-home__banner-bg">
|
||
<div class="mobile-home__banner-content">
|
||
<div class="mobile-home__brand">
|
||
<div class="mobile-home__brand-logo">亚为MOM</div>
|
||
<div class="mobile-home__brand-text">
|
||
<div class="mobile-home__brand-title">智能一体化管理系统</div>
|
||
<div class="mobile-home__brand-sub">私有化部署,可定制</div>
|
||
</div>
|
||
</div>
|
||
<div class="mobile-home__banner-deco">
|
||
<svg width="80" height="80" viewBox="0 0 80 80" fill="none">
|
||
<rect x="10" y="20" width="25" height="35" rx="3" fill="rgba(255,255,255,0.3)"/>
|
||
<rect x="40" y="10" width="30" height="45" rx="3" fill="rgba(255,255,255,0.2)"/>
|
||
<circle cx="55" cy="60" r="15" fill="rgba(255,255,255,0.15)"/>
|
||
<rect x="15" y="58" width="20" height="3" rx="1" fill="rgba(255,255,255,0.4)"/>
|
||
<rect x="15" y="64" width="14" height="3" rx="1" fill="rgba(255,255,255,0.3)"/>
|
||
</svg>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 常用功能区 -->
|
||
<div class="mobile-home__section">
|
||
<div class="mobile-home__section-header">
|
||
<div class="mobile-home__section-icon">
|
||
<svg viewBox="0 0 24 24" width="18" height="18" fill="#409eff">
|
||
<path d="M3.5 18.49l6-6.01 4 4L22 6.92l-1.41-1.41-7.09 7.97-4-4L2 16.99z"/>
|
||
</svg>
|
||
</div>
|
||
<span class="mobile-home__section-title">常用功能</span>
|
||
</div>
|
||
|
||
<div class="mobile-home__grid" v-loading="loading">
|
||
<div
|
||
v-for="item in shortcut"
|
||
:key="item.name"
|
||
class="mobile-home__grid-item"
|
||
@click="handleShortcutClick(item.url)"
|
||
>
|
||
<div class="mobile-home__grid-icon" :style="{ color: item.color, background: item.color + '18' }">
|
||
<Icon :icon="item.icon" />
|
||
</div>
|
||
<span class="mobile-home__grid-label">{{ item.name }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script lang="ts" setup>
|
||
import { useUserStore } from '@/store/modules/user'
|
||
import { usePermissionStore } from '@/store/modules/permission'
|
||
import type { Shortcut } from './types'
|
||
import { useRouter } from 'vue-router'
|
||
|
||
defineOptions({ name: 'Index' })
|
||
|
||
const { t } = useI18n()
|
||
const router = useRouter()
|
||
const userStore = useUserStore()
|
||
const permissionStore = usePermissionStore()
|
||
const loading = ref(true)
|
||
const avatar = userStore.getUser.avatar
|
||
const username = userStore.getUser.nickname
|
||
|
||
// 预设颜色列表
|
||
const colorList = [
|
||
'#1fdaca', '#ff6b6b', '#7c3aed', '#3fb27f', '#4daf1bc9', '#1a73e8',
|
||
'#f59e0b', '#10b981', '#6366f1', '#ef4444', '#8b5cf6', '#06b6d4',
|
||
'#84cc16', '#f97316', '#ec4899', '#14b8a6', '#22c55e', '#6b7280',
|
||
'#3b82f6', '#fbbf24'
|
||
]
|
||
|
||
// 获取快捷入口
|
||
let shortcut = reactive<Shortcut[]>([])
|
||
|
||
// 递归提取所有叶子节点路由
|
||
const extractLeafRoutes = (routes: any[], result: Shortcut[] = [], colorIndex = { value: 0 }, parentPath = ''): Shortcut[] => {
|
||
routes.forEach((route) => {
|
||
if (route.meta?.hidden) return
|
||
let fullPath = route.path || ''
|
||
if (fullPath && !fullPath.startsWith('/') && !fullPath.startsWith('http')) {
|
||
fullPath = parentPath.endsWith('/') ? `${parentPath}${fullPath}` : `${parentPath}/${fullPath}`
|
||
}
|
||
if (fullPath && !fullPath.startsWith('/') && !fullPath.startsWith('http')) {
|
||
fullPath = `/${fullPath}`
|
||
}
|
||
if (route.children && route.children.length > 0) {
|
||
extractLeafRoutes(route.children, result, colorIndex, fullPath)
|
||
} else {
|
||
if (fullPath) {
|
||
result.push({
|
||
name: route.meta?.title || route.name || '未命名',
|
||
icon: route.meta?.icon || 'mdi:application',
|
||
url: fullPath,
|
||
color: colorList[colorIndex.value % colorList.length]
|
||
})
|
||
colorIndex.value++
|
||
}
|
||
}
|
||
})
|
||
return result
|
||
}
|
||
|
||
const getShortcut = async () => {
|
||
const routes = permissionStore.getRouters
|
||
const leafRoutes = extractLeafRoutes(routes)
|
||
shortcut.splice(0, shortcut.length, ...leafRoutes)
|
||
}
|
||
|
||
const getAllApi = async () => {
|
||
await getShortcut()
|
||
loading.value = false
|
||
}
|
||
|
||
const handleShortcutClick = (url: string) => {
|
||
if (url.startsWith('http://') || url.startsWith('https://')) {
|
||
window.open(url, '_blank')
|
||
} else {
|
||
router.push(url)
|
||
}
|
||
}
|
||
|
||
getAllApi()
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.mobile-home {
|
||
background: #f5f5f5;
|
||
min-height: 100%;
|
||
padding-bottom: 16px;
|
||
}
|
||
|
||
/* Banner */
|
||
.mobile-home__banner {
|
||
padding: 0 12px;
|
||
margin-bottom: 12px;
|
||
}
|
||
.mobile-home__banner-bg {
|
||
background: linear-gradient(135deg, #43cea2 0%, #2a9d8f 50%, #264653 100%);
|
||
border-radius: 0 0 16px 16px;
|
||
padding: 20px 16px 24px;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
.mobile-home__banner-content {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
.mobile-home__brand {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
}
|
||
.mobile-home__brand-logo {
|
||
font-size: 28px;
|
||
font-weight: 800;
|
||
color: #fff;
|
||
letter-spacing: 2px;
|
||
text-shadow: 0 2px 8px rgba(0,0,0,0.2);
|
||
}
|
||
.mobile-home__brand-text {
|
||
color: #fff;
|
||
}
|
||
.mobile-home__brand-title {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
margin-bottom: 2px;
|
||
}
|
||
.mobile-home__brand-sub {
|
||
font-size: 12px;
|
||
opacity: 0.8;
|
||
}
|
||
.mobile-home__banner-deco {
|
||
opacity: 0.7;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
/* 功能区 */
|
||
.mobile-home__section {
|
||
margin: 0 12px;
|
||
background: #fff;
|
||
border-radius: 12px;
|
||
padding: 14px;
|
||
box-shadow: 0 1px 4px rgba(0,0,0,0.06);
|
||
}
|
||
.mobile-home__section-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
margin-bottom: 16px;
|
||
padding-bottom: 10px;
|
||
border-bottom: 2px solid #409eff;
|
||
}
|
||
.mobile-home__section-icon {
|
||
width: 28px;
|
||
height: 28px;
|
||
background: rgba(64,158,255,0.1);
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
.mobile-home__section-title {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: #303133;
|
||
}
|
||
|
||
/* 功能网格 */
|
||
.mobile-home__grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(4, 1fr);
|
||
gap: 16px 8px;
|
||
min-height: 100px;
|
||
}
|
||
.mobile-home__grid-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 6px;
|
||
cursor: pointer;
|
||
-webkit-tap-highlight-color: transparent;
|
||
|
||
&:active {
|
||
opacity: 0.7;
|
||
}
|
||
}
|
||
.mobile-home__grid-icon {
|
||
width: 44px;
|
||
height: 44px;
|
||
border-radius: 12px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 22px;
|
||
transition: transform 0.2s;
|
||
|
||
&:active {
|
||
transform: scale(0.92);
|
||
}
|
||
}
|
||
.mobile-home__grid-label {
|
||
font-size: 11px;
|
||
color: #303133;
|
||
text-align: center;
|
||
line-height: 1.3;
|
||
max-width: 64px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
|
||
/* 功能网格 */
|
||
.mobile-home__grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(4, 1fr);
|
||
gap: 12px 6px; // 网格间距变小
|
||
min-height: 100px;
|
||
}
|
||
|
||
.mobile-home__grid-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 4px; // 图标和文字间距变小
|
||
cursor: pointer;
|
||
-webkit-tap-highlight-color: transparent;
|
||
|
||
&:active {
|
||
opacity: 0.7;
|
||
}
|
||
}
|
||
|
||
.mobile-home__grid-icon {
|
||
width: 65px; // 图标变大
|
||
height: 65px;
|
||
border-radius: 14px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 26px; // 图标字体大小变大
|
||
transition: transform 0.2s;
|
||
|
||
&:active {
|
||
transform: scale(0.92);
|
||
}
|
||
}
|
||
|
||
.mobile-home__grid-label {
|
||
font-size: 12px; // 文字略大,可根据需求微调
|
||
color: #303133;
|
||
text-align: center;
|
||
line-height: 1.2;
|
||
max-width: 64px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
</style>
|