Vue2项目练手——通用后台管理项目
首页组件布局
面包屑&tag
面包屑
使用组件
使用vuex存储面包屑数据
src/store/tab.js
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
<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
<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
<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>
computed:{
...mapState({
tags: state=>state.tab.tabsList
})
}
Main.vue
<common-tag></common-tag>
全部代码:
<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
//删除指定的tag
closeTag(state,item){
const index=state.tabsList.findIndex(val=>val.name===item.name)
state.tabsList.splice(index,1) //splice(删除的位置,删除的个数)
}
全部代码:
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(删除的位置,删除的个数)
}
}
}
用户管理页
新增功能
使用的组件
- 对话框
- 表单
页面布局与校验
Users.vue
<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>