主頁 > 前端設計 > vueAdmin前端學習筆記

vueAdmin前端學習筆記

2021-09-23 13:23:27 前端設計

vueAdmin前端學習筆記

  • 1.安裝VUE專案
    • 第一步:vue-ui
    • 第二步:創建
    • 第三步:詳情
    • 第四步:選擇手動
    • 第五步:配置
    • 第六步:點擊創建專案
    • 第七步:啟動專案
    • 第八步:vsCode啟動專案
  • 2.安裝element-ui
  • 3.洗掉不必要的頁面
    • 3.1洗掉/viwes/Home.vue
    • 3.2洗掉App.vue
  • 4.安裝axios、qs、mockjs
    • 4.1安裝axios
    • 4.2安裝qs
    • 4.3安裝mockjs
  • 5.頁面路由
    • 5.1洗掉頁面
    • 5.2創建頁面
    • 5.3頁面router/index.js
  • 6.登錄頁面
    • 6.1頁面展示
    • 6.2在src/App.vue調整全域樣式
    • 6.3src/views/Login.vue
    • 6.4獲取驗證碼
      • 6.4.1安裝mockjs
      • 6.4.2創建mock.js
      • 6.4.3注冊
      • 6.4.4login頁面(重要)
      • 6.4.5頁面展示
    • 6.5登錄
      • 6.5.1使用mockjs模擬登錄
      • 6.5.2將token存到localStorage
      • 6.5.3發送login請求
      • 6.5.4測驗
  • 7.定義全域axios攔截器
    • 7.1在src/axios.js
    • 7.2在main.js全域引入
    • 7.3測驗
  • 8.index頁面撰寫
    • 8.1頁面
    • 8.2效果圖
  • 9.抽取代碼
    • 9.1抽取后的導航欄
    • 9.2抽取后的頭部
    • 9.3抽取后的Home
    • 9.4抽取后的index
    • 9.5抽取的router.js
    • 9.5抽取后的頁面
  • 10.創建頁面
    • 10.1在views新建system/User.vue
    • 10.2在views新建system/Role.vue
    • 10.3在views新建system/Menuvue
    • 10.4在router.js添加
    • 10.4測驗
  • 11.導航欄系結頁面
  • 12修改頭像和用戶名
  • 13.個人中心
    • 13.1在views/system/新建UserCenter.vue
    • 13.2在router.js添加
    • 13.3修改SideHeader.vue
    • 13.4測驗頁面
  • 14退出
  • 15將導航改為動態獲取(可忽略)
    • 15.1主要修改
    • 15.2修改后SideMenu.vue的頁面
    • 15.3mock.js
    • 15.4修改后的導航欄
  • 16.將導航欄改為動態獲取
    • 16.1mock.js模擬后端回傳的資料
    • 16.2將選單/權限存放到全域
    • 16.3將menus.js注冊到src/store/index.js
    • 16.4修改route/index.js方法
    • 16.5修改/inc/SideMenu.vue
  • 17動態標簽頁
    • 17.1在/store/modules/menus.js添加
    • 17.2在inc/新建Tabs.vue
    • 17.3頁面使用
    • 17.4修改inc/SideMenu.vue
  • 18通過url地址跳轉
  • 19退出清空資訊
  • 20.菜單頁面
    • 20.1是文字不居中
    • 20.2mock.js模擬后端
    • 20.3選單頁面
    • 20.4頁面展示
  • 21角色頁面
    • 21.1mock.js模擬后端
    • 21.2views/system/Role.vue頁面
  • 22用戶頁面
    • 22.1mock.js模擬后端
    • 22.2views/system/User.vue頁面
  • 23前端權限控制
    • 23.1在src/main.js添加
    • 23.2使用權限
    • 23.2mock.js模擬回傳權限
    • 23.3其他
  • 24結束語

1.安裝VUE專案

第一步:vue-ui

# 安裝淘寶npm
npm install -g cnpm --registry=https://registry.npm.taobao.org
# vue-cli 安裝依賴包
cnpm install --g vue-cli
# 打開vue的可視化管理工具界面
vue ui

image-20210919093326836

第二步:創建

image-20210919093628392

第三步:詳情

image-20210919093834928

第四步:選擇手動

image-20210919094103363

第五步:配置

image-20210919094230394

第六步:點擊創建專案

image-20210919094343831

第七步:啟動專案

image-20210919094612998

第八步:vsCode啟動專案

npm run serve

2.安裝element-ui

npm install element-ui --save

然后我們打開專案src目錄下的main.js,引入element-ui依賴,

import Element from 'element-ui'
import "element-ui/lib/theme-chalk/index.css"

Vue.use(Element)

這樣我們就可以愉快得在官網上選擇組件復制代碼到我們專案中直接使用啦,

3.洗掉不必要的頁面

3.1洗掉/viwes/Home.vue

3.2洗掉App.vue

4.安裝axios、qs、mockjs

  • axios:一個基于 promise 的 HTTP 庫,類ajax

  • qs:查詢引數序列化和決議庫

  • mockjs:為我們生成隨機資料的工具庫

4.1安裝axios

npm install axios --save
# 然后同樣我們在main.js中全域引入axios
import axios from 'axios'
Vue.prototype.$axios = axios //全域使用

4.2安裝qs

npm install qs --save

4.3安裝mockjs

npm install mockjs --save-dev

5.頁面路由

學習視頻

5.1洗掉頁面

5.2創建頁面

1.創建Login.vue

<template>
    <div>登錄頁面</div>
</template>

<script>
export default {
    name: "Login"
}
</script>

<style scoped>

</style>

1.創建Home.vue

<template>
  <div>測驗主頁  </div>
</template>

<script> 

export default {
  name: 'Home',
 
}
</script>

<style>

</style>

5.3頁面router/index.js

import Home from '../views/Home.vue'
import Login from '../views/Login.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/login',
    name: 'Login',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    // component: () => import(/* webpackChunkName: "about" */ '../views/Login.vue')  方式一
    component: Login  //方式二
  }
]

6.登錄頁面

6.1頁面展示

6.2在src/App.vue調整全域樣式

我們先調整一下全域的樣式

將原頁面的style便簽洗掉,更換為一下的style便簽內容,下面是調整后的整個頁面

<template>
  <div id="app">
    <router-view/>
  </div>
</template>

<style>
html, body, #app {
  font-family: 'Helvetica Neue', 'Hiragino Sans GB', 'WenQuanYi Micro Hei', 'Microsoft Yahei', sans-serif;
  height: 100%;
  padding: 0;
  margin: 0;
  font-size: 15px;
}
</style>

6.3src/views/Login.vue

一下頁面是寫好后的頁面,還未發請求前

<template>
  <el-row style="height: 100%">
    <el-col :xl="6" :lg="7">
      <!-- 標題 -->
      <h2>歡迎來到VueAdmin管理界面</h2>
      <!-- 使用 el-image引入圖片 -->
      <el-image
          :src="require('@/assets/微信圖片_20210919105336.jpg')"
          style="height: 180px; width: 180px"
      ></el-image>
      <p>公眾號:MarkerHup</p>
      <p>掃描二維碼,回復</p>
    </el-col>
    <el-col :span="1">
      <!-- 分割線 -->
      <el-divider direction="vertical"></el-divider>
    </el-col>
    <el-col :xl="6" :lg="7">
      <!--
      :model="loginForm" 系結的表單
      :rules="rules"   校驗規則
      ref="loginForm"  注冊實體,可以通過  this.$refs[loginForm]獲取
      -->
      <el-form
          :model="loginForm"
          :rules="rules"
          ref="loginForm"
          label-width="80px"
          class="demo-loginForm"
      >
        <el-form-item label="用戶名" prop="userName" style="width: 380px">
          <el-input v-model="loginForm.userName"></el-input>
        </el-form-item>

        <el-form-item label="密碼" prop="passWord" style="width: 380px">
          <el-input v-model="loginForm.passWord"></el-input>
        </el-form-item>

        <el-form-item label="驗證碼" prop="code" style="width: 380px">
          <el-input
              v-model="loginForm.code"
              style="width: 170px; float: left"
          ></el-input>
          <el-image src="" class="captchaImg"></el-image>
        </el-form-item>

        <el-form-item>
          <el-button type="primary" @click="submitForm('loginForm')"
          >登錄
          </el-button>
          <el-button @click="resetForm('loginForm')">重置</el-button>
        </el-form-item>
      </el-form>
    </el-col>
  </el-row>
</template>

<script>
export default {
  name: "Login",

  data() {
    return {
      // 表單
      loginForm: {
        userName: "",
        passWord: "",
        code: "",
      },
      //   校驗
      rules: {
        userName: [
          {required: true, message: "請輸入用戶名", trigger: "blur"},
          {
            min: 3,
            max: 50,
            message: "長度在 3 到 50 個字符",
            trigger: "blur",
          },
        ],
        passWord: [
          {required: true, message: "請輸入密碼", trigger: "change"},
        ],

        code: [{required: true, message: "請輸入驗證碼", trigger: "change"}],
      },
    };
  },
  methods: {
    submitForm(formName) {
      this.$refs[formName].validate((valid) => {
        if (valid) {
          alert("submit!");
        } else {
          console.log("error submit!!");
          return false;
        }
      });
    },
    resetForm(formName) {
      this.$refs[formName].resetFields();
    },
  },
};
</script>

<style scoped>
/**
* el-row 的設定
*    background-color: #fafafa;   設定背景顏色
*    height: 100%;  設定高度,覆寫所有的高度
*    display: flex;    橫向居中
*    align-items: center; 上下居中
*    text-align: center; 文字居中
*/

.el-row {
  background-color: #fafafa;
  height: 100%;
  display: flex;
  align-items: center;
  text-align: center;
  justify-content: center;
}

/*.el-row {*/
/*  height: 100%;*/
/*  background-color: #fafafa;   !*設定背景顏色*!*/
/*  display: flex;*/
/*  justify-content: center;*/
/*  align-items: center;*/
/*  text-align: center;*/
/*}*/

/* 分割線的高度 */
.el-divider {
  height: 200px;
}

.captchaImg {
  /*靠左邊*/
  float: left;
  /*驗證碼偏移8px*/
  margin-left: 8px;
  /*圓角*/
  border-radius: 4px;
}
</style>

6.4獲取驗證碼

現在沒有后端,用mockjs模擬生成驗證碼

6.4.1安裝mockjs

npm install mockjs --save-dev

6.4.2創建mock.js

// 引入mockjsconst
const  Mock  = require('mockjs')
// 獲取 mock.Random 物件
// 參考:https://github.com/nuysoft/Mock/wiki/Mock.Random
const Random = Mock.Random

let  Result = {
    code: 200,
    msg: '操作成功',
    data: null
}
Mock.mock('/captcha','get',()=>{
    Result.data = {
        UUID: Random.string(32),  // 獲取一個32位的隨機字串,
        captchaImg: Random.dataImage('120x40','p7n5w')  //生成驗證碼為11111的base64圖片編碼
    }
    return Result
})

6.4.3注冊

// 在src/main,js
require("./mock.js")

