vue_shop(基于vue电商管理后台网站)

vue_shop

目录
  • vue_shop
    • day01
      • 实现登录功能
        • 项目预开发处理
        • Login.vue完整代码:
        • 处理步骤:
          • 添加element-ui的表单组件
          • 添加第三方字体:
          • 添加表单验证
          • 导入axios
          • 配置弹窗提示:
          • 登录成功操作:
          • 添加路由守卫
          • 实现退出功能
          • 补充
    • day02
      • 后台首页基本布局
        • 顶部与侧边栏布局
        • axios请求拦截器
        • 侧边栏数据
        • 设置激活子菜单样式
        • 侧边菜单栏的伸缩功能
        • Welcome.vue
        • 完整home.vue代码
      • 用户列表
        • 用户列表基本结构
        • 请求用户列表数据
        • 展示用户列表数据
        • 实现用户列表分页
        • 实现更新用户状态
        • 实现搜索功能
        • 实现添加用户
    • day03
      • 修改用户
      • 删除用户
      • 权限列表
      • 角色列表
      • 分配角色
    • day04
      • 商品分类
      • 参数管理
      • 商品列表
    • day05
      • 添加商品
      • 订单列表
      • 数据统计
    • day06
      • 项目优化
    • 项目源码地址

day01

实现登录功能

项目预开发处理

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

  2. 登录逻辑:
    在登录页面输入账号和密码进行登录,将数据发送给服务器
    服务器返回登录的结果,登录成功则返回数据中带有token
    客户端得到token并进行保存,后续的请求都需要将此token发送给服务器,服务器会验证token以保证用户身份。

  3. 添加新分支login,打开vue_shop终端,使用git status确定当前项目状态。
    确定当前工作目录是干净的之后,创建一个分支进行开发,开发完毕之后将其合并到master

    git checkout -b login
    

    然后查看新创建的分支:git branch
    确定我们正在使用login分支进行开发

  • 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')
  • components文件夹中新建Login.vue组件

  • router.js中导入组件并设置规则,在App.vue中添加路由占位符:

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

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

    • 在main.js中导入global.css,使得全局样式生效 import

Login.vue完整代码:






处理步骤:

添加element-ui的表单组件

在plugins文件夹中打开element.js文件,进行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="loginFormRules",rules是一堆验证规则,定义在script中
export default {
  data() {
    return {
      //表单验证规则
      loginFormRules: {
        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'
          }
        ]
      }
    }
  }

通过的 prop属性设置验证规则



导入axios

发送ajax请求,在main.js添加:

import axios from 'axios'
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'
    
    
登录成功操作:

登录成功之后,需要将后台返回的token保存到sessionStorage中,操作完毕之后,需要跳转到/home

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,并为之添加规则

添加路由守卫

如果用户没有登录,不能访问/home,如果用户通过url地址直接访问,则强制跳转到登录页面,router.js:

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 

实现退出功能

在Home组件中添加一个退出功能按钮,给退出按钮添加点击事件,添加事件处理代码如下:

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

补充
  • 处理ESLint警告:

默认情况下,ESLint和vscode格式化工具有冲突,需要添加配置文件解决冲突。在项目根目录添加 .prettierrc 文件

{
    "semi":false,
    "singleQuote":true
}

打开.eslintrc.js文件,禁用对 space-before-function-paren 的检查:

  rules: {
    'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
    'space-before-function-paren' : 0
  },

day02

后台首页基本布局

Home.vue组件,进行布局:


  
  Header 退出 
  
  
    
    Aside
    
    Main
  


默认情况下,跟element-ui组件同名的类名可以帮助我们快速的给对应的组件添加样式,如:

.home-container {
  height: 100%;
}
.el-header{
  background-color:#373D41;
}
.el-aside{
  background-color:#333744;
}
.el-main{
  background-color:#eaedf1;
}

顶部与侧边栏布局



axios请求拦截器

