zoukankan      html  css  js  c++  java
  • 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 有事联系我
  • 相关阅读:
    Mayan游戏 (codevs 1136)题解
    虫食算 (codevs 1064)题解
    靶形数独 (codevs 1174)题解
    黑白棋游戏 (codevs 2743)题解
    神经网络 (codevs 1088) 题解
    The Rotation Game (POJ 2286) 题解
    倒水问题 (codevs 1226) 题解
    银河英雄传说 (codevs 1540) 题解
    生日蛋糕 (codevs 1710) 题解
    第一章 1.11 高阶函数
  • 原文地址:https://www.cnblogs.com/wpbing/p/14314470.html
Copyright © 2011-2022 走看看