Vue2项目总结-电商后台管理系统
创始人
2025-05-31 08:25:58

Vue2项目总结-电商后台管理系统

去年做的项目,拖了很久,总算是打起精力去做这个项目的总结,并对Vue2的相关知识进行回顾与复习

各个功能模块如果有过多重复冗杂的部分,将会抽取部分值得记录复习的地方进行记录

一:项目简介

前端技术栈

Vue2

vue-router

Element-ui

Axios

Echarts

项目构架

image-20230320182412413

功能模块

  1. 用户登录/退出模块

  2. 用户管理模块

  3. 权限管理模块

    角色列表模块

    权限列表模块

  4. 商品管理模块

    商品列表模块

    分类管理模块

    参数管理模块

  5. 订单管理模块

  6. 数据统计模块


二:各个功能模块

1:项目初始化

通过vue-cli脚手架进行配置安装

vue2配置

配置vue-router

配置axios

后端接口:http://43.143.0.76:8889/api/private/v1/

在main.js文件配置根路径: axios.defaults.baseURL = ‘http://43.143.0.76:8889/api/private/v1/’

1.1:路由器的配置

配置路由器,通过login登录之后会优先跳转到home父组件(Element-ui的布局模式),redirect重定向路由到welcome欢迎组件

const router =  new Router({routes:[{path: '/' , redirect: '/login'},{path: '/login' , component:() => import('@/components/Login.vue')},{ path: '/home' , component:() => import('@/components/Home.vue'),redirect: '/welcome',children: [{ path: '/welcome' , component:() => import('@/components/Welcome.vue') },{ path: '/users' , component:() => import('@/components/user/Users.vue')},{ path: '/rights' , component:() => import('@/components/power/Rights.vue')},{ path: '/roles' , component:() => import('@/components/power/Roles.vue')},{ path: '/categories' , component:() => import('@/components/goods/Cate.vue')},{ path: '/params' , component:() => import('@/components/goods/Params.vue')},{ path: '/goods' , component:() => import('@/components/goods/List.vue')},{ path: '/goods/add' , component: () => import('@/components/goods/Add.vue')},{ path: '/orders' , component: () => import('@/components/order/Order.vue')},{ path: '/reports' , component: () => import('@/components/report/Report.vue')}]}]
})

image-20230320191410586

2:用户登录/退出模块

相关技术点

  1. http是无状态的
  2. 通过cookie在客户端记录状态
  3. 通过session在服务器端记录状态
  4. 通过token方式维持状态

如果前端和后台不存在跨域问题,可以通过cookie和session来记录登录状态,如果存在跨域问题,通过token方式来维持登录状态

2.1:登录token原理分析

image-20230320183611304

如果不通过登录来获取服务器的token值,直接通过路由跳转对应页面,服务器无法验证token通过,有些接口功能将无法实现,由此还需要配置路由守卫来防止用户直接通过路由跳转对应页面

2.2:登录login函数逻辑代码

image-20230320190337718

这里是使用了 asyncawait 来解析对应的promise对象。async 函数返回一个 Promise 对象,可以使用 then 方法添加回调函数。当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句。