后台除了登录接口之外,都需要token权限验证,我们可以通过添加axios请求拦截器来添加token,以保证拥有获取数据的权限。在main.js中添加代码,在将axios挂载到vue原型之前添加下面的代码:

//请求在到达服务器之前,先会调用use中的这个回调函数来添加请求头信息
axios.interceptors.request.use(config=>{
  //为请求头对象,添加token验证的Authorization字段
  config.headers.Authorization = window.sessionStorage.getItem("token")
  return config
})

侧边栏数据



通过v-for双重循环渲染左侧菜单:

        
          
          
            
            
            
              
            
          
        

设置激活子菜单样式

通过更改el-menu的active-text-color属性可以设置侧边栏菜单中点击的激活项的文字颜色。
通过更改菜单项模板(template)中的i标签的类名,可以将左侧菜单栏的图标进行设置,我们需要在项目中使用第三方字体图标,在数据中添加一个iconsObj:

iconsObj: {
        '125':'iconfont icon-user',
        '103':'iconfont icon-tijikongjian',
        '101':'iconfont icon-shangpin',
        '102':'iconfont icon-danju',
        '145':'iconfont icon-baobiao'
      }

然后将图标类名进行数据绑定,绑定iconsObj中的数据:

为了保持左侧菜单每次只能打开一个,显示其中的子菜单,我们可以在el-menu中添加一个属性unique-opened
或者也可以数据绑定进行设置(此时true认为是一个bool值,而不是字符串) :unique-opened="true"

侧边菜单栏的伸缩功能

        
        
          
          
|||

然后给div添加样式,给div添加事件:

Welcome.vue

在router.js中导入子级路由组件,并设置路由规则以及子级路由的默认重定向,打开Home.vue,在main的主体结构中添加一个路由占位符。

制作好了Welcome子级路由之后,我们需要将所有的侧边栏二级菜单都改造成子级路由链接。
我们只需要将el-menu的router属性设置为true就可以了,此时当我们点击二级菜单的时候,就会根据菜单的index属性进行路由跳转,如: /110,使用index id来作为跳转的路径不合适,我们可以重新绑定index的值为:index="'/'+subItem.path"

完整home.vue代码






用户列表

  1. 新建用户列表组件 user/Users.vue

  2. 在router.js中导入子级路由组件Users.vue,并设置路由规则

  3. 当点击二级菜单的时候,被点击的二级子菜单并没有高亮,我们需要正在被使用的功能高亮显示。我们可以通过设置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
    
    
  4. 然后在数据中添加一个activePath绑定数据,并将el-menu的default-active属性设置为activePath

  5. 最后在created中将sessionStorage中的数据赋值给activePath
    this.activePath =window.sessionStorage.getItem("activePath")

用户列表基本结构

  1. .使用element-ui面包屑组件完成顶部导航路径(复制面包屑代码,在element.js中导入组件Breadcrumb,BreadcrumbItem)
  2. .使用element-ui卡片组件完成主体表格(复制卡片组件代码,在element.js中导入组件Card),再使用element-ui输入框完成搜索框及搜索按钮,
  3. 此时我们需要使用栅格布局来划分结构(复制卡片组件代码,在element.js中导入组件Row,Col),然后再使用el-button制作添加用户按钮

用户列表组件

首页 用户管理 用户列表 添加用户

请求用户列表数据



展示用户列表数据

  1. 使用表格来展示用户列表数据,使用element-ui表格组件完成列表展示数据(复制表格代码,在element.js中导入组件Table,TableColumn),在渲染展示状态时,会使用作用域插槽获取每一行的数据

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

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



    
    
    
    
    
    
        
    
    
        
    


实现用户列表分页

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

  1. 添加两个事件的事件处理函数@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组件时,用户的状态应该跟随发生改变。

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


  1. 在事件中发送请求完成状态的更改
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事件中重新请求数据即可。


    
        
    


实现添加用户

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

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

  3. 更改Dialog组件中的内容



    
    
        
            
        
        
            
        
        
            
        
        
            
        
    
    
    
        取 消
        确 定
    


  1. 添加数据绑定和校验规则:
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'}
      ]
    }
  }
}

  1. 当关闭对话框时,重置表单。给el-dialog添加@close事件,在事件中添加重置表单的代码
