0


【前端】Vue+Element UI案例:通用后台管理系统-代码总结(已开源)

文章目录

参考视频:
VUE项目,VUE项目实战,vue后台管理系统,前端面试,前端面试项目
案例链接【前端】Vue+Element UI案例:通用后台管理系统-导航栏(视频p1-16)https://blog.csdn.net/karshey/article/details/127640658【前端】Vue+Element UI案例:通用后台管理系统-Header+导航栏折叠(p17-19)https://blog.csdn.net/karshey/article/details/127652862【前端】Vue+Element UI案例:通用后台管理系统-Home组件:卡片、表格(p20-22)https://blog.csdn.net/karshey/article/details/127674643【前端】Vue+Element UI案例:通用后台管理系统-Echarts图表准备:axios封装、mock数据模拟实战(p23-25)https://blog.csdn.net/karshey/article/details/127735159【前端】Vue+Element UI案例:通用后台管理系统-Echarts图表:折线图、柱状图、饼状图(p27-30)https://blog.csdn.net/karshey/article/details/127737979【前端】Vue+Element UI案例:通用后台管理系统-面包屑、tag栏(p31-35)https://blog.csdn.net/karshey/article/details/127756733【前端】Vue+Element UI案例:通用后台管理系统-用户管理:Form表单填写、Dialog对话框弹出(p36-38)https://blog.csdn.net/karshey/article/details/127787418【前端】Vue+Element UI案例:通用后台管理系统-用户管理:Table表格增删查改、Pagination分页、搜索框(p39-42)https://blog.csdn.net/karshey/article/details/127777962【前端】Vue+Element UI案例:通用后台管理系统-登陆页面Login(p44)https://blog.csdn.net/karshey/article/details/127795302【前端】Vue+Element UI案例:通用后台管理系统-登陆页面功能:登录权限跳转、路由守卫、退出(p45-46)https://blog.csdn.net/karshey/article/details/127849502【前端】Vue+Element UI案例:通用后台管理系统-登陆不同用户显示不同菜单、动态添加路由(p47-48)https://blog.csdn.net/karshey/article/details/127865621

前言

本来不打算用博客的方式记录代码的,想用git把它上传到代码仓库。但是由于对git的使用不太熟悉,把写得完善的项目代码初始化掉了!!非常崩溃…后来废了很大力气才把代码找回来。

为了以后把代码搞丢后还能找回来,还是要写个博客来记录一下代码。

项目链接:https://pan.baidu.com/s/1fTh4m_OkqV2PIuWdCgE-QQ
提取码:35sv

项目文件目录

在这里插入图片描述

api

在这里插入图片描述

mockServe

home.js

// mock数据模拟import Mock from'mockjs'// 导入数据import videoData from'../../data/mockData/videoData'import userData from'../../data/mockData/userData'import tableData from'../../data/mockData/tableData'// 图表数据let List =[]// 直接导出exportdefault{getStatisticalData:()=>{//Mock.Random.float 产生随机数100到8000之间 保留小数 最小0位 最大0位for(let i =0; i <7; i++){
            List.push(
                Mock.mock({苹果: Mock.Random.float(100,8000,0,0),vivo: Mock.Random.float(100,8000,0,0),oppo: Mock.Random.float(100,8000,0,0),魅族: Mock.Random.float(100,8000,0,0),三星: Mock.Random.float(100,8000,0,0),小米: Mock.Random.float(100,8000,0,0)}))}// 返回给浏览器的数据return{code:20000,data:{// 饼图
                videoData,// 柱状图
                userData,// 折线图orderData:{date:['20191001','20191002','20191003','20191004','20191005','20191006','20191007'],data: List
                },
                tableData
            }}}}

permission.js

