【商城实战】专栏重磅来袭!这是一份专为开发者与电商从业者打造的超详细指南。从项目基础搭建,运用 uniapp、Element Plus、SpringBoot 搭建商城框架,到用户、商品、订单等核心模块开发,再到性能优化、安全加固、多端适配,乃至运营推广策略,102 章内容层层递进。无论是想深入钻研技术细节,还是探寻商城运营之道,本专栏都能提供从 0 到 1 的系统讲解,助力你打造独具竞争力的电商平台,开启电商实战之旅。
目录
一、创建基于 Element Plus 的后台管理项目
在开发后台管理系统时,使用 Element Plus 能极大地提高开发效率,它提供了丰富的组件和美观的样式。下面我们将逐步创建基于 Element Plus 的后台管理项目,并搭建基础页面布局。
1.1 项目初始化
首先,确保你已经安装了 Node.js 和 Vue CLI。如果尚未安装 Vue CLI,可以通过以下命令进行全局安装:
npm install -g @vue/cli
安装完成后,使用 Vue CLI 创建一个新的项目,执行以下命令:
vue create admin-system
在创建过程中,会出现一系列选项让你选择项目配置。你可以根据项目需求选择合适的配置,例如是否使用 TypeScript、是否安装路由、是否使用状态管理等。这里我们选择默认配置,快速搭建项目基础框架。选择完成后,Vue CLI 会自动下载项目所需的依赖并初始化项目结构。
1.2 安装 Element Plus
项目初始化完成后,进入项目目录:
cd admin-system
然后使用 npm 安装 Element Plus,执行以下命令:
npm install element - plus --save
安装完成后,有两种方式在项目中引入 Element Plus:全局引入和按需引入。
- 全局引入:在main.js文件中添加以下代码:
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/lib/theme-chalk/index.css'
import App from './App.vue'
const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')
这样,Element Plus 的所有组件都可以在项目中全局使用。
- 按需引入:如果项目体积对大小比较敏感,希望只引入需要的组件,可以使用按需引入的方式。首先安装unplugin - vue - components和unplugin - auto - import插件,执行以下命令:
npm install -D unplugin - vue - components unplugin - auto - import
然后在vite.config.js(如果使用 Vite 构建工具)或webpack.config.js(如果使用 Webpack 构建工具)中进行配置。以 Vite 为例,在vite.config.js中添加以下配置:
import { defineConfig } from 'vite'
import Components from 'unplugin - vue - components/vite'
import { ElementPlusResolver } from 'unplugin - vue - components/resolvers'
export default defineConfig({
plugins: [
Components({
resolvers: [ElementPlusResolver()]
})
]
})
这样配置后,在使用 Element Plus 组件时,无需手动导入,直接在模板中使用即可,例如:
<template>
<el - button type="primary">主要按钮</el - button>
</template>
1.3 基础页面布局搭建
使用 Element Plus 的 Container 布局容器来搭建基础页面布局,它提供了el - container、el - header、el - aside、el - main和el - footer等组件,方便快速构建页面结构。
在App.vue文件中编写如下代码:
<template>
<el - container style="height: 100vh">
<el - header>
<!-- 头部内容,例如导航栏、logo等 -->
<h1>商城后台管理系统</h1>
</el - header>
<el - container>
<el - aside width="200px">
<!-- 侧边栏内容,菜单等 -->
<el - menu :default - openeds="['1']">
<el - sub - menu index="1">
<template #title>
<i class="el - icon - message"></i>商品管理
</template>
<el - menu - item index="1 - 1">商品列表</el - menu - item>
<el - menu - item index="1 - 2">添加商品</el - menu - item>
</el - sub - menu>
<el - sub - menu index="2">
<template #title>
<i class="el - icon - setting"></i>订单管理
</template>
<el - menu - item index="2 - 1">订单列表</el - menu - item>
<el - menu - item index="2 - 2">订单详情</el - menu - item>
</el - sub - menu>
</el - menu>
</el - aside>
<el - main>
<!-- 主要内容区域,根据路由显示不同页面 -->
<router - view></router - view>
</el - main>
</el - container>
</el - container>
</template>
<style scoped>
.el - header {
background - color: #333;
color: #fff;
display: flex;
justify - content: space - between;
align - items: center;
padding: 0 20px;
}
.el - aside {
background - color: #f4f4f4;
}
.el - main {
background - color: #fff;
padding: 20px;
}
</style>
上述代码中,el-container作为外层容器,设置高度为视口高度100vh。el-header用于显示头部内容,设置了背景颜色和样式。el-aside作为侧边栏,包含了商品管理和订单管理的菜单。el-main作为主要内容区域,通过router-view来显示不同路由对应的页面内容。这样就完成了基础页面布局的搭建,一个简单的后台管理系统框架初见雏形。
二、配置后台管理系统菜单及路由
在完成后台管理系统的基础页面布局搭建后,配置菜单及路由是实现页面导航功能的关键步骤。合理的菜单及路由配置能够让用户方便地在不同功能页面之间进行切换,提升系统的易用性和交互性。下面我们将详细介绍如何配置后台管理系统的菜单及路由。
2.1 定义路由规则
在 Vue 项目中,我们使用 Vue Router 来定义路由规则。Vue Router 是 Vue.js 官方的路由管理器,它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。
首先,确保已经安装了 Vue Router。如果在项目初始化时没有选择安装,可以通过以下命令进行安装:
npm install vue-router --save
安装完成后,在src目录下创建router文件夹,并在该文件夹下新建index.js文件,用于定义路由规则。
假设我们有商品列表、添加商品、订单列表、订单详情等页面组件,分别位于src/views/goods和src/views/order目录下,以下是定义路由规则的示例代码:
import { createRouter, createWebHistory } from 'vue-router'
// 引入页面组件
import GoodsList from '@/views/goods/GoodsList.vue'
import AddGoods from '@/views/goods/AddGoods.vue'
import OrderList from '@/views/order/OrderList.vue'
import OrderDetail from '@/views/order/OrderDetail.vue'
const routes = [
{
path: '/goods',
name: 'Goods',
component: () => import('@/views/goods/Goods.vue'),
children: [
{
path: 'list',
name: 'GoodsList',
component: GoodsList
},
{
path: 'add',
name: 'AddGoods',
component: AddGoods
}
]
},
{
path: '/order',
name: 'Order',
component: () => import('@/views/order/Order.vue'),
children: [
{
path: 'list',
name: 'OrderList',
component: OrderList
},
{
path: 'detail/:id',
name: 'OrderDetail',
component: OrderDetail,
props: true // 将路由参数作为props传递给组件
}
]
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
在上述代码中:
- 使用createRouter和createWebHistory创建路由实例,createWebHistory表示使用 HTML5 的 history 模式,去除 URL 中的#。
- routes数组定义了路由规则,每个路由对象包含path(路径)、name(路由名)和component(组件)等属性。
- 对于商品管理和订单管理模块,使用了嵌套路由的配置方式。在Goods和Order路由下,通过children属性定义了子路由,例如GoodsList和AddGoods是Goods路由的子路由,OrderList和OrderDetail是Order路由的子路由。
- 在OrderDetail路由中,使用了动态路由参数/:id,表示该路由可以匹配不同的订单 ID,例如/order/detail/123,并且通过props: true将路由参数id作为 props 传递给OrderDetail组件,在组件中可以通过this.$route.params.id获取该参数值。
2.2 创建菜单组件
使用 Element Plus 的 Menu 组件来创建后台管理系统的菜单,Menu 组件提供了丰富的功能和样式,方便我们构建出美观且易用的菜单。
在App.vue文件中,我们已经搭建了基础的侧边栏结构,现在进一步完善菜单部分。修改el - aside部分的代码如下:
<el-aside width="200px">
<el-menu :default - openeds="['1']" router>
<el-sub-menu index="1">
<template #title>
<i class="el - icon - message"></i>商品管理
</template>
<el-menu-item :index="'/goods/list'">商品列表</el-menu-item>
<el-menu-item :index="'/goods/add'">添加商品</el-menu-item>
</el-sub-menu>
<el-sub-menu index="2">
<template #title>
<i class="el-icon-setting"></i>订单管理
</template>
<el-menu-item :index="'/order/list'">订单列表</el-menu-item>
<el-menu-item :index="'/order/detail/1'">订单详情</el-menu-item>
</el-sub-menu>
</el-menu>
</el-aside>
在上述代码中:
- el - menu组件设置了router属性,这使得菜单能够与路由进行关联,实现点击菜单时自动切换到相应的路由页面。
- :default - openeds="[‘1’]"表示默认展开索引为1的子菜单,即商品管理子菜单。
- el - sub - menu用于创建二级菜单,index属性为子菜单的唯一标识。
- el - menu - item为菜单项,index属性指定了菜单项对应的路由路径,例如’/goods/list’和’/goods/add’分别对应商品列表和添加商品的路由路径。
2.3 关联菜单与路由
通过前面的步骤,我们已经定义了路由规则并创建了菜单组件,接下来需要将菜单与路由进行关联,实现点击菜单时切换到相应页面的功能。
由于在el-menu组件上设置了router属性,Element Plus 会自动根据菜单项的index属性值来匹配路由。当用户点击菜单项时,Vue Router 会根据匹配到的路由规则,将对应的组件渲染到router - view中。
此外,路由导航守卫在路由切换过程中起着重要作用。路由导航守卫可以用来对路由的进入和离开进行控制,例如进行权限验证、页面加载前的准备工作等。
在router/index.js文件中,可以使用全局前置守卫beforeEach来实现简单的权限验证功能。假设我们需要用户登录后才能访问后台管理系统的页面,代码如下:
import router from './router'
import store from './store'
const whiteList = ['/login'] // 白名单,不需要登录即可访问的页面
router.beforeEach((to, from, next) => {
const hasToken = store.getters.token // 从状态管理中获取用户登录令牌
if (hasToken) {
if (to.path === '/login') {
next('/') // 如果已登录,访问登录页则重定向到首页
} else {
next() // 已登录,允许访问其他页面
}
} else {
if (whiteList.includes(to.path)) {
next() // 未登录,在白名单内的页面允许访问
} else {
next(`/login?redirect=${to.path}`) // 未登录,重定向到登录页,并携带当前访问路径
}
}
})
在上述代码中:
- beforeEach函数接收三个参数:to表示即将进入的目标路由对象,from表示当前导航正要离开的路由对象,next是一个函数,必须调用它来继续导航。
- 通过store.getters.token获取用户登录令牌,判断用户是否已登录。如果已登录,且访问的不是登录页,则允许访问;如果已登录但访问登录页,则重定向到首页。
- 如果未登录,判断当前访问路径是否在白名单内,在白名单内则允许访问,否则重定向到登录页,并将当前访问路径作为参数传递,以便登录成功后能自动跳转到原访问页面。
通过以上步骤,我们成功地配置了后台管理系统的菜单及路由,并实现了页面导航功能和简单的权限验证机制,为后续的功能开发奠定了坚实的基础。
三、搭建后台管理系统用户登录及权限验证机制
在后台管理系统中,用户登录及权限验证机制是保障系统安全的重要环节。它能够确保只有合法用户能够访问系统,并且根据用户的角色和权限限制其对系统功能和数据的访问。下面我们将详细介绍如何搭建后台管理系统的用户登录及权限验证机制,并展示相关的实现源码。
3.1 用户登录流程
在前端登录页面,使用 Element Plus 的 Form 组件收集用户输入的用户名和密码,通过 Axios 发送 POST 请求到后端进行验证。前端代码示例如下:
<template>
<el - container class="login - container">
<el - form ref="loginFormRef" :model="loginForm" :rules="loginRules" label - width="80px">
<el - form - item label="用户名" prop="username">
<el - input v - model="loginForm.username" placeholder="请输入用户名"></el - input>
</el - form - item>
<el - form - item label="密码" prop="password">
<el - input v - model="loginForm.password" type="password" placeholder="请输入密码"></el - input>
</el - form - item>
<el - form - item>
<el - button type="primary" @click="handleLogin">登录</el - button>
</el - form - item>
</el - form>
</el - container>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import axios from 'axios';
import { useUserStore } from '../stores/user';
const loginForm = ref({
username: '',
password: ''
});
const loginRules = ref({
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' }
]
});
const handleLogin = async () => {
try {
const response = await axios.post('/api/login', loginForm.value);
if (response.data.success) {
const userStore = useUserStore();
userStore.setToken(response.data.token);
userStore.setUserInfo(response.data.user);
// 这里可以根据业务需求进行页面跳转,如跳转到首页
} else {
// 提示用户登录失败信息
}
} catch (error) {
console.error('登录请求失败', error);
}
};
</script>
<style scoped lang="scss">
.login - container {
display: flex;
justify - content: center;
align - items: center;
height: 100vh;
background - color: #f0f2f5;
}
</style>
后端使用 Spring Boot 处理登录请求,在UserController中编写登录接口:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.example.mallbackend.dto.LoginRequest;
import com.example.mallbackend.dto.LoginResponse;
import com.example.mallbackend.service.UserService;
@RestController
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/api/login")
public LoginResponse login(@RequestBody LoginRequest loginRequest) {
return userService.login(loginRequest);
}
}
LoginRequest和LoginResponse是自定义的数据传输对象,用于封装前端传递的登录信息和返回给前端的登录结果。
3.2 Token 验证机制
前端在每次发送请求时,将 Token 添加到请求头中。在 Axios 的拦截器中进行统一处理:
import axios from 'axios';
import { useUserStore } from '../stores/user';
const service = axios.create({
baseURL: '/api',
timeout: 5000
});
service.interceptors.request.use(
config => {
const userStore = useUserStore();
if (userStore.token) {
config.headers['Authorization'] = `Bearer ${userStore.token}`;
}
return config;
},
error => {
return Promise.reject(error);
}
);
service.interceptors.response.use(
response => {
return response;
},
error => {
if (error.response.status === 401) {
// Token失效,进行相应处理,如跳转到登录页面
}
return Promise.reject(error);
}
);
export default service;
后端在 Spring Boot 项目中,使用过滤器对请求头中的 Token 进行验证。创建一个JwtAuthenticationFilter过滤器:
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.GenericFilterBean;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import com.example.mallbackend.service.UserService;
public class JwtAuthenticationFilter extends GenericFilterBean {
private static final String HEADER_STRING = "Authorization";
private static final String TOKEN_PREFIX = "Bearer ";
private final UserService userService;
private final String secret;
public JwtAuthenticationFilter(UserService userService, String secret) {
this.userService = userService;
this.secret = secret;
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
String header = request.getHeader(HEADER_STRING);
if (header != null && header.startsWith(TOKEN_PREFIX)) {
String token = header.replace(TOKEN_PREFIX, "");
Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
String username = claims.getSubject();
UserDetails userDetails = userService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(servletRequest, servletResponse);
}
}
在 Spring Security 配置类中注册该过滤器:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import com.example.mallbackend.service.UserService;
import com.example.mallbackend.filter.JwtAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final UserService userService;
private final String secret;
public SecurityConfig(UserService userService, String secret) {
this.userService = userService;
this.secret = secret;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(new JwtAuthenticationFilter(userService, secret),
UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/api/login").permitAll()
.anyRequest().authenticated();
}
}
上述代码实现了前端和后端的 Token 验证机制,确保只有合法的用户请求能够访问受保护的资源。
3.3 权限控制实现
在前端,根据用户的角色动态显示或隐藏菜单和页面元素。例如,在菜单组件中根据用户角色判断是否显示某些菜单项:
<template>
<el - menu :default - active="activeIndex" class="el - menu - vertical - demo" @select="handleSelect">
<el - menu - item index="/">
<el - icon><HomeFilled /></el - icon>
<span>首页</span>
</el - menu - item>
<el - menu - item v - if="userStore.role === 'admin'" index="/user">
<el - icon><UserFilled /></el - icon>
<span>用户管理</span>
</el - menu - item>
<el - menu - item v - if="userStore.role === 'admin' || userStore.role ==='seller'" index="/product">
<el - icon><GoodsFilled /></el - icon>
<span>商品管理</span>
</el - menu - item>
</el - menu>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { HomeFilled, UserFilled, GoodsFilled } from '@element - plus/icons - vue';
import { useUserStore } from '../stores/user';
const activeIndex = ref('/');
const handleSelect = (index: string) => {
// 这里可以添加点击菜单后的额外逻辑,如记录操作日志等
activeIndex.value = index;
};
</script>
<style scoped lang="scss">
.el - menu - vertical - demo {
height: 100%;
border - right: none;
}
</style>
在后端,通过 Spring Security 的注解和配置来实现对接口的权限控制。例如,在UserController中对某些接口添加权限注解:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.mallbackend.service.UserService;
@RestController
@RequestMapping("/api/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/admin - only")
@PreAuthorize("hasRole('ADMIN')")
public String adminOnly() {
return "This is an admin - only endpoint";
}
}
上述代码展示了前端和后端协同实现的权限控制机制,确保不同角色的用户只能访问其被授权的功能和页面。
四、总结与展望
4.1 回顾搭建过程
在本次后台管理系统的基础搭建中,我们首先使用Vue CLI创建了项目,并成功安装和引入了Element Plus组件库,通过其Container布局容器搭建了美观且实用的基础页面布局,包含了清晰的头部、侧边栏和主要内容区域。
接着,利用Vue Router定义了详细的路由规则,涵盖了商品管理、订单管理等多个模块及其子路由,同时创建了基于Element Plus Menu组件的菜单,并通过设置router
属性实现了菜单与路由的无缝关联,还运用路由导航守卫实现了简单的权限验证,确保只有合法用户能够访问特定页面。
最后,搭建了用户登录及权限验证机制。完善的用户登录流程确保了用户身份的准确识别,可靠的 Token 验证机制保障了请求的安全性,细致的权限控制实现则从前端和后端两个层面保证了不同角色用户对系统资源的合理访问,为整个商城后台管理系统的稳定运行和数据安全奠定了坚实基础。
4.2 后续优化方向
在后续的开发中,我们可以从多个方面对后台管理系统进行优化。性能优化方面,通过代码压缩、缓存机制、异步加载等技术,提高系统的响应速度和运行效率,减少页面加载时间,提升用户体验。安全加固方面,进一步完善权限管理,采用更复杂的加密算法和安全策略,防止数据泄露和非法访问,例如对用户数据进行加密存储,对敏感操作进行详细的日志记录以便追溯。用户体验改进方面,优化界面设计,使其更加简洁美观、易于操作,增加操作提示和反馈机制,让用户在使用过程中更加清晰地了解系统状态和操作结果。同时,不断完善系统的功能,根据业务需求添加更多实用的功能模块,如数据分析、报表生成等,以满足商城运营和管理的多样化需求 。通过持续的优化和完善,使后台管理系统更加稳定、高效、安全,为商城的顺利运营提供坚实的支持。