npm 是node企业管理系统定制开发的包管理工具 yarn企业管理系统定制开发是为了弥补npm企业管理系统定制开发的缺陷而出现
yarn的速度快 并行安装 离线模式
企业管理系统定制开发安装版本的统一 .lock文件
企业管理系统定制开发更简洁的输出
企业管理系统定制开发更好的语义化
npm | yarn |
---|---|
npm install | yarn |
npm install react --save | yarn add react |
npm uninstall react --save | yarn remove react |
npm install react --save-dev | yarn add react --dev |
npm update --save | yarn upgrade |
vue-cli搭建的详细过程
使用之前必须有node环境,确保电脑上安装了node.js
node -v
可以查看安装node版本
npm -v
可以查看npm是否存在了
可以选择安装和yarn
安装cnpm
npm install -g cnpm --registry=https://registry.npmmirror.com
安装cnpm
cnpm -v
查看是否安装成功
有人说cnpm可能会引起一些奇怪的bug,稳妥起见还是把npm的源改成taobao镜像就好 这里我是安装好了yarn
可以使用cnpm install -g @vue/cli
安装脚手架
vue -V
可以查看脚手架是否安装成功 显示安装的版本
创建项目
使用 vue create projectname
element-ui在网页中使用
CDN
<!DOCTYPE html><html><head> <meta charset="UTF-8"> <!-- import CSS --> <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css"></head><body> <div id="app"> <el-button @click="visible = true" type="success">Button</el-button> <el-dialog :visible.sync="visible" title="Hello world"> <p>Try Element</p> </el-dialog> </div></body> <!-- import Vue before Element --> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script> <!-- import JavaScript --> <script src="https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.9/index.js"></script> <script> new Vue({ el: '#app', data: function() { return { visible: false } } }) </script></html>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
element-ui结合脚手架使用之全部引入
npm i element-ui --save-dev
main.js中添加
import ElementUI from 'element-ui';import 'element-ui/lib/theme-chalk/index.css';Vue.use(ElementUI);
- 1
- 2
- 3
页面中使用个组件即可
打包时内容过大
按需引入
为了更好的看出完整引入和按需引入的差别 可以先对项目进行打包
npm run build
相关步骤看官网即可
vue路由的使用
npm i vue-router@3.2.0
main.js引入router
import router from './router'new Vue({ router, render: h => h(App),}).$mount('#app')
- 1
- 2
- 3
- 4
- 5
src下创建router文件夹下面有index.js
import Vue from 'vue'import VueRouter from 'vue-router'Vue.use(VueRouter)const routes = [ { path: '/', name: "Home", component: () => import('../views/Home') }]const router = new VueRouter({ routes, mode: 'history'})export default router
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
views文件夹放置对应的路由页面Home.vue
想要显示的页面上要写<router-view></router-view>
首页整ui的搭建
使用Container布局容器
想要使用less npm i less
npm i less-loader@5.0.0
views/Main.vue
左侧菜单栏的引入
navmenu
commponet/CommonAside.vue
一级菜单的实现
导航栏的文本以及数据可以存储在一个数组中,每一个在里面又是一个对象
menu: [ { path: '/', name: 'home', label: '首页', icon: 's-home', url: 'Home/Home' }, { path: '/mail', name: 'mail', 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', ulr: 'Other/PageTwo' } ] } ]
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
处理数据使用计算属性computed
computed: { noChildren() { return this.menu.filter(item => !item.children) }, hasChildren() { return this.menu.filter(item => item.children) } } //判断是否有子项 没有的话就是一级菜单
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
<el-menu-item v-for="item in noChildren" :index="item.path" :key="item.path"> <i :class="`el-icon-${item.icon}`"></i> <span slot="title">{{ item.label }}</span> </el-menu-item>
- 1
- 2
- 3
- 4
二级菜单menu的实现
<el-submenu v-for="item in hasChildren" :index="item.path" :key="item.path"> <template slot="title"> <i :class="`el-icon-${item.icon}`"></i> <span slot="title">{{item.label}}</span> </template> <el-menu-item-group v-for="(subItem,subIndex) in item.children" :key="subItem.path"> <el-menu-item :index="`${subIndex}-${subItem.path}`">{{subItem.label}}</el-menu-item> </el-menu-item-group> </el-submenu>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
menu样式和路由跳转
.el-menu-vertical-demo:not(.el-menu--collapse) { width: 200px; min-height: 400px;}.el-menu { height: 100vh; border: none; h3 { color: #fff; text-align: center; line-height: 48px; }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
component/CommonAside.vue
<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" >
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
App.vue
html,body { margin: 0; padding: 0;}#app { height: 100vh;}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
路由跳转
component/CommonAside.vue
<h3>通用后台管理系统</h3><el-menu-item v-for="item in noChildren" :index="item.path" :key="item.path" @click="clickMenu(item)"> <i :class="`el-icon-${item.icon}`"></i> <span slot="title">{{ item.label }}</span> </el-menu-item>
- 1
- 2
- 3
- 4
- 5
methods: { handleOpen(key, keyPath) { console.log(key, keyPath); }, handleClose(key, keyPath) { console.log(key, keyPath); }, clickMenu(item) { this.$router.push({ name: item.name }) } },
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
router/index.js
const routes = [ { path: '/', name: "Main", component: () => import('../views/Main'), children: [ { path: '/home', name: 'home', component: () => import('../views/home') }, { path: '/user', name: 'user', component: () => import('@/views/user') } ] }, ]
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
header组件的搭建
component/CommonHeader.vue
<template> <header> <div class="l-content"> <el-button icon="el-icon-menu" plain size="mini"></el-button> <!-- 面包屑 --> <h3 style="color: #fff;">首页</h3> </div> <div class="r-content"> <el-dropdown trigger="click" szie="mini"> <span> <img :src="userImg" class="user"> </span> <el-dropdown-menu slot="dropdown"> <el-dropdown-item command="a">个人中心</el-dropdown-item> <el-dropdown-item command="b">退出</el-dropdown-item> </el-dropdown-menu> </el-dropdown> </div> </header></template><script>export default { name: 'CommonHeader', data() { return { userImg: require('../assets/images/user.png') } }}</script>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
Main.vue
<template> <div> <el-container style="height: 100%;"> <el-aside width="auto"> <common-aside></common-aside> </el-aside> <el-container> <el-header> <common-header></common-header> </el-header> <el-main> <router-view></router-view> </el-main> </el-container> </el-container> </div></template>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
header组件样式
header { display: flex; height: 100%; justify-content: space-between; align-items: center; .l-content { display: flex; align-items: center; .el-button { margin-right: 20px; } } .r-content { .user { width: 40px; height: 40px; border-radius: 50%; } }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
vuex实现左侧折叠
兄弟组件相互通信 或者事件总线bus来做 全局事件总线
CommonHeader.vue中的按钮点击之后控制CommonAside.vue中的侧边栏的伸缩
npm i vuex
yarn add vuex@3
home组件布局
栅格布局
elementUI 中的Layout布局
views/home/index.vue
<template> <el-row class="home" :gutter="20" > <el-col :span="8" style="margin-top: 20px;"> <el-card shadow="hover"> <div class="user"> <img :src="userImg" alt=""> <div class="userinfo"> <p class="name">Admin</p> <p class="access">超级管理员</p> </div> </div> <div class="login-info"> <p>上次登录时间:<span>2021-7-19</span></p> <p>上次登录地点:<span>武汉</span></p> </div> </el-card> <el-card style="margin-top: 20px; height: 460px;"> <el-table :data="tableData"> <el-table-column v-for="(item, index) in tableLabel" :key="index" :prop="index" :label="item" > </el-table-column> </el-table> </el-card> </el-col> </el-row></template><script>export default { data() { return { userImg: require('../../assets/images/user.png'), 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 } ], tableLabel: { name: '课程', todayBuy: '今日购买', monthBuy: '本月购买', totalBuy: '总购买' } } }}</script><style></style>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
样式 home.less
.home { .user { display: flex; align-items: center; padding-bottom: 20px; margin-bottom: 20px; border-bottom: 1px solid #ccc; img { width: 150px; height: 150px; border-radius: 50%; margin-right: 40px; } &info { .name { font-size: 32px; margin-bottom: 10px; } .access { color: #999999; } } } .login-info { 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; .el-card { width: 32%; margin-bottom: 20px; } .icon { font-size: 30px; width: 80px; height: 80px; text-align: center; line-height: 80px; color: #fff; } .detail { margin-left: 15px; display: flex; flex-direction: column; justify-content: center; .num { font-size: 30px; margin-bottom: 10px; } .txt { font-size: 14px; text-align: center; color: #999999; } } } .graph { margin-top: 20px; display: flex; justify-content: space-between; .el-card { width: 48%; } } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
组件订单展示
<el-col :span="16"> <div class="num"> <el-card style="margin-top: 20px;" v-for="item in countData" :key="item.name" :body-style="{display: 'flex',padding: 0}" > <i class="icon" :class="`el-icon-${item.icon}`" :style="{ background: item.color }"></i> <div class="detail"> <p class="num">¥{{ item.value }}</p> <p class="txt">{{item.name}}</p> </div> </el-card> </div> <el-card style="height: 280px;"></el-card> <div class="graph"> <el-card style="height: 260px"></el-card> <el-card style="height: 260px"></el-card> </div> </el-col>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
countData: [ { name: '今日支付订单', value: 1235, 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: 200, icon: 'star-on', color: '#ffb980' }, { name: '本月未支付订单', value: 1234, icon: 's-goods', color: '#5ab1ef' } ]
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
axios的基本使用
npm install axios
yarn add axios
main.js中进行全局的引入
// 引入axiosimport http from 'axios'Vue.prototype.$http = http
- 1
- 2
- 3
axios的二次封装
二次封装的目的:将配置文件与axios结合,通过对当前项目的配置的环境做判断,来改变一个接口请求的地址。
api/axios.js 编写axios的工具类
import axios from 'axios'import config from '@/config'// 判断当前的运行环境const baseUrl = process.env.NODE_ENV === 'development' ? config.baseUrl.dev : config.baseUrl.proclass HttpRequest { constructor(baseUrl) { this.baseUrl = baseUrl } getInsideConfig() { const config = { baseUrl: this.baseUrl, header: {} } return config } interceptores(instance) { // 添加请求拦截器 instance.interceptors.request.use(function (config) { // 在发送请求之前做些什么 return config; }, function (error) { // 对请求错误做些什么 return Promise.reject(error); }); // 添加响应拦截器 instance.interceptors.response.use(function (response) { // 对响应数据做点什么 return response; }, function (error) { // 对响应错误做点什么 return Promise.reject(error); }); } // 接口请求时使用 request(options) { const instance = axios.create() // 把传进来的参数和这里的参数合并 options = {...this.getInsideConfig(), ...options} this.interceptores(instance) return instance(options) }}export default new HttpRequest(baseUrl)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
config/index.js 配置文件
export default { baseUrl: { // 开发环境 dev: '/api/', // 生产环境 pro: '' }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
axios的使用
api/data.js:将接口请求都写在这里
import axios from "./axios"export const getMenu = (param) => { return axios.request({ url: '/permission/getMenu', method: 'post', data: param })}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
home/index.vue
import { getMenu } from '@/api/data.js' mounted() { console.log(getMenu()) getMenu().then( res => { console.log(res) }) },
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
mock
生成随机数据,拦截 Ajax 请求
npm i mock.js
api/mock.js
具体代码
echarts表格的基本使用
npm i echarts@5.1.2
yarn add echarts@5.1.2
面包屑&tag介绍
面包屑数据处理
面包屑的数据要用vuex来存储
store/tab.js
面包屑的实现
tag功能介绍&页面编写
tag点击与删除事件
用户管理列表页功能&form组件
form组件编写
用户列表form组件的使用
用户列表新增&编辑接口调用
用户列表tabel组件编写
table组件的使用
完成用户管理剩余功能
删除部分有bug
权限管理功能
登录页面
登录权限和导航守卫
给系统添加一个登录凭证叫做“token”,这个token在登录的时候通过接口请求将用户名和密码传给后端,后端在数据库中匹配成功后返回一个凭证,前端将token缓存起来,再调用接口时传给后端验证就建立了登录权限验证
npm i js-cookie
登录接口逻辑实现
菜单权限功能
npm i vue-router@3.5.3
不同账号登录 不同角色 内容不同 普通用户少一部分页面