import Mock from'mockjs'exportdefault{getMenu:config=>{const{ username, password }=JSON.parse(config.body)// 先判断用户是否存在// 判断账号和密码是否对应if(username ==='admin'&& password ==='admin'){return{code:20000,data:{menu:[{path:'/home',name:'home',label:'首页',icon:'s-home',url:'Home.vue'},{path:'/mall',name:'mall',label:'商品管理',icon:'video-play',url:'Mall.vue'},{path:'/user',name:'user',label:'用户管理',icon:'user',url:'User.vue'},{label:'其他',icon:'location',children:[{path:'/page1',name:'page1',label:'页面1',icon:'setting',url:'PageOne.vue'},{path:'/page2',name:'page2',label:'页面2',icon:'setting',url:'PageTwo.vue'}]}],token: Mock.Random.guid(),message:'获取成功'}}}elseif(username ==='xiaoxiao'&& password ==='xiaoxiao'){return{code:20000,data:{menu:[{path:'/home',name:'home',label:'首页',icon:'s-home',url:'Home.vue'},{path:'/video',name:'video',label:'商品管理',icon:'video-play',url:'Mall.vue'}],token: Mock.Random.guid(),message:'获取成功'}}}else{return{code:-999,data:{message:'密码错误'}}}}}

index.js

import http from'../utils/request'// 请求首页数据,直接把这个对象导出exportconstgetData=()=>{// 返回一个promisereturn http.get('/home/getData')}// 下面四个:用户管理-后端-网络请求接口exportconstgetUser=(params)=>{return http.get('/user/get/', params)}exportconstcreateUser=(data)=>{return http.post('/user/create', data)}exportconstdeleteUser=(data)=>{return http.post('/user/del', data)}exportconstupdateUser=(data)=>{return http.post('/user/update', data)}// 登录权限exportconstgetMenu=(data)=>{return http.post('/permission/getMenu',data)}

mock.js

import Mock from'mockjs'import homeMock from'../api/mockServe/home'import user from'./user'import permission from'./mockServe/permission'// 定义mock拦截
Mock.mock('/api/home/getData',homeMock)// 用户管理:增删查改
Mock.mock(/\/api\/user\/get/,user.getUserList)
Mock.mock('/api/user/create','post',user.createUser)
Mock.mock('/api/user/update','post',user.updateUser)
Mock.mock('/api/user/del','post',user.deleteUser)// 登录权限
Mock.mock(/api\/permission\/getMenu/,'post',permission.getMenu)

user.js

import Mock from'mockjs'// get请求从config.url获取参数,post从config.body中获取参数functionparam2Obj(url){const search = url.split('?')[1]if(!search){return{}}returnJSON.parse('{"'+decodeURIComponent(search).replace(/"/g,'\\"').replace(/&/g,'","').replace(/=/g,'":"')+'"}')}let List =[]const count =200for(let i =0; i < count; i++){
  List.push(
    Mock.mock({id: Mock.Random.guid(),name: Mock.Random.cname(),addr: Mock.mock('@county(true)'),'age|18-60':1,birth: Mock.Random.date(),sex: Mock.Random.integer(0,1)}))}exportdefault{/**
   * 获取列表
   * 要带参数 name, page, limt; name可以不填, page,limit有默认值。
   * @param name, page, limit
   * @return {{code: number, count: number, data: *[]}}
   */getUserList:config=>{const{ name, page =1, limit =20}=param2Obj(config.url)// console.log('name:' + name, 'page:' + page, '分页大小limit:' + limit)const mockList = List.filter(user=>{if(name && user.name.indexOf(name)===-1&& user.addr.indexOf(name)===-1)returnfalsereturntrue})const pageList = mockList.filter((item, index)=> index < limit * page && index >= limit *(page -1))return{code:20000,count: mockList.length,list: pageList
    }},/**
   * 增加用户
   * @param name, addr, age, birth, sex
   * @return {{code: number, data: {message: string}}}
   */createUser:config=>{const{ name, addr, age, birth, sex }=JSON.parse(config.body)
    console.log(JSON.parse(config.body))
    List.unshift({id: Mock.Random.guid(),name: name,addr: addr,age: age,birth: birth,sex: sex
    })return{code:20000,data:{message:'添加成功'}}},/**
   * 删除用户
   * @param id
   * @return {*}
   */deleteUser:config=>{const{ id }=JSON.parse(config.body)if(!id){return{code:-999,message:'参数不正确'}}else{
      List = List.filter(u=> u.id !== id)return{code:20000,message:'删除成功'}}},/**
   * 批量删除
   * @param config
   * @return {{code: number, data: {message: string}}}
   */batchremove:config=>{let{ ids }=param2Obj(config.url)
    ids = ids.split(',')
    List = List.filter(u=>!ids.includes(u.id))return{code:20000,data:{message:'批量删除成功'}}},/**
   * 修改用户
   * @param id, name, addr, age, birth, sex
   * @return {{code: number, data: {message: string}}}
   */updateUser:config=>{const{ id, name, addr, age, birth, sex }=JSON.parse(config.body)const sex_num =parseInt(sex)
    List.some(u=>{if(u.id === id){
        u.name = name
        u.addr = addr
        u.age = age
        u.birth = birth
        u.sex = sex_num
        returntrue}})return{code:20000,data:{message:'编辑成功'}}}}

assert

这些图片在链接里:

在这里插入图片描述

components

在这里插入图片描述

CommonAside.vue

