Vue项目(电商管理后台)

电商项目

基于Vue初始化项目

项目概述

Vue项目(电商管理后台)_第1张图片

  • 电商后台管理系统的功能

Vue项目(电商管理后台)_第2张图片

  • 电商后台管理系统的开发模式(前后端分离)

Vue项目(电商管理后台)_第3张图片

  • 电商后台管理系统的技术选型

    • 前端项目技术栈
      • Vue
      • Vue-router
      • Element-UI
      • Axios
      • Echarts
    • 后端项目技术栈
      • Node.js
      • Express
      • Jwt
      • Mysql
      • Sequelize

项目初始化

  • 前端项目初始化
    • 安装Vue脚手架
    • 通过脚手架创建项目
    • 配置路由
    • 配置Element-UI:在插件中安装,搜索vue-cli-plugin-element
    • 配置Axios:在依赖中安装,搜索axios(运行依赖)
    • 初始化git仓库
    • 将本地项目托管到github或者码云中
  • 后台项目的环境安装配置
    • 安装MySQL数据库
    • 安装Node.js环境
    • 配置项目相关信息
    • 启动项目
    • 使用Postman测试后台项目接口是否正常

登录/退出功能

  • 登录业务流程

    • 在登录页面输入用户名和密码
    • 调用后台接口进行验证
    • 通过验证之后,根据后台的响应状态跳转到项目主页
  • 登录业务的相关技术点

    • http是无状态的
    • 通过cookie在客户端记录状态
    • 通过session在服务器记录状态
    • 通过token方式维持状态
  • 登录 - token原理分析

    Vue项目(电商管理后台)_第4张图片

  • 登录功能实现

