學習了狂神的vue課程后又看了這個vue的項目,因為狂神的很基礎而且講的是vue2,就打算看一個項目再加深一下,從這個課程學到了很多的東西,老師講解的很細致,我把其中我不太熟悉或者重要的點記錄下來。
課程鏈接:https://www.bilibili.com/video/BV1EE411B7SU?p=12
一:環境搭建(P1-P7)
系統展示:http://118.31.171.210/#/rights
系統功能列表:

前端項目技術棧
- Vue(vue3)
- Vue-router
- Element-UI
- Axios
- Echarts
后端項目技術棧
-
Node.js
-
Express
-
Jwt
-
Mysql
-
Sequelize
資源下載:
該項目實例: http://shop.liulongbin.top/
前端代碼:https://gitee.com/wBekvam/vue-shop-admin.git
后端代碼:https://gitee.com/wBekvam/vueShop-api-server.git
頁面預覽:http://shop.liulongbin.top/
配套資料鏈接:
https://pan.baidu.com/s/1FX_1sz0Xj-0r1R_E12Qh1g
提取碼: 8exy
vue3創建項目
一、3.0 新加入了 TypeScript 以及 PWA 的支持
二、部分命令發生了變化:
1、下載安裝 npm install -g vue@cli
2、刪除了vue list
3、創建項目 vue create
4、啟動項目 npm run serve
三、默認項目目錄結構也發生了變化:
1、移除了配置文件目錄,config 和 build 文件夾
2、移除了 static 文件夾,新增 public 文件夾,並且 index.html 移動到 public 中
3、在 src 文件夾中新增了 views 文件夾,用於分類 視圖組件 和 公共組件
首先 安裝npm3.0,在安裝之前如果曾經安裝過vue-cli2的,由於vue cli 升級到3后,package的名字從 vue-cli 改為了 @vue/cli,裝了舊版本的,建議把舊版本的卸載了,再安裝新版本。
// 卸載指令如下: npm uninstall vue-cli -g or yarn global remove vue-cli
新版本:
npm install -g @vue/cli
vue3提供了可視化創建項目,非常方便。相關視頻:https://www.bilibili.com/video/BV1EE411B7SU?p=5
可視化操作:
win+R 運行cmd
輸入vue ui 進入界面
點擊創建按鈕,進入到目錄選擇面板(http://localhost:8000/project/select)


安裝babel router formatter

注意下面第一行的Linter不能勾選。很容易出問題、




創建完成

配置Element-UI組件



配置axios


要配置碼雲,來及時把項目上傳和更新到雲端。
首先我們要擁有一個碼雲賬號,同時注冊的郵箱要設置為公開,配置ssh公鑰:https://www.bilibili.com/video/BV1EE411B7SU?p=6
完成后就要把本地項目托管到碼雲上:課程的視頻
注意創建項目時,不能用readme初始化倉庫,下面的不要勾選

在工程目錄下,shift+右鍵
Git 全局設置:
git config --global user.name "阿里"
git config --global user.email "1957598452@qq.com"
剛創建的倉庫有這個頁面

因為我們已有倉庫,所以之后要執行圖像后三行代碼,之前我要要看一下git的狀態,並把修改后的在提交到緩存

就可以。
之后使用phpmysql程序集成包,導入mysql,https://www.bilibili.com/video/BV1EE411B7SU?p=8
運行后端程序,讓后端接口跑起來,這些都是提供好的,直接用,可以用postman直接測試接口,文檔里也提供了相關的軟件和程序和接口說明。:https://www.bilibili.com/video/BV1EE411B7SU?p=9
token用來維持前端和后端是跨域(不在同一個ip+port)的狀態。
用idea打開編譯器,在終端把給項目創建切換到分支里面,這樣我們以后再更新代碼到主分支里面:https://www.bilibili.com/video/BV1EE411B7SU?p=11

刪除項目里面多余的東西,保持項目干凈,主要是圖像和初始的組件。
項目啟動也是在可視化界面進行的。

二:登入退出功能(P8-P29)
打開vue_api_ server 在終端輸入node .\app.js啟動后端項目,使得前端和后端建立連接,前端可以訪問后端接口,文檔提供了詳細的接口使用說明,可以使用postman直接測試。
先設計登入頁面如下:

因為要開發新功能,這時我們可以建立一個分支,在新的分支里面開發,最后merge到主分支里面,,命令為:git checkout -b login, git branch可以查看當前所有的分支。
對於新建的項目,為了后續的開發,刪除沒有用的項目。
先創建一個登入組件Login.vue,並注冊到路由./router/index.js里面,同時為了路由頁面都生效,還需要在App.vue里面加入路由占位符。注意初始頁面會自動跳轉到登入頁面
在assets/css/定義一下樣式global.css,並導入到main.js入口文件里面
html,body,#app{ height: 100%; margin: 0; padding: 0; }
設計登入頁面的樣子,使用到了element-ui 的表單和按鈕
template的代碼如下
<div class="addColor"> <div class="login_box"> <div class="avatar_box"> <img src="../assets/logo.png"> </div> <el-form ref="loginFormRef" :model="loginForm" label-width="0px" class="login_form" :rules="loginFormRules"> <!--用戶名 --> <el-form-item prop="username"> <el-input placeholder="用戶名" v-model="loginForm.username" prefix-icon="iconfont icon-user"> </el-input> </el-form-item> <!--密碼 --> <el-form-item prop="password" > <el-input placeholder="密碼" v-model="loginForm.password" type="password" prefix-icon="iconfont icon-3702mima"></el-input> </el-form-item> <!--按鈕 --> <el-form-item class="btn"> <el-button type="primary" @click="login">登入</el-button> <el-button type="info" @click="resetLoginForm" >重置</el-button> </el-form-item > </el-form> </div> </div>
具體的樣式設計的代碼如下
<style scoped Lang="less"> .addColor{ background-color: #2b4b6b; height: 100%; } .login_box { background-color: white; height: 350px; width: 450px; border-radius: 3px; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } .avatar_box { height: 130px; width: 130px; border-radius: 50%; border: 1px solid white; padding: 20px; box-shadow: 0 0 10px #ddd; position: absolute; left: 50%; transform: translate(-50%, -50%); } img{ height: 100%; width: 100%; background-color: white; border-radius: 50%; } .btn{ display: flex; justify-content: flex-end; } .login_form{ position: absolute; bottom: 0; width: 100%; padding: 0px 20px; box-sizing: border-box; } </style>
其中我們給input里面添加了icon,這個icon可以使用element自帶的,也可以使用阿里雲圖標庫的,本項目是使用阿里雲的,在assets里面導入fonts文件,並在main.js導入,就可以使用prefix-icon加入。
使用v-model進行雙向數據綁定,如loginForm

我們使用了element自帶的數據驗證規則進行驗證,使用:rules進行驗證

添加重置功能,要先獲取form表單對象,添加ref引用,調用重置方法


添加表單驗證方法

接下來使用axios進行表單驗證,現在main.js引入axios
import axios from 'axios' Vue.prototype.$http= axios axios.defaults.baseURL = 'http://127.0.0.1:8888/api/private/v1/'
axios向指定路由發請求並傳參,同時接受返回值,傳參可以直接通過表單雙向綁定對象拿到,對返回值類型為promise加入await和async后簡化,只獲取data數據。(視頻鏈接)
login(){ this.$refs.loginFormRef.validate(async valid => { if(!valid) return; // axios.post(`http://127.0.0.1:8888/api/private/v1/login`).then(Response=>(this.loginForm=Response.data)) // console.log(this.loginForm.username) //解析獲取返回值中的data數據 const {data:res} = await this.$http.post("login",this.loginForm) if(res.meta.status !== 200) return this.$message.error("登入失敗") this.$message.success("登入成功") window.sessionStorage.setItem("token",res.data.token) this.$router.push("/Home") }); }
element有彈出信息框message提示登入成功與否:
this.$message.error("登入失敗"),
this.$message.success("登入成功")
登入成功后,添加token記錄,並跳轉到home頁面(添加並注冊),方便后續驗證,所以總的 export default為
export default { name: "Login", data(){ return{ loginForm:{ username:'admin', password:'123456' }, loginFormRules: { username: [ { required: true, message: '請輸入用戶名', trigger: 'blur' }, { min: 3, max: 5, message: '長度在 3 到 5 個字符', trigger: 'blur' }], password: [ { required: true, message: '請輸入密碼', trigger: 'blur' }, { min: 3, max: 10, message: '長度在 3 到 5 個字符', trigger: 'blur' }] } } }, methods:{ resetLoginForm(){ this.$refs.loginFormRef.resetFields(); }, login(){ this.$refs.loginFormRef.validate(async valid => { if(!valid) return; // axios.post(`http://127.0.0.1:8888/api/private/v1/login`).then(Response=>(this.loginForm=Response.data)) // console.log(this.loginForm.username) //解析獲取返回值中的data數據 const {data:res} = await this.$http.post("login",this.loginForm) if(res.meta.status !== 200) return this.$message.error("登入失敗") this.$message.success("登入成功") window.sessionStorage.setItem("token",res.data.token) this.$router.push("/Home") }); } } }
在路由index.js里面,添加路由守衛
router.beforeEach((to, from, next) =>{ if(to.path === '/login') return next() const tokenstr = window.sessionStorage.getItem('token') if(!tokenstr) return next('/login') next() })
在home
頁面可以登出,定義一個登出功能
<template>
<div>
<el-button type="info" @click="logout">退出</el-button>
</div>
</template>
<script>
export default {
name: "Home",
methods:{
logout(){
window.sessionStorage.clear()
this.$router.push("/login")
}
}
}
</script>
<style scoped>
</style>
此時登入部分就完成了,現在將本地分支導入到主分支里面。具體過程為
git status查看文件狀態。
git add . 將變化的文件提交到緩存區。
git status 在此查看就會發現所以的更新完成
git commit -m '登入部分完成' 把代碼提交到本地緩存區。
git checkout master 切換到主分支
git merge mybranch 合並mybranch分支代碼
git push 本地主分支推送到雲端(因為主分支雲端已經存在,所以不需要創建,但是mybranch不存在,所以要創建才可以)
git checkout login 切換到分支
git push -u origin mybranch 將本地分支推送到雲端
此時架構

global.css
html,body,#app{ height: 100%; margin: 0; padding: 0; }
Home.vue
<template>
<div>
<el-button type="info" @click="logout">退出</el-button>
</div>
</template>
<script>
export default {
name: "Home",
methods:{
logout(){
window.sessionStorage.clear()
this.$router.push("/login")
}
}
}
</script>
<style scoped>
</style>
Login.vue
<template> <div class="addColor"> <div class="login_box"> <div class="avatar_box"> <img src="../assets/logo.png"> </div> <el-form ref="loginFormRef" :model="loginForm" label-width="0px" class="login_form" :rules="loginFormRules"> <!--用戶名 --> <el-form-item prop="username"> <el-input placeholder="用戶名" v-model="loginForm.username" prefix-icon="iconfont icon-user"> </el-input> </el-form-item> <!--密碼 --> <el-form-item prop="password" > <el-input placeholder="密碼" v-model="loginForm.password" type="password" prefix-icon="iconfont icon-3702mima"></el-input> </el-form-item> <!--按鈕 --> <el-form-item class="btn"> <el-button type="primary" @click="login">登入</el-button> <el-button type="info" @click="resetLoginForm" >重置</el-button> </el-form-item > </el-form> </div> </div> </template> <script> import axios from "axios"; export default { name: "Login", data(){ return{ loginForm:{ username:'admin', password:'123456' }, loginFormRules: { username: [ { required: true, message: '請輸入用戶名', trigger: 'blur' }, { min: 3, max: 5, message: '長度在 3 到 5 個字符', trigger: 'blur' }], password: [ { required: true, message: '請輸入密碼', trigger: 'blur' }, { min: 3, max: 10, message: '長度在 3 到 5 個字符', trigger: 'blur' }] } } }, methods:{ resetLoginForm(){ this.$refs.loginFormRef.resetFields(); }, login(){ this.$refs.loginFormRef.validate(async valid => { if(!valid) return; // axios.post(`http://127.0.0.1:8888/api/private/v1/login`).then(Response=>(this.loginForm=Response.data)) // console.log(this.loginForm.username) //解析獲取返回值中的data數據 const {data:res} = await this.$http.post("login",this.loginForm) if(res.meta.status !== 200) return this.$message.error("登入失敗") this.$message.success("登入成功") window.sessionStorage.setItem("token",res.data.token) this.$router.push("/Home") }); } } } </script> <style scoped Lang="less"> .addColor{ background-color: #2b4b6b; height: 100%; } .login_box { background-color: white; height: 350px; width: 450px; border-radius: 3px; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } .avatar_box { height: 130px; width: 130px; border-radius: 50%; border: 1px solid white; padding: 20px; box-shadow: 0 0 10px #ddd; position: absolute; left: 50%; transform: translate(-50%, -50%); } img{ height: 100%; width: 100%; background-color: white; border-radius: 50%; } .btn{ display: flex; justify-content: flex-end; } .login_form{ position: absolute; bottom: 0; width: 100%; padding: 0px 20px; box-sizing: border-box; } </style>
index.js
import Vue from 'vue' import VueRouter from 'vue-router' import Login from "@/components/Login"; import Home from "@/components/Home"; Vue.use(VueRouter) const routes = [ {path: "/", redirect: '/login'}, {path: "/login", component:Login}, {path: "/Home", component:Home}, ] const router = new VueRouter({ routes }) router.beforeEach((to, from, next) =>{ if(to.path === '/login') return next() const tokenstr = window.sessionStorage.getItem('token') if(!tokenstr) return next('/login') next() }) export default router
main.js
import Vue from 'vue' import App from './App.vue' import router from './router' import './assets/css/global.css' import ElementUI from 'element-ui' import 'element-ui/lib/theme-chalk/index.css' import './assets/fonts/iconfont.css' import axios from 'axios' Vue.prototype.$http= axios axios.defaults.baseURL = 'http://127.0.0.1:8888/api/private/v1/' Vue.use(ElementUI) Vue.config.productionTip = false new Vue({ router, render: h => h(App) }).$mount('#app')
App.vue
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
import
<script>
export default {
name:"app"
}
</script>
三:主頁設計(P30-P40)


主頁布局大致如上。

同時進行了樣式的設計主要是顏色和大小的設計
<style scoped> .el-header{ background-color: #373d41; display: flex; justify-content: space-between; padding-left: 0; align-items: center; color: white; font-size: 20px; } div{ display: flex; align-items: center; } span{ margin-left: 15px; } .el-aside{ background-color: #333744; } .el-main { background-color: #eaedf1; } .home-container{ height: 100%; } .iconfont{ margin-right: 10px; } .el-menu{ border-right: none; } .toggle-button{ background-color: #4A5064; font-size: 10px; line-height: 24px; color: #fff; text-align: right; letter-spacing: 0.2em; cursor: pointer; }
此時布局大致如下

現在對header進行設計,加入圖片和文字同時讓按鈕放置在最右邊,效果如下,使用了flex彈性盒模型

header代碼為
<el-header>
<div>
<img src="../assets/heima.png">
<span>電商后台管理系統</span>
</div>
<el-button type="info" @click="logout">退出</el-button>
</el-header>
樣式
.el-header{ background-color: #373d41; display: flex; justify-content: space-between; padding-left: 0; align-items: center; color: white; font-size: 20px; } div{ display: flex; align-items: center; } span{ margin-left: 15px; }
接下來在左側設計菜單布局,菜單分為兩級,可以折疊,使用到了el-menu組件


在用axios請求接口的時候,后台接口做了設計,除了登入接口之外,其它的接口你必須擁有添加了Token驗證的Authorization字段才可以訪問,所以必須在使用axios之前先使用攔截器添加token,保證擁有獲取數據的權限


配置請求的根路徑,為請求添加令牌

接下來我們要為左側菜單賦值,那么就要先從后端獲取左側菜單數據(可以為了方便自己直接寫死?!!),后端接口如下:



接下來就是把獲取的數據渲染到頁面上,對於一級菜單,我們要為一級菜單的每一項是唯一所以和item.id進行綁定,同時加index可以打開一級菜單的時候其他一級菜單不會打開,同時也具有唯一性的性質:

二級菜單一樣:

繼續優化。為選中項顯示選中的顏色
,二級圖標也可以更換
,繼續更換一級菜單的圖標,通過每項的唯一id設置圖標,

unique-opened可以讓保證每次只打開一個一級菜單,只需在el-menu上加入即可。去除側邊欄出現的線條斷斜的問題加入

接下來實現左側側邊欄的收起功能,加入一個如下的按鈕
樣式為
,定義一個折疊函數點擊就讓el-menu的折疊或者展開,collpase為true是展開,同時讓該屬性注冊在data里面,同時加入iscollpase動態修改折疊后側邊欄寬度![]()


我們希望計入home頁面后,home頁面顯示的是welcome頁面,welcome頁面定位為home頁面的子頁面,首先聲明weicome頁面並注冊到index.js,home的孩子就是有welcome,進入home直接跳轉到welcome,這樣要在主頁面設置路由占位符


我們希望點擊二級菜單可以跳轉,此時為菜單設置router屬性,就可以點擊后跳轉到默認路徑為localhost:8080/index路徑
,注意路徑是我們為el-menu-item設置的唯一index,我們改為返回值的path屬性

四:用戶列表設計(P41-P51)
接下來我們創建用戶列表,總體設計如下,首先在components創建user文件夾,同時在該文件夾下創建User文件作為用戶列表視圖,同時作為子路由放在home路由下面。


我們希望點擊二級菜單的時候菜單變色,這時候給側邊欄使用default-active屬性,綁定的值就是每一個二級菜單的唯一index,為每一個二級菜單創建一個點擊方法傳入點擊的是哪一個菜單的index,在方法里面進行注入到session,付給一個值。在data里面創建一個屬性等於session存儲的值,同時讓default-active綁定這個值,這個值可以被點擊方法動態更新,同時希望用戶第一次進入頁面后該屬性被上次所處的記錄賦值。




現在繪制用戶頁面,包含面包屑導航區和卡片視圖區域,卡片區域導入數據視圖

現在在卡片視圖區域補充完整按鈕和搜索框

接下來要補充用戶數據列表視圖,首選獲取數據,之后再進行渲染,下面是獲取數據的后台接口

對於傳入的參數可以封裝成一個數據對象放在data里面,同時,返回的結果也要聲明在data里面,創建一個獲取用戶數據的方法,初始化時就要加載



現在就可以根據獲取的數據進行視圖渲染,border和stripe是實現加邊框線和隔行變色,其中type=index是實現加入一個索引頁:
因為狀態返回的數據是布爾值,無法直接顯示,所以使用一個作用於插槽,把開關按鈕加入進去,通過插槽的scope.row.mg_state可以獲取當前行的數據,進而獲取布爾值進行switch的渲染。

同時對於操作欄我們加入刪除,修改,分配角色按鈕,同樣使用作用域插槽的技術,並為每個按鈕加入content內容提示

繼續實現分頁效果,當頁面大小和頁面切換發生的時候我們調用相應的事件把數據進行更新,同樣數據要在data里面注冊,然后進行雙向數據綁定。



對於狀態欄,我們希望用戶可以點擊開關后,這個狀態可以保存到數據庫,下次直接從數據庫加載,實現數據的記載。修改用戶狀態的接口如下:

定義一個狀態改變的函數,


現在實現搜索框功能,只需雙向綁定給queryInfo就行,這樣用戶輸入的內容就可以直接通過queryInfo傳給后台,后台會進行模糊查詢把結果在此返回前台進行渲染,同時實現了清空搜索框的快捷功能。
五:用戶列表的增刪改(P52-P72)
先去實現用戶的添加
先去添加一個對話框,同時在data里面設置addDialogVisible來進行對話框的顯示與隱藏。給添加按鈕加入事件,控制對話框的顯示與否
![]()



現在對話框如上,很簡單,我們要繼續繪制,實現如下的功能
先加入一個表單,在進行表單驗證。

addFormRules: { username: [{required: true, message: "請輸入用戶名", trigger: 'blur'}, { min: 3, max: 10, message: '用戶名的長度在3-10', trigger: 'blur' }], password: [{required: true, message: "請輸入密碼", trigger: 'blur'}, { min: 3, max: 20, message: '密碼的長度在3-20', trigger: 'blur' }], email: [{required: true, message: "請輸入郵箱", trigger: 'blur'}, { min: 6, max: 28, message: '郵箱的長度在6-28', trigger: 'blur' }, {validator: checkEmail, trigger: 'blur'}], mobile: [{required: true, message: "請輸入電話號碼", trigger: 'blur'}, { min: 3, max: 18, message: '電話號碼的長度在6-18', trigger: 'blur' }, {validator: checkMobile, trigger: 'blur'}], },
注意上面的驗證規則里面也加入了自定義驗證規則,我們自定義了手機號和郵箱的驗證,使用validator加入驗證
var checkEmail = (rule, value, cb) => { const regEmail = /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/ if (regEmail.test(value)) { return cb() } cb(new Error("請輸入合法郵箱")) } var checkMobile = (rule, value, cb) => { const regMobile = /^1[3|4|5|7|8][0-9]{9}$/ if (regMobile.test(value)) { return cb() } cb(new Error("請輸入正確的手機號")) }
之前表單里面有一個ref屬性,擁有這個屬性就可以使用refs里面自定義的方法實現相應的功能,注意refs里面包含所有聲明的ref對象,這些對象可以使用一些特殊的方法實現特定功能,這時候我們希望關閉對話框后取消保留的內容,就可以調用ref的resetFields方法


進行校驗后,用戶點擊確認的時候,我們需要根據驗證的結果做出相應的操作,所以需要為按鈕注冊一個驗證函數


現在實現驗證成功后進行表單數據的提交,先來看一下接口說明,然后就可以使用axios來進行數據提交,提交結束重新刷新視圖



接下來實現修改用戶,類似增加用戶,也要先創建一個form表單,同時在data里面添加狀態屬性editDialogVisible,為按鈕添加事件

接下來實現內容的填充,先在data里面創建一個存儲指定id內容的表單對象editform,同時在表單里面用v-model進行雙向數據綁定



先在可以給對話框加入驗證

關閉對話框下次打開的時候,希望清空上次的記錄
接下來就是提交數據,類似,先為提交按鈕加入點擊事件,實現提交


下面就是刪除數據,我們先為刪除按鈕添加刪除事件,傳入的參數是scope.row.id,當然真正刪除數據之前先詢問用戶是否真的刪除,如果是的話就再調用接口刪除數據,再重新加載數據。
async removeUserById(id){ const confirmResult = await this.$confirm( '此操作將永久刪除該用戶,是否繼續?', '提示', { confirmButtonText: '確定', cancelButtonText: '取消', type: 'warning' } ).catch(err => err) if(confirmResult !== 'confirm'){ return this.$message.info("已取消刪除") } const {data:res} = axios.delete('users/' + id) // const {data:res} = this.$http.delete('users/' + id) if(res.meta.status !== 200){ return this.$message.error("刪除用戶失敗") } this.$message.success("刪除用戶成功") this.getUserList() console.log("確認刪除") }
接下來把我們寫的代碼再次上傳到雲端,因為這些代碼是user頁面的功能,那么我們可以新加一個分支,之后再把主分支合並這個子分支。具體過程和之前的mybranch分支步驟一致,同時我們創建rights分支來進行接下來的操作
對於權限列表,先創建權限列表頁面,注冊到home的子路由里面,之后給頁面創建視圖,先加一個面包屑導航區和卡片視圖

獲取數據,api如下



對於卡片視圖

用戶和角色和權限的關系如下

此時的代碼
Users.vue
<template>
<div>
<div>
<!-- 導航欄 -->
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item :to="{ path: '/Home' }">首頁</el-breadcrumb-item>
<el-breadcrumb-item>用戶管理</el-breadcrumb-item>
<el-breadcrumb-item>列表</el-breadcrumb-item>
</el-breadcrumb>
<!-- 用戶列表區 -->
<el-card>
<el-row :gutter="20">
<el-col :span="8">
<el-input placeholder="請輸入內容" v-model="queryInfo.query" class="input-with-select" clearable
@clear="getUserList">
<el-button slot="append" icon="el-icon-search" @click="getUserList"></el-button>
</el-input>
</el-col>
<el-col span="4">
<el-button type="primary" @click="addDialogVisible = true">添加用戶</el-button>
</el-col>
</el-row>
<el-table :data="userlist" border stripe width="180px">
<el-table-column type="index" ></el-table-column>
<el-table-column label="姓名" prop="username"></el-table-column>
<el-table-column label="郵箱" prop="email"></el-table-column>
<el-table-column label="電話" prop="mobile"></el-table-column>
<el-table-column label="角色" prop="role_name"></el-table-column>
<el-table-column label="狀態">
<template slot-scope="scope">
<el-switch v-model="scope.row.mg_state" @change="userStateChanged(scope.row)">
</el-switch>
</template>
</el-table-column>
<el-table-column label="操作" prop="" width="180px">
<template slot-scope="scope">
<el-tooltip class="item" effect="dark" content="修改角色" placement="top"
:enterable="false">
<el-button @click="showEditDialog(scope.row.id)" type="primary" icon="el-icon-edit" size="mini"></el-button>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="刪除角色" placement="top" :enterable="false">
<el-button type="danger" @click="removeUserById(scope.row.id)" icon="el-icon-delete" size="mini"></el-button>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="分配角色" placement="top" :enterable="false">
<el-button type="warning" icon="el-icon-setting" size="mini"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:page-sizes="[1, 2, 3, 4]"
:page-size="queryInfo.pagesize"
:current-page="queryInfo.pagenum"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
</el-card>
<!-- 添加用戶的對話框-->
<el-dialog
title="添加用戶"
:visible.sync="addDialogVisible"
width="50%"
:before-close="handleClose"
@close="addDialogClosed"
>
<!-- 嵌套的表單-->
<el-form :rules="addFormRules" :model="addForm" ref="addFormRef" label-width="70px">
<el-form-item label="用戶名" prop="username">
<el-input v-model="addForm.username"></el-input>
</el-form-item>
<el-form-item label="密碼" prop="password">
<el-input v-model="addForm.password"></el-input>
</el-form-item>
<el-form-item label="郵箱" prop="email">
<el-input v-model="addForm.email"></el-input>
</el-form-item>
<el-form-item label="手機" prop="mobile">
<el-input v-model="addForm.mobile"></el-input>
</el-form-item>
</el-form>
<!-- 底部區域-->
<span slot="footer" class="dialog-footer">
<el-button @click="addDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="addUser">確 定</el-button>
</span>
</el-dialog>
<!-- 修改用戶的對話框-->
<el-dialog
title="修改用戶"
:visible.sync="editDialogVisible"
width="50%"
@close="editDialogClosed"
>
<el-form ref="editFormRef" :rules="editFormRules" :model="editForm" label-width="80px">
<el-form-item label="用戶名">
<el-input v-model="editForm.username" disabled></el-input>
</el-form-item>
<el-form-item label="郵箱" prop="email">
<el-input v-model="editForm.email" ></el-input>
</el-form-item>
<el-form-item label="電話號碼" prop="mobile">
<el-input v-model="editForm.mobile" ></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="editDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="editUserInfo">確 定</el-button>
</span>
</el-dialog>
</div>
</div>
</template>
<script>
import axios from "axios";
export default {
data() {
var checkEmail = (rule, value, cb) => {
const regEmail = /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/
if (regEmail.test(value)) {
return cb()
}
cb(new Error("請輸入合法郵箱"))
}
var checkMobile = (rule, value, cb) => {
const regMobile = /^1[3|4|5|7|8][0-9]{9}$/
if (regMobile.test(value)) {
return cb()
}
cb(new Error("請輸入正確的手機號"))
}
return {
queryInfo: {
query: '',
pagenum: 1,
pagesize: 2
},
userlist: [],
total: 0,
addDialogVisible: false,
editDialogVisible:false,
editForm : {},
addForm: {
username: "",
password: "",
email: "",
mobile: "",
},
addFormRules: {
username: [{required: true, message: "請輸入用戶名", trigger: 'blur'}, {
min: 3,
max: 10,
message: '用戶名的長度在3-10',
trigger: 'blur'
}],
password: [{required: true, message: "請輸入密碼", trigger: 'blur'}, {
min: 3,
max: 20,
message: '密碼的長度在3-20',
trigger: 'blur'
}],
email: [{required: true, message: "請輸入郵箱", trigger: 'blur'}, {
min: 6,
max: 28,
message: '郵箱的長度在6-28',
trigger: 'blur'
}, {validator: checkEmail, trigger: 'blur'}],
mobile: [{required: true, message: "請輸入電話號碼", trigger: 'blur'}, {
min: 3,
max: 18,
message: '電話號碼的長度在6-18',
trigger: 'blur'
}, {validator: checkMobile, trigger: 'blur'}],
},
editFormRules:{
email: [{required: true, message: "請輸入郵箱", trigger: 'blur'},{validator: checkEmail, trigger: 'blur'}],
mobile: [{required: true, message: "請輸入電話號碼", trigger: 'blur'},{validator: checkMobile, trigger: 'blur'}],
}
}
},
created() {
this.getUserList()
},
name: "users",
methods: {
async getUserList() {
const {data: res} = await this.$http.get('users', {params: this.queryInfo})
if (res.meta.status !== 200) return this.$message.error("獲取用戶列表失敗")
this.userlist = res.data.users
this.total = res.data.total
console.log(res)
},
handleSizeChange(val) {
console.log(`每頁 ${val} 條`);
this.queryInfo.pagesize = val
this.getUserList()
},
handleCurrentChange(val) {
this.queryInfo.pagenum = val
this.getUserList()
console.log(`當前頁: ${val}`);
},
//監聽switch開關的變化
async userStateChanged(userinfo) {
console.log(userinfo)
const {data: res} = await this.$http.put(`users/${userinfo.id}/state/${userinfo.mg_state}`)
if (res.meta.status !== 200) {
userinfo.mg_state = !userinfo.mg_state
return this.$message.error("更新用戶狀態變化")
} else {
this.$message.success("更新成功")
}
},
//監聽用戶對話框的關閉事件
addDialogClosed() {
this.$refs.addFormRef.resetFields()
},
addUser() {
this.$refs.addFormRef.validate(async valid => {
if (!valid) return
//發起添加用戶的網路請求
const {data: res} = await this.$http.post('users', this.addForm)
if (res.meta.status !== 201) this.$message.error("添加用戶失敗")
if (res.meta.status === 201) this.$message.success("添加用戶成功")
this.addDialogVisible = false
this.getUserList()
})
},
//展示編輯對話框
async showEditDialog(id) {
const {data:res} = await this.$http.get('users/'+id)
if(res.meta.status !== 200){
return this.$message.error("查詢用戶失敗")
}
this.editForm = res.data
this.editDialogVisible = true
},
editDialogClosed(){
this.$refs.editFormRef.resetFields()
},
editUserInfo(){
this.$refs.editFormRef.validate(
async valid => {
if(!valid) return
const {data:res} = await this.$http.put('users/' + this.editForm.id,
{
email:this.editForm.email,
mobile:this.editForm.mobile
}
)
if(res.meta.status !== 200){
return this.$message.error("更新用戶失敗")
}
this.editDialogVisible = false
this.getUserList()
this.$message.success("更新用戶信息成功")
}
)
},
async removeUserById(id){
const confirmResult = await this.$confirm(
'此操作將永久刪除該用戶,是否繼續?',
'提示',
{
confirmButtonText: '確定',
cancelButtonText: '取消',
type: 'warning'
}
).catch(err => err)
if(confirmResult !== 'confirm'){
return this.$message.info("已取消刪除")
}
const {data:res} = axios.delete('users/' + id)
// const {data:res} = this.$http.delete('users/' + id)
if(res.meta.status !== 200){
return this.$message.error("刪除用戶失敗")
}
this.$message.success("刪除用戶成功")
this.getUserList()
console.log("確認刪除")
}
}
}
</script>
<style scoped>
</style>
Home.vue
<template>
<el-container class="home-container">
<!-- 頭部-->
<el-header>
<div>
<img src="../assets/heima.png">
<span>電商后台管理系統</span>
</div>
<el-button type="info" @click="logout">退出</el-button>
</el-header>
<el-container>
<!-- 側邊欄-->
<el-aside width="isCollapse ? '64px' : '200px'">
<div class="toggle-button" @click="toggleCollpse">點擊折疊</div>
<!-- 一級標題-->
<el-menu :unique-opened="true"
background-color="#333744"
text-color="#fff"
:collapse="isCollapse"
:collapse-transition="false"
:router="true"
:default-active="activePath"
active-text-color="#409EFF">
<!-- 二級標題-->
<el-submenu :index="item.id+''" v-for="item in menulist" :key="item.id">
<template slot="title">
<i :class="iconsObj[item.id]"></i>
<span>{{item.authName}}</span>
</template>
<el-menu-item :index=" '/' + subItem.path + ''" v-for="subItem in item.children"
@click="saveNavState('/' + subItem.path)">
<template slot="title">
<i class="el-icon-menu"></i>
<span>{{subItem.authName}}</span>
</template>
</el-menu-item>
</el-submenu>
</el-menu>
</el-aside>
<el-main>
<router-view></router-view>
</el-main>
</el-container>
</el-container>
</template>
<script>
export default {
name: "Home",
data(){
return {
menulist: [],
iconsObj:{
'125': 'iconfont icon-user',
'103': 'iconfont icon-tijikongjian',
'101': 'iconfont icon-shangpin',
'102': 'iconfont icon-danju',
'145': 'iconfont icon-baobiao',
},
//是否折疊
isCollapse: false,
//被激活的地址
activePath:""
}
},
created() {
this.getMenuList()
this.activePath = window.sessionStorage.getItem('activePath')
},
methods:{
logout(){
window.sessionStorage.clear()
this.$router.push("/login")
},
//獲取所有菜單
async getMenuList(){
const {data:res} =await this.$http.get('menus')
if(res.meta.status !== 200) return this.$message.error(res.meta.msg)
this.menulist = res.data
console.log(res)
},
//點擊按鈕,切換菜單的折疊與展開
toggleCollpse(){
this.isCollapse = !this.isCollapse
},
//保存鏈接的激活狀態
saveNavState(activePath){
window.sessionStorage.setItem('activePath',activePath)
this.activePath = activePath
}
}
}
</script>
<style scoped>
.el-header{
background-color: #373d41;
display: flex;
justify-content: space-between;
padding-left: 0;
align-items: center;
color: white;
font-size: 20px;
}
div{
display: flex;
align-items: center;
}
span{
margin-left: 15px;
}
.el-aside{
background-color: #333744;
}
.el-main {
background-color: #eaedf1;
}
.home-container{
height: 100%;
}
.iconfont{
margin-right: 10px;
}
.el-menu{
border-right: none;
}
.toggle-button{
background-color: #4A5064;
font-size: 10px;
line-height: 24px;
color: #fff;
text-align: right;
letter-spacing: 0.2em;
cursor: pointer;
}
</style>
Rights.vue
<template>
<div>
<div>
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item :to="{ path: '/Home' }">首頁</el-breadcrumb-item>
<el-breadcrumb-item>權限管理</el-breadcrumb-item>
<el-breadcrumb-item>權限列表</el-breadcrumb-item>
</el-breadcrumb>
<el-card>
<el-table :data="rightsList" border stripe>
<el-table-column type="index" label="#"></el-table-column>
<el-table-column label="權限名稱" prop="authName"></el-table-column>
<el-table-column label="路徑" prop="path"></el-table-column>
<el-table-column label="權限等級" prop="level">
<template slot-scope="scope" label="狀態">
<el-tag v-if="scope.row.level === '0'">一級</el-tag>
<el-tag v-else-if="scope.row.level === '1'" type="success">二級</el-tag>
<el-tag v-if="scope.row.level === '2'" type="warning">三級</el-tag>
</template>
</el-table-column>
</el-table>
</el-card>
</div>
</div>
</template>
<script>
export default {
name: "Rights",
data(){
return {
rightsList: []
}
},
created() {
this.getRightsList()
},
methods:{
async getRightsList(){
const {data: res} = await this.$http.get('rights/list')
if(res.meta.status !== 200){
return this.$message.error("獲取權限列表失敗")
}
this.rightsList = res.data
// console.log(this.rightsList)
}
}
}
</script>
<style scoped>
</style>
index.js
import Vue from 'vue' import VueRouter from 'vue-router' import Login from "@/components/Login"; import Home from "@/components/Home"; import Welcome from "@/components/Welcome"; import Users from "@/components/user/Users"; import Rights from "@/components/power/Rights"; Vue.use(VueRouter) const routes = [ {path: "/", redirect: '/login'}, {path: "/login", component:Login}, {path: "/Home", component:Home, redirect: '/Welcome', children:[{path: '/Welcome',component: Welcome}, {path: "/Users", component: Users}, {path: '/rights', component: Rights}, ] }, ] const router = new VueRouter({ routes }) router.beforeEach((to, from, next) =>{ if(to.path === '/login') return next() const tokenstr = window.sessionStorage.getItem('token') if(!tokenstr) return next('/login') next() }) export default router
main.vue
import Vue from 'vue' import App from './App.vue' import router from './router' import './assets/css/global.css' import ElementUI from 'element-ui' import 'element-ui/lib/theme-chalk/index.css' import './assets/fonts/iconfont.css' import axios from 'axios' Vue.prototype.$http= axios axios.defaults.baseURL = 'http://127.0.0.1:8888/api/private/v1/' Vue.use(ElementUI) Vue.config.productionTip = false axios.interceptors.request.use( config => { config.headers.authorization = window.sessionStorage.getItem("token") return config } ) new Vue({ router, render: h => h(App) }).$mount('#app')
六:角色列表和分配權限與分配角色(P73-95)
接下來講解一下角色列表的功能實現,包括展示角色,添加,刪除,修改,分配權限等功能。頁面效果如下

首先就是創建一個Roles文件,然后作為子路由在home路由里面,接下來繪制基本布局,包括面包屑導航,和卡片區。



添加,刪除,修改之前的代碼思路一致直接抄就可以,注意接口別寫錯就可以。角色列表第一列是一個角色權限的展示,這個功能是分配權限的功能一致,所以我這就沒去寫,有需求再去看看p77-p84,效果如下

接下來實現分配權限,效果如下:

實現分配權限對話框





接下來繪制樹形結構


優化樹形控件,,加入選擇框和唯一的key鍵和默認一次打開所有的樹

對於用戶已有的權限可以,可以把權限對應的數字放在數組里面同時提供給default-checked-keys屬性

接下來獲取樹形結構的id,使用遞歸獲取三層結果的數據id數組



每次打開對話框要清空全局數據defkeys,防止上次的數據再次使用

調用api完成權限分配(https://www.bilibili.com/video/BV1EE411B7SU?p=91)





接下來可以完成user頁面分配角色按鈕,大同小異,不過對話框內加入了一個選擇組件

點擊按鈕分配角色

Rights
<template>
<div>
<div>
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item :to="{ path: '/Home' }">首頁</el-breadcrumb-item>
<el-breadcrumb-item>權限管理</el-breadcrumb-item>
<el-breadcrumb-item>權限列表</el-breadcrumb-item>
</el-breadcrumb>
<el-card>
<el-table :data="rightsList" border stripe>
<el-table-column type="index" label="#"></el-table-column>
<el-table-column label="權限名稱" prop="authName"></el-table-column>
<el-table-column label="路徑" prop="path"></el-table-column>
<el-table-column label="權限等級" prop="level">
<template slot-scope="scope" label="狀態">
<el-tag v-if="scope.row.level === '0'">一級</el-tag>
<el-tag v-else-if="scope.row.level === '1'" type="success">二級</el-tag>
<el-tag v-if="scope.row.level === '2'" type="warning">三級</el-tag>
</template>
</el-table-column>
</el-table>
</el-card>
</div>
</div>
</template>
<script>
export default {
name: "Rights",
data(){
return {
rightsList: []
}
},
created() {
this.getRightsList()
},
methods:{
async getRightsList(){
const {data: res} = await this.$http.get('rights/list')
if(res.meta.status !== 200){
return this.$message.error("獲取權限列表失敗")
}
this.rightsList = res.data
// console.log(this.rightsList)
}
}
}
</script>
<style scoped>
</style>
Roles
<template>
<div>
<div>
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item :to="{ path: '/Home' }">首頁</el-breadcrumb-item>
<el-breadcrumb-item>權限管理</el-breadcrumb-item>
<el-breadcrumb-item>角色列表</el-breadcrumb-item>
</el-breadcrumb>
<el-col span="4">
<el-button type="primary" @click="addDialogVisible = true">添加角色</el-button>
</el-col>
<el-card>
<el-table :data="rolelist" border stripe>
<el-table-column type="expand"></el-table-column>
<el-table-column type="index" label="#"></el-table-column>
<el-table-column label="角色名稱" prop="roleName"></el-table-column>
<el-table-column label="角色描述" prop="roleDesc"></el-table-column>
<el-table-column label="操作" prop="">
<template slot-scope="scope">
<el-tooltip class="item" effect="dark" content="編輯" placement="top"
:enterable="false">
<el-button @click="showEditDialog(scope.row.id)" type="primary" icon="el-icon-edit" ></el-button>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="刪除角色" placement="top" :enterable="false">
<el-button type="danger" @click="removeUserById(scope.row.id)" icon="el-icon-delete" ></el-button>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="分配權限" placement="top" :enterable="false">
<el-button type="warning" @click="showSetRightDialog(scope.row)" icon="el-icon-setting" >分配權限</el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
</el-card>
<el-dialog
title="添加角色"
:visible.sync="addDialogVisible"
width="50%"
@close="addDialogClosed"
>
<!-- 嵌套的表單-->
<el-form :rules="addFormRules" :model="addForm" ref="addFormRef" label-width="70px">
<el-form-item label="角色名稱" prop="roleName">
<el-input v-model="addForm.roleName"></el-input>
</el-form-item>
<el-form-item label="角色描述" prop="roleDesc">
<el-input v-model="addForm.roleDesc"></el-input>
</el-form-item>
</el-form>
<!-- 底部區域-->
<span slot="footer" class="dialog-footer">
<el-button @click="addDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="addUser">確 定</el-button>
</span>
</el-dialog>
<el-dialog
title="修改用戶"
:visible.sync="editDialogVisible"
width="50%"
@close="editDialogClosed"
>
<el-form ref="editFormRef" :rules="editFormRules" :model="editForm" label-width="80px">
<el-form-item label="角色名稱" prop="roleName">
<el-input v-model="editForm.roleName" ></el-input>
</el-form-item>
<el-form-item label="角色描述" prop="roleDesc">
<el-input v-model="editForm.roleDesc" ></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="editDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="editUserInfo">確 定</el-button>
</span>
</el-dialog>
<el-dialog
title="分配權限"
:visible.sync="setRightDialogVisible"
width="50%"
@close="setRightDialogClosed"
>
<!-- 嵌套的表單-->
<el-tree :data="rightslist" :props="treeProps" default-expand-all show-checkbox node-key="id"
:default-checked-keys="defKeys" ref="treeRef"></el-tree>
<!-- 底部區域-->
<span slot="footer" class="dialog-footer">
<el-button @click="setRightDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="allotRights">確 定</el-button>
</span>
</el-dialog>
</div>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "Roles",
data(){
return {
rolelist: [],
rightslist: [],
defKeys: [],
addDialogVisible: false,
editDialogVisible: false,
setRightDialogVisible: false,
addForm:{
roleName: "",
roleDesc: "",
},
editForm:{},
addFormRules: {
roleName: [{required: true, message: "請輸入角色名", trigger: 'blur'}, {
min: 3,
max: 10,
message: '角色名的長度在3-10',
trigger: 'blur'
}],
roleDesc: [{required: true, message: "請輸入角色描述", trigger: 'blur'}, {
min: 3,
max: 20,
message: '角色描述的長度在3-20',
trigger: 'blur'
}],},
editFormRules: {
roleName: [{required: true, message: "請輸入角色名", trigger: 'blur'}, {
min: 2,
max: 10,
message: '角色名的長度在2-10',
trigger: 'blur'
}],
roleDesc: [{required: true, message: "請輸入角色描述", trigger: 'blur'}, {
min: 2,
max: 20,
message: '角色描述的長度在2-20',
trigger: 'blur'
}],},
treeProps:{
label: 'authName',
children: 'children',
},
roleId: '',
}
},
created() {
this.getRolesList()
},
methods:{
async getRolesList(){
const {data: res} = await this.$http.get('roles')
if(res.meta.status !== 200){
return this.$message.error("獲取角色列表失敗")
}
this.rolelist = res.data
console.log(this.rolelist)
},
addDialogClosed() {
this.$refs.addFormRef.resetFields()
},
editDialogClosed(){
this.$refs.editFormRef.resetFields()
},
addUser() {
this.$refs.addFormRef.validate(async valid => {
if (!valid) return
//發起添加用戶的網路請求
const {data: res} = await this.$http.post('roles', this.addForm)
if (res.meta.status !== 201) this.$message.error("添加角色失敗")
if (res.meta.status === 201) this.$message.success("添加角色成功")
this.addDialogVisible = false
this.getRolesList()
})
},
editUserInfo(){
this.$refs.editFormRef.validate(
async valid => {
if(!valid) return
console.log(this.editForm.id)
const {data:res} = await this.$http.put('roles/' + this.editForm.roleId,
{
roleName:this.editForm.roleName,
roleDesc:this.editForm.roleDesc
}
)
console.log(res)
if(res.meta.status !== 200){
return this.$message.error("更新角色失敗")
}
this.editDialogVisible = false
this.getRolesList()
this.$message.success("更新角色信息成功")
}
)
},
async showEditDialog(id) {
console.log(id)
const {data:res} = await this.$http.get('roles/'+id)
if(res.meta.status !== 200){
return this.$message.error("查詢角色失敗")
}
this.editForm = res.data
console.log(this.editForm)
this.editDialogVisible = true
},
async removeUserById(id){
const confirmResult = await this.$confirm(
'此操作將永久刪除該用戶,是否繼續?',
'提示',
{
confirmButtonText: '確定',
cancelButtonText: '取消',
type: 'warning'
}
).catch(err => err)
if(confirmResult !== 'confirm'){
return this.$message.info("已取消刪除")
}
const {data:res} = axios.delete('roles/' + id)
// const {data:res} = this.$http.delete('users/' + id)
if(res.meta.status !== 200){
return this.$message.error("刪除用戶失敗")
}
this.$message.success("刪除用戶成功")
this.getRolesList()
console.log("確認刪除")
},
async showSetRightDialog(role){
this.roleId = role.id
const {data:res} = await axios.get('rights/tree')
if (res.meta.status !== 200){
return this.$message.error("獲取權限數據失敗")
}
this.rightslist = res.data
this.getLeafKeys(role,this.defKeys)
this.setRightDialogVisible = true
console.log(res)
},
setRightDialogClosed(){ //每次使用完畢清空數組
this.defKeys = [];
},
getLeafKeys(node,arr){
if(!node.children){
return arr.push(node.id)
}
node.children.forEach(item => this.getLeafKeys(item,arr))
},
async allotRights(){
const keys =[
...this.$refs.treeRef.getCheckedKeys(),
...this.$refs.treeRef.getHalfCheckedKeys(),
]
console.log(this.roleId)
const idstr = keys.join(',')
const {data: res} = await axios.post(`roles/${this.roleId}/rights`,{rids:idstr})
console.log(res)
if(res.meta.status !== 200){
return this.$message.error("分配權限失敗")
}
this.$message.success("分配權限成功")
this.getRolesList()
this.setRightDialogVisible = false
}
}
}
</script>
<style scoped>
</style>
Users
<template>
<div>
<div>
<!-- 導航欄 -->
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item :to="{ path: '/Home' }">首頁</el-breadcrumb-item>
<el-breadcrumb-item>用戶管理</el-breadcrumb-item>
<el-breadcrumb-item>列表</el-breadcrumb-item>
</el-breadcrumb>
<!-- 用戶列表區 -->
<el-card>
<el-row :gutter="20">
<el-col :span="8">
<el-input placeholder="請輸入內容" v-model="queryInfo.query" class="input-with-select" clearable
@clear="getUserList">
<el-button slot="append" icon="el-icon-search" @click="getUserList"></el-button>
</el-input>
</el-col>
<el-col span="4">
<el-button type="primary" @click="addDialogVisible = true">添加用戶</el-button>
</el-col>
</el-row>
<el-table :data="userlist" border stripe width="180px">
<el-table-column type="index" ></el-table-column>
<el-table-column label="姓名" prop="username"></el-table-column>
<el-table-column label="郵箱" prop="email"></el-table-column>
<el-table-column label="電話" prop="mobile"></el-table-column>
<el-table-column label="角色" prop="role_name"></el-table-column>
<el-table-column label="狀態">
<template slot-scope="scope">
<el-switch v-model="scope.row.mg_state" @change="userStateChanged(scope.row)">
</el-switch>
</template>
</el-table-column>
<el-table-column label="操作" prop="" width="180px">
<template slot-scope="scope">
<el-tooltip class="item" effect="dark" content="修改角色" placement="top"
:enterable="false">
<el-button @click="showEditDialog(scope.row.id)" type="primary" icon="el-icon-edit" size="mini"></el-button>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="刪除角色" placement="top" :enterable="false">
<el-button type="danger" @click="removeUserById(scope.row.id)" icon="el-icon-delete" size="mini"></el-button>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="分配角色" placement="top" :enterable="false">
<el-button type="warning" icon="el-icon-setting" size="mini" @click="setRole(scope.row)" @close="setRoleDialogClosed"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:page-sizes="[1, 2, 3, 4]"
:page-size="queryInfo.pagesize"
:current-page="queryInfo.pagenum"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
</el-card>
<!-- 添加用戶的對話框-->
<el-dialog
title="添加用戶"
:visible.sync="addDialogVisible"
width="50%"
@close="addDialogClosed"
>
<!-- 嵌套的表單-->
<el-form :rules="addFormRules" :model="addForm" ref="addFormRef" label-width="70px">
<el-form-item label="用戶名" prop="username">
<el-input v-model="addForm.username"></el-input>
</el-form-item>
<el-form-item label="密碼" prop="password">
<el-input v-model="addForm.password"></el-input>
</el-form-item>
<el-form-item label="郵箱" prop="email">
<el-input v-model="addForm.email"></el-input>
</el-form-item>
<el-form-item label="手機" prop="mobile">
<el-input v-model="addForm.mobile"></el-input>
</el-form-item>
</el-form>
<!-- 底部區域-->
<span slot="footer" class="dialog-footer">
<el-button @click="addDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="addUser">確 定</el-button>
</span>
</el-dialog>
<!-- 修改用戶的對話框-->
<el-dialog
title="修改用戶"
:visible.sync="editDialogVisible"
width="50%"
@close="editDialogClosed"
>
<el-form ref="editFormRef" :rules="editFormRules" :model="editForm" label-width="80px">
<el-form-item label="用戶名">
<el-input v-model="editForm.username" disabled></el-input>
</el-form-item>
<el-form-item label="郵箱" prop="email">
<el-input v-model="editForm.email" ></el-input>
</el-form-item>
<el-form-item label="電話號碼" prop="mobile">
<el-input v-model="editForm.mobile" ></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="editDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="editUserInfo">確 定</el-button>
</span>
</el-dialog>
<!-- 分配角色-->
<el-dialog
title="分配角色"
:visible.sync="setRoleDialogVisible"
width="50%"
@close="setRoleDialogClosed"
:before-close="handleClose">
<div>
<p>當前的用戶:{{userInfo.username}}}</p>
<p>當前的角色:{{userInfo.role_name}}</p>
<p>分配新角色:
<template>
<el-select v-model="selectRoleId" placeholder="請選擇">
<el-option
v-for="item in roleslist"
:key="item.id"
:label="item.roleName"
:value="item.id">
</el-option>
</el-select>
</template></p>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="setRoleDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="saveRoleInfo">確 定</el-button>
</span>
</el-dialog>
</div>
</div>
</template>
<script>
import axios from "axios";
export default {
data() {
var checkEmail = (rule, value, cb) => {
const regEmail = /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/
if (regEmail.test(value)) {
return cb()
}
cb(new Error("請輸入合法郵箱"))
}
var checkMobile = (rule, value, cb) => {
const regMobile = /^1[3|4|5|7|8][0-9]{9}$/
if (regMobile.test(value)) {
return cb()
}
cb(new Error("請輸入正確的手機號"))
}
return {
queryInfo: {
query: '',
pagenum: 1,
pagesize: 2
},
userlist: [],
roleslist: [],
total: 0,
selectRoleId:"",
addDialogVisible: false,
editDialogVisible:false,
setRoleDialogVisible:false,
editForm : {},
userInfo:{},
addForm: {
username: "",
password: "",
email: "",
mobile: "",
},
addFormRules: {
username: [{required: true, message: "請輸入用戶名", trigger: 'blur'}, {
min: 3,
max: 10,
message: '用戶名的長度在3-10',
trigger: 'blur'
}],
password: [{required: true, message: "請輸入密碼", trigger: 'blur'}, {
min: 3,
max: 20,
message: '密碼的長度在3-20',
trigger: 'blur'
}],
email: [{required: true, message: "請輸入郵箱", trigger: 'blur'}, {
min: 6,
max: 28,
message: '郵箱的長度在6-28',
trigger: 'blur'
}, {validator: checkEmail, trigger: 'blur'}],
mobile: [{required: true, message: "請輸入電話號碼", trigger: 'blur'}, {
min: 3,
max: 18,
message: '電話號碼的長度在6-18',
trigger: 'blur'
}, {validator: checkMobile, trigger: 'blur'}],
},
editFormRules:{
email: [{required: true, message: "請輸入郵箱", trigger: 'blur'},{validator: checkEmail, trigger: 'blur'}],
mobile: [{required: true, message: "請輸入電話號碼", trigger: 'blur'},{validator: checkMobile, trigger: 'blur'}],
}
}
},
created() {
this.getUserList()
},
name: "users",
methods: {
setRoleDialogClosed(){
this.selectRoleId=""
this.userInfo = {}
},
async saveRoleInfo(){
if(!this.selectRoleId){
return this.$message.error("請選擇要分配的角色")
}
const {data:res} = await this.$http.put(`users/${this.userInfo.id}/role`,{rid:this.selectRoleId})
if(res.meta.status != 200){
return this.$message.error("更新角色失敗")
}
this.$message.success("更新成功")
this.getUserList()
this.setRoleDialogVisible = false
},
async getUserList() {
const {data: res} = await this.$http.get('users', {params: this.queryInfo})
if (res.meta.status !== 200) return this.$message.error("獲取用戶列表失敗")
this.userlist = res.data.users
this.total = res.data.total
console.log(res)
},
handleSizeChange(val) {
console.log(`每頁 ${val} 條`);
this.queryInfo.pagesize = val
this.getUserList()
},
handleCurrentChange(val) {
this.queryInfo.pagenum = val
this.getUserList()
console.log(`當前頁: ${val}`);
},
//監聽switch開關的變化
async userStateChanged(userinfo) {
console.log(userinfo)
const {data: res} = await this.$http.put(`users/${userinfo.id}/state/${userinfo.mg_state}`)
if (res.meta.status !== 200) {
userinfo.mg_state = !userinfo.mg_state
return this.$message.error("更新用戶狀態變化")
} else {
this.$message.success("更新成功")
}
},
//監聽用戶對話框的關閉事件
addDialogClosed() {
this.$refs.addFormRef.resetFields()
},
addUser() {
this.$refs.addFormRef.validate(async valid => {
if (!valid) return
//發起添加用戶的網路請求
const {data: res} = await this.$http.post('users', this.addForm)
if (res.meta.status !== 201) this.$message.error("添加用戶失敗")
if (res.meta.status === 201) this.$message.success("添加用戶成功")
this.addDialogVisible = false
this.getUserList()
})
},
async setRole(userInfo){
this.userInfo = userInfo
const {data:res} = await axios.get(("roles"))
if(res.meta.status !== 200){
return this.$message.error("獲取角色列表失敗")
}
this.roleslist = res.data
this.setRoleDialogVisible = true
},
//展示編輯對話框
async showEditDialog(id) {
const {data:res} = await this.$http.get('users/'+id)
if(res.meta.status !== 200){
return this.$message.error("查詢用戶失敗")
}
this.editForm = res.data
this.editDialogVisible = true
},
editDialogClosed(){
this.$refs.editFormRef.resetFields()
},
editUserInfo(){
this.$refs.editFormRef.validate(
async valid => {
if(!valid) return
const {data:res} = await this.$http.put('users/' + this.editForm.id,
{
email:this.editForm.email,
mobile:this.editForm.mobile
}
)
if(res.meta.status !== 200){
return this.$message.error("更新用戶失敗")
}
this.editDialogVisible = false
this.getUserList()
this.$message.success("更新用戶信息成功")
}
)
},
async removeUserById(id){
const confirmResult = await this.$confirm(
'此操作將永久刪除該用戶,是否繼續?',
'提示',
{
confirmButtonText: '確定',
cancelButtonText: '取消',
type: 'warning'
}
).catch(err => err)
if(confirmResult !== 'confirm'){
return this.$message.info("已取消刪除")
}
const {data:res} = axios.delete('users/' + id)
// const {data:res} = this.$http.delete('users/' + id)
if(res.meta.status !== 200){
return this.$message.error("刪除用戶失敗")
}
this.$message.success("刪除用戶成功")
this.getUserList()
console.log("確認刪除")
}
}
}
</script>
<style scoped>
</style>
剩余的功能具有重復型,有些新內容暫時不需要,學到這些可以使用vue+element-ui設計簡單常見的前端了
剩下效果截圖如下,需要時再學。
商品列表

分類參數


商品分類:

訂單分類

數據視圖