<template><el-menudefault-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><!-- 观察数据,我们发现name是唯一标识 --><!-- 查看文档,index是唯一标识 --><el-menu-item@click="clickItem(item)"v-for="item in noChildren":key="item.name":index="item.name"><!-- 这里是字体图标,用模板字符串拼接,注意要动态绑定 --><i:class="`el-icon-${item.icon}`"></i><spanslot="title">{{ item.label }}</span></el-menu-item><el-submenuv-for="item in hasChildren":key="item.label":index="item.label"><templateslot="title"><i:class="`el-icon-${item.icon}`"></i><spanslot="title">{{ item.label }}</span></template><el-menu-item-groupv-for="subItem in item.children":key="subItem.name"><el-menu-item@click="clickItem(subItem)":index="subItem.name">{{ subItem.label }}</el-menu-item></el-menu-item-group></el-submenu></el-menu></template><stylelang="less"scoped>.el-menu-vertical-demo:not(.el-menu--collapse){width: 200px;min-height: 400px;}.el-menu{height: 100vh;
    // Aside和Header之间没有边框缝隙
    border-right: none;h3{text-align: center;line-height: 48px;color: #fff;font-size: 16px;font-weight: 400;}}</style><script>import cookie from'js-cookie'exportdefault{data(){return{};},methods:{handleOpen(key, keyPath){
            console.log(key, keyPath);},handleClose(key, keyPath){
            console.log(key, keyPath);},clickItem(item){// 防止自己跳自己的报错if(this.$route.path !== item.path &&!(this.$route.path ==='/home'&&(item.path ==='/'))){this.$router.push(item.path)}// 面包屑this.$store.commit('SelectMenu', item)}},computed:{noChildren(){// 如果没有children则返回true,会被过滤器留下returnthis.MenuData.filter(item=>!item.children)},hasChildren(){returnthis.MenuData.filter(item=> item.children)},// 要放到计算属性,自动计算isCollapse(){returnthis.$store.state.tab.isCollapse
        },// 获取菜单MenuData(){returnJSON.parse(cookie.get('menu'))||this.$store.state.tab.menu
        }}}</script>

CommonHeader.vue

