Vue2项目练手——通用后台管理项目第五节

Vue2项目练手------通用后台管理项目

首页组件布局

面包屑&tag

面包屑

使用组件

使用vuex存储面包屑数据

src/store/tab.js
javascript 复制代码
export default {
    state:{
        isCollapse:false,  //控制菜单的展开还是收起
        tabsList:[
            {
                path:'/',
                name:"home",
                label:"首页",
                icon:"s-home",
                url:'Home/Home'
            },
        ]  //面包屑数据
    },
    mutations:{
        //   修改菜单展开收起的方法
        collapseMenu(state){
            state.isCollapse=!state.isCollapse
        },
        //更新面包屑
        selectMenu(state,val){
            //判断添加的数据是否为首页
            if(val.name!=='home'){
                const index=state.tabsList.findIndex(item=>item.name===val.name)
                //如果不存在
                if(index===-1){
                    state.tabsList.push(val)
                }
            }
            state.tabsList.findIndex(val)
        }
    }
}
src/components/CommonAside.vue
html 复制代码
<template>
    <el-menu default-active="1-4-1" class="el-menu-vertical-demo" @open="handleOpen" @close="handleClose"
             :collapse="isCollapse" background-color="#545c64" text-color="#fff"
             active-text-color="#ffd04b">
      <h3>{{isCollapse?'后台':'通用后台管理系统'}}</h3>
      <el-menu-item @click="clickMenu(item)"  v-for="item in noChildren" :key="item.name" :index="item.name">
        <i :class="`el-icon-${item.icon}`"></i>
        <span slot="title">{{item.label}}</span>
      </el-menu-item>
      <el-submenu :index="item.label" v-for="item in hasChildren" :key="item.label">
        <template slot="title">
          <i :class="`el-icon-${item.icon}`"></i>
          <span slot="title">{{item.label}}</span>
        </template>
        <el-menu-item-group>
          <el-menu-item @click="clickMenu(subItem)" :index="subItem.path" :key="subItem.path" v-for="subItem in item.children">
            {{subItem.label}}
          </el-menu-item>
        </el-menu-item-group>
      </el-submenu>
    </el-menu>

</template>



<style lang="less" scoped>
.el-menu-vertical-demo:not(.el-menu--collapse) {
  width: 200px;
  min-height: 400px;
}
.el-menu{
  height: 100vh;  //占据页面高度100%
  h3{
    color: #fff;
    text-align: center;
    line-height: 48px;
    font-size: 16px;
    font-weight: 400;
  }
}
</style>

<script>
export default {
  data() {
    return {
      menuData:[
        {
          path:'/',
          name:"home",
          label:"首页",
          icon:"s-home",
          url:'Home/Home'
        },
        {
          path:'/mall',
          name:"mall",
          label:"商品管理",
          icon:"video-play",
          url:'MallManage/MallManage'
        },
        {
          path:'/user',
          name:"user",
          label:"用户管理",
          icon:"user",
          url:'userManage/userManage'
        },
        {
          label:"其他",
          icon:"location",
          children:[
            {
              path:'/page1',
              name:"page1",
              label:"页面1",
              icon:"setting",
              url:'Other/PageOne'
            },
            {
              path:'/page2',
              name:"page2",
              label:"页面2",
              icon:"setting",
              url:'Other/PageTwo'
            },
          ]
        },
      ]
    };
  },
  methods: {
    handleOpen(key, keyPath) {
      console.log(key, keyPath);
    },
    handleClose(key, keyPath) {
      console.log(key, keyPath);
    },
    clickMenu(item){
      // console.log(item)
      // console.log(this.$route.path)
      // 当页面的路由与跳转的路由不一致才允许跳转
      if(this.$route.path!==item.path && !(this.$route.path==='/home'&&(item.path==='/'))){
        this.$router.push(item.path)
      }
      this.$store.commit('selectMenu',item)
    }
  },
  mounted() {
    console.log(this.$route.path)
  },
  computed:{
    //没有子菜单的数据
    noChildren(){
      return this.menuData.filter(item=>!item.children)
    },
    //有子菜单数组
    hasChildren(){
      return this.menuData.filter(item=>item.children)
    },
    isCollapse(){
      return this.$store.state.tab.isCollapse
    }
  }
}
</script>
src/components/CommonHeader.vue
html 复制代码
<template>
  <div class="header-container">
    <div class="l-content">
      <el-button style="margin-right: 20px" icon="el-icon-menu" size="mini" @click="handleMenu"></el-button>
      <!--      面包屑-->
