// Home.vue --> element-ui'布局容器'布局;
// element.js: 使用el-ui组件,注册;
import { } from 'element-ui'
Vue.use( )
// 默认,element-ui组件名,就是类名(.类选择器)
.el-container {
height: 100%;
}
<el-container>
<el-header></el-header> //头部区域
<el-container>
<el-aside></el-aside> //左侧边栏
<el-main></el-main> //右主体区域
</el-container>
</el-container>
header头部区域布局
//div>img+span、button
.el-header {
display: flex; //开启flex布局
justify-content: space-between; //先两边贴边 再平分剩余空间
align-items: center; //垂直居中(按钮居中,div却不居中)
> div {
display: flex;
align-items: center; //图片和文字垂直居中
}
}
侧边栏布局
//左侧边栏--> element-ui'导航菜单'布局--> 按需导入 el-ui组件;删除、改造菜单栏
<el-aside>
<el-menu>
//一级菜单
<el-submenu>
<template slot="title">
<i class="el-icon-menu"></i> //图标
<span>一级菜单</span> //文本
</template>
//嵌套二级子菜单
<el-menu-item>
<template slot="title">
<i class="el-icon-location"></i>
<span>子菜单一</span>
</template>
</el-menu-item>
</el-submenu>
</el-menu>
</el-aside>
# main.js
//在请求到达,服务器之前,先调用use()函数,添加请求头信息;
axios.interceptors.request.use(config=>{
//config请求对象,config.herders就是请求头;为请求头对象,添加token 验证的 Authorization字段
config.headers.Authorization = window.sessionStorage.getItem("token")
return config //必须return config
})
/*导入axios包.就能调用 interceptors属性.属性中有一个成员叫 request,请求拦截器.通过use函数,
为请求拦截器,挂载一个回调函数,在请求发送之前,先调用这个回调函数,对请求做一下预处理;
//home.vue 页面一加载,就应获取,左侧菜单数据;created(){}生命周期函数;
export default {
data() {
return {
//保存左侧菜单数据
menuList: []
}
},
created() {
//在created 阶段,请求左侧菜单数据
this.getMenuList()
},
methods: {
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 //把获取的数据,赋值给data中 menulist[]数组
}
}
}
v-for双重循环,渲染左侧菜单
//内层 for 循环,渲染一级菜单,外层 for 循环,渲染二级菜单 (children 中的数据)
//指定不同的 index值,index值相同,点击会同时展开;index只接受字符串,不接受数值,所以+''
<el-submenu :index="item.id + ''" v-for=“item in menulist" :key="item.id">
<span>{{item.authName}}</span>
<el-menu-item :index="'/' + menuitem.path" v-for="menuitem in item.children" key...>
<span slot="title">{{subItem.authName}}</span>
动态添加字体图标
data中,自定义字体对象 iconsObj;循环生成一级菜单时,的id,作为 iconsObj 中的,图标类名;
//1.点击时,激活文本的颜色: active-text-color = "#409eff"
//2.二级菜单的图标,都一样;且没有iconfont类名,因为用的是el-ui的图标库,使用方法和,第三方引入的,不同
//3.一级菜单图标:用第三方字体图标库,入口文件,先引入第三方的 iconfont.css;
iconsObj: {
'125':'iconfont icon-user',
'103':'iconfont icon-tijikongjian',
'101':'iconfont icon-shangpin',
'102':'iconfont icon-danju',
'145':'iconfont icon-baobiao'
}
<i :class="iconsObj[item.id]"></i>
//'145'为什么是字符串 == item.id?对象['字符串']获取对应的值;不加字符串也可以;
左侧菜单,只允许,一次展开一个子菜单;el-ui 导航菜单 -->提供了unique-opened 属性
/*在 中,添加 unique-opened 属性 ;或动态绑定这个属性,
:unique-opened="true" 动态绑定,值是变量,此时true 是一个bool值;不加':'不动态绑定,true是字符串
*点击二级菜单,右侧边框对不齐,是因为的 border-right:1px(设为none)
侧边栏 伸缩功能
//添加折叠 展开按钮: '|||'
<div class="toggle-button" @click="toggleCollapse">|||</div>
.toggle-button {
letter-spacing: 0.2em; //文字和文字之间的距离
cursor: pointer; //鼠标小手
}
//标签,collapse属性,是否折叠;
:collapse="isCollapse"(设置折叠菜单为绑定的 isCollapse 值)
toggleCollapse() {this.isCollapse = !this.isCollapse}
//关闭折叠动画,太丑了 :collapse-transition="false"
//动态设置,侧边栏宽度,不能写死
<el-aside :width="isCollapse ? '64px':'200px'">
后台右侧,内容区域,添加 Welcome子组件,并设置为,默认展示组件;
// router.js:
import Welcome from './components/Welcome.vue'
{ path: '/home', component: Home, redirect: '/welcome',
children: [{ path: '/welcome', component: Welcome },
{ path: '/users', component: Users }]
}
Home.vue 右侧内容区域,添加路由占位符: <router-view></router-view>
将左侧二级菜单,改造成,路由链接
// router.js
import Users from './components/user/Users.vue'
children: [{ path: '/welcome', component: Welcome },
{ path: '/users', component: Users }]
刷新二级菜单时,保持高亮
//把 index保存在 session中,刷新页面时(home组件刚被创建时),再取出来;否则刷新后,就不高亮了
:default-active ="activePath" //当前激活菜单的index值是谁,谁高亮;
@click="saveNavState('/'+subItem.path)" //点击二级菜单时,把index值保存起来
data--> activePath:'' //保存激活的index地址
created(){ //home组件刚被创建时,立刻赋值;只在刷新时高亮;
this.activePath = window.sessionStorage.getItem("activePath");
}
saveNavState( path ){
window.sessionStorage.setItem("activePath",path);
this.activePath = path;//只在点击不同菜单时,高亮,activePath重新赋值
}
<el-row>
<el-col :span="8">
<el-input placeholder="请输入内容">
<el-button slot="append" icon="el-icon-search"></el-button>
</el-input>
</el-col>
<el-col :span="4">
<el-button>添加用户</el-button>
</el-col>
</el-row>
请求用户列表数据
data(){
return {
queryInfo: { //接口文档中,获取用户列表,需要的查询参数;
query: '',//查询关键字;可为空
pagenum: 1, //当前页码值;不能为空
pagesize: 2 //每页显示条数;不能为空
},
userList:[], //保存请求回来的用户列表数据
total:0 //总数据条数;''会报错;
}
},
created() {
this.getUserList()
},
methods:{
async getUserList() {
//发送get请求 获取列表数据:users请求地址,params请求参数
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;
}
}
展示用户列表数据
// Table表格组件: 添加 边框线 隔行变色 上 margin
状态栏:数据结构改造,操作栏:无具体数据;索引列;
1.添加索引列:(bug 没有'#'与视频中不一样)
<el-table-column type="index"></el-table-column>
2.渲染 boole值为开关;布尔值不能直接,在页面上渲染出来;使用作用域插槽,把布尔值渲染为开关
'swith开关'组件 --> v-model="true/false"属性
<template slot-scope='bloer'>
<el-switch v-model="bloer.row.mg_state"></el-switch>
</template>
//bloer接收作用域插槽数据 .row属性,获取一行数据;拿到这一行的数据 .出来状态具体的值;
通过作用域插槽,自定义操作列的渲染
# 'Pagination分页'组件 -->修改组件,绑定的数据
<el-pagination @size-change="SizeChange" @current-change="CurrentChange" :current-page="queryInfo.pagenum" :page-sizes="[1, 2, 5, 10]" :page-size="queryInfo.pagesize" layout="total, sizes, prev, pager, next, jumper" :total="total">
@size-change (切换每页显示多少条时触发)
@current-change (前往第几页时触发)
:current-page (当前是第几页)
:page-size (每页显示多少条)
:total (总共多少条)
# 添加两个事件处理函数
data -->pagesize: 2 // 每页显示条数;不能为空
pagenum: 1, // 当前页码值;不能为空
//每页显示多少条: pagesize发生改变时触发,把最新的 pagesize 赋值给 queryInfo.pagesize
SizeChange(val) {
this.queryInfo.pagesize = val;
this.getUserList(); //按照新的pagesize 重新发送请求
},
//当前第几页: 页码发生改变时触发,把最新的页码数 赋值给data 中存储的 queryInfo.pagenum
CurrentChange(val) {
this.queryInfo.pagenum = val;
this.getUserList(); //重新发送请求
}
switch 开关发生改变时,并没有同步到,数据库中保存;刷新页面,仍是没修改前的状态;
//switch组件: change事件属性,监听'switch组件'的改变;
<el-switch v-model="scope.row.mg_state" @change="userStateChanged(scope.row)">
//mg_state: v-model双向绑定,会更新状态改变;把整行最新的数据,当做参数传递;后面还会用到id属性;
//监听 switch开关,最新状态,保存到数据库中;
async userStateChanged(info) { //row接收数据
//发送put请求,进行状态修改 users/:uid/state/:type
const { data: res } = await this.$http.put(
`users/${info.id}/state/${info.mg_state}`
//':uId/:type'动态数据,拼接动态参数,尽量用``模板字符串;
)
if (res.meta.status !== 200) {
info.mg_state = !info.mg_state //修改失败,把页面用户状态,再改回去;
return this.$message.error('修改状态失败')
}
this.$message.success('更新状态成功')
}
v-model="queryinfo.query";//input输入框,双向绑定 queryinfo中的,查询关键字 query;
点击'搜索'图标--> 再次调用 @click="getuserlist" 请求数据,获取用户列表;
可清空 input框,添加 clearable 属性;
清空 input框时,重新请求数据,调用clear事件: @clear="getuserlist" 再次调用 getuserlist
1.'Dialog对话框'组件: (:visible.sync="addvisible" 控制对话框的显示或隐藏)
"添加用户"按钮 --> 弹出添加对话框 @click="addvisible = true"
"确定" "取消"按钮,点击时都要隐藏对话框 @click="addvisible = false"
2.表单验证组件
<el-form :model="addForm" :rules="addFormRules" ref="addFormRef" label-width="70px">
<el-form-item label="用户名" prop="username">
<el-input v-model="addForm.username"></el-input>
</el-form-item>
label="密码" prop="password" v-model="addForm.password"
label="邮箱" prop="email" v-model="addForm.email"
label="电话" prop="mobile" v-model="addForm.mobile"
</el-form>
# :model数据绑定 :rules="验证规则" ref= "el-form组件" 引用名称;prop=验证规则的属性
自定义邮箱和手机号的校验规则
data() {
//1.验证邮箱的规则 --> 和return平级,定义在 return外面
var checkEmail = (rule, value, cb) => { //验证规则,需要验证的值,回调函数 rule验证规则在哪里?
const regEmail = /^\w+@\w+(\.\w+)+$/
if (regEmail.test(value)) {
return cb()
}
cb(new Error('请输入合法的邮箱')) //返回一个错误提示
};
//2.验证手机号码的规则 --> 和return平级,定义在 return外面
var checkMobile = (rule, value, cb) => {
const regMobile = /^1[34578]\d{9}$/
if (regMobile.test(value)) {
return cb()
}
cb(new Error('请输入合法的手机号码')) //返回一个错误提示
};
return {
// 添加用户的 表单数据
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: 6, max: 15, message: '用户名在6~15个字符之间', trigger: 'blur' }
],
email: [
{ required: true, message: '请输入邮箱', trigger: 'blur' },
{ validator:checkEmail, message: '邮箱格式不正确,请重新输入', trigger: 'blur'}
],
mobile: [
{ required: true, message: '请输入手机号码', trigger: 'blur' },
{ validator:checkMobile, message: '手机号码不正确,请重新输入', trigger: 'blur'}
] //仅保证邮箱、手机号不为空
}
}
}
重置表单:关闭对话框,再次打开清空
# 添加 对话框(不是表单) 关闭事件: @close="clearclose";关闭对话框,清空表单;
clearclose() {
this.$refs.addFormRef.resetFields(); //重置表单
} //通过ref的引用,拿到整个表单的 引用对象,调用 resetFields()方法
添加用户时,预校验后,发送请求
//点击'确定按钮',执行预验证函数 (添加用户前,先进行表单预校验);
addUser(){
//预验证,也是要拿到,整个表单的引用对象;调用 validate()来验证 valid = true/false
this.$refs.addFormRef.validate( async valid => {
if(!valid) return
//校验失败 return;成功 发起 添加用户的 请求
const {data:res} = await this.$http.post("users",this.addForm)
if (res.meta.status !== 201) {
this.$message.error('添加用户失败')
}
this.$message.success("添加用户成功")
this.addVisible = false //关闭对话框
this.getUserList() //重新刷新用户列表数据
})
}