methods:{
  ...
  addDialogClosed(){
      //对话框关闭之后,重置表达
      this.$refs.addFormRef.resetFields();
  }
}

  1. 点击对话框中的确定按钮,发送请求完成添加用户的操作
    • 首先给确定按钮添加点击事件,在点击事件中完成业务逻辑代码
methods:{
  ....
  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()
      })
  }
}

day03

修改用户

  1. 为用户列表中的修改按钮绑定点击事件
  2. .在页面中添加修改用户对话框,并修改对话框的属性
  3. .根据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
}

  1. 在弹出窗中添加修改用户信息的表单并做响应的数据绑定以及数据验证


    
        
    
    
        
    
    
        
    


数据绑定以及验证:

//控制修改用户对话框的显示与否
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'
        }
    ]
}

  1. 监听对话框关闭事件,在对话框关闭之后,重置表单


editDialogClosed(){
    //对话框关闭之后,重置表达
    this.$refs.editFormRef.resetFields()
}

  1. 在用户点击确定按钮的时候,验证数据成功之后发送请求完成修改
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中。

  1. 导入MessageBox组件,并将MessageBox组件挂载到实例。
    Vue.prototype.$confirm = MessageBox.confirm
  2. .给用户列表中的删除按钮添加事件,并在事件处理函数中弹出确定取消窗,最后再根据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()
}

权限列表

  1. 添加权限列表路由,创建权限管理组件(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 }
      ]
......

  1. 添加面包屑导航,在Rights.vue中添加面包屑组件展示导航路径

  2. 显示数据在data中添加一个rightsList数据,在methods中提供一个getRightsList方法发送请求获取权限列表数据,在created中调用这个方法获取数据


    
    
    
    
        
    



角色列表

  1. 添加角色列表路由,添加角色列表子组件(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  }
      ]

  1. 添加面包屑导航, 在Roles.vue中添加面包屑组件展示导航路径

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




    
    
    
    
    
    
        
    




补充说明:
之前学习过类似的添加角色,删除角色,编辑角色请参照之前编写过的代码还有接口文档完成效果。

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


    


  1. 美化样式

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

  1. 添加权限删除功能, 给每一个权限的el-tag添加closable属性,是的权限右侧出现“X”图标再给el-tag添加绑定close事件处理函数
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();

}

  1. 完成权限分配功能, 先给分配权限按钮添加事件
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
}

添加分配权限对话框,并添加绑定数据setRightDialogVisible

  1. 完成树形结构弹窗,在element.js中引入Tree,注册Tree


    
    
    
        取 消
        确 定
    




分配角色

打开Users.vue,完成分配角色的功能

  1. 添加分配角色对话框


    

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

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

分配新角色:

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


    



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







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


    

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

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

分配新角色:

取 消 确 定

day04

商品分类

  1. 创建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  }
]

  1. 添加组件基本布局, 在Cate.vue组件中添加面包屑导航以及卡片视图中的添加分类按钮


  1. 请求分类数据, 请求分类数据并将数据保存在data中


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

    • 在vue 控制台中点击依赖->安装依赖->运行依赖->输入vue-table-with-tree-gird->点击安装

    • 打开main.js,导入vue-table-with-tree-grid

      import TreeTable from 'vue-table-with-tree-grid'
         .....
      Vue.config.productionTip = false
      
      //全局注册组件
      Vue.component('tree-table', TreeTable)
      3).使用组件展示分类数据
      
      





在数据中添加columns:
columns: [
    {label:'分类名称',prop:'cat_name'}
]

  1. 自定义数据列, 使用vue-table-with-tree-grid定义模板列并添加自定义列










  1. 完成分页功能






  1. 完成添加分类
......


  
    添加分类
  