6.4.4login頁面(重要)

  • 在methods添加請求驗證碼的方法
    // 獲取驗證碼
    getCaptCha() {
      this.$axios.get('/captcha').then(res => {
        debugger;
        this.loginForm.UUID = res.data.data.token;
        this.captchaImg = res.data.data.captchaImg;
      })
    }
  • 新增created在進入頁面時加載
  created() {
    this.getCaptCha();
  },
  • 新增引數

1.在loginForm里添加UUID,

2.在data里面添加captchaImg,

3.驗證碼展示

<el-image :src="captchaImg" class="captchaImg"></el-image>

6.4.5頁面展示

6.5登錄

6.5.1使用mockjs模擬登錄

在mock.js添加

Mock.mock('/login','post',()=>{    return Result})

6.5.2將token存到localStorage

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
    state: {
        token: ''
    },
    mutations: {
        SET_TOKEN: (state, token) => {
            state.token = token;
            localStorage.setItem("token", token)
        }
    },
    actions: {},
    modules: {}
})

6.5.3發送login請求

this.$axios.post('/login', this.loginForm).then(res => {
  const jwt = res.headers['authorization'];
  this.$store.commit("SET_TOKEN", jwt);
  this.$router.push("/index");
})

6.5.4測驗

7.定義全域axios攔截器

7.1在src/axios.js

