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

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

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

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

一:项目简介

前端技术栈

Vue2

vue-router

Element-ui

Axios

Echarts

项目构架

image-20230320182412413

功能模块

  1. 用户登录/退出模块

  2. 用户管理模块

  3. 权限管理模块

    角色列表模块

    权限列表模块

  4. 商品管理模块

    商品列表模块

    分类管理模块

    参数管理模块

  5. 订单管理模块

  6. 数据统计模块


二:各个功能模块

1:项目初始化

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

vue2配置

配置vue-router

配置axios

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

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

1.1:路由器的配置

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

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

image-20230320191410586

2:用户登录/退出模块

相关技术点

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

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

2.1:登录token原理分析

image-20230320183611304

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

2.2:登录login函数逻辑代码

image-20230320190337718

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

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

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

2.3:路由守卫

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

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

})

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

表单验证

image-20230320185612663

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

<!-- 表单部分 -->
      <el-form ref="loginFormRef" :model="loginForm" label-width="0px" :rules="loginFormRules" class="login_form">
        <!-- 用户名 -->
        <el-form-item prop="username">
          <el-input v-model="loginForm.username" prefix-icon="el-icon-user"></el-input>
        </el-form-item>
        <!-- 密码 -->
        <el-form-item prop="password">
          <el-input v-model="loginForm.password" type="password" prefix-icon="el-icon-lock"></el-input>
        </el-form-item>
        <!-- 按钮 -->
        <el-form-item class="login_form_login">
          <el-button type="primary" @click="login">登录</el-button>
          <el-button type="info" @click="reset">重置</el-button>
        </el-form-item>
      </el-form>
      //登录时的校验对象
      loginFormRules: {
        username: [
          { required: true, message: '请输入登录账户', trigger: 'blur' },
          {
            min: 3,
            max: 10,
            message: '长度在 3 到 10 个字符',
            trigger: 'blur',
          },
        ],
        password: [
          { required: true, message: '请输入登录密码', trigger: 'blur' },
          {
            min: 3,
            max: 15,
            message: '长度在 3 到 15 个字符',
            trigger: 'blur',
          },
        ],
      },

表单重置

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

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

2.5:退出登录

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

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

3:用户管理模块

3.1:Element-ui侧边栏

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

 <!-- 修改折叠展开的宽度样式 -->
      <el-aside :width="closeValue ? '64px' : '200px' ">
        <!-- 左侧菜单导航栏 -->
        <el-menu :default-active="activePath" class="el-menu-vertical-demo" unique-opened :collapse="closeValue" :collapse-transition="false" router>
          <div class="size" @click="menuClose">| | |</div>
          <!-- 一级菜单导航 -->
          <el-submenu :index="item.id + ''" v-for="item in menuList" :key="item.id">
            <template slot="title">
              <i :class="iconList[item.id]"></i>
              <span>{{item.authName}}</span>
            </template>
            <!-- 二级菜单导航 -->
            <el-menu-item :index="'/' + subitem.path" v-for="subitem in item.children" :key="subitem.id" @click="saveNavState('/' + subitem.path)">
              <i class="el-icon-menu"></i>
              <span>{{subitem.authName}}</span>
            </el-menu-item>
          </el-submenu>
        </el-menu>
      </el-aside>
  1. 在el-menu标签中,:default-active=""属性值是激活菜单的值,并没有设死,我们数据绑定到data中的activePath中
  2. 在el-submenu标签中,:index值是通过父级的v-for属性将menuList的所有item通过l插值语法(两个花括号)显示到模板上,需要注意的一点是,v-for属性同时需要,key属性的同时存在,否则会报错

image-20230320192717138

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

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

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

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

3.2:作用域插槽

image-20230320193927956

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

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

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

          <!-- 使用插槽来实现切换效果 -->
          <template slot-scope="scope">
            <el-switch v-model="scope.row.mg_state" @change="userStateChange(scope.row)">
            </el-switch>
          </template>
 //监听用户状态修改的事件
    async userStateChange(userInfo) {
      // console.log(userInfo)
      const { data: res } = await this.$http.put(
        `users/${userInfo.id}/state/${userInfo.mg_state}`
      )
      if (res.meta.status !== 200) {
        userInfo.mg_state = !userInfo.mg_state
        this.$message.error('更新用户状态失败!')
      }
      this.$message.success('更新用户状态成功!')
    },

