【Dv3Admin】Vue3通用自定义工作台卡片

在后台管理系统不断叠加业务模块时,树状菜单会自然生长成"又深又长"的形态,入口查找需要频繁展开与定位,使用效率下降,同时导航体验波动明显。菜单路由写死在前端还会带来新增与调整模块必须改代码重发的连锁成本,直接拖慢迭代节奏。

本文记录一次已落地的改造方案,通过后端返回路由数据,前端使用通用卡片组件动态渲染模块入口,目标聚焦在入口更直观与维护成本收敛,内容仅覆盖可用实现方式与关键约束。

文章目录

需求解析

本次改造发生在后台管理系统中,前端技术栈为 Vue + Element Plus,系统模块数量较多且存在多角色、多业务入口需求。原有实现依赖前端静态菜单配置,随着功能增加出现入口查找成本上升与配置维护复杂化的问题,需要将模块入口的控制权交给后端,并以数据驱动渲染入口卡片完成跳转。

现象 对用户的影响 对开发维护的影响
菜单层级变深、数量变多 进入目标模块需要多次展开与查找,效率下降 页面入口分散,导航调整成本变高
入口因角色差异而分化 不同用户需要不同入口,查找路径不统一 静态配置越来越复杂,维护压力增加
模块变更频繁 导航体验波动,使用习惯被打断 每次新增或调整模块都要改前端并发布

后台功能增多
传统树状菜单冗长且层级多
查找效率下降 / 维护成本上升
后端统一维护路由配置
接口返回当前用户可见路由数据
前端通用组件动态渲染卡片
点击卡片跳转对应模块
入口更直观 / 扩展更灵活 / 统一维护

目录路由地址配置

路由地址的前缀对前端项目文件目录名,后缀对应服务器数据工作台返回的具体配置,(具体的卡片路由规则可根据与后端协商)

需要注意的是,组件地址必须与前端项目的 vue 文件地址一致,否则会导致点击卡片无法跳转,这一步不能省略。

功能实现

定位对象为后端维护的模块入口信息,目的在于让每个模块的入口路径与组件地址保持一致,并由接口按当前可见范围返回数据,前端不再维护静态菜单结构;关键点在于组件地址与前端文件路径一致,否则会导致跳转失效。

定位对象为通用卡片导航组件 components / commonWorkbenches / index.vue,目的在于在进入页面时请求接口获取路由数据,按分类组织展示并点击卡片完成跳转,组件只负责渲染与导航,不写死任何菜单结构。

组件职责 目的
请求接口获取模块数据 让页面入口由后端控制,前端不再维护静态配置
按分类组织数据结构 把不同类别的入口分区展示,页面更直观
点击卡片跳转对应路由 保证用户可以直接进入目标模块
javascript 复制代码
<script lang="ts" setup>
import { ref } from 'vue'
import { GetList } from './api'
import { useRouter } from 'vue-router'
import { ArrowRight } from '@element-plus/icons-vue'

type CardItem = {
    path: string
    name: string
    image?: string
    desc?: string
}
const props = defineProps({
    apiUrl: { type: String, required: true },
    // 每组最多展示几条;<=0 表示全部
    limit: { type: Number, default: 0 }
})
const router = useRouter()
const sections = ref<{ key: string; title: string; items: CardItem[] }[]>([])
const TITLE_MAP: Record<string, string> = {
    Data: '数据信息',
    Setting: '配置信息',
    Statistics: '统计可视化'
}
const ORDER = ['Data', 'Setting', 'Statistics']
GetList(props.apiUrl).then((res: any) => {
    const src = (res && res.data) || {}
    const built = ORDER.map((k) => {
        let arr: CardItem[] = Array.isArray(src[k]) ? src[k] : []
        if (props.limit > 0) arr = arr.slice(0, props.limit)
        return { key: k, title: TITLE_MAP[k] || k, items: arr }
    }).filter(s => s.items.length > 0)
    sections.value = built
})
const handleToSubMenu = (path: string) => {
    if (!path) return
    router.push({ path })
}
</script>