<template><divclass="header-container"><divclass="l-content"><el-button@click="handleMenu"icon="el-icon-menu"size="mini"></el-button><!-- 面包屑 --><el-breadcrumbseparator="/"><el-breadcrumb-itemv-for="item in tags":key="item.path":to="{ path: item.path }">{{ item.label }}
                </el-breadcrumb-item></el-breadcrumb></div><divclass="r-content"><el-dropdown@command="handleClick"><spanclass="el-dropdown-link"><imgclass="user"src="../assets/images/user.png"alt=""></span><el-dropdown-menuslot="dropdown"><el-dropdown-item>个人信息</el-dropdown-item><el-dropdown-itemcommand="logout">退出</el-dropdown-item></el-dropdown-menu></el-dropdown></div></div></template><script>import{ mapState }from'vuex'import Cookie from'js-cookie'exportdefault{methods:{handleMenu(){// 相当于调用这个方法this.$store.commit('CollapseMenu')},handleClick(command){if(command ==='logout'){
                Cookie.remove('token')this.$router.push('/login')}}},computed:{...mapState({tags:state=> state.tab.tabList
        })}}</script><stylelang="less"scoped>.header-container{background-color: #333;height: 60px;

    // 让按钮和头像居中
    display: flex;justify-content: space-between;align-items: center;
    // 不要紧贴边框
    padding: 0 20px;.el-dropdown-link{cursor: pointer;color: #409EFF;.user{width: 40px;height: 40px;
            // 50%变圆形
            border-radius: 50%;}}}.l-content{display: flex;
    // 上下居中
    align-items: center;.el-breadcrumb{margin-left: 15px;// deep 强制生效
        /deep/.el-breadcrumb__item{.el-breadcrumb__inner{&.is-link{color: #666;}}&:last-child{.el-breadcrumb__inner{color: #fff;}}}}}</style>

CommonTags.vue

<template><divclass="tabs"><!-- closable是否可删除:除了"首页"都可删 --><!-- effect:主题,当前主题是dark,其他事plain --><el-tagv-for="(item, index) in tags":key="item.path":closable="item.name !== 'home'":effect="item.name === $route.name ? 'dark' : 'plain'"@click="changeMenu(item)"@close="handleClose(item, index)">
            {{ item.label }}
        </el-tag></div></template><script>import{ mapState }from'vuex'exportdefault{methods:{changeMenu(item){this.$router.push({name: item.name })},handleClose(item, index){// 删除面包屑数据this.$store.commit('closeTag', item)// 如果删除的刚好是自己if(item.name ===this.$route.name){const length =this.tags.length
                // 如果删除的是最后一个:跳到前一个if(length === index){this.$router.push({name:this.tags[index -1].name })}// 不是最后一个:往后一个else{this.$router.push({name:this.tags[index].name })}}}},computed:{...mapState({tags:state=> state.tab.tabList
        })}}</script><stylelang="less"scoped>.tabs{padding: 20px 20px 0 20px;.el-tag{margin-right: 15px;
        // 鼠标悬停:小手
        cursor: pointer;}}</style>

data

在这里插入图片描述

echartsData

order.js

const order ={legend:{// 图例文字颜色textStyle:{color:"#333",},},grid:{left:"20%",},// 提示框tooltip:{trigger:"axis",},xAxis:{type:"category",// 类目轴data:[],axisLine:{lineStyle:{color:"#17b3a3",},},axisLabel:{interval:0,color:"#333",},},yAxis:[{type:"value",axisLine:{lineStyle:{color:"#17b3a3",},},},],color:["#2ec7c9","#b6a2de","#5ab1ef","#ffb980","#d87a80","#8d98b3"],series:[],}exportdefault order

user.js

const user ={legend:{// 图例文字颜色textStyle:{color:"#333",},},grid:{left:"20%",},// 提示框tooltip:{trigger:"axis",},xAxis:{type:"category",// 类目轴data:[],axisLine:{lineStyle:{color:"#17b3a3",},},axisLabel:{interval:0,color:"#333",},},yAxis:[{type:"value",axisLine:{lineStyle:{color:"#17b3a3",},},},],color:["#2ec7c9","#b6a2de"],series:[],}exportdefault user

video.js

const video ={tooltip:{trigger:"item",},color:["#0f78f4","#dd536b","#9462e5","#a6a6a6","#e1bb22","#39c362","#3ed1cf",],series:[],}exportdefault video

mockData

tableData.js

const tableData =[{name:'oppo',todayBuy:500,monthBuy:3500,totalBuy:22000},{name:'vivo',todayBuy:300,monthBuy:2200,totalBuy:24000},{name:'苹果',todayBuy:800,monthBuy:4500,totalBuy:65000},{name:'小米',todayBuy:1200,monthBuy:6500,totalBuy:45000},{name:'三星',todayBuy:300,monthBuy:2000,totalBuy:34000},{name:'魅族',todayBuy:350,monthBuy:3000,totalBuy:22000}]exportdefault tableData

userData.js

// 柱状图const userData =[{date:'周一',new:5,active:200},{date:'周二',new:10,active:500},{date:'周三',new:12,active:550},{date:'周四',new:60,active:800},{date:'周五',new:65,active:550},{date:'周六',new:53,active:770},{date:'周日',new:33,active:170}]exportdefault userData

videoData.js

// 饼图const videoData =[{name:'小米',value:2999},{name:'苹果',value:5999},{name:'vivo',value:1500},{name:'oppo',value:1999},{name:'魅族',value:2200},{name:'三星',value:4500}]exportdefault videoData

CountData.js

const CountData =[{name:"今日支付订单",value:1234,icon:"success",color:"#2ec7c9",},{name:"今日收藏订单",value:210,icon:"star-on",color:"#ffb980",},{name:"今日未支付订单",value:1234,icon:"s-goods",color:"#5ab1ef",},{name:"本月支付订单",value:1234,icon:"success",color:"#2ec7c9",},{name:"本月收藏订单",value:210,icon:"star-on",color:"#ffb980",},{name:"本月未支付订单",value:1234,icon:"s-goods",color:"#5ab1ef",},]exportdefault CountData

MenuData.js

const 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'}]}]exportdefault MenuData

TableData.js

const TableData =[{name:'oppo',todayBuy:100,monthBuy:300,totalBuy:800},{name:'vivo',todayBuy:100,monthBuy:300,totalBuy:800},{name:'苹果',todayBuy:100,monthBuy:300,totalBuy:800},{name:'小米',todayBuy:100,monthBuy:300,totalBuy:800},{name:'三星',todayBuy:100,monthBuy:300,totalBuy:800},{name:'魅族',todayBuy:100,monthBuy:300,totalBuy:800}]exportdefault TableData

TableLabel.js

const TableLabel={name:'课程',todayBuy:'今日购买',monthBuy:'本月购买',totalBuy:'总购买'}exportdefault TableLabel

router

在这里插入图片描述

index.js

import Vue from"vue";import VueRouter from"vue-router";import Main from'../Views/Main'// import Home from '../Views/Home.vue'// import Mall from '../Views/Mall.vue'// import User from '../Views/User.vue'// import PageOne from '../Views/PageOne.vue'// import PageTwo from '../Views/PageTwo.vue'import Login from'../Views/Login.vue'import Cookie from'js-cookie'

Vue.use(VueRouter)const routes =[// 主路由{path:'/',name:'Main',component: Main,redirect:'/home',// 重定向children:[// 子路由// { path: '/home', name: 'home', component: Home }, // 首页// { path: '/user', name: 'user', component: User }, // 用户管理// { path: '/mall', name: 'mall', component: Mall }, // 商品管理// { path: '/page1', name: 'page1', component: PageOne }, // 页面1// { path: '/page2', name: 'page2', component: PageTwo }, // 页面2]},{path:'/login',name:'login',component: Login
    }]const router =newVueRouter({
    routes
})// 路由守卫:全局前置导航守卫
router.beforeEach((to, from, next)=>{// 获取tokenconst token = Cookie.get('token')if(!token && to.name !=='login'){next({name:'login'})}elseif(token && to.name ==='login'){next({name:'home'})}else{next()}})exportdefault router

store

在这里插入图片描述

index.js

import Vue from"vue";import Vuex from'vuex';import tab from'./tab';

Vue.use(Vuex)// 创建Vuex实例并导出exportdefaultnewVuex.Store({modules:{
        tab
    }})

tab.js

import Cookie from"js-cookie"exportdefault{state:{isCollapse:false,//导航栏是否折叠tabList:[{path:'/',name:'home',label:'首页',icon:'s-home',url:'Home/Home'}],//面包屑的数据:点了哪个路由,首页是一定有的menu:[],//不同用户的菜单},mutations:{// 修改导航栏展开和收起的方法CollapseMenu(state){
            state.isCollapse =!state.isCollapse
        },// 更新面包屑的数据SelectMenu(state, item){// 如果点击的不在面包屑数据中,则添加const index = state.tabList.findIndex(val=> val.name === item.name)if(index ===-1){
                state.tabList.push(item)}},// 删除tag:删除tabList中对应的itemcloseTag(state, item){// 要删除的是state.tabList中的itemconst index = state.tabList.findIndex(val=> val.name === item.name)
            state.tabList.splice(index,1)},// 设置不同用户的菜单setMenu(state, val){
            state.menu = val
            Cookie.set('menu',JSON.stringify(val))},// 动态添加路由addMenu(state, router){// 判断Cookieif(!Cookie.get('menu'))returnconst menu =JSON.parse(Cookie.get('menu'))
            state.menu = menu

            const menuArray =[]// 组装路由
            menu.forEach(item=>{// 判断是否有子路由if(item.children){
                    item.children = item.children.map(itemm=>{
                        itemm.component=()=>import(`../Views/${itemm.url}`)return itemm
                    })
                    menuArray.push(...item.children)}else{
                    item.component=()=>import(`../Views/${item.url}`)
                    menuArray.push(item)}});

            console.log(menuArray,'menuArray');

            menuArray.forEach(item=>{
                router.addRoute('Main', item)})}}}

