功能页面的跳转
This commit is contained in:
@@ -24,13 +24,14 @@ const pageTitle = computed(() => {
|
|||||||
const tabs = [
|
const tabs = [
|
||||||
{ name: 'Index', path: '/index', label: '首页', icon: 'home' },
|
{ name: 'Index', path: '/index', label: '首页', icon: 'home' },
|
||||||
{ name: 'Workbench', path: '/index', label: '工作台', icon: 'workbench' },
|
{ name: 'Workbench', path: '/index', label: '工作台', icon: 'workbench' },
|
||||||
{ name: 'Function', path: '/index', label: '功能', icon: 'function' },
|
{ name: 'Function', path: '/function', label: '功能', icon: 'function' },
|
||||||
{ name: 'Mine', path: '/user/center', label: '我的', icon: 'mine' }
|
{ name: 'Mine', path: '/user/center', label: '我的', icon: 'mine' }
|
||||||
]
|
]
|
||||||
|
|
||||||
const activeTab = computed(() => {
|
const activeTab = computed(() => {
|
||||||
const p = route.path
|
const p = route.path
|
||||||
if (p === '/index' || p === '/') return 'Index'
|
if (p === '/index' || p === '/') return 'Index'
|
||||||
|
if (p === '/function' || p.startsWith('/function/')) return 'Function'
|
||||||
if (p.includes('/user/center')) return 'Mine'
|
if (p.includes('/user/center')) return 'Mine'
|
||||||
return 'Index'
|
return 'Index'
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -128,6 +128,28 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/function',
|
||||||
|
component: Layout,
|
||||||
|
name: 'FunctionCenter',
|
||||||
|
meta: {
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: () => import('@/views/function/index.vue'),
|
||||||
|
name: 'FunctionIndex',
|
||||||
|
meta: {
|
||||||
|
canTo: true,
|
||||||
|
hidden: true,
|
||||||
|
noTagsView: true,
|
||||||
|
icon: 'ep:grid',
|
||||||
|
title: '功能'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/dict',
|
path: '/dict',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
|
|||||||
231
src/views/function/index.vue
Normal file
231
src/views/function/index.vue
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
<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>
|
||||||
Reference in New Issue
Block a user