3.3:Pagination 分页

image-20230320194654418

还是利用element-ui组件实现

      <!-- 分页区域 -->
      <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="queryInfo.pagenum" :page-sizes="[1, 2, 5, 10]" :page-size="queryInfo.pagesize" layout="total, sizes, prev, pager, next, jumper" :total="total">
      </el-pagination>

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

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

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

3.4:dialog对话框

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

逻辑如下:

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

image-20230320203453385
html结构

    <!-- 修改用户信息区域 -->
    <el-dialog title="修改用户信息" :visible.sync="editDialogVisible" width="50%" @close="editFormClose">
      <el-form :model="editForm" :rules="editFormRules" ref="editFormRef" label-width="100px">
        <!-- 1.用户名 -->
        <el-form-item label="用户名">
          <el-input v-model="editForm.username" disabled></el-input>
        </el-form-item>
        <!-- 2.邮箱 -->
        <el-form-item label="邮箱" prop="email">
          <el-input v-model="editForm.email"></el-input>
        </el-form-item>
        <!-- 3. 手机号码 -->
        <el-form-item label="手机号" prop="mobile">
          <el-input v-model="editForm.mobile"></el-input>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="editDialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="editUser">确 定</el-button>
      </span>
    </el-dialog>

编辑按钮结构

<!-- 修改按钮 -->
<el-button type="primary" icon="el-icon-edit" circle @click="showEditDialog(scope.row.id)"></el-button>

点击确定修改信息逻辑

//修改用户
    editUser() {
      //预校验
      this.$refs.editFormRef.validate(async (valid) => {
        if (!valid) return
        //发起修改用户信息请求
        const { data: res } = await this.$http.put(
          'users/' + this.editForm.id,
          {
            email: this.editForm.email,
            mobile: this.editForm.mobile,
          }
        )
        if (res.meta.status != 200) {
          return this.$message.error('更新用户信息失败!')
        }

        //关闭对话框
        this.editDialogVisible = false
        //刷新数据列表
        this.getUserList()
        //提示修改成功
        this.$message.success('更新用户信息成功!')
      })
    },

4:权限管理模块

4.1:展开表格列

image-20230320204715728

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

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

html结构

        <!-- 展开列 -->
        <el-table-column type="expand">
          <!-- 使用作用域插槽 -->
          <template slot-scope="scope">
            <el-row :class="['bottom' , i1 === 0 ? 'top' : '','vcenter']" v-for="(item1 ,i1) in scope.row.children" :key="item1.id">
              <!-- 一级权限渲染 -->
              <el-col :span="5">
                <el-tag closable @close="removeRightById(scope.row , item1.id)">{{item1.authName}}</el-tag>
                <i class="el-icon-caret-right"></i>
              </el-col>
              <!-- 渲染二级和三级权限 -->
              <el-col :span="19">
                <!-- 通过for循环 嵌套渲染二级权限 -->
                <el-row :class="[i2 === 0 ? '' : 'top','vcenter']" v-for="(item2 , i2) in item1.children" :key="item2.id">
                  <el-col :span="6">
                    <el-tag type="success" closable @close="removeRightById(scope.row , item2.id)">{{item2.authName}}</el-tag>
                    <i class="el-icon-caret-right"></i>
                  </el-col>
                  <el-col :span="18">
                    <el-tag type="warning" v-for="(item3) in item2.children" :key="item3.id " closable @close="removeRightById(scope.row , item3.id)">{{item3.authName}}</el-tag>
                  </el-col>
                </el-row>
              </el-col>
            </el-row>
            <el-row :span="19"></el-row>
          </template>
        </el-table-column>

