Vue 3 实践:构建管理后台布局与分类页面
本节内容将指导你如何使用 Vue 3 结合 Element Plus 组件库来搭建管理后台的整体布局以及商品分类页面。
一、引入 Element Plus
首先,需要安装 Element Plus 组件库。在项目根目录下执行以下命令:
npm install element-plus --save
二、创建主布局组件
参考 Element Plus 官方的布局示例,我们可以创建一个包含侧边导航栏和主内容区域的布局组件。以下是布局组件的模板和样式代码:
<template>
<el-container class="admin-layout">
<el-aside width="220px" class="sidebar">
<el-scrollbar>
<div class="brand-logo">Vue & WebAPI 后台</div>
<el-menu
default-openeds="['1']"
active-text-color="#ffd04b"
background-color="#303133"
text-color="#fff"
router>
<el-sub-menu index="1">
<template #title>
<el-icon><Document></el-icon> 核心模块
</template>
<el-menu-item-group>
<el-menu-item index="/"><el-icon><HomeFilled></el-icon> 仪表盘</el-menu-item>
<el-menu-item index="/categories"><el-icon><Grid></el-icon> 分类管理</el-menu-item>
<el-menu-item index="/products"><el-icon><ShoppingBag></el-icon> 商品管理</el-menu-item>
</el-menu-item-group>
</el-sub-menu>
<el-sub-menu index="2">
<template #title>
<el-icon><Setting></el-icon> 系统设置
</template>
<el-menu-item-group>
<el-menu-item index="/profile"><el-icon><User></el-icon> 个人资料</el-menu-item>
</el-menu-item-group>
</el-sub-menu>
</el-menu>
</el-scrollbar>
</el-aside>
<el-container>
<el-header class="header-bar">
<div class="toolbar">
<el-dropdown>
<el-icon class="el-icon-setting"><Tools></Tools></el-icon>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>个人中心</el-dropdown-item>
<el-dropdown-item>退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<span class="username">管理员</span>
</div>
</el-header>
<el-main class="main-content">
<router-view></router-view>
</el-main>
<el-footer>版权所有 © 2023</el-footer>
</el-container>
</el-container>
</template>
<script setup>
// No script logic needed for this basic layout structure
</script>
<style scoped>
.brand-logo {
height: 60px;
color: white;
text-align: center;
line-height: 60px;
font-size: 1.2em;
font-weight: bold;
background-color: #2b2f35;
}
.admin-layout {
height: 100vh;
}
.sidebar {
background-color: #303133;
border-right: none;
}
.header-bar {
background-color: #ffffff;
color: var(--el-text-color-primary);
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
display: flex;
align-items: center;
justify-content: flex-end;
padding: 0 20px;
}
.el-icon-setting {
margin-right: 8px;
font-size: 1.2em;
cursor: pointer;
}
.username {
font-size: 1em;
}
.main-content {
padding: 20px;
background-color: #f0f2f5;
box-shadow: inset 0 0 12px rgba(0,21,41,0.08);
margin: 10px 10px 0;
border-radius: 4px;
}
.el-footer {
text-align: center;
line-height: 60px;
background-color: #ffffff;
color: var(--el-text-color-secondary);
box-shadow: 0 -1px 4px rgba(0, 21, 41, 0.08);
}
</style>
<style> /* 全局样式 */
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 10px;
}
.text {
font-size: 14px;
}
.item {
margin-bottom: 18px;
}
.box-card {
width: 100%;
min-height: calc(100vh - 60px); /* Adjust height based on header/footer */
}
</style>
三、全局注册 Element Plus 和图标
在项目入口文件 `src/main.js` 中,进行 Element Plus 和所有 Element Plus 图标的全局注册。
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import store from './store'; // 假设你使用了 Vuex
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
import * as ElementPlusIconsVue from '@element-plus/icons-vue';
const app = createApp(App);
// 注册 Element Plus 图标
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component);
}
app.use(ElementPlus)
.use(store)
.use(router)
.mount('#app');
四、配置路由
在 `src/router/index.js` 文件中,为分类页面添加路由配置。确保路由路径与布局组件中的导航链接匹配。
import { createRouter, createWebHistory } from 'vue-router';
import HomeView from '../views/HomeView.vue'; // 假设的首页组件
const routes = [
{
path: '/',
name: 'Dashboard',
component: HomeView
},
{
path: '/about',
name: 'About',
// 路由懒加载
component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
},
{
path: '/categories',
name: 'Categories',
// 懒加载分类页面组件
component: () => import(/* webpackChunkName: "categories" */ '../views/CategoryView.vue')
}
// ... 其他路由
];
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
});
export default router;
五、创建分类页面组件
在 `src/views` 目录下新建 `CategoryView.vue` 文件,用于展示商品分类列表。该组件将使用 Element Plus 的 Card 和 Table 组件。
<template>
<el-card class="box-card" shadow="never">
<template #header>
<div class="card-header">
<span><el-icon><Grid></el-icon> 商品分类管理</span>
<el-button type="success" icon="CirclePlus" round @click="showAddDialog">新增分类</el-button>
</div>
</template>
<el-table :data="categoryList" style="width: 100%" stripe border>
<el-table-column prop="id" label="ID" width="100" align="center" />
<el-table-column prop="name" label="分类名称" min-width="200" />
<el-table-column prop="createdAt" label="创建时间" width="180" align="center" />
<el-table-column fixed="right" label="操作" width="200" align="center">
<template #default="{ row }">
<el-button type="primary" size="small" icon="Edit" @click="showEditDialog(row)">编辑</el-button>
<el-button type="danger" size="small" icon="Delete" @click="deleteCategory(row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件,此处省略 -->
</el-card>
<!-- 添加/编辑分类的对话框,此处省略 -->
</template>
<script setup>
import { ref, onMounted } from 'vue';
// 模拟分类数据
const categoryList = ref([
{ id: 1, name: '电子产品', createdAt: '2023-10-26 10:00:00' },
{ id: 2, name: '服装鞋帽', createdAt: '2023-10-26 10:05:00' },
{ id: 3, name: '家居生活', createdAt: '2023-10-26 10:10:00' },
{ id: 4, name: '图书音像', createdAt: '2023-10-26 10:15:00' },
]);
// 假设的添加分类方法
const showAddDialog = () => {
console.log('显示添加分类对话框');
// 在这里实现打开对话框的逻辑
};
// 假设的编辑分类方法
const showEditDialog = (row) => {
console.log('显示编辑分类对话框,分类ID:', row.id);
// 在这里实现打开对话框并填充数据的逻辑
};
// 假设的删除分类方法
const deleteCategory = (id) => {
console.log('删除分类,ID:', id);
// 在这里实现删除逻辑,可能需要后端交互
ElMessageBox.confirm('确定要删除该分类吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
// 实际删除操作
categoryList.value = categoryList.value.filter(item => item.id !== id);
ElMessage({
type: 'success',
message: '删除成功!',
});
})
.catch(() => {
ElMessage({
type: 'info',
message: '已取消删除',
});
});
};
onMounted(() => {
// 组件挂载时,可以从后端获取分类数据
// fetchCategories();
});
// 模拟获取分类数据的函数
// const fetchCategories = async () => {
// // ... 实际的API请求逻辑
// // categoryList.value = await api.getCategories();
// };
</script>
<style scoped>
.el-card {
margin: 20px;
}
.card-header span {
font-weight: bold;
font-size: 1.1em;
display: flex;
align-items: center;
gap: 8px;
}
.el-table__header-wrapper th {
background-color: #f5f7fa;
}
</style>