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="width: 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="width: 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。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章