*vue知识点

*创建vue脚手架

=>安装npm

=>安装node.js

=>安装vue-cli

npm install vue-cli -g //全局安装 vue-cli

=>创建 vue create ”项目名称“

=>运行vue项目 npm run serve

=>安装常用图标 npm install font—awesome

*vue项目通用

创建vue项目选择条件引用Element-UI时所用到通用部分

=>vue.config.js

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
let proxyObj = {};

proxyObj['/'] = {
//wbsocket
ws: false,
// 目标地址
//target: 'http://localhost:8081',
target: 'http://dongeast.top:8181',
// 发送请求头host会被设置target
changeOrigin: true,
// 不重写请求地址
pathWewrite: {
"^/": '/',
}
}

proxyObj['/ws'] = {
ws: true,
target: 'ws://dongeast.top:8181'
};

module.exports = {
devServer: {
host: "localhost",
port: 8080,
proxy: proxyObj,
}
}

=>main.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import Vue from 'vue'
import App from './App.vue'
import ElementUI from 'element-ui';
import router from './router'
import 'element-ui/lib/theme-chalk/index.css';

Vue.config.productionTip = false
Vue.use(ElementUI);

//可在此放置路由前置守卫

new Vue({
router,
render: h => h(App),
}).$mount('#app')

=>router.js

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
import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from '../views/Login'

Vue.use(VueRouter)

const routes = [{
path:'/',
name:'Login',
component:Login,
children: [
/*{
path: '/chat',
name: '在线聊天',
component: FriendChat,
}, */
{
path: '/adminInfo',
name: '个人中心',
component: AdminInfo
}]
},
...,
...,
...,
]
const router = new VueRouter({
routes
})
const originalPush = VueRouter.prototype.push;
VueRouter.prototype.push = function push(location) {
return originalPush.call(this, location).catch((err) => err);
};
export default router

=>api.js

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
import axios  from 'axios';
import { Loading, Message } from 'element-ui'
import router from '../router';

// 请求拦截器
axios.interceptors.request.use(success => {
if (window.sessionStorage.getItem('tokenStr')) {
success.headers['Authorization'] = window.sessionStorage.getItem('tokenStr');
}
return success;
}, error => {
console.log(error);
})

//响应拦截器
axios.interceptors.response.use(success=>{
//业务逻辑错误
if(success.status && success.status == 200){
if(success.data.code == 500 || success.data.code == 401 || success.data.code == 403){
Message.error({message:success.data.message});
return;
}
if(success.data.message){
Message.success({message:success.data.message})
}
}
return success.data;
},error =>{
if(error.response.code == 504 || error.response.code == 404){
Message.error({Message:'服务器错误!'});
}else if(error.response.code == 403){
Message.error({message:'权限不足,请联系管理员!'})
}else if(error.response.code == 401){
Message.error({message:'尚未登录,请登录!'})
router.replace('/');
}else{
if(error.response.data.message){
Message.error({message:error.response.data.message})
}else{
Message.error({message:'未知错误!'});
}
}
return;
});

let base = '';

//传送json格式的post请求
export const postRequest = (url,params)=>{
return axios({
method:'post',
url:`${base}${url}`,
data:params
})
}
//传送json格式的put请求
export const putRequest = (url, params) => {
return axios({
method: 'put',
url: `${base}${url}`,
data: params
})
}
//传送json格式的get请求
export const getRequest = (url, params) => {
return axios({
method: 'get',
url: `${base}${url}`,
data: params,
})
}
//传送json格式的delete请求
export const deleteRequest = (url, params) => {
return axios({
method: 'delete',
url: `${base}${url}`,
params,
})
}

=>App.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<div id="app">
<router-view />
<!-- 默认跳转 -->
</div>
</template>

<script>
export default {
name: 'App'

}
</script>
<style>

</style>

*选择导航器

menu.js部分

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
import { getRequest } from "./api";