......


  
  
    
      
    
    
      
    
  
  
    取 消
    确 定
  




  1. 添加级联菜单显示父级分类先导入Cascader组件,并注册
    然后添加使用级联菜单组件:

  
  




参数管理

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

  1. 添加子级组件, 添加Params.vue子组件,并在router.js中引入该组件并设置路由规则
import Params from './components/goods/Params.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  }
]

  1. 完成组件基本布局, 完成Params.vue组件的基本布局, 其中警告提示信息使用了el-alert,在element.js引入该组件并注册


  1. 完成级联选择框, 完成商品分类级联选择框


    
        选择商品分类:
        
        
    
    

......


  1. 展示参数, 展示动态参数数据以及静态属性数据


  
  
    添加参数
    
    
      
      
      
      
      
      
        
      
    
  
  
  
    添加属性
    
    
      
      
      
      
      
      
        
      
    
  






  1. 编辑参数, 完成编辑参数或属性


  
  
    
      
    
  
  
    取 消
    确 定
  




  1. 删除参数, 删除参数或属性
给两个删除按钮添加事件
删除
删除



  1. 展示动态参数可选项,动态参数可选项展示及操作在获取动态参数的方法中进行处理。
//将获取到的数据中的attr_vals字符串转换为数组
res.data.forEach(item => {
  item.attr_vals = item.attr_vals ? item.attr_vals.split(' ') : []
  //添加一个bool值控制文本框的显示或者隐藏
  item.inputVisible = false
  //添加一个inputValue保存文本框值
  item.inputValue = ''
})

//然后再修改展开行中的代码,生成el-tag和文本框以及添加按钮


  



//最后对应文本框的事件和按钮的事件添加处理函数
handleInputConfirm(row){
  //当用户在文本框中按下enter键或者焦点离开时都会触发执行
  //判断用户在文本框中输入的内容是否合法
  if(row.inputValue.trim().length===0){
    row.inputValue = ''
    row.inputVisible = false
    return
  }

  // row.inputVisible = false
  //如果用户输入了真实合法的数据,需要保存起来
},
showInput(row){
  //用户点击添加按钮时触发
  row.inputVisible = true
  //$nextTick:在页面上元素被重新渲染之后,调用回调函数的代码
  this.$nextTick(_=>{
    //让文本框自动获得焦点
    this.$refs.saveTagInput.$refs.input.focus()
  })
}

9.添加/删除可选项,添加/删除动态参数可选项