removeRightById()函数

  1. 利用了confirm弹窗组件,全局挂载,promise对象,需要.then().catch()来解析
  2. 发送delete请求的参数,利用到了es6模板字符串用法
 //删除权限
    async removeRightById(role, roleId) {
      //弹框提示用户是否要删除
      const confirmResult = await this.$confirm(
        '此操作将永久删除该文件, 是否继续?',
        '提示',
        {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning',
        }
      ).catch((err) => err)

      if (confirmResult !== 'confirm') {
        return this.$message.info('取消了删除!')
      } else {
        //向服务器发送请求删除
        const { data: res } = await this.$http.delete(
          `roles/${role.id}/rights/${roleId}`
        )

        if (res.meta.status !== 200) {
          return this.$message.error('删除权限失败!')
        } else {
          role.children = res.data
        }
      }
    },

5:商品管理模块

5.1:级联选择器

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

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

image-20230320210758628
html结构

        <el-col>
          <span>选择商品分类:</span>
          <!-- 选择分类的级联选择框 -->
          <el-cascader v-model="selectedCateKeys" :options="cateList" @change="handleChange" :props="cateProps"></el-cascader>
        </el-col>

@change事件函数

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

5.2:tabs标签页

tabs标签页

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

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

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

image-20230320211133719

结构

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

getParamsData()函数

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

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

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

5.3: Tree 树形控件

image-20230320212617593

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

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

结构

      <tree-table style="margin-top: 15px" :data="catelist" :columns="columns" :selection-type="false" :expand-type="false" show-index index-text="#" border>
        <!-- 是否有效插槽 -->
        <template slot="isok" slot-scope="scope">
          <i class="el-icon-circle-check" v-if="scope.row.cat_deleted === false" style="color: lightgreen"></i>
          <i class="el-icon-circle-close" v-else style="color:red"></i>
        </template>
        <!-- 排序插槽 -->
        <template slot="order" slot-scope="scope">
          <el-tag v-if="scope.row.cat_level === 0">一级</el-tag>
          <el-tag type="success" v-else-if="scope.row.cat_level === 1">二级</el-tag>
          <el-tag type="warning" v-else>三级</el-tag>
        </template>
        <!-- 操作插槽 -->
        <template slot="opt" slot-scope="scope">
          <!-- 编辑 -->
          <el-button type="primary" icon="el-icon-edit" size="mini" @click="showEditDialog(scope.row.cat_id)">编辑</el-button>
          <!-- 删除 -->
          <el-button type="danger" icon="el-icon-delete" size="mini" @click="removeUserById(scope.row.cat_id)">删除</el-button>
        </template>
      </tree-table>

5.4:添加商品信息模块

image-20230320213254889

5.4.1:el-steps步骤展示信息

image-20230320213348056

      <!-- 要将string转换为number , -0 -->
      <el-steps :space="200" :active="activeIndex - 0" finish-status="success" align-center>
        <el-step title="基本信息"></el-step>
        <el-step title="商品参数"></el-step>
        <el-step title="商品属性"></el-step>
        <el-step title="商品图片"></el-step>
        <el-step title="商品内容"></el-step>
        <el-step title="完成"></el-step>
      </el-steps>
5.4.2:el-tabs左侧标签页

image-20230320213546231

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

结构

  <el-tabs v-model="activeIndex" :tab-position="'left'" :before-leave="beforeTabLeave" @tab-click="tabClicked">

@tab-click函数

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

image-20230320214012692

 <el-tab-pane label="商品图片" name="3">
            <!-- 上传图片 -->
            <!-- action表示图片要上传到的后台API地址 -->
            <el-upload class="upload-demo" action="http://43.143.0.76:8889/api/private/v1/upload" :on-preview="handlePreview" :on-remove="handleRemove" list-type="picture" :headers="headerObj" :on-success="handleSuccess">
              <el-button size="small" type="primary">点击上传</el-button>
            </el-upload>
          </el-tab-pane>

handlePreview()图片预览函数

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

handlePreview()图片移除函数

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

6:数据统计模块

6.1:echarts数据报表

image-20230320214618624

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

6.2:NProgress的使用

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

  1. 导入NProgress包
  2. 配合axios请求拦截器使用
//导入NProgress包
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'

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

三:总结

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

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

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

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

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

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