<template>
    <fs-page>
        <div class="wb">
            <template v-if="sections.length">
                <section v-for="sec in sections" :key="sec.key" class="wb-section">
                    <h3 class="wb-title">
                        <span class="wb-dot"></span>{{ sec.title }}
                    </h3>

                    <div class="wb-grid">
                        <div v-for="subItem in sec.items" :key="subItem.label">
                            <div class="sub-title">{{ subItem.label }}</div>
                            <div class="wb-subgrid">
                                <el-card v-for="item in subItem.children" :key="item.path" class="wb-card"
                                    shadow="never" role="button" :aria-label="item.name" tabindex="0"
                                    @click="handleToSubMenu(item.path)" @keydown.enter="handleToSubMenu(item.path)"
                                    @keydown.space.prevent="handleToSubMenu(item.path)">
                                    <div class="wb-card__inner">
                                        <!-- 图标 -->
                                        <div class="wb-card__media">
                                            <el-image :src="item.image" fit="contain" loading="lazy"
                                                class="wb-card__img" />
                                        </div>
                                        <!-- 文案 -->
                                        <div class="wb-card__content">
                                            <div class="wb-card__title">{{ item.name }}</div>
                                            <div class="wb-card__desc">{{ item.desc || '进入模块' }}</div>
                                        </div>
                                        <!-- 箭头 -->
                                        <div class="wb-card__chevron">
                                            <el-icon>
                                                <ArrowRight />
                                            </el-icon>
                                        </div>
                                    </div>
                                </el-card>
                            </div>
                        </div>
                    </div>
                </section>
            </template>

            <el-empty v-else description="暂无数据" />
        </div>
    </fs-page>
</template>

定位对象为业务页面对通用组件的引用方式,目的在于通过传入接口地址决定展示内容,页面本身不再描述模块结构,否则会导致再次回到前端写死菜单的维护路径。

javascript 复制代码
<script lang="ts" setup>
import CommonWorkbenches from '/@/components/commonWorkbenches/index.vue'
const apiUrl = '/api/NDAYHighSchool/MoralEdu/Workbenches/web_router/'
</script>


<template>
    <CommonWorkbenches :apiUrl="apiUrl" />
</template>


<style lang="scss"></style>

总结

该方案将模块入口从树状菜单的"结构维护"切换为接口驱动的"数据渲染",后端负责入口路径与可见范围,前端专注展示与跳转,菜单臃肿与频繁发布问题得到缓解。现有实现仍存在演进空间,模块元信息可进一步结构化用于图标、排序与业务标签的统一描述,接口层可引入缓存与版本控制降低重复计算成本。

入口卡片化更适合工作台与聚合页形态,在模块规模持续增大时更容易保持可读性与扩展性,并将新增模块的接入成本压缩到接口层面。

相关推荐
Coding茶水间1 天前
基于深度学习的草莓健康度检测系统演示与介绍(YOLOv12/v11/v8/v5模型+Django+web+训练代码+数据集)
人工智能·深度学习·yolo·机器学习·django
源码之屋2 天前
计算机毕业设计:Python出行数据智能分析与预测平台 Django框架 可视化 数据分析 PyEcharts 交通 深度学习(建议收藏)✅
人工智能·python·深度学习·数据分析·django·汽车·课程设计
vx_biyesheji00012 天前
计算机毕业设计:Python网约车订单数据可视化系统 Django框架 可视化 数据大屏 数据分析 大数据 机器学习 深度学习(建议收藏)✅
大数据·python·机器学习·信息可视化·django·汽车·课程设计
I love studying!!!2 天前
Web项目:从Django入手
后端·python·django
q_35488851532 天前
计算机毕业设计:Python居民出行规律可视化分析系统 Django框架 可视化 数据分析 PyEcharts 交通 深度学习(建议收藏)✅
人工智能·python·数据分析·车载系统·django·汽车·课程设计
vx_biyesheji00012 天前
计算机毕业设计:Python城市交通出行模式挖掘系统 Django框架 可视化 数据分析 PyEcharts 交通 深度学习(建议收藏)✅
人工智能·python·深度学习·数据分析·django·汽车·课程设计
yuanmazhiwu2 天前
计算机毕业设计:Python智慧出行数据分析与模式识别系统 Django框架 可视化 数据分析 PyEcharts 交通 深度学习(建议收藏)✅
人工智能·python·算法·数据分析·django·flask·课程设计
EmmaXLZHONG3 天前
Django By Example - 学习笔记
笔记·python·学习·django
AC赳赳老秦4 天前
OpenClaw二次开发入门:自定义技能,适配自身工作需求
服务器·数据库·python·mysql·django·deepseek·openclaw
暴力袋鼠哥4 天前
基于 LightGBM 的山东高考智能择校推荐系统设计与实现
python·django·flask