day02
1.主页布局
利用element-ui中的container布局容器
在element.js中import {Button,Form,FormItem,Input,Message,Container,Header,Aside,Main} from ‘element-ui’
利用element-ui中的container布局容器
在element.js中import {Button,Form,FormItem,Input,Message,Container,Header,Aside,Main} from ‘element-ui’
Vue.use(Button)
Vue.use(Form)
Vue.use(FormItem)
Vue.use(Input)
// Vue.use(Element)
Vue.use(Container)
Vue.use(Header)
Vue.use(Main)
Vue.use(Aside)
Vue.prototype.$message = Message
利用fsCaputer取色
打开软件-》截屏-》ctrl+shift+P 就可以取色 -》复制二进制
页面设计
home.vue
<template>
<el-container class="home-container">
<!-- 头部区域-->
<el-header>Header<el-button type="info" @click="logout">退出</el-button></el-header>
<!-- 页面主体区域-->
<el-container>
<!-- 侧边栏-->
<el-aside width="200px">Aside</el-aside>
<!-- 右侧内容主题-->
<el-main>Main</el-main>
</el-container>
</el-container>
</template>
<script>
export default {
methods: {
logout() {
window.sessionStorage.clear()
this.$router.push('/login')
}
}
}
</script>
<style lang="less" scoped>
.home-container {
height: 100%;
}
.el-header {
background-color: #909399;
}
.el-aside {
background-color: #B3C0D1;
}
.el-main {
background-color: #E9EEF3;
}
</style>
如果没有充满,在App.vue中的style加入
<style>
html,body,#app {
height: 100%;
}
</style>
2.header布局
样式:
.el-header {
background-color: #909399;
display: flex;
justify-content: space-between;
/*左右贴边对齐*/
padding-left: 0;
/*消除图片左边空白*/
align-items: center;
/* 按钮不再上下贴边而是居中效果*/
color:#ffffff;
font-size: 20px;
> div {
/*嵌套一个文本的样式*/
display: flex;
align-items: center;
/*纵向上居中对齐*/
span {
margin-left: 15px;
}
/* 图片和文本之间有间距*/
}
}
!-- 头部区域-->
<el-header>
<div>
<img src="../assets/heima.png" alt="">
<span>电商后台管理系统</span>
</div>
<el-button type="info" @click="logout">退出</el-button>
</el-header>
3.侧边栏布局
引入element-ui
import {Button,Form,FormItem,Input,Message,Container,Header,Aside,Main,
Menu,Submenu,MenuItemGroup,MenuItem
} from 'element-ui'
Vue.use(Button)
Vue.use(Form)
Vue.use(FormItem)
Vue.use(Input)
// Vue.use(Element)
Vue.use(Container)
Vue.use(Header)
Vue.use(Main)
Vue.use(Aside)
Vue.use(Menu)
Vue.use(Submenu)
Vue.use(MenuItemGroup)
Vue.use(MenuItem)
Vue.prototype.$message = Message
在home.vue中
<!--侧边栏菜单区域-->
<el-menu background-color="#304156" text-color="#fff" active-text-color="#ffd04b">
<!--一级菜单-->
<el-submenu index="1">
<!--一级菜单模板区域-->
<template slot="title">
<!--图标-->
<i class="el-icon-location"></i>
<!--文本-->
<span>导航一</span>
</template>
<!--二级菜单-->
<el-menu-item index="1-1">
<template slot="title">
<!-- 图标-->
<i class="el-icon-location"></i>
<!-- 文本-->
<span>选项二</span>
</template>
</el-menu-item>
<el-menu-item index="1-2">
<template slot="title">
<!-- 图标-->
<i class="el-icon-location"></i>
<!-- 文本-->
<span>选项二</span>
</template>
</el-menu-item>
</el-submenu>
</el-menu>
</el-aside>
结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rKXRNJQC-1597069772924)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200809120821128.png)]
4.通过接口获取API数据
需要授权的API,必须在请求头中使用Authorization字段提供token令牌
通过axios请求拦截器添加token,保证拥有获取数据的权限
只要通过axios向服务器请求,都会优先调用axios.interceptors.request.use(config => {})中的回调函数,相当于请求前做一次预处理
在main.js中添加
axious.interceptors.request.use(config => {
console.log(config)
config.headers.Authorization = window.sessionStorage.getItem('token')
//最后必须要返回config
return config
})
结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ToUxhKi8-1597069772958)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200809122233012.png)]
5.获取左侧菜单数据
在Home.vue中
<script>
export default {
data() {
return {
//左侧菜单数据
menulist : []
}
},
created() {
this.getMenuList()
},
methods: {
logout() {
window.sessionStorage.clear()
this.$router.push('/login')
},
async getMenuList(){
const {data:res} = await this.$http.get('menus') //menus是根据接口文档知道的
if (res.meta.status !== 200) return this.$message.error(res.meta.msg)//200这个状态也是根据接口文档知道的
this.menulist = res.data //打印res之后可以发现,data是一个数组,所以需要在data(){}中定义menulist
console.log(res)
}
}
}
</script>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U40BXFZb-1597069772961)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200809124013620.png)]
6、通过双层for循环渲染左侧菜单
<!--一级菜单-->
<el-submenu :index="item.id + ''" v-for="item in menulist" :key="item.id">
//第一个for循环,注意menulist本身结构
<!--一级菜单模板区域-->
<template slot="title">
<!--图标-->
<i class="el-icon-location"></i>
<!--文本-->
<span>{{item.authName}}</span>
</template>
<!--二级菜单-->
<el-menu-item :index="subItem.id + '' " v-for="subItem in item.children" :key="subItem.id">
//第二个for循环,注意subItem的数据结构,:index这样是为了不固定id,就可以循环每个id
<template slot="title">
<!-- 图标-->
<i class="el-icon-location"></i>
<!-- 文本-->
<span>{{subItem.authName}}</span>
</template>
</el-menu-item>
</el-submenu>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8Kc7enbo-1597069772963)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200809131333350.png)]
7.设置图标
<!--图标-->
<i :class="iconsObj[item.id]"></i>
//在<script>的data中
data() {
return {
//左侧菜单数据
menulist : [],
iconsObj: {
'125': 'iconfont icon-user',
'103': 'iconfont icon-tijikongjian',
'101': 'iconfont icon-shangpin',
'102 ': 'iconfont icon-danju',
'145': 'iconfont icon-baobiao'
}
}
},
//在style中设置图标icon和字的距离
.iconfont {
margin-right: 10px;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AHoGoHG7-1597069772966)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200809185638377.png)]
8.侧边栏每次只能打开一个
加上unique-opened属性为true,一定要加:,不然只会认为是一个字符串(是element-ui的MenuAttribute中的属性)
<!--侧边栏菜单区域-->
<el-menu background-color="#304156" text-color="#fff" active-text-color="#409EFF" :unique-opened="true">
右侧边框线优化
.el-aside {
background-color: #304156;
.el-menu {
border-right: none;
}
9.折叠菜单功能
设置按钮样式
.toggle-button {
background-color: #0086b3;
font-size: 10px;
line-height: 24px;
color: #ffffff;
text-align: center;//居中
letter-spacing: 0.2em;//|||之间的举例
cursor: pointer;//鼠标放在上面的时候显示拇指
}
<el-aside :width="isCollapse ? '64px' : '200px'">
<div class="toggle-button" @click="toggleCollapse">
|||
</div>
<!--侧边栏菜单区域-->
<el-menu background-color="#304156" text-color="#fff" active-text-color="#409EFF"
:unique-opened="true" :collapse=“isCollapse” :collapse-transition="false">
methods中
toggleCollapse() {
this.isCollapse = !this.isCollapse
}
利用collapse来确定是否折叠,不折叠为默认值false(是element-ui的MenuAttribute中的属性)
自带动画效果,所以关闭:collapse-transition=“false”>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zZDMrqrr-1597069772971)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200809195956066.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K3sWtltg-1597069772974)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200809200113958.png)]
10.实现首页路由的重定向
首先创建Welcome.vue组件,然后在router.js中import引入,在router.js中设置路由
const router = new VueRouter({
routes:[
{path:'/login',component:Login},
{path:'/home',component: Home,
redirect: '/welcome',//重定向
children : [
{path:'/welcome', component:Welcome}//是home的子路由
]}
]
})
最后记得在Home.vue中的主题部分挂载一下
<!-- 右侧内容主题-->
<el-main>
//进行加载子页面welcome
<router-view></router-view>
</el-main>
11.侧边栏点击之后在主体显示不同页面(路由链接的改造)
router(是element-ui的MenuAttribute中的属性),启用该模式会在激活导航时以index作为path进行路由跳转
首先添加router属性:
<!--侧边栏菜单区域-->
<el-menu background-color="#304156" text-color="#fff" active-text-color="#409EFF"
:unique-opened="true" :collapse=isCollapse :collapse-transition="false" :router="true">
然后改变二级菜单的index值
<el-menu-item :index="'/'+subItem.path" v-for="subItem in item.children" :key="subItem.id">
12.通过路由形式展示用户列表组件
首先创建一个包user,然后创建组件Users.vue,在route.js中引入
import Users from "../components/user/Users.vue";
然后在home的子路由中添加children
{path:'/home',component: Home,
redirect: '/welcome',
children : [
{path:'/welcome', component:Welcome},
{path: '/users', component: Users}//path不要瞎写,根据项目跳转的数值写
]}
13.解决高亮保持的问题
根据element-ui的MenuAttribute中的default-activate属性(当前激活菜单的index值)
步骤:1.点击二级菜单的时候,把对应地址保存到SessionStorage中
2.刷新之后,即home组件创建的时候,重新取出
<el-menu background-color="#304156" text-color="#fff" active-text-color="#409EFF"
:unique-opened="true" :collapse=isCollapse :collapse-transition="false" :router="true" :default-active="activatePath">//动态获取activatePath
data() {
activatePath: ‘’
}
首先点击的时候把activatePath保存到SessionStorge中
saveNavState(activatePath) {
window.sessionStorage.setItem('activatePath',activatePath)
this.activatePath = activatePath
}
刷新之后,创建的时候把activatePath赋值给左侧菜单
created() {
this.getMenuList()
this.activatePath = window.sessionStorage.getItem("activatePath")
},
14、绘制用户列表组件的基础布局结构
面包屑:
<!--面包屑-->
<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"> //可以规范内部组件的排布,gutter表示一行的组件中间间隔
<el-col :span="7">//span标识占用长度
<el-input placeholder="请输入内容">
<el-button slot="append" icon="el-icon-search"></el-button>
</el-input>
</el-col>
<el-col :span="4">
<el-button type="primary">添加用户</el-button>
</el-col>
</el-row>
</el-card>
在assets的css的global.css中设置好看
.el-breadcrumb {
margin-bottom: 15px;
font-size: 12px;
}
.el-card {
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15) !important;
}
15、获取用户列表数据
在user.vue中
<script>
export default {
name: "Users.vue",
data() {
return {
queryInfo: {
//获取用户列表的参数对象
query: '',
pagenum: 1,
pagesize: 2,
//后端定义的数据
},
userlist:[],
total:0,
}
},
created() {
this.getUserList()
},
methods: {
async getUserList() {
//ajxous数据请求
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//看console控制台的输出结果的数据格式
this.total = res.data.total
console.log(res)
}
}
}
</script>
16.使用el-table组件渲染基本的用户列表
<el-table :data="userlist" border stripe>//data中绑定的是要渲染的数据,类型是数组,border代表添加表格线,stripe隔行换色
<el-table-column label="姓名" prop="username"></el-table-column>//lable指定当前列的标题,prop指定相应的数据
<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="状态" prop="mg_state"></el-table-column>
<el-table-column label="操作"></el-table-column>
</el-table>
.el-table {//为了好看
margin-top: 15px;
font-size: 12px;
}
17、为用户列表添加索引页
<el-table :data="userlist" border stripe>
<el-table-column label="序号" 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="状态" prop="mg_state"></el-table-column>
<el-table-column label="操作"></el-table-column>
</el-table>18
18、自定义状态栏
row是el-table-column slot的一个属性,scope.row可以拿到当前行的数据
slot-scope/v-slot是Vue自带的,row属性是ElementUI自带的,v-slot="scope"中的scope是可以…
为什么使用slot,而不直接拿数据?prop是里面显示值,slot是直接用模板把这一行的这个格的内容替换掉
<el-table-column label="状态" prop="mg_state">
<template slot-scope="scope">//通过slot-scope接收了当前作用于的数据。向欧美写法<template v-slot="scope">
{{scope.row}}}//拿到对应行的数据
<el-switch v-model="scope.row.mg_state">//绑定行数据中的一个
</el-switch>
使用的el-switch中的mg_state会覆盖掉el-table-column中的prop值
19.通过作用于插槽渲染操作
<el-table-column label="操作" width="180px">
<template v-slot="scope">
<!--修改-->
<el-button type="primary" icon="el-icon-edit" size="mini"></el-button>
<!--删除-->
<el-button type="danger" icon="el-icon-delete" size="mini"></el-button>//type=danger是红色
<!--分配角色-->
<el-tooltip effect="dark" content="分配角色" placement="top" :enterable="false">//鼠标放在按钮上提示分配角色的字样,effect表示提示的背景颜色
<el-button type="warning" icon="el-icon-setting" size="mini"></el-button>//type=warning是黄色
</el-tooltip>
</template>
</el-table-column>
20.分页形式效果
<!--分页区域-->
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="queryInfo.pagenum"
:page-sizes="[1,2,5,10]"
:page-size="queryInfo.pagesize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
<!-- size-change切换每页显示多少条
current-change页码改变触发方法
:page-size 当前每页显示多少条
total总共多少条
-->
方法:
//监听psgesize改变的事件
handleSizeChange(newSize) {
this.queryInfo.pagesize = newSize
this.getUserList()
},
//监听 页码值 改变的事件
handleCurrentChange(newPage) {
this.queryInfo.pagenum = newPage
this.getUserList()
}
样式:
.el-pagination {
margin-top: 15px;
}
21.修改用户状态
<el-table-column label="状态">
<template v-slot="scope">
<el-switch v-model="scope.row.mg_state" @change="userStateChange(scope.row)">//element-ui中的属性
</el-switch>
</template>
</el-table-column>
方法:
//监听switch开关的改变
async userStateChange(userinfo) {
const {data:res} = await this.$http.put(`users/${userinfo.id}/state/
${userinfo.mg_state}`)//接口文档中定义的1.3.3
if(res.meta.status !== 200) {
userinfo.mg_state = !userinfo.mg_state
return this.$message.error('更新用户状态失败!')
}
this.$message.success('更新用户状态成功')
}
22.搜索功能
文本框绑定数据,clearable是为了能够可以清空,@clear是为了点击×之后可以显示全部的用户
<el-input placeholder="请输入内容" v-model="queryInfo.query" clearable @clear="getUserList">
<!-- queryInfo.query就是搜索关键字 -->
<el-button slot="append" icon="el-icon-search" @click="getUserList"></el-button>
</el-input>
按钮绑定事件
<el-button slot="append" icon="el-icon-search" @click="getUserList"></el-button><el-button slot="append" icon="el-icon-search" @click="getUserList"></el-button>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jh5sapSw-1597069772976)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200810110612565.png)]
23.添加用户功能
data中定义addDialogVisible为false
//控制添加用户对话框的显示与隐藏
addDialogVisible:false
弹出对话框样式
<el-dialog
title="提示"
:visible.sync="addDialogVisible"
width="50%">
<!-- :visible.sync="addDialogVisible"判断提示框是否可见 -->
<!-- 内容主题区域 -->
<span>这是一段信息</span>
<!-- 底部区域 -->
<span slot="footer" class="dialog-footer">
<el-button @click="addDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="addDialogVisible = false">确 定</el-button>
</span>
</el-dialog>
点击添加用户,弹出对话框
<el-button type="primary" @click="addDialogVisible = true">添加用户</el-button>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9tvXzdP4-1597069772978)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200810112041976.png)]
24.渲染添加用户的表单
<!-- 添加用户的对话框 -->
<el-dialog
title="添加用户"
:visible.sync="addDialogVisible"
width="50%">
<!-- :visible.sync="addDialogVisible"判断提示框是否可见 -->
<!-- 内容主题区域 -->
<el-form :model="addForm" label-width="70px" :rules="addFormRules" ref="addFormRef">
<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" type="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="addDialogVisible = false">确 定</el-button>
</span>
</el-dialog>
data区域
//控制添加用户对话框的显示与隐藏
addDialogVisible:false,
//添加用户的表单数据
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'},
{min:6,max:15, message: '用户名长度在6~15个字符之间',trigger: 'blur'}
],
mobile: [
{required: true, message:"请输入邮箱",trigger:'blur'},
{min:6,max:15, message: '用户名长度在6~15个字符之间',trigger: 'blur'}
]
}
}
25.自定义校验规则
1.定义校验规则:使用箭头函数并命名
2用validater使用规则
以下是在data里面
//验证邮箱的规则
var checkEmail = (rules,value,cb) => {
const regEmail = "[0-9a-zA-Z_.-]+[@][0-9a-zA-Z_.-]+([.][a-zA-Z]+){1,2}";
if (regEmail.test(value)) { return cb()}
cb(new Error('请输入合法的邮箱'))
}
//验证手机号的规则
var checkMobile = (rules,value,cb) => {
const regMobile = /^1([38]\d|5[0-35-9]|7[3678])\d{8}$/;
if (regMobile.test(value)) { return cb()}
cb(new Error('请输入合法的手机号'))
}
//以上,不在return里面定义数据,不然会造成变量的污染,在return里面定义会在当前作用域下显示
以下是在data的return里面
email: [
{required: true, message:"请输入邮箱",trigger:'blur'},
{min:6,max:15, message: '用户名长度在6~15个字符之间',trigger: 'blur'},
{validator:checkEmail,trigger: 'blur'}//使用自定义校验规则
],
mobile: [
{required: true, message:"请输入手机号",trigger:'blur'},
{min:6,max:15, message: '用户名长度在6~15个字符之间',trigger: 'blur'},
{validator:checkMobile,trigger: 'blur'}//使用自定义校验规则
26.实现添加添加用户表单的重置操作
监听表单的关闭事件
<el-dialog
title="添加用户"
:visible.sync="addDialogVisible"
width="50%" @close="addDialogClosed">
重置表单的内容
重置一般都是用refs引用
//监听添加用户对话框的关闭时间
addDialogClosed() {
this.$refs.addFormRef.resetFields();
// 重置 refs的resetFields是引用
}
27.添加用户的表单欲校验
改变确定button的点击事件
<el-button type="primary" @click="addUser">确 定</el-button>
//点击按钮,添加新用户
addUser() {
this.$refs.addFormRef.validate(valid => {
if (!valid) return
//可以发起添加用户的网络请求
})
28.调用API接口完成添加用户
//点击按钮,添加新用户
addUser() {
this.$refs.addFormRef.validate(async valid => {
if (!valid) return
//可以发起添加用户的网络请求
const {data:res} = await this.$http.post(this.addForm)
if (res.meta.status !== 201) {
this.$message.error('添加用户失败')
}
this.$message.error('添加用户成功');
this.addDialogVisible = false;
//重新获取用户列表数据
this.getUserList();
})
}