232 lines
6.1 KiB
Vue
232 lines
6.1 KiB
Vue
<template>
|
|
<div class="mobile-function">
|
|
<!-- 搜索栏 -->
|
|
<div class="mobile-function__search">
|
|
<el-input v-model="searchText" placeholder="搜索功能" clearable :prefix-icon="Search" />
|
|
</div>
|
|
|
|
<!-- 功能分类列表 -->
|
|
<div class="mobile-function__content">
|
|
<div v-for="category in filteredCategories" :key="category.path" class="function-category">
|
|
<div class="function-category__header">
|
|
<Icon :icon="category.icon || 'ep:menu'" class="function-category__icon" />
|
|
<span class="function-category__title">{{ category.title }}</span>
|
|
</div>
|
|
<div class="function-category__grid">
|
|
<div
|
|
v-for="item in category.children"
|
|
:key="item.path"
|
|
class="function-item"
|
|
@click="handleNavigate(item)"
|
|
>
|
|
<div class="function-item__icon" :style="{ background: getGradientColor(item.path) }">
|
|
<Icon :icon="item.icon || 'ep:document'" />
|
|
</div>
|
|
<span class="function-item__label">{{ item.title }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 空状态 -->
|
|
<div v-if="filteredCategories.length === 0" class="mobile-function__empty">
|
|
<el-empty description="暂无功能" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { Search } from '@element-plus/icons-vue'
|
|
import { ref, computed, onMounted } from 'vue'
|
|
import { useRouter } from 'vue-router'
|
|
import { usePermissionStore } from '@/store/modules/permission'
|
|
|
|
defineOptions({ name: 'FunctionIndex' })
|
|
|
|
const router = useRouter()
|
|
const permissionStore = usePermissionStore()
|
|
const searchText = ref('')
|
|
|
|
// 渐变色配置
|
|
const gradientColors = [
|
|
'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
|
'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)',
|
|
'linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)',
|
|
'linear-gradient(135deg, #fa709a 0%, #fee140 100%)',
|
|
'linear-gradient(135deg, #30cfd0 0%, #330867 100%)',
|
|
'linear-gradient(135deg, #a8edea 0%, #fed6e3 100%)',
|
|
'linear-gradient(135deg, #ff9a9e 0%, #fecfef 100%)',
|
|
'linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%)',
|
|
'linear-gradient(135deg, #ff6e7f 0%, #bfe9ff 100%)',
|
|
'linear-gradient(135deg, #e0c3fc 0%, #8ec5fc 100%)',
|
|
'linear-gradient(135deg, #f77062 0%, #fe5196 100%)',
|
|
'linear-gradient(135deg, #c471f5 0%, #fa71cd 100%)',
|
|
'linear-gradient(135deg, #48c6ef 0%, #6f86d6 100%)',
|
|
'linear-gradient(135deg, #96fbc4 0%, #f9f586 100%)',
|
|
'linear-gradient(135deg, #fddb92 0%, #d1fdff 100%)',
|
|
'linear-gradient(135deg, #89f7fe 0%, #66a6ff 100%)',
|
|
'linear-gradient(135deg, #a1c4fd 0%, #c2e9fb 100%)',
|
|
'linear-gradient(135deg, #d299c2 0%, #fef9d7 100%)',
|
|
'linear-gradient(135deg, #fccb90 0%, #d57eeb 100%)',
|
|
'linear-gradient(135deg, #e8198b 0%, #c7eafd 100%)'
|
|
]
|
|
|
|
// 根据路径获取渐变色
|
|
const getGradientColor = (path: string) => {
|
|
const hash = path.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0)
|
|
return gradientColors[hash % gradientColors.length]
|
|
}
|
|
|
|
// 从路由中获取菜单数据
|
|
const menuCategories = computed(() => {
|
|
const routers = permissionStore.getRouters
|
|
const categories: any[] = []
|
|
|
|
routers.forEach(route => {
|
|
// 只处理非隐藏的一级菜单
|
|
if (!route.meta?.hidden && route.children && route.children.length > 0) {
|
|
const children = route.children.filter(child => {
|
|
// 过滤掉隐藏的子菜单和没有标题的菜单
|
|
return !child.meta?.hidden && child.meta?.title && child.path
|
|
})
|
|
|
|
if (children.length > 0) {
|
|
categories.push({
|
|
title: route.meta?.title || route.name,
|
|
icon: route.meta?.icon,
|
|
path: route.path,
|
|
children: children.map(child => ({
|
|
title: child.meta?.title,
|
|
icon: child.meta?.icon,
|
|
path: child.path.startsWith('/') ? child.path : `${route.path}/${child.path}`
|
|
}))
|
|
})
|
|
}
|
|
}
|
|
})
|
|
|
|
return categories
|
|
})
|
|
|
|
// 过滤后的分类
|
|
const filteredCategories = computed(() => {
|
|
if (!searchText.value) return menuCategories.value
|
|
|
|
const keyword = searchText.value.toLowerCase()
|
|
return menuCategories.value.map(category => ({
|
|
...category,
|
|
children: category.children.filter((item: any) =>
|
|
item.title.toLowerCase().includes(keyword)
|
|
)
|
|
})).filter(category => category.children.length > 0)
|
|
})
|
|
|
|
// 导航到功能页面
|
|
const handleNavigate = (item: any) => {
|
|
if (item.path) {
|
|
router.push(item.path)
|
|
}
|
|
}
|
|
|
|
onMounted(() => {
|
|
console.log('菜单数据:', menuCategories.value)
|
|
})
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.mobile-function {
|
|
min-height: 100vh;
|
|
background: #f5f5f5;
|
|
}
|
|
|
|
.mobile-function__search {
|
|
padding: 12px;
|
|
background: #fff;
|
|
border-bottom: 1px solid #eee;
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 10;
|
|
}
|
|
|
|
.mobile-function__content {
|
|
padding: 12px;
|
|
}
|
|
|
|
.mobile-function__empty {
|
|
padding: 60px 20px;
|
|
text-align: center;
|
|
}
|
|
|
|
.function-category {
|
|
margin-bottom: 20px;
|
|
|
|
&__header {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-bottom: 12px;
|
|
padding-left: 4px;
|
|
}
|
|
|
|
&__icon {
|
|
font-size: 18px;
|
|
color: #409eff;
|
|
margin-right: 8px;
|
|
}
|
|
|
|
&__title {
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
color: #303133;
|
|
}
|
|
|
|
&__grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(4, 1fr);
|
|
gap: 12px;
|
|
}
|
|
}
|
|
|
|
.function-item {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
padding: 12px 8px;
|
|
background: #fff;
|
|
border-radius: 12px;
|
|
cursor: pointer;
|
|
transition: all 0.3s;
|
|
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
|
|
|
|
&:active {
|
|
transform: scale(0.95);
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
|
|
}
|
|
|
|
&__icon {
|
|
width: 48px;
|
|
height: 48px;
|
|
border-radius: 12px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin-bottom: 8px;
|
|
color: #fff;
|
|
font-size: 24px;
|
|
}
|
|
|
|
&__label {
|
|
font-size: 12px;
|
|
color: #606266;
|
|
text-align: center;
|
|
line-height: 1.2;
|
|
word-break: break-all;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 375px) {
|
|
.function-category__grid {
|
|
grid-template-columns: repeat(3, 1fr);
|
|
}
|
|
}
|
|
</style>
|