文章目录

  • 前言
  • 项目文件目录
  • api
    • mockServe
      • home.js
      • permission.js
    • index.js
    • mock.js
    • user.js
  • assert
  • components
    • CommonAside.vue
    • CommonHeader.vue
    • CommonTags.vue
  • data
    • echartsData
      • order.js
      • user.js
      • video.js
    • mockData
      • tableData.js
      • userData.js
      • videoData.js
    • CountData.js
    • MenuData.js
    • TableData.js
    • TableLabel.js
  • router
    • index.js
  • store
    • index.js
    • tab.js
  • utils
    • request.js
  • Views
    • Home.vue
    • Login.vue
    • Main.vue
    • Mall.vue
    • PageOne.vue
    • PageTwo.vue
    • User.vue
  • App.vue
  • main.js
  • 要安装的依赖
  • 已上传到Gitee

参考视频: 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 =[]
// 直接导出
export default {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'
export default {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: '获取成功'}}} else if (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'// 请求首页数据,直接把这个对象导出
export const getData = () => {// 返回一个promisereturn http.get('/home/getData')
}// 下面四个:用户管理-后端-网络请求接口
export const getUser = (params) => {return http.get('/user/get/', params)
}export const createUser = (data) => {return http.post('/user/create', data)
}export const deleteUser = (data) => {return http.post('/user/del', data)
}export const updateUser = (data) => {return http.post('/user/update', data)
}// 登录权限
export const getMenu = (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中获取参数
function param2Obj (url) {const search = url.split('?')[1]if (!search) {return {}}return JSON.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)}))
}export default {/*** 获取列表* 要带参数 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) return falsereturn true})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 = nameu.addr = addru.age = ageu.birth = birthu.sex = sex_numreturn true}})return {code: 20000,data: {message: '编辑成功'}}}
}

assert

这些图片在链接里:

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><!-- 观察数据,我们发现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><span slot="title">{{ item.label }}</span></el-menu-item><el-submenu v-for="item in hasChildren" :key="item.label" :index="item.label"><template slot="title"><i :class="`el-icon-${item.icon}`"></i><span slot="title">{{ item.label }}</span></template><el-menu-item-group v-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><style lang="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'
export default {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,会被过滤器留下return this.MenuData.filter(item => !item.children)},hasChildren() {return this.MenuData.filter(item => item.children)},// 要放到计算属性,自动计算isCollapse() {return this.$store.state.tab.isCollapse},// 获取菜单MenuData() {return JSON.parse(cookie.get('menu')) || this.$store.state.tab.menu}}
}
</script>

CommonHeader.vue

<template><div class="header-container"><div class="l-content"><el-button @click="handleMenu" icon="el-icon-menu" size="mini"></el-button><!-- 面包屑 --><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 @command="handleClick"><span class="el-dropdown-link"><img class="user" src="../assets/images/user.png" alt=""></span><el-dropdown-menu slot="dropdown"><el-dropdown-item>个人信息</el-dropdown-item><el-dropdown-item command="logout">退出</el-dropdown-item></el-dropdown-menu></el-dropdown></div></div>
</template><script>
import { mapState } from 'vuex'
import Cookie from 'js-cookie'
export default {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><style lang="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><div class="tabs"><!-- closable是否可删除:除了"首页"都可删 --><!-- effect:主题,当前主题是dark,其他事plain --><el-tag v-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'export default {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><style lang="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: [],
}export default 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: [],
}export default user

video.js

const video = {tooltip: {trigger: "item",},color: ["#0f78f4","#dd536b","#9462e5","#a6a6a6","#e1bb22","#39c362","#3ed1cf",],series: [],
}export default 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}
]export default 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}
]export default 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}
]export default 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",},
]
export default 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'}]}
]export default 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}
]export default TableData

TableLabel.js

const TableLabel={name:'课程',todayBuy:'今日购买',monthBuy:'本月购买',totalBuy:'总购买'
}export default 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 = new VueRouter({routes
})// 路由守卫:全局前置导航守卫
router.beforeEach((to, from, next) => {// 获取tokenconst token = Cookie.get('token')if (!token && to.name !== 'login') {next({ name: 'login' })} else if (token && to.name === 'login') {next({ name: 'home' })} else {next()}
})export default router

store

index.js

import Vue from "vue";
import Vuex from 'vuex';
import tab from './tab';Vue.use(Vuex)// 创建Vuex实例并导出
export default new Vuex.Store({modules:{tab}
})

tab.js

import Cookie from "js-cookie"export default {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 = valCookie.set('menu', JSON.stringify(val))},// 动态添加路由addMenu(state, router) {// 判断Cookieif (!Cookie.get('menu')) returnconst menu = JSON.parse(Cookie.get('menu'))state.menu = menuconst 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);
})export default http

Views

Home.vue

<template><el-row><el-col :span="8"><!-- user卡片 --><el-card><div class="user"><img src="../assets/images/user.png" alt=""><div class="userInfo"><p div class="name">Admin</p><p div class="access">超级管理员</p></div></div><div class="loginInfo"><p>上次登录时间:<span>2021-7-19</span></p><p>上次登陆地点:<span>武汉</span></p></div></el-card><!-- table卡片 --><el-card style="margin-top: 20px;"><el-table :data="TableData" style="width: 100%"><!-- 这里的val,key对应的是对象里的 --><el-table-column v-for="(value, key) in TableLabel" :prop="key" :label="value"></el-table-column></el-table></el-card></el-col><el-col :span="16"><div class="num"><el-card v-for="item in CountData" :key="item.name" :body-style="{ display: 'flex', padding: 0 }"><i class="icon" :class="`el-icon-${item.icon}`" :style="{ backgroundColor: item.color }"></i><div class="details"><p class="price">{{ priceFormate(item.value) }}</p><p class="desc">{{ item.name }}</p></div></el-card></div><!-- echarts图表 --><div style="margin-left:20px"><!-- 折线图 --><el-card style="height:280px"><div ref="echarts1" style="height:280px"></div></el-card><div class="graph"><!-- 柱状图 --><el-card style="height:280px"><div ref="echarts2" style="height:280px"></div></el-card><!-- 饼状图 --><el-card style="height:320px"><div ref="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'export default {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 = xAxisDataecharts1Option.xAxis = xAxisDataecharts1Option.yAxis = {}echarts1Option.series = []// 配置seriesxAxis.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 = videoecharts3Option.series = {data: videoData,type: 'pie'}echarts3.setOption(echarts3Option);})}
}
</script><style lang="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-form ref="form" class="login_container" :model="login" status-icon :rules="rules" label-width="70px"><!-- h3要放在里面:只能有一个根,且title也是表单的一部分 --><h3 class="login_title">用户登录</h3><!-- prop对应rules里的键 --><el-form-item label="用户名" prop="username"><el-input v-model="login.username" autocomplete="off"></el-input></el-form-item><el-form-item label="密码" prop="password"><el-input type="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'
export default {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){// 记录cookieCookie.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><style lang="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-aside width="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'
export default {data(){return{}},components:{CommonAside,CommonHeader,CommonTags}
}
</script><style lang="less" scoped>
.el-header{padding:0;
}
</style>

Mall.vue

<template><h1>Mall</h1>
</template><script>
export default {}
</script><style></style>

PageOne.vue

<template><h1>Page1</h1></template><script>export default {}</script><style></style>

PageTwo.vue

<template><h1>Page2</h1></template><script>export default {}</script><style></style>

User.vue

<template><div class="manage"><div class="manage-header"><!-- 新增按钮 --><el-button type="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-item label="姓名" prop="name"><el-input placeholder="请输入姓名" v-model="form.name"></el-input></el-form-item><el-form-item label="年龄" prop="age"><el-input placeholder="请输入年龄" v-model="form.age"></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="出生日期"><el-form-item prop="birth"><el-date-picker type="date" placeholder="请选择日期" v-model="form.birth" value-format="yyyy-MM-DD"></el-date-picker></el-form-item></el-form-item><el-form-item label="地址" prop="addr"><el-input placeholder="请输入地址" v-model="form.addr"></el-input></el-form-item></el-form><div slot="footer" class="dialog-footer"><el-button @click="closeDialog">取 消</el-button><el-button type="primary" @click="submit">确 定</el-button></div></el-dialog><!-- 搜索框 --><el-form :inline="true"><el-form-item><el-input v-model="searchForm.name" placeholder="请输入名称"></el-input></el-form-item><el-form-item><el-button type="primary" @click="search">查询</el-button></el-form-item></el-form></div><div class="common-table"><!-- 用户数据Table --><el-table :data="tableData" stripe style="width: 100%" height="90%"><el-table-column prop="name" label="姓名"></el-table-column><el-table-column prop="age" label="年龄"></el-table-column><el-table-column prop="sex" label="性别"><template slot-scope="scope"><span>{{ scope.row.sex == 1 ? '男' : '女' }}</span></template></el-table-column><el-table-column prop="birth" label="出生日期"></el-table-column><el-table-column prop="addr" label="地址"></el-table-column><!-- 自定义列 --><el-table-column label="操作"><template slot-scope="scope"><el-button @click="handleEdit(scope.row)">编辑</el-button><el-button type="danger" @click="handleDelete(scope.row)">删除</el-button></template></el-table-column></el-table><!-- 分页 --><div class="pager"><el-pagination layout="prev, pager, next" :total="total" @current-change="currentChange"></el-pagination></div></div></div>
</template><script>
import { getUser, createUser, deleteUser, updateUser } from '../api/index'
export default {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.listthis.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 = valthis.getList()},// 搜索search() {this.getList()}},mounted() {this.getList()}
}
</script><style lang="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><div id="app"><router-view></router-view></div>
</template><script>
export default {}
</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)new Vue({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+Element UI案例:通用后台管理系统-代码总结(已开源)相关推荐

  1. 【前端】Vue+Element UI案例:通用后台管理系统-用户管理:Table表格增删查改、Pagination分页、搜索框

    文章目录 目标 代码 0.结构 1.按钮-删除 2.按钮-编辑 3.debug 4.样式 5.分页Pagination:功能 6.分页Pagination:样式 7.搜索框:功能 8.搜索框:样式 总 ...

  2. 【前端】Vue+Element UI案例:通用后台管理系统-用户管理:Form表单填写、Dialog对话框弹出

    文章目录 目标 代码 0.页面结构 1.新增按钮和弹出表单:结构 2.新增按钮和弹出表单:点击新增弹出表单 3.表单样式 4.表单验证 5.表单的提交和取消功能:接口.mock相关准备 6.表单的提交 ...

  3. 【前端】Vue+Element UI案例:通用后台管理系统-登陆不同用户显示不同菜单、动态添加路由

    文章目录 目标 代码 0.动态地显示菜单:store 1.动态注册路由 2.解决刷新后摆平问题 总代码 本篇修改的代码文件 tab.js 参考视频: VUE项目,VUE项目实战,vue后台管理系统,前 ...

  4. 17. 基于Vue+Element+nodeJs+Express+MySql后台管理系统-前端主页面

    这期开始主页面的编写,在src\views目录下创建主页面Home.vue,主要是管理系统页面布局,头部和左侧导航栏. 一.Home.vue 页面 1.1 Home.vue 页面html部分: < ...

  5. html全局布局 vue_基于Vue+Element的电商后台管理系统

    前言 mall项目后台管理系统的前端项目. 项目介绍 mall-admin-web是一个电商后台管理系统的前端项目,基于Vue+Element实现. 主要包括商品管理.订单管理.会员管理.促销管理.运 ...

  6. 用vue(element ui)快速开发后台管理

    平时我们在用vue开发网站后台管理的时候.比如说要写管理文章的功能,要先去写列表页面,编辑页面,添加页面.另外还需要程序提供对应的增删改查接口 图片上传接口等等,那么有没有一种快速的方法.可以用程序来 ...

  7. web前端-Vue element UI中的el-table勾选框 展示隐藏;设置默认勾选、禁用

    1.el-table如果我们想新增一个勾选框,在  .vue文件中 <el-table-column type="selection" width="55" ...

  8. web前端-vue element UI el-table,el-table-column表格添加行点击事件

    el-table如下数据 一.el-table 整行都可以点击事件 定义点击事件 @row-click="openDetails":openDetails为方法名 <!-- ...

  9. 【Vue 快速入门】从零开始搭建 VUE + Element UI后台管理系统框架

    [Vue 快速入门]从零开始搭建 VUE + Element UI后台管理系统框架 前言 后台管理系统前端框架,现在很流行的形式都是,上方和左侧都是导航菜单,中间是具体的内容.比如阿里云.七牛云.头条 ...

最新文章

  1. 单例模式及getInstance()的用法
  2. Java新鲜东西,带有标签的continue和break
  3. Socket,非阻塞,fcntl
  4. python123作业答案第七周-python一周练习
  5. java虚拟机-程序计数器PC Register
  6. (转)linux下vi编辑器编写C语言的配置
  7. 在虚拟机linux上安装gdb,linux下gdb的安装和使用
  8. quickserver java_QuickServer--在吵闹的环境里快速搭建自己的TcpServer(Pragmatic系列) - java - CSDN技术中心...
  9. 从程序员到CTO的Java技术路线图 JAVA职业规划 JAVA职业发展路线图 系统后台框架图、前端工程师技能图 B2C电子商务基础系统架构解析...
  10. Windows移动开发(四)——闭关修炼
  11. BERT时代,向量语义检索我们关注什么?
  12. Swift 新建 APP 黑屏问题
  13. php 重定义数组k,PHP基础篇之数组
  14. TakeColor鼠标位置不对/取色不准
  15. matlab中prod函数、mean函数、median函数——小白MAT LAB学习笔记
  16. 程序员删库后发同学群问如何恢复,同学:跑路吧,记得跑快点
  17. FPGA控制_步进电机模块使用说明
  18. python制作简易动态二维码
  19. 粉刷匠计算机音乐,音乐《粉刷匠》
  20. 一站式原创文章神器,让你轻松创作高质量文章

热门文章

  1. 老男孩教育day2学习总结
  2. C# VS2019 Form 状态栏添加系统时间实时刷新
  3. Kamiya丨Kamiya艾美捷抗FLAG多克隆说明书
  4. 指南针操盘软件评价,指南针操盘软件使用经历,
  5. 计算机网络复习题1(含答案及解析及知识点)
  6. BitBlt实现透明背景贴图
  7. CCriticalSection的使用
  8. 通达信凹口平量柱选股公式,倍量柱之后调整再上升
  9. ios 加载大量图片崩溃_加载高清大图崩溃问题
  10. Siki_Unity_2-7_Stealth秘密行动