加油吧!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/2215.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

精心整理前端主流框架学习路径

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 前端主流框架 前端框架指的是用于构建Web前端应用程序的框架&#xff0c;使用框架进行前端开发带来以下显著优势&#xff1a; 提高开发效率&#xff1a;前端框架提供了现成的…

STM32的CAN总线调试经验分享

相关文章 CAN总线简易入门教程 CAN总线显性电平和隐性电平详解 STM32的CAN总线调试经验分享 文章目录相关文章背景CAN总线CAN控制器CAN收发器调试过程硬件排查CAN分析仪芯片CAN控制器调试总结背景 最近负责的一个项目用的主控芯片是STM32F407IGT6&#xff0c;需要和几个电机控…

DWF文件怎么用CAD打开?DWF输入CAD步骤

DWF是一种开放、安全的文件格式&#xff0c;它可以将丰富的设计数据高效率地分发给需要查看、评审或打印这些数据的任何人。那么&#xff0c;DWF文件如何打开呢&#xff1f;下面就和小编一起来了解一下DWF输入浩辰CAD软件中的具体操作步骤吧&#xff01; DWF输入CAD中步骤&…

安装CentOS系统

打开 Oracle VM VirtualBox 点击新建 输入名称 点击下一步 点击下一步 点击创建 点击下一步 点击下一步 分配30G硬盘 点击创建 创建成功 点击启动按钮 选择 CentOS 系统 iso 镜像文件 点击启动 按键盘方向键 “上键”&#xff0c;选择第一项 按键盘回车键&#xff0c;然后等待 …

QT搭建MQTT开发环境

QT搭建MQTT开发环境 第一步、明确安装的QT版本 注意&#xff1a; 从QT5.15.0版本开始&#xff0c;官方不再提供离线版安装包&#xff0c;除非你充钱买商业版。 而在这里我使用的QT版本为5.15.2&#xff0c;在线安装了好久才弄好&#xff0c;还是建议使用离线安装的版本 在这里…

代码随想录复习——单调栈篇 每日温度 下一个更大元素12 接雨水 柱状图中最大的矩形

739.每日温度 每日温度 暴力解法双指针 def dailyTemperatures(self, temperatures: List[int]) -> List[int]:n len(temperatures)res [0] * nfor i in range(n):for j in range(i,n):if temperatures[j] < temperatures[i]: continueelse: res[i] j-ibreakreturn …

pytorch 计算混淆矩阵

混淆矩阵是评估模型结果的一种指标 用来判断分类模型的好坏 预测对了 为对角线 还可以通过矩阵的上下角发现哪些容易出错 从这个 矩阵出发 可以得到 acc &#xff01; precision recall 特异度&#xff1f; 目标检测01笔记AP mAP recall precision是什么 查全率是什么 查准率…

【K8S系列】深入解析Pod对象(一)

目录 序言 1.问题引入 1.1 问题描述 2 问题解答 2.1 pod 属性 2.1.1 NodeSelector 2.1.2 HostAliases 2.1.3 shareProcessNamespace 2.1.4 NodeName 2.1.5 其他pod属性 2.2 容器属性 2.2.1 ImagePullPolicy 2.2.2 Lifecycle 3 总结 4. 投票 序言 任何一件事情&am…

一文读懂强化学习!

一.了解强化学习1.1基本概念强化学习是考虑智能体&#xff08;Agent&#xff09;与环境&#xff08;Environment&#xff09;的交互问题&#xff1a;智能体处在一个环境中&#xff0c;每个状态为智能体对当前环境的感知&#xff1b;智能体只能通过动作来影响环境&#xff0c;当…

空间信息智能应用团队研究成果介绍及人才引进

目录1、多平台移动测量技术1.1 车载移动测量系统1.2 机载移动测量系统2、数据处理与应用技术研究2.1 点云与影像融合2.2 点云配准与拼接2.3 点云滤波与分类2.4 道路矢量地图提取2.5 道路三维自动建模2.6 道路路面三维病害分析2.7 多期点云三维变形分析2.8 地表覆盖遥感监测分析…

