Files
mom-web/src/views/function/index.vue

232 lines
6.1 KiB
Vue
Raw Normal View History

2026-03-11 18:24:00 +08:00
<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>