utils

在这里插入图片描述

request.js

import axios from"axios";// 封装一个axios实例const http = axios.create({// 通用请求的地址前缀baseURL:'/api',// 超时时间timeout:100000})// 请求拦截器
http.interceptors.request.use(function(config){// 在发送请求之前做什么return config;},function(error){// 对请求错误做什么return Promise.reject(error);})// 添加响应拦截器
http.interceptors.response.use(function(response){// 对响应数据做什么return response;},function(error){// 对响应错误做什么return Promise.reject(error);})exportdefault http

Views

在这里插入图片描述

Home.vue

<template><el-row><el-col:span="8"><!-- user卡片 --><el-card><divclass="user"><imgsrc="../assets/images/user.png"alt=""><divclass="userInfo"><pdivclass="name">Admin</p><pdivclass="access">超级管理员</p></div></div><divclass="loginInfo"><p>上次登录时间:<span>2021-7-19</span></p><p>上次登陆地点:<span>武汉</span></p></div></el-card><!-- table卡片 --><el-cardstyle="margin-top: 20px;"><el-table:data="TableData"style="width: 100%"><!-- 这里的val,key对应的是对象里的 --><el-table-columnv-for="(value, key) in TableLabel":prop="key":label="value"></el-table-column></el-table></el-card></el-col><el-col:span="16"><divclass="num"><el-cardv-for="item in CountData":key="item.name":body-style="{ display: 'flex', padding: 0 }"><iclass="icon":class="`el-icon-${item.icon}`":style="{ backgroundColor: item.color }"></i><divclass="details"><pclass="price">{{ priceFormate(item.value) }}</p><pclass="desc">{{ item.name }}</p></div></el-card></div><!-- echarts图表 --><divstyle="margin-left:20px"><!-- 折线图 --><el-cardstyle="height:280px"><divref="echarts1"style="height:280px"></div></el-card><divclass="graph"><!-- 柱状图 --><el-cardstyle="height:280px"><divref="echarts2"style="height:280px"></div></el-card><!-- 饼状图 --><el-cardstyle="height:320px"><divref="echarts3"style="height:320px"></div></el-card></div></div></el-col></el-row></template><script>import TableLabel from'../data/TableLabel'import CountData from'../data/CountData'import{ getData }from'../api/index'import*as echarts from'echarts'// echarts的配置数据import order from'../data/echartsData/order'import user from'../data/echartsData/user'import video from'../data/echartsData/video'exportdefault{data(){return{TableData:[],
            TableLabel,
            CountData
        }},methods:{priceFormate(price){return"¥"+ price
        }},mounted(){getData().then((data)=>{// console.log(data);this.TableData = data.data.getStatisticalData.data.tableData

            // echarts图表// 折线图// 基于准备好的dom,初始化echarts实例const echarts1 = echarts.init(this.$refs.echarts1)var echarts1Option = order
            // ES6解构语法var{ orderData, userData, videoData }= data.data.getStatisticalData.data

            // 获取x轴:要求是一个对象const xAxis = Object.keys(orderData.data[0])const xAxisData ={data: xAxis
            }// 配置
            echarts1Option.legend = xAxisData
            echarts1Option.xAxis = xAxisData
            echarts1Option.yAxis ={}
            echarts1Option.series =[]// 配置series
            xAxis.forEach(key=>{
                echarts1Option.series.push({name: key,type:'line',// key对应的orderData的所有值data: orderData.data.map(item=> item[key])})})// 使用刚指定的配置项和数据显示图表。
            echarts1.setOption(echarts1Option);// 柱状图const echarts2 = echarts.init(this.$refs.echarts2)var echarts2Option = user

            // 配置
            echarts2Option.xAxis.data = userData.map(item=> item.date)
            echarts2Option.series =[{name:'新增用户',data: userData.map(item=> item.new),// 类型:bar是柱状图 type:'bar'},{name:'活跃用户',data: userData.map(item=> item.active),type:'bar'}]

            echarts2.setOption(echarts2Option);// 饼状图const echarts3 = echarts.init(this.$refs.echarts3)var echarts3Option = video
            echarts3Option.series ={data: videoData,type:'pie'}
            echarts3.setOption(echarts3Option);})}}</script><stylelang="less"scoped>.user{
    // 垂直居中
    display: flex;align-items: center;

    // 外边距:分割线距离loginInfo的距离
    margin-bottom: 20px;
    // 内边距:分割线距离User的距离
    padding-bottom: 20px;border-bottom: 1px solid #ccc;img{width: 150px;height: 150px;border-radius: 50%;margin-right: 40px;}.userInfo{.name{font-size: 32px;margin-bottom: 10px;}.access{color: #999999;}}}.loginInfo{p{line-height: 28px;font-size: 14px;color: #999999;span{color: #666666;margin-left: 60px;}}}.num{display: flex;
    // 要换行
    flex-wrap: wrap;
    // 从头到尾均匀排列
    justify-content: space-between;margin-left: 20px;.el-card{width: 32%;margin-bottom: 20px;.icon{width: 80px;height: 80px;line-height: 80px;text-align: center;font-size: 30px;color: #fff;}.details{
            // 竖着排且居中
            display: flex;flex-direction: column;justify-content: center;margin-left: 15px;.price{font-size: 30px;margin-bottom: 10px;line-height: 30px;height: 30px;}.desc{font-size: 14px;color: #999;text-align: center;}}}}.graph{display: flex;
    // 两个靠边
    justify-content: space-between;margin-top: 20px;.el-card{width: 49%;}}</style>

Login.vue

<template><el-formref="form"class="login_container":model="login"status-icon:rules="rules"label-width="70px"><!-- h3要放在里面:只能有一个根,且title也是表单的一部分 --><h3class="login_title">用户登录</h3><!-- prop对应rules里的键 --><el-form-itemlabel="用户名"prop="username"><el-inputv-model="login.username"autocomplete="off"></el-input></el-form-item><el-form-itemlabel="密码"prop="password"><el-inputtype="password"v-model="login.password"autocomplete="off"></el-input></el-form-item><el-form-item><el-button@click="submit"type="primary"style="margin-left:30px;margin-top:10px">提交</el-button></el-form-item></el-form></template><script>import Cookie from'js-cookie'import{ getMenu }from'../api/index'exportdefault{data(){return{// 登陆数据login:{username:'',password:''},// 校验规则rules:{username:[{required:'true',message:'请输入用户名',trigger:'blur'}],password:[{required:'true',message:'请输入用户名',trigger:'blur'}]}}},methods:{submit(){// 表单的校验this.$refs.form.validate((valid)=>{if(valid){// 传入表单数据getMenu(this.login).then((data)=>{// console.log(data);if(data.data.code===20000){// 记录cookie
                            Cookie.set('token',data.data.data.token)// 设置菜单this.$store.commit('setMenu',data.data.data.menu)// 动态添加路由this.$store.commit('addMenu',this.$router)// 跳转到首页this.$router.push('/home')}else{// 验证失败的弹窗this.$message.error(data.data.data.message);}})}})}}}</script><stylelang="less"scoped>.login_container{width: 350px;border: 1px solid #eaeaea;

    // 居中
    margin: 180px auto;padding: 35px 35px 15px 35px;

    // 让padding在width里面
    box-sizing: border-box;border-radius: 15px;background-color: #fff;box-shadow: 0 0 25px #cac6c6;.login_title{color: #505458;
        // 左右居中
        text-align: center;margin-bottom: 40px;}.el-input{width: 198px;}}</style>

Main.vue

<template><el-container><!-- width自适应,不然header与aside有间隔 --><el-asidewidth="auto"><common-aside/></el-aside><el-container><el-header><common-header/></el-header><common-tags/><el-main><router-view></router-view></el-main></el-container></el-container></template><script>import CommonAside from'../components/CommonAside.vue'import CommonHeader from'../components/CommonHeader.vue'import CommonTags from'../components/CommonTags.vue'exportdefault{data(){return{}},components:{
        CommonAside,
        CommonHeader,
        CommonTags
    }}</script><stylelang="less"scoped>.el-header{padding:0;}</style>

Mall.vue

<template><h1>Mall</h1></template><script>exportdefault{}</script><style></style>

PageOne.vue

<template><h1>Page1</h1></template><script>exportdefault{}</script><style></style>

PageTwo.vue

<template><h1>Page2</h1></template><script>exportdefault{}</script><style></style>

User.vue

<template><divclass="manage"><divclass="manage-header"><!-- 新增按钮 --><el-buttontype="primary"@click="handlecreate">+ 新增</el-button><!-- 对话框:点击新增或编辑才会弹出表单 --><!-- :before-close="closeDialog" 点击关闭的x之前要做的事情 --><el-dialog:title="modalType == 0 ? '新建' : '编辑'":visible.sync="dialogVisible"width="50%":before-close="closeDialog"><!-- 表单Form --><!-- ref=form:为了通过this.$refs调用组件的方法 --><el-form:inline="true":model="form":rules="rules"ref="form"label-width="80px"><!-- 每一项表单域:el-form-item --><el-form-itemlabel="姓名"prop="name"><el-inputplaceholder="请输入姓名"v-model="form.name"></el-input></el-form-item><el-form-itemlabel="年龄"prop="age"><el-inputplaceholder="请输入年龄"v-model="form.age"></el-input></el-form-item><el-form-itemlabel="性别"prop="sex"><el-selectv-model="form.sex"placeholder="请输入性别"><el-optionlabel="男":value="1"></el-option><el-optionlabel="女":value="0"></el-option></el-select></el-form-item><el-form-itemlabel="出生日期"><el-form-itemprop="birth"><el-date-pickertype="date"placeholder="请选择日期"v-model="form.birth"value-format="yyyy-MM-DD"></el-date-picker></el-form-item></el-form-item><el-form-itemlabel="地址"prop="addr"><el-inputplaceholder="请输入地址"v-model="form.addr"></el-input></el-form-item></el-form><divslot="footer"class="dialog-footer"><el-button@click="closeDialog">取 消</el-button><el-buttontype="primary"@click="submit">确 定</el-button></div></el-dialog><!-- 搜索框 --><el-form:inline="true"><el-form-item><el-inputv-model="searchForm.name"placeholder="请输入名称"></el-input></el-form-item><el-form-item><el-buttontype="primary"@click="search">查询</el-button></el-form-item></el-form></div><divclass="common-table"><!-- 用户数据Table --><el-table:data="tableData"stripestyle="width: 100%"height="90%"><el-table-columnprop="name"label="姓名"></el-table-column><el-table-columnprop="age"label="年龄"></el-table-column><el-table-columnprop="sex"label="性别"><templateslot-scope="scope"><span>{{ scope.row.sex == 1 ? '男' : '女' }}</span></template></el-table-column><el-table-columnprop="birth"label="出生日期"></el-table-column><el-table-columnprop="addr"label="地址"></el-table-column><!-- 自定义列 --><el-table-columnlabel="操作"><templateslot-scope="scope"><el-button@click="handleEdit(scope.row)">编辑</el-button><el-buttontype="danger"@click="handleDelete(scope.row)">删除</el-button></template></el-table-column></el-table><!-- 分页 --><divclass="pager"><el-paginationlayout="prev, pager, next":total="total"@current-change="currentChange"></el-pagination></div></div></div></template><script>import{ getUser, createUser, deleteUser, updateUser }from'../api/index'exportdefault{data(){return{// 表单绑定的数据form:{name:'',age:'',sex:'',birth:'',addr:''},// 表单验证规则rules:{name:[{required:true,message:'请输入名称',trigger:'blur'}],age:[{required:true,message:'请输入年龄',trigger:'blur'}],sex:[{required:true,message:'请输入性别',trigger:'blur'}],birth:[{required:true,message:'请输入日期',trigger:'blur'}],addr:[{required:true,message:'请输入地址',trigger:'blur'}],},// 表单是否打开dialogVisible:false,// 列表数据tableData:[],// 打开表单:新建0,编辑1modalType:0,// 分页的对象pageData:{page:1,limit:20},// 分页页数total:0,// 搜索框表单searchForm:{name:''}}},methods:{// 获取列表数据getList(){// 由接口文档知传入一个对象:要返回的是当前页面数据和搜索到的数据的交集getUser({params:{...this.pageData,...this.searchForm }}).then((data)=>{this.tableData = data.data.list
        this.total = data.data.count ||0})},// 表单提交submit(){// 要用箭头函数,若用function会报错,不知道为什么this.$refs.form.validate((valid)=>{// 符合校验if(valid){// 提交数据if(this.modalType ===0){// 新增createUser(this.form).then(()=>{this.getList()})}else{// 编辑updateUser(this.form).then(()=>{this.getList()})}// 清空,关闭this.closeDialog()}})},// 关闭对话框closeDialog(){// 先重置this.$refs.form.resetFields()// 后关闭this.dialogVisible =false},// 编辑按钮handleEdit(index){this.modalType =1this.openForm()// 深拷贝this.form =JSON.parse(JSON.stringify(index))},// 删除按钮handleDelete(index){this.$confirm('此操作将永久删除该用户, 是否继续?','提示',{confirmButtonText:'确定',cancelButtonText:'取消',type:'warning'}).then(()=>{// 删除操作:根据后端接口,参数是对象,id是唯一标识符deleteUser({id: index.id }).then(()=>{this.$message({type:'success',message:'删除成功!'})this.getList()});}).catch(()=>{// 点击取消:不删除了this.$message({type:'info',message:'已取消删除'});});},// 新建按钮handlecreate(){this.modalType =0this.openForm()},// 打开表单openForm(){this.dialogVisible =true},// 改变页码currentChange(val){this.pageData.page = val
      this.getList()},// 搜索search(){this.getList()}},mounted(){this.getList()}}</script><stylelang="less"scoped>.manage{height: 100%;.manage-header{display: flex;justify-content: space-between;align-items: center;}.common-table{height: 90%;position: relative;.pager{position: absolute;right:20px;bottom: 0;}}}</style>

App.vue

<template><divid="app"><router-view></router-view></div></template><script>exportdefault{}</script><style>html,
body,
h3,
p{margin: 0;padding: 0
}</style>

main.js

import Vue from'vue'import App from'./App.vue'import ElementUI from'element-ui'import'element-ui/lib/theme-chalk/index.css';import router from'./router/index'import store from'./store/index'import'./api/mock'

Vue.config.productionTip =false
Vue.use(ElementUI)newVue({
  router,
  store,render:h=>h(App),created(){
    store.commit('addMenu', router)}}).$mount('#app')

要安装的依赖

"dependencies":{"axios":"^1.1.3","core-js":"^3.8.3","echarts":"^5.1.2","element-ui":"^2.15.10","js-cookie":"^3.0.1","less":"^4.1.3","less-loader":"^11.1.0","mockjs":"^1.1.0","vue":"^2.6.14","vue-router":"^3.6.5","vuex":"^3.6.2"}

已上传到Gitee

代码已经上传到Gitee:https://gitee.com/karshey/general-background

标签: vue.js 前端 ui

本文转载自: https://blog.csdn.net/karshey/article/details/127891703
版权归原作者 karshey 所有, 如有侵权,请联系我们删除。

“【前端】Vue+Element UI案例:通用后台管理系统-代码总结(已开源)”的评论:

还没有评论