Vue项目(电商管理后台)_第5张图片

  • 登录状态保持

    • 如果服务器和客户端同源,建议可以使用cookie或者session来保持登录状态
    • 如果客户端和服务器跨域了,建议使用token进行维持登录状态。
  • 登录逻辑

    • 在登录页面输入账号和密码进行登录,将数据发送给服务器
    • 服务器返回登录的结果,登录成功则返回数据中带有token
    • 客户端得到token并进行保存,后续的请求都需要将此token发送给服务器,服务器会验证token以保证用户身份。
  • 添加新分支login,在login分支中开发当前项目vue_shop:

    • 打开vue_shop终端,使用git status确定当前项目状态。

    • 确定当前工作目录是干净的之后,创建一个分支进行开发,开发完毕之后将其合并到master
      git checkout -b login

    • 然后查看新创建的分支:git branch

  • 打开项目的src目录,点击main.js文件(入口文件)

    import Vue from 'vue'
    import App from './App.vue'
    import router from './router'
    import './plugins/element.js'
    
    Vue.config.productionTip = false
    
    
    new Vue({
      router,
      render: h => h(App)
    }).$mount('#app')
    
  • 再打开App.vue(根组件),将根组件的内容进行操作梳理(template中留下根节点,script中留下默认导出,去掉组件,style中去掉所有样式)

    
    
    
    
    
    
  • 再打开router.js(路由),将routes数组中的路由规则清除,然后将views删除,将components中的helloworld.vue删除

    import Vue from 'vue'
    import Router from 'vue-router'
    
    Vue.use(Router)
    
    export default new Router({
      routes: [
        
      ]
    })
    
  • 在components文件夹中新建Login.vue组件,添加template,script,style标签,style标签中的scoped可以防止组件之间的样式冲突,没有scoped则样式是全局的

    
    
    
    
    
    
  • 在router.js中导入组件并设置规则

  • 在App.vue中添加路由占位符

    const router = new Router({
      routes: [
        { path: '/', redirect: '/login' },
        { path: '/login', component: Login }
      ]
    })
    
  • 当我们给Login.vue中的内容添加样式的时候,会报错“缺少less-loader”,需要配置less加载器(开发依赖),安装less(开发依赖)

  • 然后需要添加公共样式,在assets文件夹下面添加css文件夹,创建global.css文件,添加全局样式

    /* 全局样式表 */
    html,body,#app{
        width: 100%;
        height: 100%;
        margin: 0;
        padding: 0; 
    }
    
  • 在main.js中导入global.css,使得全局样式生效 import “./assets/css/global.css”

  • Login.vue文件中的代码如下

    
    
    
    
    
    
  • 添加element-ui的表单组件

    import Vue from 'vue'
    import { Button } from 'element-ui'
    import { Form, FormItem } from 'element-ui'
    import { Input } from 'element-ui'
    
    Vue.use(Button)
    Vue.use(Form)
    Vue.use(FormItem)
    Vue.use(Input)
    
  • 添加第三方字体

    • 复制素材中的fonts文件夹到assets中,在入口文件main.js中导入import ‘./assets/fonts/iconfont.css’
      然后直接在 接着添加登录盒子
  • 添加表单验证的步骤

    rules:export default{ data(){return{......, rules: {
              name: [
                { required: true, message: '请输入活动名称', trigger: 'blur' },
                { min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' }
              ],
              region: [
                { required: true, message: '请选择活动区域', trigger: 'change' }
              ]
    }
    
  • 导入axios以发送ajax请求

    设置请求的根路径:axios.defaults.baseURL = 'http://127.0.0.1:8888/api/private/v1/';
    挂载axios:Vue.prototype.$http = axios;
    
  • .配置弹窗提示

    • 在plugins文件夹中打开element.js文件,进行elementui的按需导入
    • import {Message} from ‘element-ui’
    • 进行全局挂载:Vue.prototype.$message = Message;
    • 在login.vue组件中编写弹窗代码:this.$message.error(‘登录失败’)
  • 登录成功之后的操作

    • 将登录成功之后的 token,保存到客户端的 sessionStorage 中
    • 项目中出了登录之外的其他API接口,必须在登录之后才能访问
    • token 只应在当前网站打开期间生效,所以将 token 保存在 sessionStorage 中
    login() {
          //点击登录的时候先调用validate方法验证表单内容是否有误
          this.$refs.LoginFormRef.validate(async valid => {
            console.log(this.loginFormRules)
            //如果valid参数为true则验证通过
            if (!valid) {
              return
            }
    
            //发送请求进行登录
            const { data: res } = await this.$http.post('login', this.loginForm)
            //   console.log(res);
            if (res.meta.status !== 200) {
              return this.$message.error('登录失败:' + res.meta.msg) //console.log("登录失败:"+res.meta.msg)
            }
    
            this.$message.success('登录成功')
            console.log(res)
            //保存token
            window.sessionStorage.setItem('token', res.data.token)
            // 导航至/home
            this.$router.push('/home')
          })
        }
    
  • 添加一个组件Home.vue,并为之添加规则

    
    
    
    
    
    
  • 添加路由规则

    const router = new Router({
      routes: [
        { path: '/', redirect: '/login' },
        { path: '/login', component: Login },
        { path: '/home', component: Home }
      ]
    })
    
  • 添加路由守卫

    • 如果用户没有登录,不能访问/home,如果用户通过url地址直接访问,则强制跳转到登录页面
    import Vue from 'vue'
    import Router from 'vue-router'
    import Login from './components/Login.vue'
    import Home from './components/Home.vue'
    
    Vue.use(Router)
    
    const router = new Router({
      routes: [
        { path:'/', redirect:'/login'},
        { path:'/login' , component:Login },
        { path:'/home' , component:Home}
      ]
    })
    
    //挂载路由导航守卫,to表示将要访问的路径,from表示从哪里来,next是下一个要做的操作
    router.beforeEach((to,from,next)=>{ 
      if(to.path === '/login')
        return next();
      
      //获取token
      const tokenStr = window.sessionStorage.getItem('token');
    
      if(!tokenStr)
        return next('/login');
    
      next();
    
    })
    
    export default router 
    
  • 实现退出功能

    export default {
        methods:{
            logout(){
                window.sessionStorage.clear();
                this.$router.push('/login');
            }
        }
    }
    

主页布局

  • 整体布局

Vue项目(电商管理后台)_第6张图片

  • 侧边菜单栏

    Vue项目(电商管理后台)_第7张图片

    
    
  • 通过接口获取菜单数据

    • 通过axios请求拦截器添加token,保证拥有获取数据的权限

    • 后台除了登录接口之外,都需要token权限验证,我们可以通过添加axios请求拦截器来添加token,以保证拥有获取数据的权限

      Vue项目(电商管理后台)_第8张图片

    • 请求侧边栏数据

      
      
    • 请求侧边栏数据

      
        
        
          
          
          
          
            
            
          
        
      
      
    • 设置激活子菜单样式

      • 通过更改el-menu的active-text-color属性可以设置侧边栏菜单中点击的激活项的文字颜色

      • 通过更改菜单项模板(template)中的i标签的类名,可以将左侧菜单栏的图标进行设置,我们需要在项目中使用第三方字体图标

        iconsObj: {
                '125':'iconfont icon-user',
                '103':'iconfont icon-tijikongjian',
                '101':'iconfont icon-shangpin',
                '102':'iconfont icon-danju',
                '145':'iconfont icon-baobiao'
              }
        
    • 为了保持左侧菜单每次只能打开一个,显示其中的子菜单,我们可以在el-menu中添加一个属性unique-opened或者也可以数据绑定进行设置(此时true认为是一个bool值,而不是字符串) :unique-opened=“true”

      Vue项目(电商管理后台)_第9张图片

    • 制作侧边菜单栏的伸缩功能

       
              
                
                
      |||
    • 在后台首页添加子级路由

      • 新增子级路由组件Welcome.vue
      • 在router.js中导入子级路由组件,并设置路由规则以及子级路由的默认重定向
      • 打开Home.vue,在main的主体结构中添加一个路由占位符
      • 制作好了Welcome子级路由之后,我们需要将所有的侧边栏二级菜单都改造成子级路由链接
      • 只需要将el-menu的router属性设置为true就可以了,此时当我们点击二级菜单的时候,就会根据菜单的index
      • 属性进行路由跳转,如: /110,
      • 使用index id来作为跳转的路径不合适,我们可以重新绑定index的值为 :index="’/’+subItem.path"
    • 完成用户列表主体区域

      • 新建用户列表组件 user/Users.vue
      • 在router.js中导入子级路由组件Users.vue,并设置路由规则
      • 当点击二级菜单的时候,被点击的二级子菜单并没有高亮,需要正在被使用的功能高亮显示
      • 可以通过设置el-menu的default-active属性来设置当前激活菜单的index
      • 但是default-active属性也不能写死,固定为某个菜单值
      • 所以可以先给所有的二级菜单添加点击事件,并将path值作为方法的参数
      • @click=“saveNavState(’/’+subItem.path)”
      • 在saveNavState方法中将path保存到sessionStorage中
      • saveNavState( path ){
      • //点击二级菜单的时候保存被点击的二级菜单信息
        window.sessionStorage.setItem(“activePath”,path);
        this.activePath = path;
        }
      • 然后在数据中添加一个activePath绑定数据,并将el-menu的default-active属性设置为activePath
      • 最后在created中将sessionStorage中的数据赋值给activePath
        this.activePath = window.sessionStorage.getItem(“activePath”)
    • 绘制用户列表基本结构

      • 使用element-ui面包屑组件完成顶部导航路径(复制面包屑代码,在element.js中导入组件Breadcrumb,BreadcrumbItem)

      • 使用element-ui卡片组件完成主体表格(复制卡片组件代码,在element.js中导入组件Card),再

      • 使用element-ui输入框完成搜索框及搜索按钮,

      • 此时需要使用栅格布局来划分结构(复制卡片组件代码,在element.js中导入组件Row,Col),然后再使用el-button制作添加用户按钮

        用户列表组件

        首页 用户管理 用户列表 添加用户
    • 请求用户列表数据

      
      
    • 将用户列表数据展示

      • 使用表格来展示用户列表数据,使用element-ui表格组件完成列表展示数据(复制表格代码,在element.js中导入组件Table,TableColumn)

      • 在渲染展示状态时,会使用作用域插槽获取每一行的数据

      • 再使用switch开关组件展示状态信息(复制开关组件代码,在element.js中导入组件Switch)

      • 而渲染操作列时,也是使用作用域插槽来进行渲染的,在操作列中包含了修改,删除,分配角色按钮,当我们把鼠标放到分配角色按钮上时希望能有一些文字提示,此时我们需要使用文字提示组件(复制文字提示组件代码,在element.js中导入组件Tooltip),将分配角色按钮包含

        
        
            
            
            
            
            
            
                
            
            
                
            
        
        
    • 实现用户列表分页

      • 使用表格来展示用户列表数据,可以使用分页组件完成列表分页展示数据(复制分页组件代码,在element.js中导入组件Pagination)

      • 更改组件中的绑定数据

        
                    
                    
        
      • 添加两个事件的事件处理函数@size-change,@current-change

        handleSizeChange(newSize) {
          //pagesize改变时触发,当pagesize发生改变的时候,我们应该
          //以最新的pagesize来请求数据并展示数据
          //   console.log(newSize)
          this.queryInfo.pagesize = newSize;
          //重新按照pagesize发送请求,请求最新的数据
          this.getUserList();  
        },
        handleCurrentChange( current ) {
          //页码发生改变时触发当current发生改变的时候,我们应该
          //以最新的current页码来请求数据并展示数据
          //   console.log(current)
          this.queryInfo.pagenum = current;
          //重新按照pagenum发送请求,请求最新的数据
          this.getUserList();  
        }
        
      • 实现更新用户状态

        • 当用户点击列表中的switch组件时,用户的状态应该跟随发生改变。

          • 首先监听用户点击switch组件的事件,并将作用域插槽的数据当做事件参数进行传递

          • 
            
          • 在事件中发送请求完成状态的更改

            async userStateChanged(row) {
              //发送请求进行状态修改
              const { data: res } = await this.$http.put(
                `users/${row.id}/state/${row.mg_state}`
              )
              //如果返回状态为异常状态则报错并返回
              if (res.meta.status !== 200) {
                row.mg_state = !row.mg_state
                return this.$message.error('修改状态失败')
              }
              this.$message.success('更新状态成功')
            },
            
      • 实现搜索功能

        • 添加数据绑定,添加搜索按钮的点击事件(当用户点击搜索按钮的时候,调用getUserList方法根据文本框内容重新请求用户列表数据)

        • 当在输入框中输入内容并点击搜索之后,会按照搜索关键字搜索,我们希望能够提供一个X删除搜索关键字并重新获取所有的用户列表数据,只需要给文本框添加clearable属性并添加clear事件,在clear事件中重新请求数据即可

          
              
                  
              
          
          
      • 实现添加用户

        • 当我们点击添加用户按钮的时候,弹出一个对话框来实现添加用户的功能,首先我们需要复制对话框组件的代码并在element.js文件中引入Dialog组件

        • 接下来我们要为“添加用户”按钮添加点击事件,在事件中将addDialogVisible设置为true,即显示对话框

        • 更改Dialog组件中的内容

          
          
              
              
                  
                      
                  
                  
                      
                  
                  
                      
                  
                  
                      
                  
              
              
              
                  取 消
                  确 定
              
          
          
        • 添加数据绑定和校验规则

          data() {
            //验证邮箱的规则
            var checkEmail = (rule, value, cb) => {
              const regEmail = /^\w+@\w+(\.\w+)+$/
              if (regEmail.test(value)) {
                return cb()
              }
              //返回一个错误提示
              cb(new Error('请输入合法的邮箱'))
            }
            //验证手机号码的规则
            var checkMobile = (rule, value, cb) => {
              const regMobile = /^1[34578]\d{9}$/
              if (regMobile.test(value)) {
                return cb()
              }
              //返回一个错误提示
              cb(new Error('请输入合法的手机号码'))
            }
            return {
              //获取查询用户信息的参数
              queryInfo: {
                // 查询的条件
                query: '',
                // 当前的页数,即页码
                pagenum: 1,
                // 每页显示的数据条数
                pagesize: 2
              },
              //保存请求回来的用户列表数据
              userList: [],
              total: 0,
              //是否显示添加用户弹出窗
              addDialogVisible: false,
              // 添加用户的表单数据
              addForm: {
                username: '',
                password: '',
                email: '',
                mobile: ''
              },
              // 添加表单的验证规则对象
              addFormRules: {
                username: [
                  { required: true, message: '请输入用户名称', trigger: 'blur' },
                  {
                    min: 3,
                    max: 10,
                    message: '用户名在3~10个字符之间',
                    trigger: 'blur'
                  }
                ],
                password: [
                  { required: true, message: '请输入密码', trigger: 'blur' },
                  {
                    min: 6,
                    max: 15,
                    message: '用户名在6~15个字符之间',
                    trigger: 'blur'
                  }
                ],
                email: [
                    { required: true, message: '请输入邮箱', trigger: 'blur' },
                    { validator:checkEmail, message: '邮箱格式不正确,请重新输入', trigger: 'blur'}
                ],
                mobile: [
                    { required: true, message: '请输入手机号码', trigger: 'blur' },
                    { validator:checkMobile, message: '手机号码不正确,请重新输入', trigger: 'blur'}
                ]
              }
            }
          }
          
        • 当关闭对话框时,重置表单

          • 给el-dialog添加@close事件,在事件中添加重置表单的代码

            addDialogClosed(){
                  //对话框关闭之后,重置表达
                  this.$refs.addFormRef.resetFields();
              }
            
        • 点击对话框中的确定按钮,发送请求完成添加用户的操作

          • 首先给确定按钮添加点击事件,在点击事件中完成业务逻辑代码

             addUser(){
                  //点击确定按钮,添加新用户
                  //调用validate进行表单验证
                  this.$refs.addFormRef.validate( async valid => {
                      if(!valid) return this.$message.error("请填写完整用户信息");
                      //发送请求完成添加用户的操作
                      const {data:res} = await this.$http.post("users",this.addForm)
                      //判断如果添加失败,就做提示
                      if (res.meta.status !== 200)
                          return this.$message.error('添加用户失败')
                      //添加成功的提示
                      this.$message.success("添加用户成功")
                      //关闭对话框
                      this.addDialogVisible = false
                      //重新请求最新的数据
                      this.getUserList()
                  })
              }
            
  • 在使用图形化 vue-cli 创建项目时,默认启用了 Eslint 规则,启动项目时报错

    • 利用格式化代码,顺着 Eslint 规范来

    • 在项目根路径创建 .prettierrc 文件,文件内容 如下

      {
      	"semi":false,//去除当前文件的分号
      	"singleQuote":true//把当前文件中所有的双引号替换为单引号
      }
      
    • 直接关闭 Eslint,在 package.json 中添加如下代码

      "vue":{
          "lintOnSave": false
        }
      

Vue项目(电商管理后台)_第10张图片

  • 修改用户信息

    • 为用户列表中的修改按钮绑定点击事件

    • 在页面中添加修改用户对话框,并修改对话框的属性

    • 根据id查询需要修改的用户数据

      //展示编辑用户的对话框
      async showEditDialog(id) {
          //发送请求根据id获取用户信息
          const { data: res } = await this.$http.get('users/' + id)
          //判断如果添加失败,就做提示
          if (res.meta.status !== 200) return this.$message.error('获取用户信息失败')
          //将获取到的数据保存到数据editForm中
          this.editForm = res.data
          //显示弹出窗
          this.editDialogVisible = true
      }
      
    • 在弹出窗中添加修改用户信息的表单并做响应的数据绑定以及数据验证

      
      
          
              
          
          
              
          
          
              
          
      
      
    • 数据绑定以及验证

      //控制修改用户对话框的显示与否
      editDialogVisible: false,
      //修改用户的表单数据
      editForm: {
          username: '',
          email: '',
          mobile: ''
      },
      //修改表单的验证规则对象
      editFormRules: {
          email: [
              { required: true, message: '请输入邮箱', trigger: 'blur' },
              {
              validator: checkEmail,
              message: '邮箱格式不正确,请重新输入',
              trigger: 'blur'
              }
          ],
          mobile: [
              { required: true, message: '请输入手机号码', trigger: 'blur' },
              {
              validator: checkMobile,
              message: '手机号码不正确,请重新输入',
              trigger: 'blur'
              }
          ]
      }
      
    • 监听对话框关闭事件,在对话框关闭后,重置表单

      
      
      editDialogClosed(){
          //对话框关闭之后,重置表达
          this.$refs.editFormRef.resetFields()
      }
      
    • 在用户点击确定按钮的时候,验证数据成功之后发送请求完成修改

      editUser() {
          //用户点击修改表单中的确定按钮之后,验证表单数据
          this.$refs.editFormRef.validate(async valid => {
          if (!valid) return this.$message.error('请填写完整用户信息')
          //发送请求完成修改用户的操作
          const { data: res } = await this.$http.put(
              'users/' + this.editForm.id,
              this.editForm
          )
          //判断如果修改失败,就做提示
          if (res.meta.status !== 200) return this.$message.error('修改用户失败')
          //修改成功的提示
          this.$message.success('修改用户成功')
          //关闭对话框
          this.editDialogVisible = false
          //重新请求最新的数据
          this.getUserList()
          })
      }
      
    • 删除用户

      • 在点击删除按钮的时候,应该跳出提示信息框,让用户确认要进行删除操作。
      • 如果想要使用确认取消提示框,我们需要先将提示信息框挂载到vue中。
    • 导入MessageBox组件,并将MessageBox组件挂载到实例。

    • 给用户列表中的删除按钮添加事件,并在事件处理函数中弹出确定取消窗,最后再根据id发送删除用户的请求

      async removeUserById(id){
          //弹出确定取消框,是否删除用户
          const confirmResult = await this.$confirm('请问是否要永久删除该用户','删除提示',{
          confirmButtonText:'确认删除',
          cancelButtonText:'取消',
          type:'warning'
          }).catch(err=>err)
          //如果用户点击确认,则confirmResult 为'confirm'
          //如果用户点击取消, 则confirmResult获取的就是catch的错误消息'cancel'
          if(confirmResult != "confirm"){
              return this.$message.info("已经取消删除")
          }
          //发送请求根据id完成删除操作
          const {data:res} = await this.$http.delete('users/'+id)
          //判断如果删除失败,就做提示
          if (res.meta.status !== 200) return this.$message.error('删除用户失败')
          //修改成功的提示
          this.$message.success('删除用户成功')
          //重新请求最新的数据
          this.getUserList()
      }
      

权限管理模块

  • 权限列表
    • 添加权限列表路由
    • 创建权限管理组件(Rights.vue),并在router.js添加对应的路由规则
import Rights from './components/power/Rights.vue'
......
      path: '/home', component: Home, redirect: '/welcome', children: [
        { path: "/welcome", component: Welcome },
        { path: "/users", component: Users },
        { path: "/rights", component: Rights }
      ]
......
  • 添加面包屑导航
    • 在Rights.vue中添加面包屑组件展示导航路径
  • 显示数据
    • 在data中添加一个rightsList数据,在methods中提供一个getRightsList方法发送请求获取权限列表数据,在created中调用这个方法获取数据

    
    
    
    
        
    


  • 角色列表
    • 添加角色列表路由
    • 添加角色列表子组件(power/Roles.vue),并添加对应的规则
path: '/home', component: Home, redirect: '/welcome', children: [
        { path: "/welcome", component: Welcome },
        { path: "/users", component: Users },
        { path: "/rights", component: Rights },
        { path: "/roles", component: Roles  }
      ]
  • 添加面包屑导航
    • 在Roles.vue中添加面包屑组件展示导航路径
  • 显示数据

    在data中添加一个roleList数据,在methods中提供一个getRoleList方法发送请求获取权限列表数据,在created中调用这个方法获取数据



    
    
    
    
    
    
        
    



  • 生成权限列表
    • 使用三重嵌套for循环生成权限下拉列表


    

  • 美化样式

    • 通过设置global.css中的#app样式min-width:1366px 解决三级权限换行的问题,通过给一级权限el-row添加display:flex,align-items:center的方式解决一级权限垂直居中的问题,二级权限也类似添加,因为需要给多个内容添加,可以将这个样式设置为一个.vcenter{display:flex;align-items:center}
  • 添加权限删除功能

    • 给每一个权限的el-tag添加closable属性,是的权限右侧出现“X”图标,再给el-tag添加绑定close事件处理函数removeRightById(scope.row,item1.id)
async removeRightById(role,rightId){
    //弹窗提示用户是否要删除
    const confirmResult = await this.$confirm('请问是否要删除该权限','删除提示',{
        confirmButtonText:'确认删除',
        cancelButtonText:'取消',
        type:'warning'
    }).catch(err=>err)
    //如果用户点击确认,则confirmResult 为'confirm'
    //如果用户点击取消, 则confirmResult获取的就是catch的错误消息'cancel'
    if(confirmResult != "confirm"){
        return this.$message.info("已经取消删除")
    }

    //用户点击了确定表示真的要删除
    //当发送delete请求之后,返回的数据就是最新的角色权限信息
    const {data:res} = await this.$http.delete(`roles/${role.id}/rights/${rightId}`)
    if (res.meta.status !== 200)
        return this.$message.error('删除角色权限失败')

    //无需再重新加载所有权限
    //只需要对现有的角色权限进行更新即可
    role.children = res.data
    // this.getRoleList();

}
  • 完成权限分配功能
    • 先给分配权限按钮添加事件
async showSetRightDialog() {
    //当点击分配权限按钮时,展示对应的对话框
    this.setRightDialogVisible = true;
    //获取所有权限的数据
    const {data:res} = await this.$http.get('rights/tree')
    //如果返回状态为异常状态则报错并返回
    if (res.meta.status !== 200)
        return this.$message.error('获取权限树失败')
    //如果返回状态正常,将请求的数据保存在data中
    this.rightsList = res.data
}
  • 完成树形结构弹窗
    • 在element.js中引入Tree,注册Tree


    
    
    
        取 消
        确 定
    



  • 分配角色
    • 打开Users.vue,完成分配角色的功能
    • 添加分配角色对话框


    

当前的用户:{{userInfo.username}}

当前的角色:{{userInfo.role_name}}

分配新角色:

取 消 确 定
  • 给分配角色按钮添加点击事件,点击之后弹出一个对话框进行角色分配


    


data(){
    ......
    //控制显示分配角色对话框
    setRoleDialogVisible:false,
    //保存正在操作的那个用户信息
    userInfo:{},
    //保存所有的角色信息
    rolesList:[],
    //保存用户选中的角色id
    selectedRoleId:''
},
methods:{
    ......
    async setRole( userInfo ){
      //保存起来以供后续使用
      this.userInfo = userInfo;
      //获取所有的角色信息,以备下拉列表使用
      //发送请求根据id完成删除操作
      const { data: res } = await this.$http.get('roles')
      //判断如果删除失败,就做提示
      if (res.meta.status !== 200) return this.$message.error('获取角色列表失败')
      
      this.rolesList = res.data;
      //展示分配角色对话框
      this.setRoleDialogVisible = true;

      
    }
}
  • 在element.js中引入Select,Option,注册Select,Option






  • 当用户点击对话框中的确定之后,完成分配角色的操作


    

当前的用户:{{userInfo.username}}

当前的角色:{{userInfo.role_name}}

分配新角色:

取 消 确 定
methods:{ ....... async saveRoleInfo(){ //当用户点击确定按钮之后 //判断用户是否选择了需要分配的角色 if(!this.selectedRoleId){ return this.$message.error('请选择需要分配的角色') } //发送请求完成分配角色的操作 const {data:res} = await this.$http.put(`users/${this.userInfo.id}/role`,{rid:this.selectedRoleId}) //判断如果删除失败,就做提示 if (res.meta.status !== 200) return this.$message.error('分配角色失败') this.$message.success('分配角色成功') this.getUserList(); //关闭对话框 this.setRoleDialogVisible = false }, setRoleDialogClosed(){ //当关闭对话框的时候,重置下拉框中的内容 this.selectedRoleId = '' this.userInfo = {} } }

分类管理模块

  • 商品分类

    • 创建categories子级路由组件并设置路由规则

      import Cate from './components/goods/Cate.vue'
      
      path: '/home', component: Home, redirect: '/welcome', children: [
          { path: "/welcome", component: Welcome },
          { path: "/users", component: Users },
          { path: "/rights", component: Rights },
          { path: "/roles", component: Roles  },
          { path: "/categories", component: Cate  }
      ]
      
    • 添加组件基本布局

      • 在Cate.vue组件中添加面包屑导航以及卡片视图中的添加分类按钮

        
        
    • 请求分类数据

      • 请求分类数据并将数据保存在data中

        
        
    • 使用插件展示数据

      • 使用第三方插件vue-table-with-tree-grid展示分类数据

        //全局注册组件
        Vue.component('tree-table', TreeTable
        
    • 自定义数据列

      • 使用vue-table-with-tree-grid定义模板列并添加自定义列

        //先在columns中添加一个列
        columns: [
            {label:'分类名称',prop:'cat_name'},
            //type:'template'(将该列设置为模板列),template:'isok'(设置该列模板的名称为isok)
            {label:'是否有效',prop:'',type:'template',template:'isok'},
            {label:'排序',prop:'',type:'template',template:'order'},
            {label:'操作',prop:'',type:'template',template:'opt'}
        ]
        
        
        
        
        
        
        
        
        
    • 完成分页功能

      
      
      
      
      //添加对应的事件函数
      methods:{
        .......
        handleSizeChange(newSize){
          //当pagesize发生改变时触发
          this.queryInfo.pagesize = newSize;
          this.getCateList();
        },
        handleCurrentChange(newPage){
          //当pagenum发生改变时触发
          this.queryInfo.pagenum = newPage;
          this.getCateList();
        }
      }
      
    • 完成添加分类

      
      
        
          添加分类
        
      
      ......
      
      
        
        
          
            
          
          
            
          
        
        
          取 消
          确 定
        
      
      
      
      //用来显示或隐藏添加分类对话框
      addCateDialogVisible: false,
      //添加分类的表单数据对象
      addCateForm:{
        //分类名称
        cat_name:'',
        //添加分类的父级id,0则表示父级为0.添加一级分类
        cat_pid:0,
        //添加分类的等级,0则表示添加一级分类
        cat_level:0
      },
      //添加分类校验规则
      addCateFormRules:{
        //验证规则
        cat_name:[ {required:true , message:'请输入分类名称',trigger:'blur'} ]
      },
      //保存1,2级父级分类的列表
      parentCateList:[]
      .......
      showAddCateDialog() {
        //调用getParentCateList获取分类列表
        this.getParentCateList()
        //显示添加分类对话框
        this.addCateDialogVisible = true
      },
      async getParentCateList(){
        //获取父级分类数据列表
        const { data: res } = await this.$http.get('categories', {
          params: {type:2}
        })
      
        if (res.meta.status !== 200) {
          return this.$message.error('获取商品分类列表数据失败')
        }
        this.parentCateList = res.data
      }
      
    • 添加级联菜单显示父级分类

    • 先导入Cascader组件,并注册

    • 然后添加使用级联菜单组件

      
        
        
      
      
      添加数据
      //配置级联菜单中数据如何展示
      cascaderProps:{
        value:'cat_id',
        label:'cat_name',
        children:'children',
        expandTrigger:'hover'
      },
      //绑定用户选择的分类值
      selectedKeys:[]
      .....
      methods:{
        .....
        parentCateChange(){
          //级联菜单中选择项发生变化时触发
          console.log(this.selectedKeys)
          //如果用户选择了父级分类
          if(this.selectedKeys.length > 0){
            //则将数组中的最后一项设置为父级分类
            this.addCateForm.cat_pid = this.selectedKeys[this.selectedKeys.length - 1]
            //level也要跟着发生变化
            this.addCateForm.cat_level = this.selectedKeys.length
            return
          }else{
            this.addCateForm.cat_pid = 0
            this.addCateForm.cat_level = 0
            return
          }
        },
        addCateDialogClosed(){
          //当关闭添加分类对话框时,重置表单
          this.$refs.addCateFormRef.resetFields()
          this.selectedKeys = [];
          this.addCateForm.cat_pid = 0
          this.addCateForm.cat_level = 0
        },
        addCate() {
          //点击确定,完成添加分类
          console.log(this.addCateForm)
          this.$refs.addCateFormRef.validate(async valid => {
            if (!valid) return
            //发送请求完成添加分类
            const { data: res } = await this.$http.post(
              'categories',
              this.addCateForm
            )
      
            if (res.meta.status !== 201) {
              return this.$message.error('添加分类失败')
            }
      
            this.$message.success('添加分类成功')
            this.getCateList()
            this.addCateDialogVisible = false
          })
        }
      }
      

      Vue项目(电商管理后台)_第11张图片

Vue项目(电商管理后台)_第12张图片

  • 参数管理

    • 只允许给三级分类内容设置参数,参数分为动态参数和静态参数属性

    • 添加子级组件

      • 添加Params.vue子组件,并在router.js中引入该组件并设置路由规则

        path: '/home', component: Home, redirect: '/welcome', children: [
          { path: "/welcome", component: Welcome },
          { path: "/users", component: Users },
          { path: "/rights", component: Rights },
          { path: "/roles", component: Roles  },
          { path: "/categories", component: Cate  },
          { path: "/params", component: Params  }
        ]
        
    • 完成组件基本布局

      • 完成Params.vue组件的基本布局

        
        
    • 完成级联选择框

      
      
          
              选择商品分类:
              
              
          
          
      
      ......
      
      

    Vue项目(电商管理后台)_第13张图片

    • 展示参数

      
      
        
        
          添加参数
          
          
            
            
            
            
            
            
              
            
          
        
        
        
          添加属性
          
          
            
            
            
            
            
            
              
            
          
        
      
      
      
      
      
      
      
      
  • 数据展示

    • 添加数据表格展示数据以及分页功能的实现,搜索功能的实现

    • 在main.js中添加代码

      //创建过滤器将秒数过滤为年月日,时分秒
      Vue.filter('dateFormat',function(originVal){
        const dt = new Date(originVal)
        const y = dt.getFullYear()
        const m = (dt.getMonth()+1+'').padStart(2,'0')
        const d = (dt.getDate()+'').padStart(2,'0')
      
        const hh = (dt.getHours()+'').padStart(2,'0')
        const mm = (dt.getMinutes()+'').padStart(2,'0')
        const ss = (dt.getSeconds()+'').padStart(2,'0')
      
        return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
      })
      
    • 添加功能代码

      
      
          
          
              
                  
                      
                  
              
              
                  添加商品
              
          
      
          
          
              
              
              
              
              
                  
              
              
                  
              
          
      
          
          
          
      
      
      //绑定数据以及添加方法
      
      
  • 实现删除商品

    //绑定按钮点击事件
    
    
    //事件函数代码编写
    async removeGoods(goods_id) {
      //根据id删除对应的参数或属性
      //弹窗提示用户是否要删除
      const confirmResult = await this.$confirm(
        '请问是否要删除该商品',
        '删除提示',
        {
          confirmButtonText: '确认删除',
          cancelButtonText: '取消',
          type: 'warning'
        }
      ).catch(err => err)
      //如果用户点击确认,则confirmResult 为'confirm'
      //如果用户点击取消, 则confirmResult获取的就是catch的错误消息'cancel'
      if (confirmResult != 'confirm') {
        return this.$message.info('已经取消删除')
      }
    
      //没有取消就是要删除,发送请求完成删除
      const {data:res} = await this.$http.delete(`goods/${goods_id}`)
    
      if (res.meta.status !== 200) {
        return this.$message.error('删除商品失败')
      }
    
      this.$message.success('删除商品成功')
      this.getGoodsList()
    }
    
  • 添加商品

    • 在List.vue中添加编程式导航,并创建添加商品路由组件及规则

      //在List.vue中添加编程式导航
      
          添加商品
      
      
      goAddPage(){
          this.$router.push('/goods/add')
      }
      
    • 在router.js中引入goods/Add.vue,并添加路由规则

      import GoodAdd from './components/goods/Add.vue'
      path: '/home', component: Home, redirect: '/welcome', children: [
        { path: "/welcome", component: Welcome },
        { path: "/users", component: Users },
        { path: "/rights", component: Rights },
        { path: "/roles", component: Roles  },
        { path: "/categories", component: Cate  },
        { path: "/params", component: Params  },
        { path: "/goods", component: GoodList  },
        { path: "/goods/add", component: GoodAdd  }
      ]
      
  • 布局Add.vue组件

    
    
    
    
    
    
  • 添加tab栏切换验证

    • 也就是说不输入某些内容,无法切换到别的tab栏
    //首先给tabs添加tab切换前事件
    
    ......
    
    
    //再到methods编写事件函数beforeTabLeave
    beforeTabLeave(activeName,oldActiveName){
      //在tab栏切换之前触发,两个形参为切换前,后的tab栏name
      if(oldActiveName === '0'){
          //在第一个标签页的时候
          if(this.addForm.goods_cat.length !== 3){
              this.$message.error('请选择商品的分类')
              return false
          }else if(this.addForm.goods_name.trim() === ''){
              this.$message.error('请输入商品名称')
              return false
          }else if(this.addForm.goods_price.trim() === '0'){
              this.$message.error('请输入商品价格')
              return false
          }else if(this.addForm.goods_weight.trim() === '0'){
              this.$message.error('请输入商品重量')
              return false
          }else if(this.addForm.goods_number.trim() === '0'){
              this.$message.error('请输入商品数量')
              return false
          }
      }
    }
    

    Vue项目(电商管理后台)_第19张图片

  • 展示信息

    • 在商品参数信息展示中使用的el-checkbox,el-checkbox-group组件,打开element.js引入组件并注册组件

      //在用户点击tab栏时触发事件
      
      ........
      
      //在参数信息,商品属性面板中添加循环生成结构的代码
      
        
        
            
            
                
            
        
      
      
        
        
            
        
      
      
      //在data数据中添加保存动态参数和静态属性的数组
      export default {
        data() {
          return {
            ......
            //动态参数列表
            manyTableData: [],
            //静态属性列表
            onlyTableData:[]
            }
        },methods: {
          .......
          async tabClicked() {
            //当用户点击切换tab栏时触发
            if (this.activeIndex === '1') {
              //发送请求获取动态参数
              const { data: res } = await this.$http.get(
                `categories/${this.cateId}/attributes`,
                { params: { sel: 'many' } }
              )
      
              if (res.meta.status !== 200) {
                return this.$message.error('获取动态参数列表失败')
              }
              //将attr_vals字符串转换为数组
              res.data.forEach(item => {
                item.attr_vals =
                  item.attr_vals.length === 0 ? [] : item.attr_vals.split(' ')
              })
              this.manyTableData = res.data
            } else if (this.activeIndex === '2') {
              //发送请求获取静态属性
              const { data: res } = await this.$http.get(
                `categories/${this.cateId}/attributes`,
                { params: { sel: 'only' } }
              )
      
              if (res.meta.status !== 200) {
                return this.$message.error('获取静态属性列表失败')
              }
      
              this.onlyTableData = res.data
            }
          }
        },
        //添加 计算属性获取三级分类
        computed: {
          cateId() {
            if (this.addForm.goods_cat.length === 3) {
              return this.addForm.goods_cat[2]
            }
            return null
          }
        }
      }
      

      Vue项目(电商管理后台)_第20张图片

订单管理模块

  • 创建路由

    • 创建订单列表路由组件并添加路由规则

      //在components中新建order文件夹,新建Order.vue组件,组件中添加代码如下
      
      
      
      
      
      
      //打开router.js导入Order.vue并添加规则
      import Order from './components/order/Order.vue'
      
      path: '/home', component: Home, redirect: '/welcome', children: [
        { path: "/welcome", component: Welcome },
        { path: "/users", component: Users },
        { path: "/rights", component: Rights },
        { path: "/roles", component: Roles  },
        { path: "/categories", component: Cate  },
        { path: "/params", component: Params  },
        { path: "/goods", component: GoodList  },
        { path: "/goods/add", component: GoodAdd  },
        { path: "/orders", component: Order  }
      ]
      
  • 实现数据展示及分页

    
    
        
        
            
                
                    
                
            
        
    
        
        
            
            
            
            
                
            
            
            
                
            
            
                
            
        
    
        
        
        
    
    
    
    
  • 制作省市区县联动

    
    
    //给修改地址按钮添加点击事件
    
    //添加修改地址对话框,在卡片视图下方添加
    
    
        
        
            
                
            
            
                
            
        
        
            取 消
            确 定
        
    
    
    //js部分的代码
    
    
    
  • 制作物流进度对话框

    • 打开element.js

      import {
          Timeline,TimelineItem
      } from 'element-ui'
      
      Vue.use(Timeline)
      Vue.use(TimelineItem)
      
    • 打开order.vue组件

      
      
          
          
              
                  {{activity.context}}
              
          
      
      
      
      

数据统计模块

  • 数据统计

  • 创建路由

    //在components中新建report文件夹,新建Report.vue组件,组件中添加代码如下
    
        
    
    
    
    
  • 打开router.js

    import Report from './components/report/Report.vue'
    path: '/home', component: Home, redirect: '/welcome', children: [
      { path: "/welcome", component: Welcome },
      { path: "/users", component: Users },
      { path: "/rights", component: Rights },
      { path: "/roles", component: Roles  },
      { path: "/categories", component: Cate  },
      { path: "/params", component: Params  },
      { path: "/goods", component: GoodList  },
      { path: "/goods/add", component: GoodAdd  },
      { path: "/orders", component: Order  },
      { path: "/reports", component: Report  }
    ]
    
  • 导入ECharts并使用

    
        
    
    
    
    

基于Vue技术栈进行项目开发

使用Vue的第三方组件进行项目开发

前后端分离的开发模式

项目优化上线

实现步骤:
A.生成打包报告,根据报告优化项目
B.第三方库启用CDN
C.Element-UI组件按需加载
D.路由懒加载
E.首页内容定制

添加进度条

  • 给项目添加进度条效果,先打开项目控制台,打开依赖,安装nprogress
  • 打开main.js,编写如下代码
//导入进度条插件
import NProgress from 'nprogress'
//导入进度条样式
import 'nprogress/nprogress.css'
.....
//请求在到达服务器之前,先会调用use中的这个回调函数来添加请求头信息
axios.interceptors.request.use(config => {
  //当进入request拦截器,表示发送了请求,我们就开启进度条
  NProgress.start()
  //为请求头对象,添加token验证的Authorization字段
  config.headers.Authorization = window.sessionStorage.getItem("token")
  //必须返回config
  return config
})
//在response拦截器中,隐藏进度条
axios.interceptors.response.use(config =>{
  //当进入response拦截器,表示请求已经结束,我们就结束进度条
  NProgress.done()
  return config
})

根据报错修改代码

  • 根据ESLint的警告提示更改对应的代码
  • 在.prettierrc文件中更改设置"printWidth":200, 将每行代码的文字数量更改为200
{
    "semi":false,
    "singleQuote":true,
    "printWidth":200
}

执行build

  • 安装一个插件(babel-plugin-transform-remove-console)在项目build阶段移除所有的console信息
  • 打开项目控制台,点击依赖->开发依赖,输入babel-plugin-transform-remove-console,安装
  • 打开babel.config.js,编辑代码如下:
//项目发布阶段需要用到的babel插件
const productPlugins = []

//判断是开发还是发布阶段
if(process.env.NODE_ENV === 'production'){
  //发布阶段
  productPlugins.push("transform-remove-console")
}

module.exports = {
  "presets": [
    "@vue/app"
  ],
  "plugins": [
    [
      "component",
      {
        "libraryName": "element-ui",
        "styleLibraryName": "theme-chalk"
      }
    ],
    ...productPlugins
  ]
}

生成打包报告

  • 命令行形式生成打包报告
    • vue-cli-service build --report
  • 在vue控制台生成打包报告
    • 点击“任务”=>“build”=>“运行”
    • 运行完毕之后点击右侧“分析”,“控制台”面板查看报告

修改webpack的默认配置

  • 默认情况下,vue-cli 3.0生成的项目,隐藏了webpack配置项,如果我们需要配置webpack
  • 需要通过vue.config.js来配置。
  • 在项目根目录中创建vue.config.js文件,
module.exports = {
    chainWebpack:config=>{
        //发布模式
        config.when(process.env.NODE_ENV === 'production',config=>{
            //entry找到默认的打包入口,调用clear则是删除默认的打包入口
            //add添加新的打包入口
            config.entry('app').clear().add('./src/main-prod.js')
        })
        //开发模式
        config.when(process.env.NODE_ENV === 'development',config=>{
            config.entry('app').clear().add('./src/main-dev.js')
        })
    }
}

加载外部CDN

  • 默认情况下,依赖项的所有第三方包都会被打包到js/chunk-vendors.******.js文件中,导致该js文件过大
  • 那么可以通过externals排除这些包,使它们不被打包到js/chunk-vendors.******.js文件中
module.exports = {
    chainWebpack:config=>{
        //发布模式
        config.when(process.env.NODE_ENV === 'production',config=>{
            //entry找到默认的打包入口,调用clear则是删除默认的打包入口
            //add添加新的打包入口
            config.entry('app').clear().add('./src/main-prod.js')

            //使用externals设置排除项
            config.set('externals',{
                vue:'Vue',
                'vue-router':'VueRouter',
                axios:'axios',
                lodash:'_',
                echarts:'echarts',
                nprogress:'NProgress',
                'vue-quill-editor':'VueQuillEditor'
            })
        })
        //开发模式
        config.when(process.env.NODE_ENV === 'development',config=>{
            config.entry('app').clear().add('./src/main-dev.js')
        })
    }
}
  • 设置好排除之后,为了使我们可以使用vue,axios等内容,我们需要加载外部CDN的形式解决引入依赖项。
  • 打开开发入口文件main-prod.js,删除掉默认的引入代码
import Vue from 'vue'
import App from './App.vue'
import router from './router'
// import './plugins/element.js'
//导入字体图标
import './assets/fonts/iconfont.css'
//导入全局样式
import './assets/css/global.css'
//导入第三方组件vue-table-with-tree-grid
import TreeTable from 'vue-table-with-tree-grid'
//导入进度条插件
import NProgress from 'nprogress'
//导入进度条样式
// import 'nprogress/nprogress.css'
// //导入axios
import axios from 'axios'
// //导入vue-quill-editor(富文本编辑器)
import VueQuillEditor from 'vue-quill-editor'
// //导入vue-quill-editor的样式
// import 'quill/dist/quill.core.css'
// import 'quill/dist/quill.snow.css'
// import 'quill/dist/quill.bubble.css'

axios.defaults.baseURL = 'http://127.0.0.1:8888/api/private/v1/'
//请求在到达服务器之前,先会调用use中的这个回调函数来添加请求头信息
axios.interceptors.request.use(config => {
  //当进入request拦截器,表示发送了请求,我们就开启进度条
  NProgress.start()
  //为请求头对象,添加token验证的Authorization字段
  config.headers.Authorization = window.sessionStorage.getItem("token")
  //必须返回config
  return config
})
//在response拦截器中,隐藏进度条
axios.interceptors.response.use(config =>{
  //当进入response拦截器,表示请求已经结束,我们就结束进度条
  NProgress.done()
  return config
})
Vue.prototype.$http = axios

Vue.config.productionTip = false

//全局注册组件
Vue.component('tree-table', TreeTable)
//全局注册富文本组件
Vue.use(VueQuillEditor)

//创建过滤器将秒数过滤为年月日,时分秒
Vue.filter('dateFormat',function(originVal){
  const dt = new Date(originVal)
  const y = dt.getFullYear()
  const m = (dt.getMonth()+1+'').padStart(2,'0')
  const d = (dt.getDate()+'').padStart(2,'0')

  const hh = (dt.getHours()+'').padStart(2,'0')
  const mm = (dt.getMinutes()+'').padStart(2,'0')
  const ss = (dt.getSeconds()+'').padStart(2,'0')

  return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
})

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')
  • 然后打开public/index.html添加外部cdn引入代码


  
    
    
    
    
    电商后台管理系统

    
    
    
    
    
    
    
    

    
    
    
    
    
    
    
    
    

    
    

  
  
    
    

定制首页内容

  • 开发环境的首页和发布环境的首页展示内容的形式有所不同
  • 如开发环境中使用的是import加载第三方包,而发布环境则是使用CDN,那么首页也需根据环境不同来进行不同的实现
  • 可以通过插件的方式来定制首页内容,打开vue.config.js,编写代码如下:
module.exports = {
    chainWebpack:config=>{
        config.when(process.env.NODE_ENV === 'production',config=>{
            ......
            
            //使用插件
            config.plugin('html').tap(args=>{
                //添加参数isProd
                args[0].isProd = true
                return args
            })
        })

        config.when(process.env.NODE_ENV === 'development',config=>{
            config.entry('app').clear().add('./src/main-dev.js')

            //使用插件
            config.plugin('html').tap(args=>{
                //添加参数isProd
                args[0].isProd = false
                return args
            })
        })
    }
}
  • 然后在public/index.html中使用插件判断是否为发布环境并定制首页内容


  
    
    
    
    
    <%= htmlWebpackPlugin.options.isProd ? '' : 'dev - ' %>电商后台管理系统

    <% if(htmlWebpackPlugin.options.isProd){ %>
    
    
    ........
    
    
    <% } %>
  
  .......

路由懒加载

  • 当路由被访问时才加载对应的路由文件,就是路由懒加载。
  • 路由懒加载实现步骤:
    • 安装 @babel/plugin-syntax-dynamic-import
  • 在babel.config.js中声明该插件,打开babel.config.js
//项目发布阶段需要用到的babel插件
const productPlugins = []

//判断是开发还是发布阶段
if(process.env.NODE_ENV === 'production'){
  //发布阶段
  productPlugins.push("transform-remove-console")
}

module.exports = {
  "presets": [
    "@vue/app"
  ],
  "plugins": [
    [
      "component",
      {
        "libraryName": "element-ui",
        "styleLibraryName": "theme-chalk"
      }
    ],
    ...productPlugins,
    //配置路由懒加载插件
    "@babel/plugin-syntax-dynamic-import"
  ]
}
  • 将路由更改为按需加载的形式,打开router.js,更改引入组件代码如下:
import Vue from 'vue'
import Router from 'vue-router'
const Login = () => import(/* webpackChunkName:"login_home_welcome" */ './components/Login.vue')
// import Login from './components/Login.vue'
const Home = () => import(/* webpackChunkName:"login_home_welcome" */ './components/Home.vue')
// import Home from './components/Home.vue'
const Welcome = () => import(/* webpackChunkName:"login_home_welcome" */ './components/Welcome.vue')
// import Welcome from './components/Welcome.vue'
const Users = () => import(/* webpackChunkName:"user" */ './components/user/Users.vue')
// import Users from './components/user/Users.vue'
const Rights = () => import(/* webpackChunkName:"power" */ './components/power/Rights.vue')
// import Rights from './components/power/Rights.vue'
const Roles = () => import(/* webpackChunkName:"power" */ './components/power/Roles.vue')
// import Roles from './components/power/Roles.vue'
const Cate = () => import(/* webpackChunkName:"goods" */ './components/goods/Cate.vue')
// import Cate from './components/goods/Cate.vue'
const Params = () => import(/* webpackChunkName:"goods" */ './components/goods/Params.vue')
// import Params from './components/goods/Params.vue'
const GoodList = () => import(/* webpackChunkName:"goods" */ './components/goods/List.vue')
// import GoodList from './components/goods/List.vue'
const GoodAdd = () => import(/* webpackChunkName:"goods" */ './components/goods/Add.vue')
// import GoodAdd from './components/goods/Add.vue'
const Order = () => import(/* webpackChunkName:"order" */ './components/order/Order.vue')
// import Order from './components/order/Order.vue'
const Report = () => import(/* webpackChunkName:"report" */ './components/report/Report.vue')
// import Report from './components/report/Report.vue'

项目上线

  • 通过node创建服务器
  • 在vue_shop同级创建一个文件夹vue_shop_server存放node服务器
  • 使用终端打开vue_shop_server文件夹,输入命令 npm init -y
  • 初始化包之后,输入命令 npm i express -S
  • 打开vue_shop目录,复制dist文件夹,粘贴到vue_shop_server中
  • 在vue_shop_server文件夹中创建app.js文件,编写代码如下:
const express = require('express')

const app = express()

app.use(express.static('./dist'))

app.listen(8998,()=>{
    console.log("server running at http://127.0.0.1:8998")
})
  • 然后再次在终端中输入 node app.js

  • 开启gzip压缩

  • 打开vue_shop_server文件夹的终端,输入命令:npm i compression -D

  • 打开app.js,编写代码:

const express = require('express')

const compression = require('compression')

const app = express()

app.use(compression())
app.use(express.static('./dist'))

app.listen(8998,()=>{
    console.log("server running at http://127.0.0.1:8998")
})
  • 配置https服务
  • 配置https服务一般是后台进行处理,前端开发人员了解即可。
  • 首先,需要申请SSL证书,进入https://freessl.cn官网
  • 在后台导入证书,打开今天资料/素材,复制素材中的两个文件到vue_shop_server中
  • 打开app.js文件,编写代码导入证书,并开启https服务
const express = require('express')
const compression = require('compression')
const https = require('https')
const fs = require('fs')

const app = express()
//创建配置对象设置公钥和私钥
const options = {
    cert:fs.readFileSync('./full_chain.pem'),
    key:fs.readFileSync('./private.key')
}

app.use(compression())
app.use(express.static('./dist'))

// app.listen(8998,()=>{
//     console.log("server running at http://127.0.0.1:8998")
// })

//启动https服务
https.createServer(options,app).listen(443)

的babel插件
const productPlugins = []

//判断是开发还是发布阶段
if(process.env.NODE_ENV === ‘production’){
//发布阶段
productPlugins.push(“transform-remove-console”)
}

module.exports = {
“presets”: [
“@vue/app”
],
“plugins”: [
[
“component”,
{
“libraryName”: “element-ui”,
“styleLibraryName”: “theme-chalk”
}
],
…productPlugins,
//配置路由懒加载插件
“@babel/plugin-syntax-dynamic-import”
]
}


- 将路由更改为按需加载的形式,打开router.js,更改引入组件代码如下:

import Vue from 'vue'
import Router from 'vue-router'
const Login = () => import(/* webpackChunkName:"login_home_welcome" */ './components/Login.vue')
// import Login from './components/Login.vue'
const Home = () => import(/* webpackChunkName:"login_home_welcome" */ './components/Home.vue')
// import Home from './components/Home.vue'
const Welcome = () => import(/* webpackChunkName:"login_home_welcome" */ './components/Welcome.vue')
// import Welcome from './components/Welcome.vue'
const Users = () => import(/* webpackChunkName:"user" */ './components/user/Users.vue')
// import Users from './components/user/Users.vue'
const Rights = () => import(/* webpackChunkName:"power" */ './components/power/Rights.vue')
// import Rights from './components/power/Rights.vue'
const Roles = () => import(/* webpackChunkName:"power" */ './components/power/Roles.vue')
// import Roles from './components/power/Roles.vue'
const Cate = () => import(/* webpackChunkName:"goods" */ './components/goods/Cate.vue')
// import Cate from './components/goods/Cate.vue'
const Params = () => import(/* webpackChunkName:"goods" */ './components/goods/Params.vue')
// import Params from './components/goods/Params.vue'
const GoodList = () => import(/* webpackChunkName:"goods" */ './components/goods/List.vue')
// import GoodList from './components/goods/List.vue'
const GoodAdd = () => import(/* webpackChunkName:"goods" */ './components/goods/Add.vue')
// import GoodAdd from './components/goods/Add.vue'
const Order = () => import(/* webpackChunkName:"order" */ './components/order/Order.vue')
// import Order from './components/order/Order.vue'
const Report = () => import(/* webpackChunkName:"report" */ './components/report/Report.vue')
// import Report from './components/report/Report.vue'

项目上线

  • 通过node创建服务器
  • 在vue_shop同级创建一个文件夹vue_shop_server存放node服务器
  • 使用终端打开vue_shop_server文件夹,输入命令 npm init -y
  • 初始化包之后,输入命令 npm i express -S
  • 打开vue_shop目录,复制dist文件夹,粘贴到vue_shop_server中
  • 在vue_shop_server文件夹中创建app.js文件,编写代码如下:
const express = require('express')

const app = express()

app.use(express.static('./dist'))

app.listen(8998,()=>{
    console.log("server running at http://127.0.0.1:8998")
})
  • 然后再次在终端中输入 node app.js

  • 开启gzip压缩

  • 打开vue_shop_server文件夹的终端,输入命令:npm i compression -D

  • 打开app.js,编写代码:

const express = require('express')

const compression = require('compression')

const app = express()

app.use(compression())
app.use(express.static('./dist'))

app.listen(8998,()=>{
    console.log("server running at http://127.0.0.1:8998")
})
  • 配置https服务
  • 配置https服务一般是后台进行处理,前端开发人员了解即可。
  • 首先,需要申请SSL证书,进入https://freessl.cn官网
  • 在后台导入证书,打开今天资料/素材,复制素材中的两个文件到vue_shop_server中
  • 打开app.js文件,编写代码导入证书,并开启https服务
const express = require('express')
const compression = require('compression')
const https = require('https')
const fs = require('fs')

const app = express()
//创建配置对象设置公钥和私钥
const options = {
    cert:fs.readFileSync('./full_chain.pem'),
    key:fs.readFileSync('./private.key')
}

app.use(compression())
app.use(express.static('./dist'))

// app.listen(8998,()=>{
//     console.log("server running at http://127.0.0.1:8998")
// })

//启动https服务
https.createServer(options,app).listen(443)

你可能感兴趣的:(Vue)