零基础学Vue--day08项目

1. 后台首页基本布局

零基础学Vue--day08项目_第1张图片

// 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>

2. axios请求拦截器

  • 后台除了登录接口之外,都需要 token 权限验证,通过 axios 请求拦截器,来添加 token,以保证拥有获取数据的权限;
  • 登录时 Authorization = null,登录成功后,服务器才颁发token令牌,并保存在session中;
  • 需要授权的 API ,必须在请求头中,使用 Authorization 字段,提供 token 令牌,Authorization=token;
# 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函数,
  为请求拦截器,挂载一个回调函数,在请求发送之前,先调用这个回调函数,对请求做一下预处理;

3. 请求侧边栏数据

//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 属性
零基础学Vue--day08项目_第2张图片

/*在 中,添加 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'">

4. 添加子级路由

后台右侧,内容区域,添加 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-link> 太麻烦,< el-menu >标签,提供了 router属性
  • :router=“true” 此时点击二级菜单,以 index值,作为 path路由跳转路径;
  • :index="’/’+subItem.path" 路由地址必须以’/‘开头,path属性没有’/’,手动补全;

5. 完成用户列表主体区域

// 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重新赋值 
} 

6. 绘制用户列表

零基础学Vue--day08项目_第3张图片

  • 面包屑导航组件: 点击首页跳转到 '/home’组件;
    < el-breadcrumb-item :to="{ path: ‘/home’ }">首页< /el-breadcrumb-item>
  • 卡片组件:
    box-shadow:0 1px 1px rgba(0,0,0,0.15)!important; //阴影增加权重
  • 搜索区域与添加按钮区域 --> 布局 分栏间隔组件(栅格系统)
<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属性,获取一行数据;拿到这一行的数据 .出来状态具体的值;

通过作用域插槽,自定义操作列的渲染

  • 必须要拿到对应的id,后期删除、修改都要用(用作用域插槽);
  • 'button按钮’组件 -->调整 ‘尺寸 大小 颜色 图标样式’
  • '分配角色’按钮,添加文字提示,使用’Tooltip文字提示’组件;
    bug:鼠标进入提示框,不隐藏;:enterable =“false” 不动态绑定,"false"是字符串,不是布尔值;

7. 列表分页

# '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(); //重新发送请求
}

8. 更新用户开关状态

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('更新状态成功')
}

零基础学Vue--day08项目_第4张图片

9. 实现搜索功能

v-model="queryinfo.query"//input输入框,双向绑定 queryinfo中的,查询关键字 query;
点击'搜索'图标--> 再次调用 @click="getuserlist" 请求数据,获取用户列表;
可清空 input框,添加 clearable 属性;
清空 input框时,重新请求数据,调用clear事件: @clear="getuserlist" 再次调用 getuserlist

10. 添加用户

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() //重新刷新用户列表数据 
  })
}

你可能感兴趣的:(vue)