ChatGPT在安全研究领域的应用实践

引言ChatGPT是一个人工智能技术驱动的自然语言处理工具&#xff0c;它能够通过理解和学习人类的语言来进行对话&#xff0c;并能进行连续对话。目前ChatGPT已经官方已经更新模型到4.0版本&#xff0c;宣称它是“最先进的系统&#xff0c;能生产更安全和更有用的回复”。当前使用…

wandb:可视化和超参数寻优

参考博客&#xff1a;https://zhuanlan.zhihu.com/p/591047340 1、注册账号 首先&#xff0c;去wandb官网注册一个账号&#xff0c;选择个人使用即可&#xff08;根据个人需要&#xff09; 然后&#xff0c;登录得到一个API key 2、wandb使用 &#xff08;1&#xff09;命令…

Spring框架学习--xml和Annotation方式实现IOC

AnnotationXml的spring-IOC和全Annotation的spring-IOC 文章目录AnnotationXml的spring-IOC和全Annotation的spring-IOC学习目标第二章 基于AnnotationXml的spring-IOC【重点】1、annotationxml【入门案例】(5)【1】目标【2】实现【2.1】创建项目【2.3】改写AccountDaoImpl【2.…

刷题记录(2023.3.14 - 2023.3.18)

[第五空间 2021]EasyCleanup 临时文件包含考点 分析源码&#xff0c;两个特殊的点&#xff0c;一个是 eval&#xff0c;另一个是 include eval 经过了 strlen filter checkNums 三个函数 include 经过了 strlen filter 两个函数 filter 检测是否包含特定的关键字或字符 fun…

【数据结构与算法】用栈实现队列

文章目录&#x1f63b;前言如何用栈实现队列&#xff1f;用栈实现队列整体的实现代码&#x1f63c;写在最后&#x1f63b;前言 &#x1f61d;上一章我们用队列实现了一个栈&#xff08;-> 传送门 <-&#xff09;&#xff0c;而这一章就带大家用栈实现一个队列。 &#x1…

< 每日算法:在排序数组中查找元素的第一个和最后一个位置 >

每日算法 - JavaScript解析&#xff1a;在排序数组中查找元素的第一个和最后一个位置 一、任务描述&#xff1a;> 示例 1> 示例 2> 示例 3二、题意解析三、解决方案&#xff1a;往期内容 &#x1f4a8;一、任务描述&#xff1a; 给你一个按照非递减顺序排列的整数数组…

C++基础算法③——排序算法(选择、冒泡附完整代码)

排序算法 1、选择排序 2、冒泡排序 1、选择排序 基本思想&#xff1a;从头至尾扫描序列&#xff0c;每一趟从待排序元素中找出最小(最大)的一个元素值&#xff0c;然后与第一个元素交换值&#xff0c;接着从剩下的元素中继续这种选择和交换方式&#xff0c;最终得到一个有序…

冲击蓝桥杯-时间问题(必考)

目录 前言&#xff1a; 一、时间问题 二、使用步骤 1、考察小时&#xff0c;分以及秒的使用、 2、判断日期是否合法 3、遍历日期 4、推算星期几 总结 前言&#xff1a; 时间问题可以说是蓝桥杯&#xff0c;最喜欢考的问题了,因为时间问题不涉及到算法和一些复杂的知识&#xf…

第十四届蓝桥杯三月真题刷题训练——第 18 天

目录 第 1 题&#xff1a;排列字母 问题描述 运行限制 代码&#xff1a; 第 2 题&#xff1a;GCD_数论 问题描述 输入格式 输出格式 样例输入 样例输出 评测用例规模与约定 运行限制 第 3 题&#xff1a;选数异或 第 4 题&#xff1a;背包与魔法 第 1 题&#x…

1649_Excel中删除重复的数据

全部学习汇总&#xff1a; GreyZhang/windows_skills: some skills when using windows system. (github.com) 长久时间的开发工作性质导致我对Windows生态下的很多工具没有一个深度的掌握。有时候&#xff0c;别说深度&#xff0c;可能最为浅显的操作都是不熟悉的。这个不仅仅…
最新文章