① 尚品汇的后台管理系统【尚硅谷】【Vue】

发布时间:2023-04-22 09:00

后台管理系统项目简介

  1. 什么是后台管理系统项目?

    在前端领域当中,开发后台管理系统项目,并非是Java,PHP等后台语言项目

    在前面课程当中,我们已经开发了一个项目【尚品汇电商平台项目】,这个项目主要针对的是用户(游客),可以让用户在平台当中购买产品。

    但是你需要想明白一件事情,用户购买产品信息从何而来呀?

    比如:前台项目当中的数据来源于卖家(公司),但是需要注意的时候,卖家它不会数据库操作。对于卖家而言,需要把产品的信息上传于服务器,写入数据库。

    卖家并非程序员,不会数据库操作(增删改查)。导致卖家,找了一个程序员,开发一个产品,可以让我进行可视化操作数据库(增删改查)

    卖家(公司)组成:老板、员工。
    老板:开发这个项目,对于老板而言,什么都可以操作。【产品的上架、产品的下架、查看员工的个人业绩、其他等等】
    员工:可能就是查看个人业绩

  2. 后台管理系统:可以让用户通过一个可视化工具,可以实现对于数据库进行增删改查的操作。
    而且需要注意,根据不同的角色(老板、员工),看到的、操作内容是不同的。

    对于后台管理系统项目,一般而言,是不需要注册的。

模板介绍

简洁版: https://github.com/PanJiaChen/vue-admin-template 我们用这个
加强版: https://github.com/PanJiaChen/vue-element-admin

  1. 解压后发现文件夹里没有node_modules文件夹,我们需要安装依赖npm install

  2. 然后我们运行一下---->去看他的package.json文件下"dev": "vue-cli-service serve",说明我们用dev来运行npm run dev

运行界面如下:
① 尚品汇的后台管理系统【尚硅谷】【Vue】_第1张图片

模板的文件与文件夹认知【简洁版】

build
----index.js 是webpack配置文件【很少修改这个文件】
mock
----mock数据的文件夹【模拟一些假的数据mockjs实现的】,因为咱们实际开发的时候,利用的是真是接口

node_modules
------项目依赖的模块

public
------ico图标,静态页面,public文件夹里面经常放置一些静态资源,而且在项目打包的时候webpack不会编译这个文件夹,原封不动的打包到dist文件夹里面

src
-----程序员源代码的地方
------api文件夹:涉及请求相关的
------assets文件夹:里面放置一些静态资源(一般共享的),放在aseets文件夹里面静态资源,在webpack打包的时候,会进行编译
------components文件夹:一般放置非路由组件获取全局组件
------icons 这个文件夹的里面放置了一些svg矢量图
------layout文件夹:他里面放置一些组件与混入
------router文件夹:与路由相关的
-----store文件夹:一定是与vuex相关的
-----style文件夹:与样式先关的
------utils文件夹:request.js 是axios二次封装文件⭐️
------views文件夹:里面放置的是路由组件

App.vue:根组件
main.js:入口文件
permission.js:与导航守卫先关、
settings:项目配置项文件
.env.development:开发环境的配置文件
.env.producation:生产环境的配置文件

后台管理系统API接口在线文档

http://39.98.123.211:8170/swagger-ui.html
http://39.98.123.211:8216/swagger-ui.html

最新后台文档swagger地址:
http://39.98.123.211:8510/swagger-ui.html#/

完成登录业务

  1. 静态组件完成
  2. 书写API(换成真实的接口)
  3. axios二次封装
  4. 换成真实接口之后需要解决代理跨域问题(解决代理跨域问题)

① 尚品汇的后台管理系统【尚硅谷】【Vue】_第2张图片

完成静态组件

修改里面的样式以及内容

这里修改了背景图片,以及把英文的都换成了中文

① 尚品汇的后台管理系统【尚硅谷】【Vue】_第3张图片

⚠️这里引入背景图片可以用@符号,但是前面要加~