import axios from "axios";import router from "@/router";import Element from "element-ui"axios.defaults.daseURL = "http://localhost:8081"const request = axios.create({    timeout: 5000,  //超時時間    headers: {        'Content-Type': 'application/json; charset=utf-8' //  回傳的JSON型別資料    }})// 請求的攔截,查看是否有tokenrequest.interceptors.request.use(config => {    config.headers['Authorization'] = localStorage.getItem("token") // 請求頭帶上token    return config})// 回傳的結果request.interceptors.response.use(response => {    let res = response.data;    console.log("response")    console.log(res)    if (res.code === 200) {        return response    } else {        Element.Message.error(res.msg ? res.msg : '系統例外!', {duration: 3 * 1000})        return Promise.reject(response.data.msg)    }}, error => {   // 例外的情況    console.log(error)    if (error.response.data) {        error.message = error.response.data.msg    }    // 401沒有權限    if (error.response.status === 401) {        router.push("/login")    }    Element.Message.error(error.message, {duration: 3 * 1000})    return Promise.reject(error)})export default request

7.2在main.js全域引入

7.3測驗

1.在mock.js中的login方法添加錯誤資訊

2.頁面測驗

8.index頁面撰寫

8.1頁面

在src/views/創建index.vue

<template>
  <el-container>
    <!--  側邊欄-->
    <el-aside width="200px">


      <el-menu
          default-active="2"
          class="el-menu-vertical-demo"
          @open="handleOpen"
          @close="handleClose"
          background-color="#545c64"
          text-color="#fff"
          active-text-color="#ffd04b">
        <router-link to="/index">
          <el-menu-item index="Index">
            <template slot="title"><i class="el-icon-s-home"></i> <span slot="title">首頁</span></template>
          </el-menu-item>
        </router-link>
        <el-submenu index="1">
          <template slot="title"><i class="el-icon-s-operation"></i> <span>系統管理</span></template>
          <el-menu-item index="1-1">
            <template slot="title"><i class="el-icon-s-custom"></i> <span slot="title">用戶管理</span></template>
          </el-menu-item>
          <el-menu-item index="1-2">
            <template slot="title"><i class="el-icon-rank"></i> <span slot="title">角色管理</span></template>
          </el-menu-item>
          <el-menu-item index="1-3">
            <template slot="title"><i class="el-icon-menu"></i> <span slot="title">選單管理</span></template>
          </el-menu-item>
        </el-submenu>
        <el-submenu index="2">
          <template slot="title"><i class="el-icon-s-tools"></i> <span>系統工具</span></template>
          <el-menu-item index="2-2">
            <template slot="title"><i class="el-icon-s-order"></i> <span slot="title">數字字典</span></template>
          </el-menu-item>
        </el-submenu>
      </el-menu>


    </el-aside>
    <el-container>
      <!--    頭部資訊-->
      <el-header>
        <strong>VueAdmin后臺管理系統學習</strong>
        <div class="header-avatar">
          <!--          頭像-->
          <el-avatar src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"></el-avatar>
          <!--        下拉框-->
          <el-dropdown>
            <span class="el-dropdown-link">
              Admin<i class="el-icon-arrow-down el-icon--right"></i>
            </span>
            <el-dropdown-menu slot="dropdown">
              <el-dropdown-item>個人中心</el-dropdown-item>
              <el-dropdown-item>退出</el-dropdown-item>
            </el-dropdown-menu>
          </el-dropdown>
          <!--          最后面的文字跳轉-->
          <el-link href="https://shimowendang.com/docs/pxwyJHgqcWjWkTKX/read" target="_blank">網站</el-link>
          <el-link href="https://shimowendang.com/docs/pxwyJHgqcWjWkTKX/read" target="_blank">學習</el-link>
        </div>
      </el-header>
      <!--    主體資訊-->
      <el-main>主體資訊</el-main>
    </el-container>
  </el-container>
</template>

<script>

export default {
  name: 'Index',

}
</script>

<style>
.el-container {
  padding: 0;
  margin: 0;
  height: 100%;
}

.el-header {
  background-color: cadetblue;
  color: #333;
  text-align: center;
  line-height: 60px;
}

.el-aside {
  background-color: #D3DCE6;
  color: #333;
  line-height: 200px;
}

.el-main {
  color: #333;
  text-align: center;
  line-height: 160px;
}

.header-avatar {
  float: right;
  width: 210px;
  display: flex;
  justify-content: space-around;
  align-items: center;
}

.el-dropdown-link {
  cursor: pointer;
  color: #409EFF;
}

.el-menu-vertical-demo {
  height: 100%;
}
</style>

8.2效果圖

9.抽取代碼

將index中的左側導航欄代碼和頭部代碼抽取為公共的

9.1抽取后的導航欄

在views新建inc/SideMenu

<template>
  <el-menu
      default-active="2"
      class="el-menu-vertical-demo"
      @open="handleOpen"
      @close="handleClose"
      background-color="#545c64"
      text-color="#fff"
      active-text-color="#ffd04b">
    <router-link to="/index">
      <el-menu-item index="Index">
        <template slot="title"><i class="el-icon-s-home"></i> <span slot="title">首頁</span></template>
      </el-menu-item>
    </router-link>
    <el-submenu index="1">
      <template slot="title"><i class="el-icon-s-operation"></i> <span>系統管理</span></template>
      <el-menu-item index="1-1">
        <template slot="title"><i class="el-icon-s-custom"></i> <span slot="title">用戶管理</span></template>
      </el-menu-item>
      <el-menu-item index="1-2">
        <template slot="title"><i class="el-icon-rank"></i> <span slot="title">角色管理</span></template>
      </el-menu-item>
      <el-menu-item index="1-3">
        <template slot="title"><i class="el-icon-menu"></i> <span slot="title">選單管理</span></template>
      </el-menu-item>
    </el-submenu>
    <el-submenu index="2">
      <template slot="title"><i class="el-icon-s-tools"></i> <span>系統工具</span></template>
      <el-menu-item index="2-2">
        <template slot="title"><i class="el-icon-s-order"></i> <span slot="title">數字字典</span></template>
      </el-menu-item>
    </el-submenu>
  </el-menu>
</template>

<script>

export default {
  name: 'SideMenu',

}
</script>

<style>
.el-menu-vertical-demo {
  height: 100%;
}
</style>

9.2抽取后的頭部

<template>

  <el-header>
    <strong>VueAdmin后臺管理系統學習</strong>
    <div class="header-avatar">
      <!--          頭像-->
      <el-avatar src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"></el-avatar>
      <!--        下拉框-->
      <el-dropdown>
            <span class="el-dropdown-link">
              Admin<i class="el-icon-arrow-down el-icon--right"></i>
            </span>
        <el-dropdown-menu slot="dropdown">
          <el-dropdown-item>個人中心</el-dropdown-item>
          <el-dropdown-item>退出</el-dropdown-item>
        </el-dropdown-menu>
      </el-dropdown>
      <!--          最后面的文字跳轉-->
      <el-link href="https://shimowendang.com/docs/pxwyJHgqcWjWkTKX/read" target="_blank">網站</el-link>
      <el-link href="https://shimowendang.com/docs/pxwyJHgqcWjWkTKX/read" target="_blank">學習</el-link>
    </div>
  </el-header>
</template>

<script>

export default {
  name: 'SideHeader',

}
</script>

<style>

.header-avatar {
  float: right;
  width: 210px;
  display: flex;
  justify-content: space-around;
  align-items: center;
}
.el-dropdown-link {
  cursor: pointer;
  color: #409EFF;
}

.el-container {
  padding: 0;
  margin: 0;
  height: 100%;
}

.el-header {
  background-color: cadetblue;
  color: #333;
  text-align: center;
  line-height: 60px;
}

.el-aside {
  background-color: #D3DCE6;
  color: #333;
  line-height: 200px;
}
</style>

9.3抽取后的Home

<!--<template>-->
<!--  <div>測驗主頁  </div>-->
<!--</template>-->

<!--<script> -->

<!--export default {-->
<!--  name: 'Home',-->
<!-- -->
<!--}-->
<!--</script>-->

<!--<style>-->

<!--</style>-->


<template>
  <el-container>
    <!--  側邊欄-->
    <el-aside width="200px">
      <SideMenu></SideMenu>
    </el-aside>
    <el-container>
      <!--    頭部資訊-->
      <el-header>
        <SideHeader></SideHeader>
      </el-header>
      <!--    主體資訊-->
      <el-main>
        <router-view></router-view>
      </el-main>
    </el-container>
  </el-container>
</template>

<script>
import SideMenu from "@/views/inc/SideMenu";
import SideHeader from "@/views/inc/SideHeader";

export default {
  name: 'Home',
  components: {SideMenu, SideHeader},
}
</script>

<style>
.el-container {
  padding: 0;
  margin: 0;
  height: 100%;
}

.el-header {
  background-color: cadetblue;
  color: #333;
  text-align: center;
  line-height: 60px;
}

.el-aside {
  background-color: #D3DCE6;
  color: #333;
  line-height: 200px;
}

.el-main {
  color: #333;
  text-align: center;
  line-height: 160px;
}

.header-avatar {
  float: right;
  width: 210px;
  display: flex;
  justify-content: space-around;
  align-items: center;
}

.el-dropdown-link {
  cursor: pointer;
  color: #409EFF;
}
</style>

9.4抽取后的index

<template>
  <div>測驗主頁  </div>
</template>

<script>

export default {
  name: 'Index',

}
</script>

<style>

</style>

9.5抽取的router.js

將index路由放到children中

9.5抽取后的頁面

10.創建頁面

10.1在views新建system/User.vue

<template>
  <div>用戶測驗</div>
</template>

<script>

export default {
  name: 'User',

}
</script>

<style>

</style>

10.2在views新建system/Role.vue

<template>
  <div>角色測驗</div>
</template>

<script>

export default {
  name: 'Role',

}
</script>

<style>

</style>

10.3在views新建system/Menuvue

<template>
  <div>選單測驗</div>
</template>

<script>

export default {
  name: 'Menu',
}
</script>

<style>

</style>

10.4在router.js添加

以下是代碼router.js代碼

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import Login from '../views/Login.vue'
import Index from '@/views/index'
import User from "@/views/system/User";
import Role from "@/views/system/Role";
import Menu from "@/views/system/Menu";

Vue.use(VueRouter)

const routes = [
    {
        path: '/',
        name: 'Home',
        component: Home,
        children: [
            {
                path: '/index',
                name: 'Index',
                component: Index
            },
            {
                path: '/users',
                name: 'SysUser',
                component: User
            },
            {
                path: '/roles',
                name: 'SysRole',
                component: Role
            },
            {
                path: '/menus',
                name: 'SysMenu',
                component: Menu
            },
        ]
    },
    {
        path: '/login',
        name: 'Login',
        // route level code-splitting
        // this generates a separate chunk (about.[hash].js) for this route
        // which is lazy-loaded when the route is visited.
        // component: () => import(/* webpackChunkName: "about" */ '../views/Login.vue')  方式一
        component: Login  //方式二
    },

]

const router = new VueRouter({
    mode: 'history',
    base: process.env.BASE_URL,
    routes
})

export default router

10.4測驗

分別訪問:看頁面的變化

http://localhost:8080/menus

http://localhost:8080/roles

http://localhost:8080/users

11.導航欄系結頁面

完成后點擊頁面測驗看是否發生變化

<router-link to="/users">

</router-link>

12修改頭像和用戶名

在inc/SideHeader中修改頭像和用戶名從后端獲取

13.個人中心

13.1在views/system/新建UserCenter.vue

<template>
  <div style="text-align: center;"><h2>你好!{{ userInfo.username }} 同學</h2>
    <el-form :model="passForm" status-icon :rules="rules" ref="passForm" label-width="100px">
      <el-form-item label="舊密碼" prop="currentPass">
        <el-input type="password" v-model="passForm.currentPass" autocomplete="off"></el-input>
      </el-form-item>
      <el-form-item label="新密碼" prop="password">
        <el-input type="password" v-model="passForm.password" autocomplete="off"></el-input>
      </el-form-item>
      <el-form-item label="確認密碼" prop="checkPass">
        <el-input type="password" v-model="passForm.checkPass" autocomplete="off"></el-input>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="submitForm('passForm')">提交</el-button>
        <el-button @click="resetForm('passForm')">重置</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>
<script>
export default {
  name: "Login", data() {
    var validatePass = (rule, value, callback) => {
      if (value === '') {
        callback(new Error('請再次輸入密碼'));
      } else if (value !== this.passForm.password) {
        callback(new Error('兩次輸入密碼不一致!'));
      } else {
        callback();
      }
    };
    return {
      userInfo: {},
      passForm: {password: '111111', checkPass: '111111', currentPass: '111111'},
      rules: {
        password: [{required: true, message: '請輸入新密碼', trigger: 'blur'}, {
          min: 6,
          max: 12,
          message: '長度在 6 到 12 個字符',
          trigger: 'blur'
        }],
        checkPass: [{required: true, validator: validatePass, trigger: 'blur'}],
        currentPass: [{required: true, message: '請輸入當前密碼', trigger: 'blur'},]
      }
    }
  }, created() {
    this.getUserInfo()
  }, methods: {
    getUserInfo() {
      this.$axios.get("/sys/userInfo").then(res => {
        this.userInfo = res.data.data;
      })
    }, submitForm(formName) {
      this.$refs[formName].validate((valid) => {
        if (valid) {
          const _this = this
          this.$axios.post('/sys/user/updataPass', this.passForm).then(res => {
            _this.$alert(res.data.msg, '提示', {
              confirmButtonText: '確定', callback: action => {
                this.$refs[formName].resetFields();
              }
            });
          })
        } else {
          console.log('error submit!!');
          return false;
        }
      });
    }, resetForm(formName) {
      this.$refs[formName].resetFields();
    }
  }
}
</script>
<style scoped>
.el-form {
  width: 420px;
  margin: 50px auto;
}
</style>

13.2在router.js添加

{
    path: '/userCenter',
    name: 'UserCenter',
    component: ()=>import("@/views/system/UserCenter")
},

13.3修改SideHeader.vue

洗掉所有頁面的 line-height: 160px;

13.4測驗頁面

14退出

  • 在SideHeader.vue給退出添加點擊按鈕
<!-- 在el-dropdown-item標簽里, @click不可以用,要使用@click.native-->
<el-dropdown-item @click.native="logout">退出</el-dropdown-item>
  • 在SideHeader.vue的methods撰寫點擊退出方法
logout(){
  this.$axios.post("/logout").then(res => {
    localStorage.clear();
    sessionStorage.clear();
    this.$store.commit("resetState");
    this.$router.push("/login")
  })
},
  • 在store/index.js添加洗掉的方法

  • 在mock.js模擬退出請求
Mock.mock('/logout','post',()=>{    return Result})
  • 測驗

點擊頁面退出看效果

15將導航改為動態獲取(可忽略)

可以忽略,直接過16

先用mock.js模擬從服務器回傳資料

15.1主要修改

15.2修改后SideMenu.vue的頁面

<template>
  <el-menu
      default-active="2"
      class="el-menu-vertical-demo"
      @open="handleOpen"
      @close="handleClose"
      background-color="#545c64"
      text-color="#fff"
      active-text-color="#ffd04b">
    <router-link to="/index">
      <el-menu-item index="Index">
        <template slot="title"><i :class="el-icon-s-home"></i> <span slot="title">首頁</span></template>
      </el-menu-item>
    </router-link>

    <el-submenu :index=menu.name
                v-for="menu in menuList">
      <template slot="title">
        <i :class="menu.icon"></i>
        <span>{{ menu.title }}</span>
      </template>
      <router-link :to="item.path" v-for="item in menu.children">
        <el-menu-item :index="item.name">
          <template slot="title">
            <i :class="item.icon"></i> 
            <span slot="title">{{ item.title }}</span>
          </template>
        </el-menu-item>
      </router-link>
    </el-submenu>
  </el-menu>
</template>

<script>

export default {
  name: 'SideMenu',
  data() {
    return {
      menuList: [],
    }

  },
  created() {
    this.getMenuList();
  },
  methods: {
    getMenuList() {
      this.$axios.get("/sys/menuList").then(res => {
        this.menuList = res.data.data;
      })
    }
  }


}
</script>

<style>
.el-menu-vertical-demo {
  height: 100%;
}

.a {
  text-decoration: none;
}
</style>

15.3mock.js

Mock.mock('/sys/menuList', 'get', () => {
    Result.data = [
        {
            name: 'SysManga',
            title: '系統管理',
            icon: 'el-icon-s-operation',
            path: '',
            component: '',
            children: [
                {
                    name: 'SysUser',
                    title: '用戶管理',
                    icon: 'el-icon-s-custom',
                    path: '/users',
                    children: []
                }]
        }, {
            name: 'SysTools',
            title: '系統工具',
            icon: 'el-icon-s-tools',
            path: '',
            children: [
                {
                    name: 'SysDict',
                    title: '數字字典',
                    icon: 'el-icon-s-order',
                    path: '/dicts',
                    children: []
                },]
        }
    ]
    return Result
})

15.4修改后的導航欄

16.將導航欄改為動態獲取

16.1mock.js模擬后端回傳的資料

修改15的mock.js后的方法

Mock.mock('/sys/menuList', 'get', () => {

    // 導航選單
    let nav = [
        {
            name: 'SysManga',
            title: '系統管理',
            component:'',
            icon: 'el-icon-s-operation',
            path: '',
            component: '',
            children: [
                {
                    name: 'SysUser',
                    title: '用戶管理',
                    component:'system/User',
                    icon: 'el-icon-s-custom',
                    path: '/users',
                    children: []
                }]
        }, {
            name: 'SysTools',
            title: '系統工具',
            component:'',
            icon: 'el-icon-s-tools',
            path: '',
            children: [
                {
                    name: 'SysDict',
                    title: '數字字典',
                    component:'',
                    icon: 'el-icon-s-order',
                    path: '/dicts',
                    children: []
                },]
        }
    ]
    // 權限
    let authoritys = []
    Result.data = {
        nav: nav,
        authoritys: authoritys
    }
    return Result
})

16.2將選單/權限存放到全域

在/store/創建modules/menus.js

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default {
    state: {
        // 導航欄選單
        menuList: [],
        // 權限
        permList: [],
        // 每次重繪都獲取選單,消耗服務器,可以將他存到sessionStorage中
        hasRoute: false
    },
    mutations: {
        setMenuList(state, menuList) {
            state.menuList = menuList
        },
        setPermList(state, permList) {
            state.setPermList = permList;
        },
        changeRouteStatus(state, hasRoute) {
            state.hasRoute = hasRoute;
            sessionStorage.setItem("hasRoute", hasRoute)
        },
    },
    actions: {},
    modules: {}
}

16.3將menus.js注冊到src/store/index.js

16.4修改route/index.js方法

添加這個方法,注釋掉寫死的children的選單,動態獲取,下面有修改后的整個頁面

router.beforeEach(((to, from, next) => {

    let hasRoute = store.state.menus.hasRoute
    console.log("hasRoute", hasRoute)
    // 當它沒有的時候獲取選單
    if (!hasRoute) {
        // 獲取選單的方法,并校驗token(token是之前存到localStorage中的)
        axios.get("/sys/menuList", {
            headers: {
                Authorization: localStorage.getItem("token")
            }
        }).then(res => {
            // 拿到menuList
            store.commit("setMenuList", res.data.data.nav)
            // 拿到用戶權限
            store.commit("setPermList", res.data.data.authoritys)
            // 動態系結路由(把拿到的menuList回圈)
            let newRoutes = router.options.routes
            res.data.data.nav.forEach(menu => {
                if (menu.children) {
                    menu.children.forEach(e => {
                        // 轉換成路由
                        if (e.component) {
                            console.log("e.component", e.component)
                            let route = {
                                name: e.name,
                                path: e.path,
                                mate: {
                                    icon: e.icon,
                                    title: e.title
                                },
                                component: () => import("@/views/" + e.component + '.vue')
                            }
                            // 把轉換后的路由添加到路由管理器
                            if (route) {
                                newRoutes[0].children.push(route)
                            }
                        }

                    })
                }
            })
            console.log("newRoutes", newRoutes)
            router.addRoutes(newRoutes)
            // 獲取到選單,將他改為true
            hasRoute = true
            store.commit("changeRouteStatus", hasRoute)

        })
    }

    next()
}))

修改后的整個頁面

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import Login from '../views/Login.vue'
import Index from '@/views/index'
import User from "@/views/system/User";
import Role from "@/views/system/Role";
import Menu from "@/views/system/Menu";
import axios from "axios";
import store from "@/store"

Vue.use(VueRouter)

const routes = [
    {
        path: '/',
        name: 'Home',
        component: Home,
        children: [
            {
                path: '/index',
                name: 'Index',
                component: Index
            },
            {
                path: '/userCenter',
                name: 'UserCenter',
                component: () => import("@/views/system/UserCenter")
            },
            // {
            //     path: '/users',
            //     name: 'SysUser',
            //     component: User
            // },
            // {
            //     path: '/roles',
            //     name: 'SysRole',
            //     component: Role
            // },
            // {
            //     path: '/menus',
            //     name: 'SysMenu',
            //     component: Menu
            // },
        ]
    },
    {
        path: '/login',
        name: 'Login',
        // route level code-splitting
        // this generates a separate chunk (about.[hash].js) for this route
        // which is lazy-loaded when the route is visited.
        // component: () => import(/* webpackChunkName: "about" */ '../views/Login.vue')  方式一
        component: Login  //方式二
    },

]

const router = new VueRouter({
    mode: 'history',
    base: process.env.BASE_URL,
    routes
})

router.beforeEach(((to, from, next) => {

    let hasRoute = store.state.menus.hasRoute
    console.log("hasRoute", hasRoute)
    // 當它沒有的時候獲取選單
    if (!hasRoute) {
        // 獲取選單的方法,并校驗token(token是之前存到localStorage中的)
        axios.get("/sys/menuList", {
            headers: {
                Authorization: localStorage.getItem("token")
            }
        }).then(res => {
            // 拿到menuList
            store.commit("setMenuList", res.data.data.nav)
            // 拿到用戶權限
            store.commit("setPermList", res.data.data.authoritys)
            // 動態系結路由(把拿到的menuList回圈)
            let newRoutes = router.options.routes
            res.data.data.nav.forEach(menu => {
                if (menu.children) {
                    menu.children.forEach(e => {
                        // 轉換成路由
                        if (e.component) {
                            console.log("e.component", e.component)
                            let route = {
                                name: e.name,
                                path: e.path,
                                mate: {
                                    icon: e.icon,
                                    title: e.title
                                },
                                component: () => import("@/views/" + e.component + '.vue')
                            }
                            // 把轉換后的路由添加到路由管理器
                            if (route) {
                                newRoutes[0].children.push(route)
                            }
                        }

                    })
                }
            })
            console.log("newRoutes", newRoutes)
            router.addRoutes(newRoutes)
            hasRoute = true
            console.log("hasRoute2", hasRoute)
            store.commit("changeRouteStatus", hasRoute)

        })
    }

    next()
}))

export default router

16.5修改/inc/SideMenu.vue

修改后的整個頁面

<template>
  <el-menu
      default-active="2"
      class="el-menu-vertical-demo"
      @open="handleOpen"
      @close="handleClose"
      background-color="#545c64"
      text-color="#fff"
      active-text-color="#ffd04b">
    <router-link to="/index">
      <el-menu-item index="Index">
        <template slot="title">
          <i :class="el-icon-s-home"></i>
          <span slot="title">首頁</span></template>
      </el-menu-item>
    </router-link>

    <el-submenu :index=menu.name
                v-for="menu in menuList">
      <template slot="title">
        <i :class="menu.icon"></i>
        <span>{{ menu.title }}</span>
      </template>
      <router-link :to="item.path" v-for="item in menu.children">
        <el-menu-item :index="item.name">
          <template slot="title">
            <i :class="item.icon"></i>
            <span slot="title">{{ item.title }}</span>
          </template>
        </el-menu-item>
      </router-link>
    </el-submenu>
  </el-menu>
</template>

<script>

export default {
  name: 'SideMenu',
  data() {
    return {
      // menuList: [],
    }

  },
  computed: {
    // this.getMenuList();
    menuList: {
      get() {
        //return this.$store.state+ menus.js的模塊名+menuList
        return this.$store.state.menus.menuList
      }
    }
  },
  methods: {
    // getMenuList() {
    //   this.$axios.get("/sys/menuList").then(res => {
    //     this.menuList = res.data.data.nav;
    //   })
    // }
  }


}
</script>

<style>
.el-menu-vertical-demo {
  height: 100%;
}

.a {
  text-decoration: none;
}
</style>

17動態標簽頁

17.1在/store/modules/menus.js添加

17.2在inc/新建Tabs.vue

<template>
  <el-tabs v-model="editableTabsValue" type="card" closable @tab-remove="removeTab" @tab-click="clickTab">
    <el-tab-pane
        v-for="(item, index) in editableTabs"
        :key="item.name"
        :label="item.title"
        :name="item.name"
    >
    </el-tab-pane>
  </el-tabs>
</template>

<script>

export default {
  name: 'Tabs',
  data() {
    return {
      // editableTabsValue: this.$store.state.menus.editableTabsValue,
      // editableTabs: this.$store.state.menus.editableTabs,
    }
  },

  // tab標簽放到computed里面監聽
  computed: {
    editableTabsValue: {
      // getter方法
      get() {
        return this.$store.state.menus.editableTabsValue
      },
      // setter方法
      set(val) {
        this.$store.state.menus.editableTabsValue = val
      }
    },
    editableTabs: {
      // getter方法
      get() {
        return this.$store.state.menus.editableTabs
      },
      // setter方法
      set(val) {
        this.$store.state.menus.editableTabs = val
      }
    }
  },
  methods: {
    // 點擊tab標簽切換頁面資料
    clickTab(target) {
      this.$router.push({name: target.name})
    },

    removeTab(targetName) {
      let tabs = this.editableTabs;
      let activeName = this.editableTabsValue;

      // 首頁不能洗掉
      if (activeName==='Index'){
        return
      }

      if (activeName === targetName) {
        tabs.forEach((tab, index) => {
          if (tab.name === targetName) {
            let nextTab = tabs[index + 1] || tabs[index - 1];
            if (nextTab) {
              activeName = nextTab.name;
            }
          }
        });
      }

      this.editableTabsValue = activeName;
      this.editableTabs = tabs.filter(tab => tab.name !== targetName);
      // 洗掉標簽,就切換標簽
      this.$router.push({name: activeName})
    }
  }

}
</script>

<style>

</style>

17.3頁面使用

在視圖上面添加Tabs組件

17.4修改inc/SideMenu.vue

添加selectMenu點擊事件添加tab標簽

:default-active是標簽點擊是相應的選單高亮

<template>

<!--點擊tab時選單高亮 :default-active=this.$store.state.menus.editableTabsValue-->
  <el-menu
      :default-active=this.$store.state.menus.editableTabsValue
      class="el-menu-vertical-demo"
      background-color="#545c64"
      text-color="#fff"
      active-text-color="#ffd04b">
    <router-link to="/index">
      <el-menu-item index="Index">
        <template slot="title">
          <i class="el-icon-s-home"></i>
          <span slot="title">首頁</span></template>
      </el-menu-item>
    </router-link>

    <el-submenu :index=menu.name
                v-for="menu in menuList">
      <template slot="title">
        <i :class="menu.icon"></i>
        <span>{{ menu.title }}</span>
      </template>
      <router-link :to="item.path" v-for="item in menu.children">
        <el-menu-item :index="item.name" @click="selectMenu(item)">
          <template slot="title">
            <i :class="item.icon"></i>
            <span slot="title">{{ item.title }}</span>
          </template>
        </el-menu-item>
      </router-link>
    </el-submenu>
  </el-menu>
</template>

<script>

export default {
  name: 'SideMenu',
  data() {
    return {
      // menuList: [],
    }

  },
  computed: {
    // this.getMenuList();
    menuList: {
      get() {
        //return this.$store.state+ menus.js的模塊名+menuList
        return this.$store.state.menus.menuList
      }
    }
  },
  methods: {
    // getMenuList() {
    //   this.$axios.get("/sys/menuList").then(res => {
    //     this.menuList = res.data.data.nav;
    //   })
    // }
    selectMenu(item){
      this.$store.commit("addTab",item)
    }
  },
}
</script>

<style>
.el-menu-vertical-demo {
  height: 100%;
}

.a {
  text-decoration: none;
}
</style>

18通過url地址跳轉

在src/App.vue添加方法

http://localhost:8080/users跳轉不過去,添加以下代碼

<script>
export default {
  name: "App",
  watch: {
    $route(to, from) {
      debugger;
      console.log("to",to)
      if (to.path != "/login") {
        let obj = {
          name: to.name,
          title: to.name
        }
        this.$store.commit("addTab", obj)
      }
    }
  }
}
</script>

19退出清空資訊

在src/store/modules/menus.js的mutations方法里添加,因為退出已經呼叫過resetState這個,直接添加即可

resetState: (state) => {
    // 導航欄選單
    state.menuList = [],
        // 權限
        state.permList = [],
        // 每次重繪都獲取選單,消耗服務器,可以將他存到sessionStorage中
        state.hasRoute = false,
        // tab標簽默認為首頁
        state.editableTabsValue = 'Index',
        state.editableTabs = [{
            title: '首頁',
            name: 'Index',
        }]
},

20.菜單頁面

20.1是文字不居中

將.el-main的text-align: center;刪除

.el-main {
  color: #333;
  /*text-align: center;*/
  /*line-height: 160px;*/
  padding: 0;
}

20.2mock.js模擬后端

 選單管理 
// 獲取選單管理串列
Mock.mock('/sys/menu/list', 'get', () => {
    let menus = [
        {
            "id": 1,
            "created": "2021-01-15T18:58:18",
            "updated": "2021-01-15T18:58:20",
            "statu": 1,
            "parentId": 0,
            "name": "系統管理",
            "path": "",
            "perms": "sys:manage",
            "component": "",
            "type": 0,
            "icon": "el-icon-eleme",
            "ordernum": 1,
            "children": [
                {
                    "id": 2,
                    "created": "2021-01-15T19:03:45",
                    "updated": "2021-01-15T19:03:48",
                    "statu": 1,
                    "parentId": 1,
                    "name": "用戶管理",
                    "path": "/sys/users",
                    "perms": "sys:user:list",
                    "component": "sys/User",
                    "type": 1,
                    "icon": "el-icon-s-custom",
                    "ordernum": 1,
                    "children": [
                        {
                            "id": 9,
                            "created": "2021-01-17T21:48:32",
                            "updated": null,
                            "statu": 1,
                            "parentId": 2,
                            "name": "添加用戶",
                            "path": null,
                            "perms": "sys:user:save",
                            "component": null,
                            "type": 2,
                            "icon": null,
                            "ordernum": 1,
                            "children": []
                        },
                        {
                            "id": 10,
                            "created": "2021-01-17T21:49:03",
                            "updated": "2021-01-17T21:53:04",
                            "statu": 1,
                            "parentId": 2,
                            "name": "修改用戶",
                            "path": null,
                            "perms": "sys:user:update",
                            "component": null,
                            "type": 2,
                            "icon": null,
                            "ordernum": 2,
                            "children": []
                        },
                        {
                            "id": 11,
                            "created": "2021-01-17T21:49:21",
                            "updated": null,
                            "statu": 1,
                            "parentId": 2,
                            "name": "洗掉用戶",
                            "path": null,
                            "perms": "sys:user:delete",
                            "component": null,
                            "type": 2,
                            "icon": null,
                            "ordernum": 3,
                            "children": []
                        },
                        {
                            "id": 12,
                            "created": "2021-01-17T21:49:58",
                            "updated": null,
                            "statu": 1,
                            "parentId": 2,
                            "name": "分配角色",
                            "path": null,
                            "perms": "sys:user:role",
                            "component": null,
                            "type": 2,
                            "icon": null,
                            "ordernum": 4,
                            "children": []
                        },
                        {
                            "id": 13,
                            "created": "2021-01-17T21:50:36",
                            "updated": null,
                            "statu": 1,
                            "parentId": 2,
                            "name": "重置密碼",
                            "path": null,
                            "perms": "sys:user:repass",
                            "component": null,
                            "type": 2,
                            "icon": null,
                            "ordernum": 5,
                            "children": []
                        }
                    ]
                },
                {
                    "id": 3,
                    "created": "2021-01-15T19:03:45",
                    "updated": "2021-01-15T19:03:48",
                    "statu": 1,
                    "parentId": 1,
                    "name": "角色管理",
                    "path": "/sys/roles",
                    "perms": "sys:role:list",
                    "component": "sys/Role",
                    "type": 1,
                    "icon": "el-icon-rank",
                    "ordernum": 2,
                    "children": []
                },

            ]
        },
        {
            "id": 5,
            "created": "2021-01-15T19:06:11",
            "updated": null,
            "statu": 1,
            "parentId": 0,
            "name": "系統工具",
            "path": "",
            "perms": "sys:tools",
            "component": null,
            "type": 0,
            "icon": "el-icon-s-tools",
            "ordernum": 2,
            "children": [
                {
                    "id": 6,
                    "created": "2021-01-15T19:07:18",
                    "updated": "2021-01-18T16:32:13",
                    "statu": 1,
                    "parentId": 5,
                    "name": "數字字典",
                    "path": "/sys/dicts",
                    "perms": "sys:dict:list",
                    "component": "sys/Dict",
                    "type": 1,
                    "icon": "el-icon-s-order",
                    "ordernum": 1,
                    "children": []
                }
            ]
        }
    ]

    Result.data = menus

    return Result
})

// 根據id獲取
Mock.mock(RegExp('/sys/menu/info/*'), 'get', () => {

    Result.data = {
        "id": 3,
        "statu": 1,
        "parentId": 1,
        "name": "角色管理",
        "path": "/sys/roles",
        "perms": "sys:role:list",
        "component": "sys/Role",
        "type": 1,
        "icon": "el-icon-rank",
        "orderNum": 2,
        "children": []
    }

    return Result
})

// 新增修改選單
Mock.mock(RegExp('/sys/menu/*'), 'post', () => {
    return Result
})

// 查出選單
Mock.mock(RegExp('/sys/menu/*'), 'delete', () => {
    return Result
})

20.3選單頁面

<template>
  <div>
    <!--    新增表單-->
    <el-form :inline="true">
      <el-form-item>
        <el-button type="primary" @click="onSubmit">新增</el-button>
      </el-form-item>
    </el-form>

    <!--    Table表格-->
    <el-table
        :data="tableData"
        style="width: 100%;margin-bottom: 20px;"
        row-key="id"
        border
        stripe
        default-expand-all
        :tree-props="{children: 'children', hasChildren: 'hasChildren'}">
      <el-table-column
          prop="name"
          label="名稱"
          sortable
          width="180">
      </el-table-column>
      <el-table-column prop="perms" label="權限編碼" width="180"></el-table-column>
      <el-table-column prop="icon" label="圖示">
        <template slot-scope="scope">
          <i :class="scope.row.icon"></i>
        </template>
      </el-table-column>

      <el-table-column prop="type" label="型別" width="120">
        <template slot-scope="scope">
          <el-tag v-if="scope.row.type === 0" size="small">目錄</el-tag>
          <el-tag v-else-if="scope.row.type === 1" size="small" type="success">選單</el-tag>
          <el-tag v-else-if="scope.row.type === 2" size="small" type="info">按鈕</el-tag>
        </template>
      </el-table-column>
      <el-table-column prop="path" label="選單URL"></el-table-column>
      <el-table-column prop="component" label="選單組件"></el-table-column>
      <el-table-column prop="orderNum" label="排序號"></el-table-column>
      <el-table-column prop="statu" label="狀態" width="120">
        <template slot-scope="scope">
          <el-tag v-if="scope.row.statu === 0" size="small" type="danger">禁用</el-tag>
          <el-tag v-else-if="scope.row.statu === 1" size="small" type="success">正常</el-tag>
        </template>
      </el-table-column>

      <!--      fixed="right"固定操作欄-->
      <el-table-column prop="icon" label="操作" width="120" fixed="right">
        <template slot-scope="scope">
          <el-button type="text" @click="changeUpdate(scope.row)">編輯</el-button>
          <el-divider direction="vertical"></el-divider>

          <!--          <el-button type="text">洗掉</el-button>-->
          <!--          洗掉-->
          <template>
            <el-popconfirm title="這是一段內容確定洗掉嗎?" @confirm="delHandle(scope.row)">
              <el-button type="text" slot="reference">洗掉</el-button>
            </el-popconfirm>
          </template>
        </template>
      </el-table-column>
    </el-table>

    <!--    新增或修改的彈窗-->
    <el-dialog
        title="新增"
        :visible.sync="dialogVisible"
        width="500px"
        :before-close="handleClose">
      <!--      彈框里的表單-->
      <el-form :model="editForm" :rules="rules" ref="editForm" label-width="100px" class="demo-editForm">
        <!--        三級分類-->
        <el-form-item label="上級選單" prop="parentId">
          <el-select v-model="editForm.parentId" placeholder="請選擇上級選單">
            <!-- 一級目錄-->
            <template v-for="(item,index) in tableData">
              <el-option :label=item.name :value=item.id></el-option>
              <!-- 二級目錄-->
              <template v-for="child in item.children">
                <el-option :label=child.name :value=child.id>
                  <span>{{ '      - ' + child.name }}</span>
                </el-option>
              </template>
            </template>
          </el-select>
        </el-form-item>

        <el-form-item label="選單名稱" prop="name" label-width="100px">
          <el-input v-model="editForm.name" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="權限編碼" prop="perms" label-width="100px">
          <el-input v-model="editForm.perms" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="圖示" prop="icon" label-width="100px">
          <el-input v-model="editForm.icon" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="選單URL" prop="path" label-width="100px">
          <el-input v-model="editForm.path" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="選單組件" prop="component" label-width="100px">
          <el-input v-model="editForm.component" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="型別" prop="type" label-width="100px">
          <el-radio-group v-model="editForm.type">
            <el-radio :label=0>目錄</el-radio>
            <el-radio :label=1>選單</el-radio>
            <el-radio :label=2>按鈕</el-radio>
          </el-radio-group>
        </el-form-item>
        <el-form-item label="狀態" prop="statu" label-width="100px">
          <el-radio-group v-model="editForm.statu">
            <el-radio :label=0>禁用</el-radio>
            <el-radio :label=1>正常</el-radio>
          </el-radio-group>
        </el-form-item>
        <el-form-item label="排序號" prop="orderNum" label-width="100px">
          <el-input-number v-model="editForm.orderNum" :min="1" label="排序號">1</el-input-number>
        </el-form-item>
      </el-form>

      <div slot="footer" class="dialog-footer">
        <el-button @click="resetForm('editForm')">取 消</el-button>
        <el-button type="primary" @click="submitEditForm('editForm')">確 定</el-button>
      </div>

    </el-dialog>

  </div>
</template>

<script>

export default {
  name: 'Role',
  data() {
    return {
      // 彈框
      dialogVisible: false,
      // 新增資料
      editForm: {},
      // form表單
      tableData: [],
      // 表單校驗
      rules: {
        parentId: [{required: true, message: '請選擇上級選單', trigger: 'blur'}],
        name: [{required: true, message: '請輸入名稱', trigger: 'blur'}],
        perms: [{required: true, message: '請輸入權限編碼', trigger: 'blur'}],
        type: [{required: true, message: '請選擇狀態', trigger: 'blur'}],
        orderNum: [{required: true, message: '請填入排序號', trigger: 'blur'}],
        statu: [{required: true, message: '請選擇狀態', trigger: 'blur'}]
      },
    }
  },

  created() {
    this.getMenuTree()
  },
  methods: {
    // 獲取串列
    getMenuTree() {
      this.$axios.get("/sys/menu/list").then(res => {
        this.tableData = res.data.data
      })

    },
    // 點擊新增
    onSubmit() {
      // 點擊新增彈窗
      this.dialogVisible = true
    },
    // X 關閉
    handleClose(done) {
      this.editForm = {}
      this.dialogVisible = false
    },
    // 清空表單的方法
    resetForm(formName) {
      debugger;
      this.$refs[formName].resetFields();
      this.editForm = {}
      this.dialogVisible = false
    },

    // 新增或修改方法
    submitEditForm(editForm) {
      this.$refs["editForm"].validate((valid) => {
        if (valid) {
          // 如果id不為null,修改
          if (this.editForm.id != null) {
            debugger
            this.$axios.post("/sys/menu/update", editForm).then(res => {
              // 保存成功彈框訊息提醒
              this.$message({
                message: '修改成功',
                type: 'success',
                // onClose是保存成功后關閉后查詢串列
                onClose: () => {
                  this.getMenuTree()
                }
              });
              this.editForm = {}
              this.dialogVisible = false;
            })

          } else { // 否則新增
            debugger
            this.$axios.post("/sys/menu/save", editForm).then(res => {
              // 保存成功彈框訊息提醒
              this.$message({
                message: '保存成功',
                type: 'success',
                // onClose是保存成功后關閉后查詢串列
                onClose: () => {
                  this.getMenuTree()
                }
              });
              this.editForm = {}
              this.dialogVisible = false;
            })
          }
        }
      });
    },
    // 點擊編輯查詢回顯
    changeUpdate(row) {
      let id = row.id;
      this.$axios.get("/sys/menu/info/" + id).then(res => {
        this.editForm = res.data.data;
        // 打開視窗
        this.dialogVisible = true;
      })
    },
    // 洗掉方法
    delHandle(row) {
      let id = row.id;
      this.$axios.delete("/sys/menu/delete/" + id).then(res => {
        // 洗掉成功彈框訊息提醒
        this.$message({
          message: '洗掉成功',
          type: 'success',
          // onClose是保存成功后關閉后查詢串列
          onClose: () => {
            this.getMenuTree()
          }
        });
      })
    },
  }
}
</script>

<style>

</style>

20.4頁面展示

21角色頁面

21.1mock.js模擬后端

 角色管理 
// 角色串列
Mock.mock(RegExp('/sys/role/list*'), 'get', () => {

    Result.data = {
        "records": [
            {
                "id": 3,
                "created": "2021-01-04T10:09:14",
                "updated": "2021-01-30T08:19:52",
                "statu": 1,
                "name": "普通用戶",
                "code": "normal",
                "remark": "只有基本查看功能",
                "menuIds": []
            },
            {
                "id": 6,
                "created": "2021-01-16T13:29:03",
                "updated": "2021-01-17T15:50:45",
                "statu": 1,
                "name": "超級管理員",
                "code": "admin",
                "remark": "系統默認最高權限,不可以編輯和任意修改",
                "menuIds": []
            }
        ],
        "total": 2,
        "size": 10,
        "current": 1,
        "orders": [],
        "optimizeCountSql": true,
        "hitCount": false,
        "countId": null,
        "maxLimit": null,
        "searchCount": true,
        "pages": 1
    }

    return Result

})

// 根據id獲取角色資訊
Mock.mock(RegExp('/sys/role/info/*'), 'get', () => {

    Result.data = {
        "id": 6,
        "created": "2021-01-16T13:29:03",
        "updated": "2021-01-17T15:50:45",
        "statu": 1,
        "name": "超級管理員",
        "code": "admin",
        "remark": "系統默認最高權限,不可以編輯和任意修改",
        "menuIds": [1,3]
    }

    return Result
})

// 保存或修改角色
Mock.mock(RegExp('/sys/role/*'), 'post', () => {

    return Result
})

// 洗掉角色
Mock.mock(RegExp('/sys/role/delete/*'), 'delete', () => {

    return Result
})

21.2views/system/Role.vue頁面

<template>
  <div>
    <!--    頂部搜索新增洗掉按鈕  start-->
    <el-form :inline="true" :model="searchForm" class="demo-form-inline">
      <el-form-item label="名稱">
        <el-input v-model="searchForm.name"
                  placeholder="角色名稱"
                  clearable></el-input>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="getRoleList()">查詢</el-button>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="onSubmit">新增</el-button>
      </el-form-item>
      <el-form-item>
        <el-popconfirm title="這是一段內容確定批量洗掉嗎?" @confirm="delByIds">
          <el-button type="danger" slot="reference" :disabled="delStatus">批量洗掉</el-button>
        </el-popconfirm>
      </el-form-item>
    </el-form>
    <!--    頂部搜索新增洗掉按鈕  end-->

    <!--    資料table start -->
    <el-table
        ref="multipleTable"
        :data="tableData"
        tooltip-effect="dark"
        border
        stripe
        style="width: 100%"
        @selection-change="handleSelectionChange">
      <el-table-column type="selection" width="55"></el-table-column>
      <el-table-column prop="name" label="名稱" width="120"></el-table-column>
      <el-table-column prop="code" label="唯一編碼" width="120"></el-table-column>
      <el-table-column prop="remark" label="描述"></el-table-column>
      <el-table-column prop="statu" label="狀態" width="120">
        <template slot-scope="scope">
          <el-tag v-if="scope.row.statu === 0" size="small" type="danger">禁用</el-tag>
          <el-tag v-else-if="scope.row.statu === 1" size="small" type="success">正常</el-tag>
        </template>
      </el-table-column>

      <!--      fixed="right"固定操作欄-->
      <el-table-column prop="icon" label="操作" width="200" fixed="right">
        <template slot-scope="scope">
          <el-button type="text" @click="perHandle(scope.row)">分配權限</el-button>
          <el-divider direction="vertical"></el-divider>
          <el-button type="text" @click="changeUpdate(scope.row)">編輯</el-button>
          <el-divider direction="vertical"></el-divider>

          <!--          <el-button type="text">洗掉</el-button>-->
          <!--          洗掉-->
          <template>
            <el-popconfirm title="這是一段內容確定洗掉嗎?" @confirm="delHandle(scope.row)">
              <el-button type="text" slot="reference">洗掉</el-button>
            </el-popconfirm>
          </template>
        </template>
      </el-table-column>
    </el-table>
    <!--    資料table end -->

    <!--    分頁功能 start
       :current-page="current"  當前頁
        :page-size="size"  每頁有幾條
        :total="total"  總條數
    -->
    <el-pagination
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
        layout="total, sizes, prev, pager, next, jumper"
        :page-sizes="[10, 20, 50, 100]"
        :current-page="current"
        :page-size="size"
        :total="total">
    </el-pagination>
    <!--    分頁功能 end-->

    <!--    新增或修改的彈窗 start-->
    <el-dialog
        title="新增"
        :visible.sync="dialogVisible"
        width="500px"
        :before-close="handleClose">
      <!--      彈框里的表單-->
      <el-form :model="editForm" :rules="rules" ref="editForm" label-width="100px" class="demo-editForm">
        <el-form-item label="角色名稱" prop="name" label-width="100px">
          <el-input v-model="editForm.name" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="唯一編碼" prop="code" label-width="100px">
          <el-input v-model="editForm.code" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="描述" prop="remark" label-width="100px">
          <el-input v-model="editForm.remark" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="狀態" prop="statu" label-width="100px">
          <el-radio-group v-model="editForm.statu">
            <el-radio :label=0>禁用</el-radio>
            <el-radio :label=1>正常</el-radio>
          </el-radio-group>
        </el-form-item>
      </el-form>

      <div slot="footer" class="dialog-footer">
        <el-button @click="resetForm('editForm')">取 消</el-button
        <el-button type="primary" @click="submitEditForm('editForm')">確 定</el-button>
      </div>

    </el-dialog>
    <!--    新增或修改的彈窗 end-->

    <!--    分配權限 start-->
    <el-dialog
        title="分配權限"
        :visible.sync="dialogPrem"
        width="500px"
    >
      <!--      分配權限樹
      defaultProps 分配權限指定label和children
       :default-expand-all=true 屬性全部展開
       check-strictly="true" 子父節點不再強制關聯
      -->
      <el-tree
          :data="premData"
          show-checkbox
          node-key="id"
          ref="premTree"
          :check-strictly="true"
          :default-expand-all=true
          :props="defaultProps">
      </el-tree>

      <div slot="footer" class="dialog-footer">
        <el-button @click="handleClose">取 消</el-button>
        <el-button type="primary" @click="submitPremForm('permForm')">分配權限</el-button>
      </div>

    </el-dialog>
    <!--    分配權限 end-->
  </div>
</template>

<script>

export default {
  name: 'Role',
  data() {
    return {
      // 分配權限彈框
      dialogPrem: false,
      // 分配權限資料來源
      premData: [],
      // 分配權限指定label和children
      defaultProps: {
        children: 'children',
        label: 'name'
      },
      // 搜索引數
      params: {},
      // 新增或修改提交的資料
      editForm: {},
      // 新增或修改彈框
      dialogVisible: false,
      // 分頁引數(默認為第一頁,每頁10條,總條數0)
      current: 1,
      size: 10,
      total: 0,
      // 搜索條件
      searchForm: {},
      // 頂部洗掉
      delStatus: true,
      // 多選框
      multipleSelection: [],
      // 洗掉時的ids
      ids: [],
      // 串列資料
      tableData: [],
      // 表單校驗
      rules: {
        name: [{required: true, message: '請輸入角色名稱', trigger: 'blur'}],
        code: [{required: true, message: '請輸入唯一編碼', trigger: 'blur'}],
        statu: [{required: true, message: '請選擇狀態', trigger: 'blur'}]
      },

    }
  },
  created() {
    this.getRoleList()
  },
  methods: {
    toggleSelection(rows) {
      if (rows) {
        rows.forEach(row => {
          this.$refs.multipleTable.toggleRowSelection(row);
        });
      } else {
        this.$refs.multipleTable.clearSelection();
      }
    },

    // 分頁方法
    handleSizeChange(val) {
      console.log(`每頁 ${val} 條`);
      this.size = val;
      this.getRoleList();
    },
    // 分頁方法
    handleCurrentChange(val) {
      console.log(`當前頁: ${val}`);
      this.current = val;
      this.getRoleList();
    },
    // 點擊新增
    onSubmit() {
      // 點擊新增彈窗
      this.dialogVisible = true
    },
    // X 關閉
    handleClose(done) {
      this.editForm = {}
      this.dialogVisible = false
      // 分配權限彈框
      this.dialogPrem = false,
          // 分配權限資料來源
          this.premData = []
    },
    // 清空表單的方法
    resetForm(formName) {
      debugger;
      this.$refs[formName].resetFields();
      this.editForm = {}
      this.dialogVisible = false
    },
    // 查詢串列
    getRoleList() {
      // 搜索的引數
      this.params = {
        name: this.searchForm.name,
        current: this.current,
        size: this.size
      },
          // 發起請求
          this.$axios.get("/sys/role/list", this.params).then(res => {
            this.tableData = res.data.data.records;
            this.size = res.data.data.size;
            this.current = res.data.data.current;
            this.total = res.data.data.total;
          })
    },
    // 新增或修改方法
    submitEditForm(editForm) {
      this.$refs["editForm"].validate((valid) => {
        if (valid) {
          // 如果id不為null,修改
          debugger
          this.$axios.post("/sys/role/" + (this.editForm.id ? 'update' : 'save'), editForm).then(res => {
            // 保存成功彈框訊息提醒
            this.$message({
              message: '操作成功',
              type: 'success',
              // onClose是保存成功后關閉后查詢串列
              onClose: () => {
                this.getRoleList()
              }
            });
            this.editForm = {}
            this.dialogVisible = false;
          })

        }
      });
    },
    // 編輯回顯資料
    changeUpdate(row) {
      this.$axios.get("/sys/role/info/" + row.id).then(res => {
        this.editForm = res.data.data
        this.dialogVisible = true
      })
    },
    // 單個洗掉
    delHandle(row) {
      let id = row.id;
      this.$axios.delete("/sys/role/delete/" + id).then(res => {
        // 洗掉成功彈框訊息提醒
        this.$message({
          message: '洗掉成功',
          type: 'success',
          // onClose是保存成功后關閉后查詢串列
          onClose: () => {
            this.getMenuTree()
          }
        });
      })
    },
    // 批量洗掉(選擇多選框激活批量洗掉)
    handleSelectionChange(val) {
      console.log("多選框", val)
      this.multipleSelection = val;
      // 來判斷是否激活批量洗掉
      this.delStatus = val.length == 0
    },
    // 批量洗掉
    delByIds() {
      this.multipleSelection.forEach(row => {
        this.ids.push(row.id)
      })
      console.log(this.ids)
      this.$axios.delete("/sys/role/delete/" + this.ids).then(res => {
        // 洗掉成功彈框訊息提醒
        this.$message({
          message: '批量洗掉成功',
          type: 'success',
          // onClose是保存成功后關閉后查詢串列
          onClose: () => {
            this.getMenuTree()
          }
        });
      })
    },

    // 點擊分配權限
    perHandle(row) {
      // 查詢所有的選單資料
      this.$axios.get("/sys/menu/list").then(res => {
        this.premData = res.data.data;
        console.log(this.premData)
      })

      // 查詢已經存在的
      this.$axios.get("/sys/role/info/" + row.id).then(res => {
        // 通過這個賦值,menuIds里是已選擇的id集合
        this.$refs.premTree.setCheckedKeys(res.data.data.menuIds);
        this.permForm = res.data.data;
      })

      // 彈窗打開
      this.dialogPrem = true

    },

    // 分配權限提交
    submitPremForm() {
      // 通過key已選擇的id
      let menus = this.$refs.premTree.getCheckedKeys();
      console.log(menus);
      this.$axios.post("/sys/role/perm" + this.permForm.id, menus).then(res => {
        // 洗掉成功彈框訊息提醒
        this.$message({
          message: '分配權限成功',
          type: 'success',
          // onClose是保存成功后關閉后查詢串列
          onClose: () => {
            this.getRoleList()
          }
        });
        this.dialogPrem = false;
      })

    },
  }

}
</script>

<style>

/*分頁樣式,向右便宜,上下有10個邊框*/
.el-pagination {
  float: right;
  margin-top: 10px;
}
</style>

22用戶頁面

22.1mock.js模擬后端

 用戶管理 

Mock.mock(RegExp('/sys/user/list*'), 'get', () => {
    Result.data = {
        "records": [
            {
                "id": 1,
                "created": "2021-01-12T22:13:53",
                "updated": "2021-01-16T16:57:32",
                "statu": 1,
                "username": "admin",
                "password": "$2a$10$R7zegeWzOXPw871CmNuJ6upC0v8D373GuLuTw8jn6NET4BkPRZfgK",
                "avatar": "https://image-1300566513.cos.ap-guangzhou.myqcloud.com/upload/images/5a9f48118166308daba8b6da7e466aab.jpg",
                "email": "123@qq.com",
                "city": "廣州",
                "lastLogin": "2020-12-30T08:38:37",
                "roles": [
                    {
                        "id": 6,
                        "created": "2021-01-16T13:29:03",
                        "updated": "2021-01-17T15:50:45",
                        "statu": 1,
                        "name": "超級管理員",
                        "code": "admin",
                        "remark": "系統默認最高權限,不可以編輯和任意修改",
                        "menuIds": []
                    },
                    {
                        "id": 3,
                        "created": "2021-01-04T10:09:14",
                        "updated": "2021-01-30T08:19:52",
                        "statu": 1,
                        "name": "普通用戶",
                        "code": "normal",
                        "remark": "只有基本查看功能",
                        "menuIds": []
                    }
                ]
            },
            {
                "id": 2,
                "created": "2021-01-30T08:20:22",
                "updated": "2021-01-30T08:55:57",
                "statu": 1,
                "username": "test",
                "password": "$2a$10$0ilP4ZD1kLugYwLCs4pmb.ZT9cFqzOZTNaMiHxrBnVIQUGUwEvBIO",
                "avatar": "https://image-1300566513.cos.ap-guangzhou.myqcloud.com/upload/images/5a9f48118166308daba8b6da7e466aab.jpg",
                "email": "test@qq.com",
                "city": null,
                "lastLogin": null,
                "roles": [
                    {
                        "id": 3,
                        "created": "2021-01-04T10:09:14",
                        "updated": "2021-01-30T08:19:52",
                        "statu": 1,
                        "name": "普通用戶",
                        "code": "normal",
                        "remark": "只有基本查看功能",
                        "menuIds": []
                    }
                ]
            }
        ],
        "total": 2,
        "size": 10,
        "current": 1,
        "orders": [],
        "optimizeCountSql": true,
        "hitCount": false,
        "countId": null,
        "maxLimit": null,
        "searchCount": true,
        "pages": 1
    }

    return Result
})


Mock.mock(RegExp('/sys/user/*'), 'post', () => {
    return Result
})

Mock.mock(RegExp('/sys/user/info/*'), 'get', () => {

    Result.data = {
        "id": 2,
        "created": "2021-01-30T08:20:22",
        "updated": "2021-01-30T08:55:57",
        "statu": 1,
        "username": "test",
        "password": "$2a$10$0ilP4ZD1kLugYwLCs4pmb.ZT9cFqzOZTNaMiHxrBnVIQUGUwEvBIO",
        "avatar": "https://image-1300566513.cos.ap-guangzhou.myqcloud.com/upload/images/5a9f48118166308daba8b6da7e466aab.jpg",
        "email": "test@qq.com",
        "city": null,
        "lastLogin": null,
        "roles": []
    }
    return Result
})

22.2views/system/User.vue頁面

<template>
  <div> <!--搜索框-->
    <el-form :inline="true" :model="searchForm">
      <el-form-item>
        <el-input v-model="searchForm.username" placeholder="名稱" clearable></el-input>
      </el-form-item>
      <el-form-item>
        <el-button>搜索</el-button>
        <!--         :v-if="hasAuth('sys:user:save')-->
        <el-button type="primary"
                   @click="dialogFormVisible = true"
                   :v-if="hasAuth('sys:user:save')"
        >新增
        </el-button>
        <el-popconfirm title="確定要洗掉這些記錄嗎?" @confirm="delHandle(null)" style="margin-left: 10px;"
        >
          <el-button type="danger" slot="reference" :disabled="delBtnStu">批量洗掉</el-button>
        </el-popconfirm>
      </el-form-item>
    </el-form>
    <!--串列-->
    <el-table ref="multipleTable" border stripe :data="tableData" tooltip-effect="dark" style="width: 100%"
              @selection-change="handleSelectionChange">
      <el-table-column type="selection" width="55"></el-table-column>
      <el-table-column label="頭像" width="50">
        <template slot-scope="scope">
          <el-avatar size="small" :src="scope.row.avatar"></el-avatar>
        </template>
      </el-table-column>
      <el-table-column prop="username" label="用戶名" width="120"></el-table-column>
      <el-table-column label="角色名稱" width="180">
        <template slot-scope="scope">
          <el-tag style="margin-right: 5px;" size="small" type="info" v-for="item in scope.row.roles">{{ item.name }}
          </el-tag>
        </template>
      </el-table-column>
      <el-table-column prop="email" label="郵箱"></el-table-column>
      <el-table-column prop="phone" label="手機號"></el-table-column>
      <el-table-column label="狀態">
        <template slot-scope="scope">
          <el-tag v-if="scope.row.statu === 0" size="small" type="danger">禁用</el-tag>
          <el-tag v-else-if="scope.row.statu === 1" size="small" type="success">正常</el-tag>
        </template>
      </el-table-column>
      <el-table-column prop="created" label="創建時間" width="200"></el-table-column>
      <el-table-column width="260px" label="操作">
        <template slot-scope="scope">
          <el-button type="text" @click="roleHandle(scope.row.id)">分配角色</el-button>
          <el-divider direction="vertical"></el-divider>
          <el-button type="text" @click="repassHandle(scope.row.id, scope.row.username)"
          >重置密碼
          </el-button>
          <el-divider direction="vertical"></el-divider>
          <el-button type="text" @click="editHandle(scope.row.id)">編輯</el-button>
          <el-divider direction="vertical"></el-divider>
          <el-popconfirm title="確定要洗掉這條記錄嗎?" @confirm="delHandle(scope.row.id)">
            <el-button type="text" slot="reference">洗掉</el-button>
          </el-popconfirm>
        </template>
      </el-table-column>
    </el-table>

    <!--頁碼-->
    <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="current"
                   :page-sizes="[10, 20, 50, 100]" :page-size="size" layout="total, sizes, prev, pager, next, jumper"
                   :total="total"></el-pagination>
    <el-dialog title="用戶資訊" :visible.sync="dialogFormVisible" width="600px">
      <el-form :model="editForm" :rules="editFormRules" ref="editForm">
        <el-form-item label="用戶名" prop="username" label-width="100px">
          <el-input v-model="editForm.username" autocomplete="off"></el-input>
          <el-alert title="初始密碼為888888" :closable="false" type="info" style="line-height: 12px;"></el-alert>
        </el-form-item>
        <el-form-item label="郵箱" prop="email" label-width="100px">
          <el-input v-model="editForm.email" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="手機號" prop="phone" label-width="100px">
          <el-input v-model="editForm.phone" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="狀態" prop="statu" label-width="100px">
          <el-radio-group v-model="editForm.statu">
            <el-radio :label="0">禁用</el-radio>
            <el-radio :label="1">正常</el-radio>
          </el-radio-group>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="resetForm('editForm')">取 消</el-button>
        <el-button type="primary" @click="submitEditForm('editForm')">確 定</el-button>
      </div>
    </el-dialog>

    <!-- 分配權限對話框 -->
    <el-dialog title="分配角色" :visible.sync="roleDialogFormVisible" width="600px" @closed="resetForm('roleForm')">
      <el-form :model="roleForm" ref="roleForm">
        <el-tree :data="roleTreeData" show-checkbox ref="roleTree" node-key="id" :default-expand-all=true
                 :props="defaultProps"></el-tree>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="resetForm('roleForm')">取 消</el-button>
        <el-button type="primary" @click="submitRoleForm('roleForm')">確 定</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>

export default {
  name: 'User',
  data() {
    return {
      searchForm: {username: ''},
      editForm: {},
      editFormRules: {
        username: [{required: true, message: '請輸入用戶名稱', trigger: 'blur'}],
        email: [{required: true, message: '請輸入郵箱', trigger: 'blur'}],
        statu: [{required: true, message: '請選擇狀態', trigger: 'blur'}]
      },
      current: 1,
      total: 0,
      size: 10,
      dialogFormVisible: false,
      tableData: [],
      multipleSelection: [],
      delBtnStu: true,
      roleDialogFormVisible: false,
      roleForm: {},
      defaultProps: {children: 'children', label: 'name'},
      roleTreeData: [],
      treeCheckedKeys: [],
    }
  }, methods: {
    toggleSelection(rows) {
      if (rows) {
        rows.forEach(row => {
          this.$refs.multipleTable.toggleRowSelection(row);
        });
      } else {
        this.$refs.multipleTable.clearSelection();
      }
    }, handleSelectionChange(rows) {
      this.multipleSelection = rows;
      this.delBtnStu = rows.length == 0
    }, getUserList() {
      this.$axios.get('/sys/user/list', {
        params: {
          name: this.searchForm.name,
          current: this.current,
          size: this.size
        }
      }).then(res => {
        this.tableData = res.data.data.records
        this.current = res.data.data.current
        this.size = res.data.data.size
        this.total = res.data.data.total
      })
    }, handleSizeChange(val) {
      this.size = val
      this.getUserList()
    }, handleCurrentChange(val) {
      this.current = val
      this.getUserList()
    }, submitEditForm(formName) {
      this.$refs[formName].validate((valid) => {
        if (valid) {
          this.$axios.post('/sys/user/' + (this.editForm.id ? "update" : "save"), this.editForm).then(res => {
            console.log(res.data)
            this.resetForm(formName)
            this.$message({
              showClose: true, message: '恭喜你,操作成功', type: 'success', onClose: () => {
                this.getUserList()
              }
            });
          })
        } else {
          console.log('error submit!!');
          return false;
        }
      });
    }, editHandle(id) {
      console.log(id)
      this.$axios.get("/sys/user/info/" + id).then(res => {
        this.editForm = res.data.data
        this.dialogFormVisible = true
      })
    }, delHandle(id) {
      var ids = []
      id ? ids.push(id) : this.multipleSelection.forEach(row => {
        ids.push(row.id)
      })
      console.log(ids)
      this.$axios.post("/sys/user/delete", ids).then(res => {
        this.$message({
          showClose: true, message: '恭喜你,操作成功', type: 'success', onClose: () => {
            this.getUserList()
          }
        });
      })
    }, resetForm(formName) {
      this.$refs[formName].resetFields();
      this.editForm = {}
      this.dialogFormVisible = false
      this.roleDialogFormVisible = false
    }, roleHandle(id) {
      this.$axios.get("/sys/user/info/" + id).then(res => {
        const sysuser = res.data.data
        var roleIds = []
        sysuser.roles.forEach(row => {
          roleIds.push(row.id)
        })
        console.log("roleIds")
        console.log(roleIds)
        this.roleForm = res.data.data
        console.log("this.treeCheckedKeys")
        console.log(this.treeCheckedKeys)
        this.$axios.get("/sys/role/list").then(res => {
          this.roleTreeData = res.data.data.records
          this.$refs.roleTree.setCheckedKeys(roleIds);
        })
      })
      this.roleDialogFormVisible = true
    }, submitRoleForm(formName) {
      var roleIds = []
      roleIds = this.$refs.roleTree.getCheckedKeys()
      console.log(roleIds)
      console.log(this.roleForm.id)
      this.$axios.post("/sys/user/role/" + this.roleForm.id, roleIds).then(res => {
        this.$message({
          showClose: true, message: '恭喜你,操作成功', type: 'success', onClose: () => {
            this.resetForm(formName)
            this.getUserList()
          }
        });
        this.roleDialogFormVisible = false
      })
    }, repassHandle(id, username) {
      this.$confirm('將重置用戶【' + username + '】的密碼, 是否繼續?', '提示', {
        confirmButtonText: '確定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        this.$axios.post("/sys/user/repass", id).then(res => {
          this.$message({
            showClose: true, message: '恭喜你,操作成功', type: 'success', onClose: () => {
            }
          });
        })
      })
    }
  },
  created() {
    this.getUserList()
  },

}</script>