<!--      <span class="text">首页</span>-->
      <el-breadcrumb separator="/">
        <el-breadcrumb-item v-for="item in tags" :key="item.path" :to="{ path: item.path }">{{ item.label }}</el-breadcrumb-item>
      </el-breadcrumb>
    </div>
    <div class="r-content">
      <el-dropdown>
          <span class="el-dropdown-link">
            <img src="@/assets/user.webp" alt="">
          </span>
                <el-dropdown-menu slot="dropdown">
                  <el-dropdown-item>个人中心</el-dropdown-item>
                  <el-dropdown-item>退出</el-dropdown-item>
                </el-dropdown-menu>
              </el-dropdown>
    </div>
  </div>

</template>

<script>
import {mapState} from 'vuex'
export default {
  name: "CommonHeader",
  methods:{
    handleMenu(){
      this.$store.commit('collapseMenu')
    }
  },
  computed:{
    ...mapState({
      tags: state=>state.tab.tabsList
    })
  }
}
</script>

<style scoped lang="less">
.header-container {
  height: 60px;
  background-color: #333;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0 20px;

  .text {
    color: #fff;
    font-size: 14px;
    margin-left: 10px;
  }
  .r-content{
    img{
      width: 40px;
      height: 40px;
      border-radius: 50%;
    }
  }
  .l-content{
    display: flex;
    align-items: center;
    /deep/.el-breadcrumb__item{   /*元素没有绑定data-v-5a90ec03这样的编号时候,样式不起作用,使用deep进行穿刺可解决问题*/
      .el-breadcrumb__inner{
        font-weight: normal;
        &.is-link{
          color: #666;
        }
      }
      &:last-child{
        .el-breadcrumb__inner {
          color: #fff;
        }
      }
    }
  }
}
</style>

tag

使用组件

文件目录

CommonTag.vue

html 复制代码
<template>
  <div class="tabs">
    <el-tag
        v-for="(item,index) in tags"
        :key="item.path"
        :closable="item.name!=='home'"
        :effect="$route.name===item.name?'dark':'plain'"
        @click="changeMenu(item)"
        @close="handleClose(item,index)"
        size="small"
    >
      {{ item.label }}
    </el-tag>
  </div>
</template>

<script>
import {mapState,mapMutations} from 'vuex'
    export default {
        name: "CommonTag",
      data(){
          return{}
      },
      computed:{
          ...mapState({
            tags: state=>state.tab.tabsList
          })
      },
      methods:{
        ...mapMutations(['closeTag']),
        //  点击tag跳转的功能
        changeMenu(item){
          if(this.$route.path!==item.path && !(this.$route.path==='/home'&&(item.path==='/'))){
                    this.$router.push({name:item.name})
          }
        },
        //点击tag删除的功能
        handleClose(item,index){
          //调用store中的mutation
          this.closeTag(item)
          const length = this.tags.length;
          //跳转之后的逻辑
          if(item.name!==this.$route.name){
            return
          }
          //表示删除的是最后一项
          if(index===length){
            this.$router.push({
              name:this.tags[index-1].name
            })
          }else{
            this.$router.push({
              name:this.tags[index].name
            })
          }
        }
      }
    }
</script>

<style scoped lang='less'>
.tabs{
  padding: 20px;
  .el-tag{
    margin-right: 15px;
    cursor: pointer;
  }
}
</style>
javascript 复制代码
computed:{
    ...mapState({
      tags: state=>state.tab.tabsList
    })
  }

Main.vue

html 复制代码
<common-tag></common-tag>

全部代码:

html 复制代码
<template>
  <div>
    <el-container>
      <el-aside width="auto">
        <CommonAside></CommonAside>
      </el-aside>
      <el-container>
        <el-header>
          <CommonHeader></CommonHeader>
        </el-header>
        <common-tag></common-tag>
        <el-main>
<!--         路由出口,路由匹配到的组件将渲染在这里 -->
          <router-view></router-view>
        </el-main>
      </el-container>
    </el-container>
  </div>

</template>

<script>
import CommonAside from "@/components/CommonAside.vue";
import CommonHeader from "@/components/CommonHeader.vue";
import CommonTag from "@/components/CommonTag";
export default {
  name: "Main",
  components:{CommonAside,CommonHeader,CommonTag}
}
</script>

<style scoped>
.el-header{
  padding: 0;
  margin: 0;
}
.el-menu{
  border-right: none;
}
</style>

tabs.js

javascript 复制代码
//删除指定的tag
        closeTag(state,item){
            const index=state.tabsList.findIndex(val=>val.name===item.name)
            state.tabsList.splice(index,1)   //splice(删除的位置,删除的个数)
        }

全部代码:

javascript 复制代码
export default {
    state:{
        isCollapse:false,  //控制菜单的展开还是收起
        tabsList:[
            {
                path:'/',
                name:"home",
                label:"首页",
                icon:"s-home",
                url:'Home/Home'
            },
        ]  //面包屑数据
    },
    mutations:{
        //   修改菜单展开收起的方法
        collapseMenu(state){
            state.isCollapse=!state.isCollapse
        },
        //更新面包屑
        selectMenu(state,val){
            //判断添加的数据是否为首页
            if(val.name!=='home'){
                // console.log("state",state)
                const index=state.tabsList.findIndex(item=>item.name===val.name)
                //如果不存在
                if(index===-1){
                    state.tabsList.push(val)
                }
            }
        },
        //删除指定的tag
        closeTag(state,item){
            const index=state.tabsList.findIndex(val=>val.name===item.name)
            state.tabsList.splice(index,1)   //splice(删除的位置,删除的个数)
        }
    }
}

用户管理页

新增功能

使用的组件

  1. 对话框
  2. 表单

页面布局与校验

Users.vue

html 复制代码
<template>
  <div class="manage">
    <el-dialog
        title="提示"
        :visible.sync="dialogVisible"
        width="40%"
        :before-close="handleClose">
<!--      用户的表单信息-->
      <el-form ref="form" :inline="true" :rules="rules" :model="form" label-width="80px">
        <el-form-item label="姓名" prop="name">
          <el-input v-model="form.name" placeholder="请输入姓名"></el-input>
        </el-form-item>
        <el-form-item label="年龄" prop="age">
          <el-input v-model="form.age" placeholder="请输入年龄"></el-input>
        </el-form-item>
        <el-form-item label="性别" prop="sex">
          <el-select v-model="form.sex" placeholder="请选择">
            <el-option label="男" value="1"></el-option>
            <el-option label="女" value="0"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="出生日期" prop="birth">
          <el-date-picker
              type="date"
              placeholder="选择日期"
              v-model="form.birth" style="width: 100%;"></el-date-picker>
        </el-form-item>
        <el-form-item label="地址" prop="addr">
          <el-input v-model="form.addr" placeholder="请输入地址"></el-input>
        </el-form-item>
      </el-form>

      <span slot="footer" class="dialog-footer">
        <el-button @click="cancel">取 消</el-button>
        <el-button type="primary" @click="submit">确 定</el-button>
      </span>
    </el-dialog>
    <div class="manage-header">
      <el-button type="primary" @click="dialogVisible=true">+新增</el-button>
    </div>
  </div>

</template>

<script>
export default {
  name: "Users",
  data(){
    return {
      dialogVisible:false,
      form: {
        name: '',
        age: '',
        sex: '',
        birth: '',
        addr: '',
      },
      rules: {
        name: [{required: true, message: "请输入姓名"}],
        age: [{required: true, message: "请输入年龄"}],
        sex: [{required: true, message: "请选择性别"}],
        birth: [{required: true, message: "请选择出生日期"}],
        addr: [{required: true, message: "请输入地址"}],
      }
    }
  },
  methods:{
    //提交用户表单
    submit(){
      this.$refs.form.validate((valid)=>{
        if(valid){
        //  后续对表单数据的处理
          console.log(this.form)

          //清空表单数据
          this.$refs.form.resetFields()
          //关闭弹窗
          this.dialogVisible=false
        }
      })
    },
    //弹窗关闭的时候
    handleClose(){
      //清空表单
      this.$refs.form.resetFields()
      this.dialogVisible=false
    },
    cancel(){
      this.handleClose()
    }
  }
}
</script>

<style scoped>

</style>
相关推荐
Larcher28 分钟前
新手也能学会,100行代码玩AI LOGO
前端·llm·html
徐子颐41 分钟前
从 Vibe Coding 到 Agent Coding:Cursor 2.0 开启下一代 AI 开发范式
前端
小月鸭1 小时前
如何理解HTML语义化
前端·html
jump6801 小时前
url输入到网页展示会发生什么?
前端
诸葛韩信1 小时前
我们需要了解的Web Workers
前端
brzhang1 小时前
我觉得可以试试 TOON —— 一个为 LLM 而生的极致压缩数据格式
前端·后端·架构
yivifu2 小时前
JavaScript Selection API详解
java·前端·javascript
这儿有一堆花2 小时前
告别 Class 组件:拥抱 React Hooks 带来的函数式新范式
前端·javascript·react.js
十二春秋2 小时前
场景模拟:基础路由配置
前端
六月的可乐2 小时前
实战干货-Vue实现AI聊天助手全流程解析
前端·vue.js·ai编程