另外把背景图片的大小调整为 100% 100%

.login-container {
  min-height: 100%;
  width: 100%;
  background: url(~@/assets/城堡.jpg);
  background-size: 100% 100%;
  overflow: hidden;

书写API(换成真实的接口)

这里之前登录的使用模拟的数据(mock),我们换成真实的接口

Swagger UI这里找接口

① 尚品汇的后台管理系统【尚硅谷】【Vue】_第4张图片

url改为接口里的url

//   src/api/user.js

// 对外暴露登录的接口函数
export function login(data) {
  return request({
    url: 'dev-api/admin/acl/index/login',
    method: 'post',
    data
  })
}
// 对外暴露获取用户信息的接口函数
export function getInfo(token) {
  return request({
    url: 'dev-api/admin/acl/index/info',
    method: 'get',
    params: { token }
  })
}
// 对外暴露退出登录的接口函数
export function logout() {
  return request({
    url: 'dev-api/admin/acl/index/logout',
    method: 'post'
  })
}

axios二次封装修改

这里我们的请求头携带的token字段

① 尚品汇的后台管理系统【尚硅谷】【Vue】_第5张图片

响应拦截器

我们换成了真实的接口,真实服务器返回的code ,有可能是20000,也有可能是200

① 尚品汇的后台管理系统【尚硅谷】【Vue】_第6张图片

解决代理跨域问题

开发中 Server(devServer) | webpack 中文网 (webpackjs.com)代理跨域

① 尚品汇的后台管理系统【尚硅谷】【Vue】_第7张图片

我们有两个端口号,一个是8170,一个是8510,所以我们可以设置多个代理跨域

我们这里的传递的是 /dev-apidev-list,target地址也要修改

//    vue.config.js
    //  配置代理跨域
    proxy: {
      // 关于用户的接口
      "/dev-api": { 
        target: "http://39.98.123.211:8170",
        pathRewrite: { "^/dev-api": "" }
      },
      // 关于品牌的接口
      "/dev-list": {
        target: "http://39.98.123.211:8510",
        pathRewrite: { "^/dev-list": "" }
      },
    }

① 尚品汇的后台管理系统【尚硅谷】【Vue】_第8张图片

① 尚品汇的后台管理系统【尚硅谷】【Vue】_第9张图片

⚠️修改完配置项,要重新启动项目

完成退出登录业务

① 尚品汇的后台管理系统【尚硅谷】【Vue】_第10张图片

完成静态组件

修改退出登录的样式和结构,将英文修改成中文的

① 尚品汇的后台管理系统【尚硅谷】【Vue】_第11张图片

书写API(换成真实的接口)

//   src/api/user.js
// 对外暴露退出登录的接口函数
export function logout() {
  return request({
    url: '/admin/acl/index/logout',
    method: 'post'
  })
}

路由的搭建

① 尚品汇的后台管理系统【尚硅谷】【Vue】_第12张图片

我们只需要首页,删掉其余不需要的路由文件夹,然后重新配置路由

我们的一级路由都是在Layout这个骨架下搭建的,所以一级路由的componen都写Layout

① 尚品汇的后台管理系统【尚硅谷】【Vue】_第13张图片

首页(一级路由)

权限管理(一级路由)

商品管理(一级路由)

​ -------品牌管理(二级路由)

​ ---- --平台属性管理(二级路由)

​ -------Spu管理(二级路由)

​ -------Sku管理(二级路由)

// src/router/index.js

  { // 一级路由
    path:'/product',
    component:Layout, // 在Layout的骨架下!!
    name:'Product',
    meta:{title:'商品管理',icon:'el-icon-goods'}, // // title是设置侧边栏的文字
    children:[
      // 二级路由
      {
        path:'trademark',
        name:'TradeMark',
        component:()=>import('@/views/product/tradeMark'),
        meta:{title:'品牌管理'},
      },
      {
        path:'attr',
        name:'Attr',
        component:()=>import('@/views/product/Attr'),
        meta:{title:'平台属性管理'},
      },
      {
        path:'spu',
        name:'Spu',
        component:()=>import('@/views/product/Spu'),
        meta:{title:'Spu管理'},
      },
      {
        path:'sku',
        name:'Sku',
        component:()=>import('@/views/product/Sku'),
        meta:{title:'Sku管理'},
      },
    ],
  },

搭建后的:

① 尚品汇的后台管理系统【尚硅谷】【Vue】_第14张图片

另外,我们的内容 AppMain 需要与左侧有个内边距,我们去加个padding:20px

完成品牌管理业务tradeMark❓

① 尚品汇的后台管理系统【尚硅谷】【Vue】_第15张图片

完成静态组件

① 尚品汇的后台管理系统【尚硅谷】【Vue】_第16张图片

  1. 添加button按钮

    //   src/views/product/tradeMark/index.vue
    
    添加
    
  2. 添加table表格—显示的标题要居中—且序号的占比要小

    el-table属性:

    ​ data:表格组件将来需要展示的数据【数组类型】

    ​ border:是给表格带上边框

    el-table-column属性:

    ​ label:显示的标题

    ​ width:对应列的宽度

    ​ align:对齐方式

     
     
       
       
       
       
     
    
  3. 分页器----要居中

    el-pagination的属性:

    ​ current-page:当前第几页

    ​ page-size:每一页展示多少数据

    ​ layout:实现分页器顺序布局 ->后面的数据在最右面

    ​ total:数据总条数

        
        
        
        
    

品牌列表展示❓

  1. 书写相关API接口
  2. 获取数据进行存储

① 尚品汇的后台管理系统【尚硅谷】【Vue】_第17张图片

① 尚品汇的后台管理系统【尚硅谷】【Vue】_第18张图片

获取分页列表数据 /admin/product/baseTrademark/{page}/{limit} GET 带参数

书写相关API接口

//  src/api/product/tradeMark.js
// 品牌管理的相关接口

// 我们发请求需要用的是封装好的axios
import request from '@/utils/request'
// 获取品牌分页列表数据    dev-api/admin/product/baseTrademark/{page}/{limit}      GET    
export const reqTradeMarkList = (page, limit) => {
    return request({ url: `dev-list/admin/product/baseTrademark/${page}/${limit}`, method: 'GET' })
}

为方便使用,将四个模块的接口函数统一对外暴露

① 尚品汇的后台管理系统【尚硅谷】【Vue】_第19张图片

为了方便,可以任意使用接口函数,可以在入口函数中,将接口挂载在原型上

//   src/main.js
// 引入相关API请求接口---挂载在原型上
import API from '@/api/index'
// 组件实例的原型的原型指向的是Vue.prototype
// 任意组件可以使用API相关的接口
Vue.prototype.$API = API;

获取数据

我们挂载的时候需要获取数据,点击xx页的时候也要获取数据,所以我们将获取数据写在方法里面


已经得到的数据,接下来开始展示

① 尚品汇的后台管理系统【尚硅谷】【Vue】_第20张图片

展示数据❓

① 尚品汇的后台管理系统【尚硅谷】【Vue】_第21张图片

  1. elementUI当中的table组件,展示的数据是以一列一列进行展示数据的

  2. 如果需要显示索引,可以增加一列el-table-column,设置type属性为index即可显示从 1 开始的索引号。

  3. prop:对应列内容的字段名,也可以使用 property 属性【字符串类型】

el-tabel :data="list"表示这个表格展示的是哪里面的数据

第一列 我们要展示的是序列号,可以用 type="index"表示从1 开始展示索引号

第二列 我们展示的是品牌类型,在list 里面的tmName,我们可以用prop:对应列内容的字段名

    
        
      
        
      
        
      
        
        
      
        
      
        
      
        
    

第三列 展示的是品牌logo,是个图片,我们有地址,我们可以用【作用域插槽】来展示图片

slot-scope="{ row, $index }"代表的是子组件回传过来的数据,也就是list

​ 然后我们进行动态展示图片:src

❓第四列 有两个按钮,为什么要用作用域插槽??????

分页器渲染数据

    
    

完成相关点击事件

当点击页数的时候,修改data里面的page,然后再次发请求

@current-change="handleCurrentChange"当你点击其他页数时触发

    
    

    // 点击页码进行切换
    handleCurrentChange(pager) {
      // pager你点击的页数
      // 修改参数,然后再发请求
      this.page = pager ;
      this.getPageList();
    },

@size-change="handleSizeChange"当pageSize【展示数据条数】改变时触发

    // 当分页器某一页需要展示数据的条数发生变化时触发
    handleSizeChange(limit){
      // 修改数据,再次发请求
      this.limit = limit ;
      this.getPageList();
    },

完成添加品牌与修改品牌业务

当你点击品牌管理页面 左上角【添加】的时候,会弹出一个类似遮罩层的页面

当你点击【修改】,也会弹出一个类似遮罩层的页面-----------显示对话框

完成静态页面

① 尚品汇的后台管理系统【尚硅谷】【Vue】_第22张图片

  1. 我们这里用elementui的 dialog 对话框

    ① 尚品汇的后台管理系统【尚硅谷】【Vue】_第23张图片

        
        
        
          
          
            
            
              
            
            
            
             
            
          
          
          
        
    
  2. 上传图片用elementui的Upload上传

    ① 尚品汇的后台管理系统【尚硅谷】【Vue】_第24张图片

 
            
            
            
只能上传jpg/png文件,且不超过500kb
  1. 粘贴elementui里面相关的数据和方法与样式

    data(){
    	return{
          dialogFormVisible: false, // 对话框显示与隐藏的控制属性
          imageUrl: "", // 上传图片使用的属性
    	}
    },
    methods:{
    	// 上传图片相关的回调
        handleAvatarSuccess(res, file) {
          this.imageUrl = URL.createObjectURL(file.raw);
        },
        beforeAvatarUpload(file) {
          const isJPG = file.type === "image/jpeg";
          const isLt2M = file.size / 1024 / 1024 < 2;
    
          if (!isJPG) {
            this.$message.error("上传头像图片只能是 JPG 格式!");
          }
          if (!isLt2M) {
            this.$message.error("上传头像图片大小不能超过 2MB!");
          }
          return isJPG && isLt2M;
        },
    
    }
    

完成 添加品牌 的功能

当点击【添加品牌】按钮

  1. 收集相应【品牌名称和LOGO】
  2. 将收集到的发请求提交给后台
  3. 然后再发请求获取品牌列表数据

书写相关的API接口

① 尚品汇的后台管理系统【尚硅谷】【Vue】_第25张图片

新增品牌 /admin/product/baseTrademark/save POST 带参数

// 处理 添加品牌的 操作 /admin/product/baseTrademark/save    POST  带参数 品牌名称和品牌logo
// 切记:对于新增的品牌,给服务器传递参数,不需要传递ID,ID是由服务器生成
export const reqAddTradeMark = (tradeMark) =>{
    return request({url:'dev-list/admin/product/baseTrademark/save',method:'POST',data:tradeMark})
}

收集数据并发请求

  1. 收集到表单元素里的数据

    要收集到elementui里面的表单数据,就要在该表单上面添加 model属性,将来表单验证也需要这个属性

       
            
            
             	 
            
    

    收集LOGO图片

    这里收集数据不能使用v-model,因为不是表单元素,我们这里用了另一个el-upload

    action:设置图片上传的地址

    on-success 可以检测到图片上传成功,图片上传成功会执行一次

    before-upload :可以在上传图片之前,会执行一次

          
                
                
                
    只能上传jpg/png文件,且不超过500kb
        // 点击【添加品牌】的按钮
        showDialog() {
          // 显示对话框
          this.dialogFormVisible = true;
          // 先清除之前的数据
          this.tmForm = {tmName:'',logoUrl:''}
        },
        // 图片上传成功
        handleAvatarSuccess(res, file) {
          // res是你上传成功后服务器返回你的数据,里面是图片在服务器的真实地址
          // file :上传成功后服务器返回的前端数据
          // 收集上传图片数据
          this.tmForm.logoUrl = res.data;
        },
    
  2. 发请求

    当点击【添加品牌】里的【确定】按钮时

    1. 将弹出的对话框隐藏
    2. 发请求(添加品牌
    3. 如果成功,弹出一个message框:可能是添加成功,也有可能是修改成功,然后重新获取新的品牌列表
    确 定
    
        // 点击【添加按钮】里面的【确定按钮】(可以添加品牌|修改品牌)
        async addOrUpdateTradeMark() {
          // 将弹出的对话框隐藏
          this.dialogFormVisible = false;
          // 发请求(添加品牌 | 修改品牌)
          let result = await this.$API.trademark.reqAddTradeMark(this.tmForm);
          if (result.code == 200) {
            // 弹出一个message框:可能是添加成功,也有可能是修改成功
            this.$message(this.tmForm.id ? "修改品牌成功" : "添加品牌成功");
            // 获取新的品牌列表
            this.getPageList();
          }
        },
    

完成 修改品牌 的功能❓

书写相关的API

修改 品牌的操作 /admin/product/baseTrademark/update PUT 带参数 ID和品牌名称和品牌logo

// 修改 品牌的操作  /admin/product/baseTrademark/update  PUT   带参数   ID和品牌名称和品牌logo
// 切记:对于修改某一个品牌的参数,前端携带的参数需要带上id,你需要告诉服务器修改的是哪一个品牌
export const reqUpdateTradeMark = (tradeMark) =>{
    return request({url:'dev-list/admin/product/baseTrademark/update',method:'PUT',data:tradeMark})
}

收集数据并发请求❓

  1. 当你点击【修改】按钮时,会弹出一个对话框,里面默认显示你点这个品牌的信息

    所以我们点击【修改】按钮时,会把row这一品牌的信息传过去,这里用到了作用域插槽

    ① 尚品汇的后台管理系统【尚硅谷】【Vue】_第26张图片

    
        
        
    

​ tmForm 是我们收集的弹出框的表单数据,并非我们要展示的列表数据,我们要用tmFrom当我们的参数去发请求

​ list 是我们要展示的列表数据

​ ❓为什么要用浅拷贝 tmForm????

(这里不用浅拷贝的话,当你在弹出框更改品牌名称的时候,品牌列表展示页面会自动的更新你修改的数据—无论你点了【取消】还是【确定修改】)

    // 点击【修改某一个品牌】按钮
    updateTrademark(row) {
      // row 是当前用户选中的这个品牌的信息,服务器返回给我们的
      // 显示对话框
      this.dialogFormVisible = true;
      // 将已有的品牌信息赋值给tmForm进行展示
      // 将服务器返回的品牌信息,直接赋值给了tmForm进行展示
      // tmForm存储的就是服务器返回的品牌信息
      this.tmForm = {...row} ;
    },
  1. 当你修改完成后,点击【确定】按钮,发请求去修改品牌,请求成功后再发请求获取品牌列表进行展示
    1. 将弹出的对话框隐藏
    2. 判断是【添加】还是【修改】 看是否tmForm.id
    3. 是修改的就发修改的请求,是添加的就发添加的请求
    4. 请求成功后,弹出一个message框:可能是添加成功,也有可能是修改成功
    5. 再次获取新的品牌列表
    // 点击【添加按钮】里面的【确定按钮】(可以添加品牌|修改品牌)
    async addOrUpdateTradeMark() {
      // 1. 将弹出的对话框隐藏
      this.dialogFormVisible = false;
      // 2. 判断是【添加】还是【修改】 看是否tmForm.id
      let result = null;
      if (this.tmForm.id) {
        // 是修改
        result = await this.$API.trademark.reqUpdateTradeMark(this.tmForm);
      } else {
        // 发请求(添加品牌 | 修改品牌)
        result = await this.$API.trademark.reqAddTradeMark(this.tmForm);
      }

      if (result.code == 200) {
        // 3. 弹出一个message框:可能是添加成功,也有可能是修改成功
        this.$message({
          type: 'success',
          message: this.tmForm.id ? "修改品牌成功" : "添加品牌成功",
        });
        // 4. 获取新的品牌列表
        this.getPageList();
      }
    },

表单验证功能

① 尚品汇的后台管理系统【尚硅谷】【Vue】_第27张图片


Form 组件提供了表单验证的功能,只需要通过rules 属性传入约定的验证规则,并将 Form-Item 的 prop属性设置为需校验的字段名即可。校验规则参见 async-validator

① 尚品汇的后台管理系统【尚硅谷】【Vue】_第28张图片

① 尚品汇的后台管理系统【尚硅谷】【Vue】_第29张图片

① 尚品汇的后台管理系统【尚硅谷】【Vue】_第30张图片

① 尚品汇的后台管理系统【尚硅谷】【Vue】_第31张图片

① 尚品汇的后台管理系统【尚硅谷】【Vue】_第32张图片

① 尚品汇的后台管理系统【尚硅谷】【Vue】_第33张图片

最重要的是自定义校验规则!!!form表单校验–Vue UI 框架 (eleme.cn)

完整代码




完成删除品牌业务

① 尚品汇的后台管理系统【尚硅谷】【Vue】_第34张图片

当点击【删除】按钮时,会弹出一个弹框【确定要删除吗】,然后有【确定】和【取消】两个按钮

  1. 点击【删除】按钮,给【删除】绑定点击事件deleteTradeMark,要传参数(你点击该品牌的信息—row)

  2. 弹出一个弹框,里面是【确定删除】还是【取消】

  3. 确定后,发请求删除该品牌,然后再发请求获取品牌列表数据;

书写相关API

删除该品牌 /admin/product/baseTrademark/remove/{id} DELETE 带参数

// 删除该品牌  /admin/product/baseTrademark/remove/{id}   DELETE  带参数
export const reqDeleteTradeMark = (id) => {
    return request({ url: `dev-list/admin/product/baseTrademark/remove/${id}`, method: 'DELETE' })
}

发请求完成删除功能

  1. 弹窗
    ① 尚品汇的后台管理系统【尚硅谷】【Vue】_第35张图片

        // 删除品牌的操作
        deleteTradeMark(row) {
          // 弹窗
          this.$confirm(`你确定删除${row.tmName}吗?`, "提示", {
            confirmButtonText: "确定",
            cancelButtonText: "取消",
            type: "warning",
          })
            .then(() => {
              // 当用户点击【确定】按钮的时候触发
              this.$message({
                type: "success",
                message: "删除成功!",
              });
            })
            .catch(() => {
              // 当用户点击【取消】按钮的时候会触发
              this.$message({
                type: "info",
                message: "已取消删除",
              });
            });
        },
    
  2. 发请求----完成删除功能

        // 删除品牌的操作
        deleteTradeMark(row) {
          // 弹窗
          this.$confirm(`你确定删除${row.tmName}吗?`, "提示", {
            confirmButtonText: "确定",
            cancelButtonText: "取消",
            type: "warning",
          })
            .then(async () => {
              // 1. 当用户点击【确定】按钮的时候触发
              // 2. 向服务器发请求
              let result = await this.$API.trademark.reqDeleteTradeMark(row.id);
              if (result.code == 200) {
                // 3. 弹出message框
                this.$message({
                  type: "success",
                  message: "删除成功!",
                });
                // 4. 再次获取品牌列表数据
                this.getPageList();
              }
            })
            .catch(() => {
              // 当用户点击【取消】按钮的时候会触发
              this.$message({
                type: "info",
                message: "已取消删除",
              });
            });
        },
    

完成平台属性管理业务attr

应该会跳转到下一文章

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号