<style>

</style>

23前端權限控制

23.1在src/main.js添加

Vue.mixin({
    methods: {
        hasAuth(perm) {
            var authority = this.$store.state.menus.permList
            console.log(authority)
            return authority.indexOf(perm) > -1
        }
    }
})

23.2使用權限

:v-if="hasAuth('sys:user:save')"

23.2mock.js模擬回傳權限

// 權限
let authoritys = ['sys:user:save']

23.3其他

之前寫的

1.在src/store/modules/menus.js設定權限的方法

2.在src/router/index.js拿到權限并設定到localStorage中

// 拿到用戶權限
store.commit("setPermList", res.data.data.authoritys)

24結束語

VueAdmin - 前后端分離后臺管理系統

線上演示:https://www.markerhub.com/vueadmin
登錄密碼:1234567

前端筆記:https://shimo.im/docs/pxwyJHgqcWjWkTKX/

后端筆記:https://shimo.im/docs/OnZDwoxFFL8bnP1c/

原始碼分享:
https://github.com/markerhub/vueadmin
https://gitee.com/markerhub/VueAdmin

視頻講解:https://www.bilibili.com/video/BV1af4y1s7Wh/

轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/302249.html

標籤:其他

上一篇:ES6 新特性知識點總結

下一篇:Java專案:在線水果商城系統(java+JSP+Spring+SpringMVC +MyBatis+html+mysql)

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • vue移動端上拉加載

    可能做得過于簡單或者比較low,請各位大佬留情,一起探討技術 ......

    uj5u.com 2020-09-10 04:38:07 more
  • 優美網站首頁,頂部多層導航

    一個個人用的瀏覽器首頁,可以把一下常用的網站放在這里,平常打開會比較方便。 第一步,HTML代碼 <script src=https://www.cnblogs.com/szharf/p/"js/jquery-3.4.1.min.js"></script> <div id="navigate"> <ul> <li class="labels labels_1"> ......

    uj5u.com 2020-09-10 04:38:47 more
  • 頁面為要加<!DOCTYPE html>

    最近因為寫一個js函式,需要用到$(window).height(); 由于手寫demo的時候,過于自信,其實對前端方面的認識也不夠體系,用文本檔案直接敲出來的html代碼,第一行沒有加上<!DOCTYPE html> 導致了$(window).height();的結果直接是整個document的高 ......

    uj5u.com 2020-09-10 04:38:52 more
  • WordPress網站程式手動升級要做好資料備份

    WordPress博客網站程式在進行升級前,必須要做好網站資料的備份,這個問題良家佐言是遇見過的;在剛開始接觸WordPress博客程式的時候,因為升級問題和博客網站的修改的一些嘗試,良家佐言是吃盡了苦頭。因為購買的是西部數碼的空間和域名,每當佐言把自己的WordPress博客網站搞到一塌糊涂的時候 ......

    uj5u.com 2020-09-10 04:39:30 more
  • WordPress程式不能升級為5.4.2版本的原因

    WordPress是一款個人博客系統,受到英文博客愛好者和中文博客愛好者的追捧,并逐步演化成一款內容管理系統軟體;它是使用PHP語言和MySQL資料庫開發的,用戶可以在支持PHP和MySQL資料庫的服務器上使用自己的博客。每一次WordPress程式的更新,就會牽動無數WordPress愛好者的心, ......

    uj5u.com 2020-09-10 04:39:49 more
  • 使用CSS3的偽元素進行首字母下沉和首行改變樣式

    網頁中常見的一種效果,首字改變樣式或者首行改變樣式,效果如下圖。 代碼: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, ......

    uj5u.com 2020-09-10 04:40:09 more
  • 關于a標簽的講解

    什么是a標簽? <a> 標簽定義超鏈接,用于從一個頁面鏈接到另一個頁面。 <a> 元素最重要的屬性是 href 屬性,它指定鏈接的目標。 a標簽的語法格式:<a href=https://www.cnblogs.com/summerxbc/p/"指定要跳轉的目標界面的鏈接">需要展示給用戶看見的內容</a> a標簽 在所有瀏覽器中,鏈接的默認外觀如下: 未被訪問的鏈接帶 ......

    uj5u.com 2020-09-10 04:40:11 more
  • 前端輪播圖

    在需要輪播的頁面是引入swiper.min.js和swiper.min.css swiper.min.js地址: 鏈接:https://pan.baidu.com/s/15Uh516YHa4CV3X-RyjEIWw 提取碼:4aks swiper.min.css地址 鏈接:https://pan.b ......

    uj5u.com 2020-09-10 04:40:13 more
  • 如何設定html中的背景圖片(全屏顯示,且不拉伸)

    1 <style>2 body{background-image:url(https://uploadbeta.com/api/pictures/random/?key=BingEverydayWallpaperPicture); 3 background-size:cover;background ......

    uj5u.com 2020-09-10 04:40:16 more
  • Java學習——HTML詳解(上)

    HTML詳解 初識HTML Hyper Text Markup Language(超文本標記語言) 1 <!--DOCTYPE:告訴瀏覽器我們要使用什么規范--> 2 <!DOCTYPE html> 3 <html lang="en"> 4 <head> 5 <!--meta 描述性的標簽,描述一些 ......

    uj5u.com 2020-09-10 04:40:33 more
最新发布
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

    好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......

    uj5u.com 2023-04-20 07:59:23 more
  • 生產事故-走近科學之消失的JWT

    入職多年,面對生產環境,盡管都是小心翼翼,慎之又慎,還是難免捅出簍子。輕則滿頭大汗,面紅耳赤。重則系統停擺,損失資金。每一個生產事故的背后,都是寶貴的經驗和教訓,都是專案成員的血淚史。為了更好地防范和遏制今后的各類事故,特開此專題,長期更新和記錄大大小小的各類事故。有些是親身經歷,有些是經人耳傳口授 ......

    uj5u.com 2023-04-18 07:55:04 more
  • 記錄--Canvas實作打飛字游戲

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 打開游戲界面,看到一個畫面簡潔、卻又富有挑戰性的游戲。螢屏上,有一個白色的矩形框,里面不斷下落著各種單詞,而我需要迅速地輸入這些單詞。如果我輸入的單詞與螢屏上的單詞匹配,那么我就可以獲得得分;如果我輸入的單詞錯誤或者時間過長,那么我就會輸 ......

    uj5u.com 2023-04-04 08:35:30 more
  • 了解 HTTP 看這一篇就夠

    在學習網路之前,了解它的歷史能夠幫助我們明白為何它會發展為如今這個樣子,引發探究網路的興趣。下面的這張圖片就展示了“互聯網”誕生至今的發展歷程。 ......

    uj5u.com 2023-03-16 11:00:15 more
  • 藍牙-低功耗中心設備

    //11.開啟藍牙配接器 openBluetoothAdapter //21.開始搜索藍牙設備 startBluetoothDevicesDiscovery //31.開啟監聽搜索藍牙設備 onBluetoothDeviceFound //30.停止監聽搜索藍牙設備 offBluetoothDevi ......

    uj5u.com 2023-03-15 09:06:45 more
  • canvas畫板(滑鼠和觸摸)

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>canves</title> <style> #canvas { cursor:url(../images/pen.png),crosshair; } #canvasdiv{ bo ......

    uj5u.com 2023-02-15 08:56:31 more
  • 手機端H5 實作自定義拍照界面

    手機端 H5 實作自定義拍照界面也可以使用 MediaDevices API 和 <video> 標簽來實作,和在桌面端做法基本一致。 首先,使用 MediaDevices.getUserMedia() 方法獲取攝像頭媒體流,并將其傳遞給 <video> 標簽進行渲染。 接著,使用 HTML 的 < ......

    uj5u.com 2023-01-12 07:58:22 more
  • 記錄--短視頻滑動播放在 H5 下的實作

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 短視頻已經無數不在了,但是主體還是使用 app 來承載的。本文講述 H5 如何實作 app 的視頻滑動體驗。 無聲勝有聲,一圖頂百辯,且看下圖: 網址鏈接(需在微信或者手Q中瀏覽) 從上圖可以看到,我們主要實作的功能也是本文要講解的有: ......

    uj5u.com 2023-01-04 07:29:05 more
  • 一文讀懂 HTTP/1 HTTP/2 HTTP/3

    從 1989 年萬維網(www)誕生,HTTP(HyperText Transfer Protocol)經歷了眾多版本迭代,WebSocket 也在期間萌芽。1991 年 HTTP0.9 被發明。1996 年出現了 HTTP1.0。2015 年 HTTP2 正式發布。2020 年 HTTP3 或能正... ......

    uj5u.com 2022-12-24 06:56:02 more
  • 【HTML基礎篇002】HTML之form表單超詳解

    ??一、form表單是什么

    ??二、form表單的屬性

    ??三、input中的各種Type屬性值

    ??四、標簽 ......

    uj5u.com 2022-12-18 07:17:06 more