export const initMenu = (router, store) => {
if (store.state.routes.length > 0) {
return;
}
getRequest("/system/cfg/menu").then(data => {
if (data) {
// 格式化Router
let fmtRoutes = formatRoutes(data);
// 添加到路由
router.addRoutes(fmtRoutes);
//将数据存入vuex
store.commit('initRoutes', fmtRoutes);
}
})
}

// routes是从后端接收过来的data数据
export const formatRoutes = (routes) => {
let fmtRoutes = [];
routes.forEach(router => {
let {
path,
component,
name,
iconCls,
children
} = router;
if (children && children instanceof Array) {
// 递归
children = formatRoutes(children);
}
let fmRouter = {
path: path,
name: name,
iconCls: iconCls,
children: children,
component(resolve) {
if (component.startsWith('Home')) {
require(['../views/' + component + '.vue'], resolve);
} else if (component.startsWith('Emp')) {
require(['../views/emp/' + component + '.vue'], resolve);
} else if (component.startsWith('Per')) {
require(['../views/per/' + component + '.vue'], resolve);
} else if (component.startsWith('Sal')) {
require(['../views/sal/' + component + '.vue'], resolve);
} else if (component.startsWith('Sta')) {
require(['../views/sta/' + component + '.vue'], resolve);
} else if (component.startsWith('Sys')) {
require(['../views/sys/' + component + '.vue'], resolve);
}
}
}
fmtRoutes.push(fmRouter)
});
return fmtRoutes;
}

vue部分

①不定死(推荐)(首页左侧)

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
 <el-container>
<el-aside width="200px">
<el-menu router unique-opened><!-- 默认不展开列表 -->
<el-submenu :index="index + ''" v-for="(item, index) in routes" :key="index">
<template slot="title">
<i :class="item.iconCls" style="color: skyblue; margin-right: 5px"></i>
<span>{{ item.name }}</span>
</template>
<el-menu-item :index="children.path"
v-for="(children, indexj) in item.children" :key="indexj">
{{ children.name }}
</el-menu-item>
</el-submenu>
</el-menu>
</el-aside>
<el-main>
<el-breadcrumb v-if="this.$router.currentRoute.path != '/home'">
<el-breadcrumb-item :to="{path:'/home'}">首页</el-breadcrumb-item>
<el-breadcrumb-item>{{this.$router.currentRoute.name}}</el-breadcrumb-item>
</el-breadcrumb>
<div class="homeWelcome" v-if="this.$router.currentRoute.path == '/home'">
欢迎来到云E办系统!
</div>
<router-view class="homeRouterView"></router-view>
</el-main>
</el-container>
export default {
name:'Home',
computed:{
routes(){
return this.$store.state.routes;
}
}
}

②定死(不推荐)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<el-container>
<el-aside width="200px">
<el-menu @select="menuClick">
<!-- 菜单点击事件 -->
<el-submenu index="1">
<template slot="title"><i class="el-icon-location"></i>导航一</template>
<el-menu-item-group>
<el-menu-item index="/adminInfo">选项1</el-menu-item>
<el-menu-item index="1-2">选项2</el-menu-item>
</el-menu-item-group>
</el-submenu>
</el-menu>
</el-aside>
<el-main>
<router-view />
</el-main>
</el-container>
methods:{
menuClick(index){
this.$router.push(index)
}
}

*图标

=>安装 npm install font—awesome 或 npm install font-awesome —save

在main.js添加

1
import 'font-awesome/css/font-awesome.css'

在vue中添加

1
2
3
4
5
<template slot="title">
<i :class="item.iconCls" style="color: skyblue; margin-right: 5px"></i>
<!-- :class="item.iconCls"绑定数据库里的图标 -->
<span>{{ item.name }}</span>
</template>

*登录功能

*规则问题:输入框提示为空

1
2
3
4
5
6
7
8
9
10
<el-form :rules="rules" v-loading="loading" ref="loginForm" :model="loginForm" class="loginContainer">
出现红色提示为空
</el-form>
data(){
rules:{
username:[{required:true,message:'请输入用户名',trigger:'blur'}],
password:[{required:true,message:'请输入密码',trigger:'blur'}],
code:[{required:true,message:'请输入验证码',trigger:'blur'}],
}
}

