• vue前端的使用


    简单梳理一下这次vue项目的知识点

    utils

    request.js

    主要是封装axios模块用得
    非组件模块可以这样加载使用 element 的 message 提示组件
    import { Message } from 'element-ui'

    // 创建一个 axios 实例,说白了就是复制了一个 axios
    // 我们通过这个实例去发请求,把需要的配置配置给这个实例来处理
    const request = axios.create({
      baseURL: 'http://ttapi.research.itcast.cn/', // 请求的基础路径
    
      // 定义后端返回的原始数据的处理
      // 参数 data 就是后端返回的原始数据(未经处理的 JSON 格式字符串)
      transformResponse: [function (data) {
        // Do whatever you want to transform the data
        // console.log(data)
    
        // 后端返回的数据可能不是 JSON 格式字符串
        // 如果不是的话,那么 JSONbig.parse 调用就会报错
        // 所以我们使用 try-catch 来捕获异常,处理异常的发生
        try {
          // 如果转换成功,则直接把结果返回
          return JSONbig.parse(data)
        } catch (err) {
          console.log('转换失败', err)
          // 如果转换失败了,则进入这里
          // 我们在这里把数据原封不动的直接返回给请求使用
          return data
        }
    
        // axios 默认在内部使用 JSON.parse 来转换处理原始数据
        // return JSON.parse(data)
      }]
    })
    

    在这里面可以设置请求拦截器,响应拦截器
    请求拦截器可以对一些请求进行预处理,比如加入token啥的

    // 请求拦截器
    request.interceptors.request.use(
      // 任何所有请求会经过这里
      // config 是当前请求相关的配置信息对象
      // config 是可以修改的
      function (config) {
        const user = JSON.parse(window.localStorage.getItem('user'))
    
        // 如果有登录用户信息,则统一设置 token
        if (user) {
          config.headers.Authorization = `Bearer ${user.token}`
        }
    
        // 然后我们就可以在允许请求出去之前定制统一业务功能处理
        // 例如:统一的设置 token
    
        // 当这里 return config 之后请求在会真正的发出去
        return config
      },
      // 请求失败,会经过这里
      function (error) {
        return Promise.reject(error)
      }
    )
    

    api

    permission.js

    role.js

    user.js

    都会从utils中导入request,为不同的请求设置访问后端的端口号,请求方法添加参数
    然后再export出去,让别的模块调用

    router

    index.js

    路由是设置访问的时候会到达哪个自己定义的组件上去,也可以做一些权限控制,同时也可以形成父子组件的形式。

    import Vue from 'vue'
    import VueRouter from 'vue-router'
    
    // 在 VueCLI 创建的项目中 @ 表示 src 目录
    // 它是 src 目录的路径别名
    // 好处:它不受当前文件路径影响
    // 注意:@ 就是 src 路径,后面别忘了写那个斜杠
    // 使用建议:如果加载的资源路径就在当前目录下,那就正常写
    //       如果需要进行父级路径查找的都使用 @
    import Login from '@/views/login/'
    import Layout from '@/views/layout/'
    import Home from '@/views/root'
    import User from '@/views/user'
    import Permission from '@/views/permission'
    
    // 非组件模块可以这样加载使用 element 的 message 提示组件
    // 注意import要写在Vue.use(VueRouter)前面
    import { Message } from 'element-ui'
    Vue.use(VueRouter)
    
    // 路由配置表
    const routes = [
      {
        path: '/login',
        name: 'login',
        component: Login
      },
      {
        path: '/',
        // 命名路由 layout 有一个默认子路由,这个名字没有意义,所以警告
        // 正确的做法是:如果有默认子路由,就不要给父路由起名字了
        // name: 'layout',
        component: Layout,
        children: [
          {
            path: '', // path 为空,会作为默认子路由渲染
            // 路由的名字是干啥的?
            // 参考:https://gitee.com/lipengzhou/toutiao-publish-admin/issues/I1F1BA
            name: 'home',
            component: Home
          },
          {
            path: '/user',
            name: 'user',
            component: User
          },
          {
            path: '/permission',
            name: 'permission',
            component: Permission
          }
        ]
      }
    ]
    
    const router = new VueRouter({
      routes
    })
    
    // 路由导航守卫(拦截器)的作用就是控制页面的访问状态
    // beforeEach 是全局前置守卫,任何页面的访问都要经过这里
    // 路由导航守卫:说白了所有页面的导航都会经过这里
    // 守卫页面的导航的
    // to:要去的路由信息
    // from:来自哪里的路由信息
    // next:放行方法
    router.beforeEach((to, from, next) => {
      // 如果要访问的页面不是 /login,校验登录状态
      // 如果没有登录,则跳转到登录页面
      // 如果登录了,则允许通过
      // 允许通过
      // next()
    
      var user = JSON.parse(window.localStorage.getItem('user'))
      if (to.path !== '/login') {
        if (user) {
          // 校验非登录页面的登录状态
          var roleNames = []
          var roleList = user.roleList
          for (var i = 0; i < roleList.length; i++) {
            roleNames.push(roleList[i].roleName)
            console.log(roleNames)
          }
          if (to.path === '/permission') {
            if (roleNames.indexOf('permissionControl') !== -1) {
              next()
            } else {
              Message.error('不好意西,没有权限')
              next('/')
            }
          } else {
            next()
          }
        } else {
          // 没有登录,跳转到登录页面
          next('/login')
        }
      } else {
        // 登录页面,正常允许通过
        next()
      }
    })
    
    // 我们在组件中使用的 this.$router 其实就是这个模块中的 router
    export default router
    
    

    Views

    login/index.vue

    登录组件

    表单

    el-form 表单组件
    每个表单项都必须使用 el-form-item 组件包裹

    表单提交可以设置:loading 让他转起来等待

    配置 Form 表单验证:

    1. 必须给 el-from 组件绑定 model 为表单数据对象
    2. 给需要验证的表单项 el-form-item 绑定 prop 属性
      注意:prop 属性需要指定表单对象中的数据名称
    3. 通过 el-from 组件的 rules 属性配置验证规则
      如果内置的验证规则不满足,也可以自定义验证规则

    手动触发表单验证:

    1. 给 el-form 设置 ref 起个名字(随便起名,不要重复即可)
    2. 通过 ref 获取 el-form 组件,调用组件的 validate 进行验证
    formRules: { // 表单验证规则配置
            // 要验证的数据名称:规则列表[]
            account: [
              { required: true, message: '请输入手机号', trigger: 'change' },
              { pattern: /^[0-9]*$/, message: '请输入正确的号码格式', trigger: 'change' }
            ],
            password: [
              { required: true, message: '验证码不能为空', trigger: 'change' },
              { pattern: /^[0-9]*$/, message: '请输入正确密码格式' }
            ],
            agree: [
              {
                // 自定义校验规则: 因为复选框里面没有这个required,需要自己定义规则,所以需要使用validator
                // 验证通过:callback()
                // 验证失败:callback(new Error('错误消息'))
                // value为是否勾选上,数据值
                validator: (rule, value, callback) => {
                  if (value) {
                    callback()
                  } else {
                    callback(new Error('请同意用户协议'))
                  }
                },
                // message: '请勾选同意用户协议',
                trigger: 'change'
              }
    

    是否同意协议验证要在点击登录的时候进行验证,所以开启手动验证

    methods: {
        onLogin () {
          // 获取表单数据(根据接口要求绑定数据)
          // const user = this.user
          // 表单验证
          // validate 方法是异步的 参数可以是两个,一个valid是验证的结果,另一个是error,缺失的项
          this.$refs['login-form'].validate((valid) => {
            // 如果表单验证失败,停止请求提交
            if (!valid) {
              return
            }
            // 验证通过,请求登录
            this.login()
          })
        }
    }
    

    登录成功了,把usre放入本地存储中

    window.localStorage.setItem('user', JSON.stringify(res.data.data))

    并且关闭转圈圈,简单提示一下

    this.$message({
      message: '登录成功',
      type: 'success'
    })
    

    layout/index.vue

    layout/components/asside.vue

    先说一下传递参数的问题,在index.vue,点击左上角,然后asside组件向右滑动,再点击划出来
    首先index页面点击图标触发事件,@click="isCollapse = !isCollapse",因为asside组件双向绑定了isCollapse值

    <app-aside class="aside-menu" :is-collapse="isCollapse"/>

    所以在asside.vue中

    export default {
      name: 'AppAside', // 注意这个名字,父组件可以使用AppAside,也可以app-aside
      components: {},
      props: ['is-collapse'], //  注意这里传过来的参数是is-collapse,但是要使用的话写的是isCollapse
      data () {
        return {
          // isCollapse: true
        }
      },
      computed: {},
      watch: {},
      created () {},
      mounted () {},
      methods: {}
    }
    

    然后动态的改变图标样式

    
              <!--
                class 样式处理
                  {
                    css类名: 布尔值
                  }
                  true:作用类名
                  false:不作用类名
               -->
              <i
                :class="{
                  'el-icon-s-fold': isCollapse,
                  'el-icon-s-unfold': !isCollapse
                }"
                @click="isCollapse = !isCollapse"
              ></i>
    

    一些注意事项

    <el-dropdown-item>设置</el-dropdown-item>
    <!--
      组件默认是不识别原生事件的,除非内部做了处理
      https://cn.vuejs.org/v2/guide/components-custom-events.html#%E5%B0%86%E5%8E%9F%E7%94%9F%E4%BA%8B%E4%BB%B6%E7%BB%91%E5%AE%9A%E5%88%B0%E7%BB%84%E4%BB%B6
     -->
    <el-dropdown-item
      @click.native="onLogout"
    >退出</el-dropdown-item>
    

    permission/index.vue

    主要有个树形表格的使用,主要在el-table中有个
    :tree-props="{children: 'children', hasChildren: 'hasChildren'}"

    对于属性表格数据的处理,还是递归来写的

    getTreeData (permissionData, pid) {
      var res = []
      for (var i = 0; i < permissionData.length; i++) {
        var node = permissionData[i]
        if (pid !== null && node.pid === pid) {
          node.children = this.getTreeData(permissionData, node.id)
          res.push(node)
        }
      }
      return res
    }
    

    .sync用法

    当子组件需要更新 title 的值时,它需要显式地触发一个更新事件:
    this.$emit('update:title', newValue)
    这样title的属性在子组件内部更新,父组件也能感知的到,实现了“双向绑定”。

    Table 表格组件

    1. 把需要展示的数组列表数据绑定给 table 组件的 data 属性
      注意:你不用去 v-for 遍历,它自己会遍历

    2. 设计表格列 el-table-column
      width 可以设定表格列的宽度
      label 可以设定列的标题
      prop 用来设定要渲染的列表项数据字段,只能展示文本

    3. 表格列默认只能渲染普通文本,如果需要展示其它内容,例如放个按钮啊、放个图片啊,那就需要自定义表格列模板了:https://element.eleme.cn/#/zh-CN/component/table#zi-ding-yi-lie-mo-ban

    <el-table-column
      prop="userName"
      label="用户名">
    <template slot-scope="scope">
      <el-popover
        placement="top-start"
        title="tips"
        width="200"
        trigger="hover"
        content="点击查看详情">
        <el-tag slot="reference" @click="getDetail1(scope.row.id)" style=" 100px">{{ scope.row.userName }}</el-tag>
      </el-popover>
    </template>
    </el-table-column>
    
    <el-table-column
              label="操作">
              <!-- 如果需要自定义表格列模板,则把需要自定义的内容放到 template 里面 -->
              <template slot-scope="scope">
                <!-- Form -->
                <el-button
                  circle
                  icon="el-icon-edit"
                  type="primary"
                  @click="getDetail(scope.row.id)"
                ></el-button>
                <el-dialog :title="addOrUpdate" :visible.sync="dialogFormVisible" :append-to-body="true" >
                  <el-form :model="form" :rules="rules" style=" 450px">
                    <el-form-item label="用户名" :label-width="formLabelWidth" prop="userName" >
                      <el-input v-model="form.userName" autocomplete="off" :disabled="inputFlag"></el-input>
                    </el-form-item>
                    <el-form-item label="账号" :label-width="formLabelWidth" prop="account">
                      <el-input v-model="form.account" autocomplete="off" :disabled="inputFlag"></el-input>
                    </el-form-item>
                    <el-form-item label="密码" :label-width="formLabelWidth" prop="password">
                      <el-input v-model="form.password" autocomplete="off" :disabled="inputFlag"></el-input>
                    </el-form-item>
                    <el-form-item label="角色" style="margin-left: 80px">
                      <el-checkbox-group v-model="checkList" :disabled="inputFlag">
                      <el-checkbox v-for="(role,index) in roleList"
                        :key="index"
                        :label="role.id"
                        :value="role.id">{{ role.roleName }}
                      </el-checkbox>
                      </el-checkbox-group>
                    </el-form-item>
                  </el-form>
                  <div slot="footer" class="dialog-footer">
                    <el-button @click="dialogFormVisible = false">取 消</el-button>
                    <el-button type="primary" @click="toUpdate()">确 定</el-button>
                  </div>
                </el-dialog>
                <el-button
                  style="margin-left: 20px"
                  type="danger"
                  icon="el-icon-delete"
                  circle
                  @click="toDelete(scope.row.id)"
                ></el-button>
              </template>
            </el-table-column>
    

    数据分页

    <!-- /数据列表 -->
    
          <!-- 列表分页 -->
          <!--
            total 用来设定总数据的条数
            它默认按照 10 条每页计算总页码
            page-size 每页显示条目个数,支持 .sync 修饰符,默认每页 10 条
    
            90 3 90 / 3 = 30
           -->
          <el-pagination
            layout="prev, pager, next"
            background
            :total="totals"
            :page-size="limit"
            :disabled="loading"
            :current-page.sync="page"
            @current-change="onCurrentChange"
          />
          <!--  onCurrentChange为自己定义的触发页数改变时的方法-->
          <!-- /列表分页 -->
    

    路由的name

    如果你要使用 JavaScript 跳转到这个动态路由,则你需要这样写:

    this.$router.push('/user/' + 用户ID)
    如果是在模板中进行路由导航,那就是这样的:

    User
    以上的方式虽然简单粗暴,但是通过拼接字符串得到完整路由路径进行导航不太直观。

    所以更好的方式就是给路由配置对象起一个名字,就像下面这样,这个 name 和 path 没有任何关系,它就是一个代号,需要注意的是路由的 name 不能重复。

    const router = new VueRouter({
      routes: [
        {
          path: '/user/:userId',
          name: 'user',
          component: User
        }
      ]
    })
    

    现在你可以这样处理路由导航:

    router.push({ name: 'user', params: { userId: 123 }})
    User
    所以结论就是:无论是否需要使用路由的 name,都建议给它写上,当你需要的时候就非常有用了,这是一个建议的做法。

    后端

    对于后端,最好都要配上RestController,然后POST或者GET要和前端的保持一致,还有参数问题,要加上RequestBody 或者RequestParam注解

    问题点1:

    如果Content-Type设置为“application/x-www-form-urlencoded;charset=UTF-8”无论是POST请求还是GET请求都是可以通过这种方式成功获取参数,但是如果前端POST请求中的body是Json对象的话,会报上述错误。

    请求中传JSON时设置的Content-Type 如果是application/json或者text/json时,JAVA中request.getParameter("")怎么也接收不到数据。这是因为,Tomcat的HttpServletRequest类的实现类为org.apache.catalina.connector.Request(实际上是org.apache.coyote.Request)。

    问题点2:

    当前端请求的Content-Type是Json时,可以用@RequestBody这个注解来解决。@RequestParam 底层是通过request.getParameter方式获得参数的,换句话说,@RequestParam 和request.getParameter是同一回事。因为使用request.getParameter()方式获取参数,可以处理get 方式中queryString的值,也可以处理post方式中 body data的值。所以,@RequestParam可以处理get 方式中queryString的值,也可以处理post方式中 body data的值。@RequestParam用来处理Content-Type: 为 application/x-www-form-urlencoded编码的内容,提交方式GET、POST。

    @RequestBody接受的是一个json对象的字符串,而不是Json对象,在请求时往往都是Json对象,用JSON.stringify(data)的方式就能将对象变成json字符串。

    总结:

    前端请求传Json对象则后端使用@RequestParam;

    前端请求传Json对象的字符串则后端使用@RequestBody。

    个人qq:835493858 有事联系我
  • 相关阅读:
    设计模式原则—依赖倒转原则(三)
    设计模式原则—单一职责原则(二)
    一步一个脚印学习WCF系列之WCF基础术语—契约的名称与命名空间(二)
    命名规范汇总文档供下载
    BCM平台全自动刷机软件,TFTP正式版1.62隆重发布,增加固件记忆功能
    WayOs内置免拉黑,就是把免拉黑程序集成在WayOs内部,增加守护进程及修改访问参数立即生效
    WayOs帐号获取保存工具增加提交的功能,WayOs扩展WAN口工具1.6发布增加网卡和VLAN混合组网功能
    好奇:WayOs破解、OEM、修复、打包等工具大全,满足大家的好奇心发下截图
    WayOs PPPoE群集服务器:自动同步用户信息,包括增加,更新,同步操作!保证多台服务器数据同步
    WayOs全面支持安卓手机,防二级路由开关、充值卡到期自动跳转页面功能隆重发布
  • 原文地址:https://www.cnblogs.com/wpbing/p/14314470.html
Copyright © 2020-2023  润新知