给el-tag添加删除事件
{{item}}





  1. 数据展示,添加数据表格展示数据以及分页功能的实现,搜索功能的实现在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}`
})



    
    
        
            
                
            
        
        
            添加商品
        
    

    
    
        
        
        
        
        
            
        
        
            
        
    

    
    
    


//绑定数据以及添加方法


  1. 实现删除商品
//绑定按钮点击事件




在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组件, 布局过程中需要使用Steps组件,在element.js中引入并注册该组件,并在global.css中给组件设置全局样式

    import {Steps,Step} from 'element-ui'
    Vue.use(Step)
    Vue.use(Steps)
    
    //global.css
    .el-steps{
        margin:15px 0;
    }
    .el-step__title{
        font-size: 13px;
    }
    
    

    然后再在Add.vue中进行页面布局

    
    
    
    
    
    
    
  • 添加tab栏切换验证, 也就是说不输入某些内容,无法切换到别的tab栏

    //首先给tabs添加tab切换前事件
    
    ......
    
    
    
    
    
  • .展示信息, 展示商品参数信息,商品属性信息
    在商品参数信息展示中使用的el-checkbox,el-checkbox-group组件,打开element.js引入组件并注册组件

    //在用户点击tab栏时触发事件
    
    ........
    
    //在参数信息,商品属性面板中添加循环生成结构的代码
    
      
      
          
          
              
          
      
    
    
      
      
          
      
    
    
    
    
    

day05

添加商品

  1. 完成图片上传,使用upload组件完成图片上传在element.js中引入upload组件,并注册因为upload组件进行图片上传的时候并不是使用axios发送请求所以,我们需要手动为上传图片的请求添加token,即为upload组件添加headers属性
//在页面中添加upload组件,并设置对应的事件和属性

  
  
    点击上传
  

//在el-card卡片视图下面添加对话框用来预览图片


  




  1. 使用富文本插件,想要使用富文本插件vue-quill-editor,就必须先从依赖安装该插件引入并注册vue-quill-editor,打开main.js,编写如下代码
//导入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'
......
//全局注册组件
Vue.component('tree-table', TreeTable)
//全局注册富文本组件
Vue.use(VueQuillEditor)

使用富文本插件vue-quill-editor



  
  
  
  添加商品




  1. 添加商品,完成添加商品的操作在添加商品之前,为了避免goods_cat数组转换字符串之后导致级联选择器报错我们需要打开vue控制条,点击依赖,安装lodash,把addForm进行深拷贝
//打开Add.vue,导入lodash


订单列表

  1. 创建路由,创建订单列表路由组件并添加路由规则
//在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  }
]

  1. 实现数据展示及分页


    
    
        
            
                
            
        
    

    
    
        
        
        
        
            
        
        
        
            
        
        
            
        
    

    
    
    




  1. 制作省市区县联动,打开今天的资料,找到素材文件夹,复制citydata.js文件到components/order文件夹中然后导入citydata.js文件
//给修改地址按钮添加点击事件

//添加修改地址对话框,在卡片视图下方添加


    
    
        
            
        
        
            
        
    
    
        取 消
        确 定
    


//js部分的代码



  1. 制作物流进度对话框,因为我们使用的是element-ui中提供的Timeline组件,所以需要导入并注册组件打开element.js,编写代码会进行导入和注册
import {
    Timeline,TimelineItem
} from 'element-ui'

Vue.use(Timeline)
Vue.use(TimelineItem)

打开Order.vue文件,添加代码实现物流进度对话框



    
    
        
            {{activity.context}}
        
    




数据统计

  1. 创建路由 创建数据统计路由组件并添加路由规则
//在components中新建report文件夹,新建Report.vue组件,组件中添加代码如下

    




打开router.js,导入Report.vue并设置路由规则

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  }
]

  1. 导入ECharts并使用

    




day06

项目优化

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

  1. 添加进度条,给项目添加进度条效果,先打开项目控制台,打开依赖,安装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
})

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

  1. 执行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
  ]
}

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

  2. 修改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')
        })
    }
}

补充:
chainWebpack可以通过链式编程的形式,修改webpack配置
configureWebpack可以通过操作对象的形式,修改webpack配置

  1. 加载外部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引入代码



  
    
    
    
    
    电商后台管理系统

    
    
    
    
    
    
    
    

    
    
    
    
    
    
    
    
    

    
    

  
  
    
    
  1. 定制首页内容
    开发环境的首页和发布环境的首页展示内容的形式有所不同
    如开发环境中使用的是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){ %>
    
    
    ........
    
    
    <% } %>
  
  .......

  1. 路由懒加载
    当路由被访问时才加载对应的路由文件,就是路由懒加载。
    路由懒加载实现步骤:

    • 安装 @babel/plugin-syntax-dynamic-import
      打开vue控制台,点击依赖->安装依赖->开发依赖->搜索@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'

  1. 项目上线
    A.通过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

​ B.开启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)

注意:因为我们使用的证书有问题,所以无法正常使用https服务

D.使用pm2管理应用
打开vue_shop_server文件夹的终端,输入命令:npm i pm2 -g
使用pm2启动项目,在终端中输入命令:pm2 start app.js --name 自定义名称
查看项目列表命令:pm2 ls
重启项目:pm2 restart 自定义名称
停止项目:pm2 stop 自定义名称
删除项目:pm2 delete 自定义名称

项目源码地址

码云:https://gitee.com/garyxirapper/vue_shop.git

github:https://github.com/LionSSSN/vue_shop.git

你可能感兴趣的:(vue_shop(基于vue电商管理后台网站))