*获取用户登录信息

main.js

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
//路由前置守卫
router.beforeEach((to, from, next) => {
if (window.sessionStorage.getItem('tokenStr')) {
initMenu(router, store);
if (!window.sessionStorage.getItem('user')) {
// 判断用户信息是否存在
return getRequest('/admin/info').then(resp => {
if (resp) {
//存入用户信息
window.sessionStorage.setItem('user', JSON.stringify(resp));
// this.$store.commit('INIT_ADMIN', resp);
store.commit("INIT_ADMIN", resp);
next();
}
});
}
next();
} else {
// bug:防止复制网址直接跳过登录
if (to.path == '/') {
next();
} else {
next('/?redirect=' + to.path)
}
}
})

vue部分

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
<el-header class="homeHeader">
<div class="title">云E办</div>
<div>
<el-dropdown class="userInfo" @command="commandHandler">
<span class="el-dropdown-link">
{{user.name}}<i><img :src="user.userFace"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="userinfo">个人中心</el-dropdown-item>
<el-dropdown-item command="setting">设置</el-dropdown-item>
<el-dropdown-item command="logout">注销登录</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</el-header>
data(){
return{
user:JSON.parse(window.sessionStorage.getItem('user'))
}
},
computed:{
routes(){
return this.$store.state.routes;
}
},
methods:{
commandHandler(command){
if(command == 'logout'){
this.$confirm('此操作将注销登录, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
//注销登录
this.postRequest('/logout');
window.sessionStorage.removeItem('tokenStr');
window.sessionStorage.removeItem('user');
//清空菜单
this.$store.commit('initRoutes',[]);
//跳转登录页面
this.$router.replace('/')
}).catch(() => {
this.$message({
type: 'info',
message: '已取消操作'
});
});
}
}
}

*主界面

*面包屑

1
2
3
4
5
6
7
8
9
<el-breadcrumb v-if="this.$router.currentRoute.path != '/home'"><!-- 判断书否为首页,如果是则不展示面包屑 -->
<el-breadcrumb-item :to="{path:'/home'}">首页</el-breadcrumb-item>
<!-- 当前路由名称 -->
<el-breadcrumb-item>{{this.$router.currentRoute.name}}</el-breadcrumb-item>
</el-breadcrumb>
<div class="homeWelcome" v-if="this.$router.currentRoute.path == '/home'">
欢迎来到云E办系统!
</div>
<router-view class="homeRouterView"></router-view>

*login.vue

1
2
3
4
5
6
7
8
9
10
11
this.postRequest('/login',this.loginForm).then(resp=>{
if(resp){
this.loading = false;
//存储用户token
const tokenStr = resp.obj.tokenHead + resp.obj.token;
window.sessionStorage.setItem('tokenStr',tokenStr);
//页面跳转首页
let path = this.$router.replace('/home');
this.$$router.replace((path=='/'||path==undefined)?'/home': path)
}
})

*选择框

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<el-select
size="small"
v-model="jl.titleLevel"
placeholder="职称等级"
style="margin-left: 10px; margin-right: 10px">
<el-option
v-for="item in titleLevels"
:key="item"
:label="item"
:value="item">
</el-option>
data() {
return {
jl: {
name: "",
titleLevel: "",
},
titleLevels: ["正高级", "副高级", "中级", "初级", "员级"],
},
}

*组件

1
2
3
4
5
@keydown.enter.native='事件'   键盘事件
icon="图标" 添加图标
placeholder="请输入中文名" 提示内容
size="mini" 变小输入框
:title="数据" 动态绑定标题
1
2
3
show-checkbox    默认选中子节点
:key="index" key绑定的是index索引值,索引变化,key也会改变
:props=" " 组件数据的一个字段,期望从父组件传下来