如果登录成功,将服务器的token值保存到客户端的sessionStorage中,利用seiItem属性键值对的方法存储,以便之后的后续请求都携带token认证

    login() {this.$refs.loginFormRef.validate(async (valid) => {// console.log(valid)if (!valid) {return} else {const { data: res } = await this.$http.post('login', this.loginForm)// console.log(data)if (res.meta.status != 200) {this.$message.error('登录失败!')} else {this.$message.success('登录成功!')//将token值保存到客户端中window.sessionStorage.setItem('token', res.data.token)//并且跳转路由this.$router.push('/home')}}})},

2.3:路由守卫

在router.js中配置路由守卫,目的是为了防止用户未通过登录,而是选择更改路由跳转到对应页面。

//挂载路由守卫
router.beforeEach((to , from , next) => {
//to 表示将要跳转的页面
//from 表示从哪个页面来的
//next() 表示放行  next('/login')表示强制跳转到登录页面if(to.path === '/login'){// console.log(to)next()}else{//获取用户当前客户端的token值const tokenStr = window.sessionStorage.getItem('token')//如果没有token值,强制跳转到登录页面//如果存在token,就放行if(!tokenStr){next('/login')}else{next()}}})

2.4:Element-ui的表单验证和表单重置

表单验证

image-20230320185612663

:rules属性绑定,data中的表单验证对象

登录重置
      //登录时的校验对象loginFormRules: {username: [{ required: true, message: '请输入登录账户', trigger: 'blur' },{min: 3,max: 10,message: '长度在 3 到 10 个字符',trigger: 'blur',},],password: [{ required: true, message: '请输入登录密码', trigger: 'blur' },{min: 3,max: 15,message: '长度在 3 到 15 个字符',trigger: 'blur',},],},

表单重置

this指向vue的原型对象,通过原型对象绑定对应的resetFields函数

    reset() {// console.log(this)//实现表单的重置功能this.$refs.loginFormRef.resetFields()},

2.5:退出登录

直接调用sessionStorage.clear()函数清空存储的token即可,同时跳转到/login页面即可

    exit() {window.sessionStorage.clear()this.$router.push('/login')},

3:用户管理模块

3.1:Element-ui侧边栏

在回顾具体功能模块前提不得不提一下element-ui的侧边栏组件

 
| | |
{{subitem.authName}}
  1. 在el-menu标签中,:default-active=""属性值是激活菜单的值,并没有设死,我们数据绑定到data中的activePath中
  2. 在el-submenu标签中,:index值是通过父级的v-for属性将menuList的所有item通过l插值语法(两个花括号)显示到模板上,需要注意的一点是,v-for属性同时需要,key属性的同时存在,否则会报错

image-20230320192717138

 data() {return {menuList: [],iconList: {125: 'el-icon-s-custom',103: 'el-icon-lock',101: 'el-icon-shopping-cart-1',102: 'el-icon-s-order',145: 'el-icon-s-data',},closeValue: false,activePath: '',}},created() {this.getMenuList()this.activePath = window.sessionStorage.getItem('activePath')},//保存链接的激活状态saveNavState(activePath) {window.sessionStorage.setItem('activePath', activePath)this.activePath = activePath},

savNavState函数,解决了图标在当前选项高亮,但当重进还是会选择上一高亮位置,但内容则是welcome组件内容

通过sessionStorage存储当前的activePath,在created()组件被创建的时候再从sessionStorage中取出

  //保存链接的激活状态saveNavState(activePath) {window.sessionStorage.setItem('activePath', activePath)this.activePath = activePath},

3.2:作用域插槽

image-20230320193927956

其余的element-ui界面布局组件没什么难度,跟着文档走就可以了,需要回顾的就是slot-scope作用域插槽

这里是想实现一个按钮来切换状态的效果,element-ui提供了这个组件,但是我们同时还要实现,点击切换状态还要修改数据库中的数据

作用域插槽可以理解为:父传子,传结构,根据子组件的中的数据传结构

          
 //监听用户状态修改的事件async userStateChange(userInfo) {// console.log(userInfo)const { data: res } = await this.$http.put(`users/${userInfo.id}/state/${userInfo.mg_state}`)if (res.meta.status !== 200) {userInfo.mg_state = !userInfo.mg_statethis.$message.error('更新用户状态失败!')}this.$message.success('更新用户状态成功!')},

3.3:Pagination 分页

image-20230320194654418

还是利用element-ui组件实现

      

分页功能不仅仅这里需要,以后的项目一些业务都是需要分页功能的。

首先需要创建分页数据对象,pagenum(当前页数),pagesize(每页显示数据条数),total(总条数)。将其返回给后端,后端返回对应数据。

	queryInfo: {query: '',//当前的页数pagenum: 1,//当前每页显示多少条数据pagesize: 2,},// 页码总数数据total: 0,

3.4:dialog对话框

点击编辑按钮会弹出一个对话框来实现我们的编辑功能

逻辑如下:

  1. 点击编辑按钮,触发点击事件。展示dialog同时通过id搜索该用户的个人信息,将其展现。
  2. 用户通过更改本文内容,双向数据绑定到editform表单对象中
  3. 点击取消,修改visible属性(布尔值)来隐藏该对话框
  4. 点击确定,通过请求修改对应数据库中信息,同时列表需要刷新,再次调用获取用户数据函数,也修改visible属性隐藏对话框

image-20230320203453385
html结构

    取 消确 定

编辑按钮结构



点击确定修改信息逻辑

//修改用户editUser() {//预校验this.$refs.editFormRef.validate(async (valid) => {if (!valid) return//发起修改用户信息请求const { data: res } = await this.$http.put('users/' + this.editForm.id,{email: this.editForm.email,mobile: this.editForm.mobile,})if (res.meta.status != 200) {return this.$message.error('更新用户信息失败!')}//关闭对话框this.editDialogVisible = false//刷新数据列表this.getUserList()//提示修改成功this.$message.success('更新用户信息成功!')})},

4:权限管理模块

4.1:展开表格列

image-20230320204715728

主要还是通过作用域插槽和v-for,还有嵌套的权限属性实现,当然还有tag标签

  1. el-row标签中利用v-for渲染出父级元素,蓝色标签,通过作用域插槽传数据,同时el-tag将该值渲染成蓝色标签
  2. 叉叉移除函数,removeRightById,由于每个权限都有对应的id,所以通过id来删除数据库中数据
  3. 嵌套

html结构

        

removeRightById()函数

  1. 利用了confirm弹窗组件,全局挂载,promise对象,需要.then().catch()来解析
  2. 发送delete请求的参数,利用到了es6模板字符串用法
 //删除权限async removeRightById(role, roleId) {//弹框提示用户是否要删除const confirmResult = await this.$confirm('此操作将永久删除该文件, 是否继续?','提示',{confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning',}).catch((err) => err)if (confirmResult !== 'confirm') {return this.$message.info('取消了删除!')} else {//向服务器发送请求删除const { data: res } = await this.$http.delete(`roles/${role.id}/rights/${roleId}`)if (res.meta.status !== 200) {return this.$message.error('删除权限失败!')} else {role.children = res.data}}},

5:商品管理模块

5.1:级联选择器

element-ui提供的级联选择器,有时候会出现bug,element-ui的版本不断地更新也在修正

  1. v-model = "selectedCateKeys"数据双向绑定数组
  2. @change事件,当选中节点变化时触发
  3. :options绑定商品列表
  4. :props绑定对象的某个值,实现多级级联选择器

image-20230320210758628
html结构

        选择商品分类:

@change事件函数

    //监听级联选择器选中框变换async handleChange() {this.getParamsData()},
      //指定级联选择器的配置对象cateProps: {value: 'cat_id',label: 'cat_name',chidren: 'children',},

5.2:tabs标签页

tabs标签页

  1. v-model双向数据绑定对应active选中的数据,这里是many和only
  2. @tab-click事件监听标签页改变触发

实现选择级联选择器的商品时候,展示对应的动态参数逻辑如下

  1. 通过级联选择器的handleChange和tabs标签页的handleTabClick两个事件,都调用getParamsData()获取商品参数函数
  2. 通过每个商品的特定id获取对应的参数信息

image-20230320211133719

结构

      
data(){//Tabs标签页的双向绑定数据activeName: 'many',
}
  1. 首先级联选择器的长度如果不是3,即选中的只是一二级菜单就清空,不展示
  2. 第三级菜单,根据所选分类的ID,和当前所处的面板,获取对应的参数

getParamsData()函数

 //获取参数列表数据async getParamsData() {//如果选中的不是三级菜单,数组长度就为1或2,就清空数组if (this.selectedCateKeys.length !== 3) {this.selectedCateKeys = []this.manyTableData = []this.onlyTableData = []return} else {//根据所选分类的ID,和当前所处的面板,获取对应的参数const { data: res } = await this.$http.get(`categories/${this.cateId}/attributes`,{params: {sel: this.activeName,},})if (res.meta.status !== 200) {this.$message.error('获取参数列表失败!')} else {//成功// console.log(res.data)//存储动态参数数据和静态属性数据res.data.forEach((item) => {item.attr_vals = item.attr_valsitem.attr_vals = item.attr_vals ? item.attr_vals.split(',') : []//控制文本框的显示与隐藏// item.inputVisible = falsethis.$set(item, 'inputVisible', false)//文本框中输入的值item.inputValue = ''})// console.log(res.data)if (this.activeName === 'many') {this.manyTableData = res.data} else {this.onlyTableData = res.data}}}},

将后台返回的数据,进行forEach遍历存储,还利用了split()分割函数

//存储动态参数数据和静态属性数据res.data.forEach((item) => {item.attr_vals = item.attr_valsitem.attr_vals = item.attr_vals ? item.attr_vals.split(',') : []//控制文本框的显示与隐藏// item.inputVisible = falsethis.$set(item, 'inputVisible', false)//文本框中输入的值item.inputValue = ''})

5.3: Tree 树形控件

image-20230320212617593

在商品分类模块中,对于分类名称利用到了tree树形控件,用清晰的层级结构展示信息,可展开或折叠。

  1. :data数据绑定刀catelist,商品分类列表。
  2. :columns属性columns纵列分布
  3. 依旧使用作用域插槽,同时利用了v-if来控制对应的显示与隐藏,利用表达式的值

结构

      

5.4:添加商品信息模块

image-20230320213254889

5.4.1:el-steps步骤展示信息

image-20230320213348056

      
5.4.2:el-tabs左侧标签页

image-20230320213546231

  1. @tab-click,当tab标签页被选中时触发事件

结构

  

@tab-click函数

    //当tab标签页被选中时触发事件async tabClicked() {// console.log(this.activeIndex)//访问的是商品参数面板if (this.activeIndex === '1') {//发起请求获取参数const { data: res } = await this.$http.get(`categories/${this.cateId}/attributes`,{params: { sel: 'many' },})if (res.meta.status !== 200) {this.$message.error('获取商品参数失败!')} else {res.data.forEach((item) => {item.attr_vals =item.attr_vals.length === 0 ? [] : item.attr_vals.split(',')})// this.$message.success('成功!')this.manyTableData = res.data// console.log(this.manyTableDate)}} else if (this.activeIndex === '2') {//发起请求获取参数const { data: res } = await this.$http.get(`categories/${this.cateId}/attributes`,{params: { sel: 'only' },})if (res.meta.status !== 200) {this.$message.error('获取商品参数失败!')} else {this.onlyTableData = res.data// console.log(this.onlyTableData)}}},
5.4.3:upload上传图片
  1. :preview:”handlePreview“,处理图片预览函数
  2. :on-preview=“handlePreview”,处理图片移除函数

image-20230320214012692

 点击上传

handlePreview()图片预览函数

    //处理图片预览handlePreview(file) {// console.log(file)this.previewPath = file.response.data.urlthis.previewVisible = true},

handlePreview()图片移除函数

    //处理图片移除handleRemove(file) {//1.获取将要删除的图片的临时路径const filePath = file.response.data.tmp_path//2.从pics数组中,找到该图片的索引值const i = this.addForm.pics.findIndex((x) => {x.pic === filePath})//3.调用数组的splice方法,从pics数组中移除this.addForm.pics.splice(i, 1)},

6:数据统计模块

6.1:echarts数据报表

image-20230320214618624

// 基于准备好的dom,初始化echarts实例var myChart = echarts.init(this.$refs.main)const { data: res } = await this.$http.get('reports/type/1')if (res.meta.status !== 200) return this.$message.error('初始化折线图失败!')const data = _.merge(res.data, this.options)// 绘制图表myChart.setOption(data)

6.2:NProgress的使用

NProgress 是前端轻量级 web 进度条插件

  1. 导入NProgress包
  2. 配合axios请求拦截器使用
//导入NProgress包
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'//配置axios请求拦截器
//在requst 拦截器中,展示进度条,NProgress.start()
axios.interceptors.request.use(config => {NProgress.start()//为请求头对象,添加token验证的Authorization字段config.headers.Authorization = window.sessionStorage.getItem('token')return config
})
//在response中 隐藏进度条NProgress.done()
axios.interceptors.response.use(config => {NProgress.done()return config
})

三:总结

总算把一直拖着的项目知识点整理整理完了,ohYeah!!!

回想看完成这个项目,用自己学的vue的知识一步一步的完成,其中有许多相同的部分,但还是自己一步一步的完成了,期间遇到许许多多的error报错但还是上网不断的搜索搜索,debug。

对于路由的掌握更加得心应手了,包括登录的token认证,路由守卫,请求拦截器等等。

但也看过别人的大型项目,是将请求同一封装到一个request,js文件中,这样的好处更多是避免一个请求的方式出错减少更多的修改,以后要还有项目一定尝试尝试。

回想看最头疼的也是最有收获的部分就是分类参数模块的级联选择器配合tabs标签页的使用,添加参数等等。

通过这次的整理也算是对vue2的一个整体复习,要开始步入vue3和ts的学习了。

加油吧!

相关内容

热门资讯

游戏服务器是什么怎么租用 游戏服务器是什么怎么租用 我是艾西,作为一个常年与游戏行业保持着高频率的服务器供应商&...
Flink-转换算子  基本转换算子         map(映射)         filter(过滤&#...
2023年金三银四大厂高频Ja... Java 面试 谈到 Java 面试,相信大家第一时间脑子里想到的词肯定是金三银四&#...
C语言手撕一个Hash表(Ha... 什么是Hash Table 散列表用的是数组支持按照下标随机访问数据的特性,所以散列表...
springMVC01- 文章目录今日目标一、SpringMVC简介1 SpringMVC概述问题导入1.1 SpringMV...
Electron开发的应用利用... 技术选型: 1、electron:21.3.3 2、electron-v...
【Elastic (ELK) ... 目录 一、ES 基本概念介绍 1.1 ES 是什么 1.2 ES 主要功能 1.3 ES 相关术语 ...
指定wb用户在指定日期范围内的... 一、操作步骤 只记录过程,不讲述原理 1.获取用户ID和cookie 用户ID在进入个...
sheng的学习笔记-IO多路... 基础概念IO分为几种:同步阻塞的BIO,同步非阻塞的NIO,...
接口自动化测试(Python+...  目录:导读 (1)接口自动化测试的优缺点 (2)Pyth...
重构条件-Consolidat... 重构条件-Consolidate Conditional Expression合并条件式二 1.合并...
【论文阅读】BiSeNet V... 前言BiSeNet V2延续了v1版本的双边结构,分别处理空间细节信息、高层语义信息。...
二、马尔可夫决策过程与贝尔曼方... 这里写目录标题1 马尔可夫性质2 马尔可夫过程3 马尔可夫奖励过程(Markov re...
golang端口重用 文章目录前言SO_REUSEADDR简介Python中的用法golang用法其他学习总结 前言 服...
Zabbix“专家坐诊”第18... 问题一 Q:Zabbix5.0版本,如图,请问这里怎么修改...
深度学习技巧应用5-神经网络中... 大家好,我是微学AI,今天给大家带来深度学习技巧应用5-神经网络中的模型...
Mongodb 常用基本语法与... 常用操作 1、 Help查看命令提示 db.help(); 2、 切换/创建数据库 use t...
java中Long型数据大小比... 起因 今天在做项目的时候,想构建一个树形结构,从数据库中查询出了所有数据...
【Linux】-- 进程概念 基本概念进程(Process):是操作系统进行资源分配的最小单位。一个进程是一个程序的一次执行过程。...
2023-03-22干活小计: transformer: position-embedding: 残差:我也会了 ad...
verilog(基础知识) 摘要:主要写自己的学习内容,可能不完整 概述 对硬件描述,主要是对芯片设计进行验证人员对其进行验证...
MySQL函数 - 字符串函数... 文章目录1 字符串函数2 数值函数3 日期函数4 流程函数 函数是指一段可以直接被另一段程序调用的程...
Word2010(详细布局解释... 目录一、界面介绍二、选项卡1、文件选项卡(保存、打开、新建、打印、保存并发送、选项&#...
ProTradex是链上衍生品... 目前,链上衍生品市场的总市值已经超过100亿美元,链上衍生品市场的产品类...
spring boot 集成 ... 要将 PostGIS 集成到 Spring Boot 应用程序中,需要按照以下步骤进行操作:1. 将...
【DDIM精读】公式推导加代码... 【DDIM精读】公式推导加代码分析。1.前言:ddim总览2.均值(μ\...
系统开发-McCabe复杂度(... 系统开发(上)-软件设计(三十二)https...
每日学术速递3.22 CV - 计算机视觉 |  ML - 机器学习 |  RL - 强化学习 | NLP 自然语言处理 ...
CCF-CSP题解 第二题(J... 目录 201312-2:ISBN号码 201403-2:窗口 20140...
在服务器上搭建nacos集群-... 搭建集群需要具备JDK环境,1个Nginx+3个nacos注册中心+1...