一.项目创建配置
1.1.项目创建
新建一个空文件夹,在地址栏输入cmd指令
在cmd里执行如下命令
1.1.1.项目初始化
npm init -y 进行初始化
1.1.2.创建项目脚手架
npm i -D @vue/cli 创建脚手架
npx vue -V 查看脚手架版本指令
1.1.3.通过脚手架构建项目
npx vue create myvuedemo 通过脚手架构建
进入项目目录,启动项目
浏览器输入上面地址,展示页面如下,代表启动成功
1.2.项目配置
vue前端开发工具一般用 vscode和hbuild,这里我用vscode演示
打开vue.config.js,
做以下配置
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
lintOnSave: false ,
devServer:{
open:true,//自动打开
host:'localhost',
proxy:{ // 配置代理
'/api':{
target:'http://localhost:8080',//前后端交互时用
changeOrigin:true, //允许跨域
pathRewrite:{
'^/api':''
}
}
}
}
})
二.集成elementui框架
2.1.整合elementui
进入myvuedemo文件夹,进入cmd,执行 npm i element-ui -S
打开package.json,可以看到多出很多 elementUI 的东西
2.2.elementui引用
进入src->main.js里面
import ElementUI from 'element-ui' //引入ElementUI
import 'element-ui/lib/theme-chalk/index.css' //导入样式
Vue.use(ElementUI) //使用
修改原始HelloWorld.vue 改为Home.vue,打开Home.vue,修改内容为elementui的一些按钮
<div class="hello">
<h1>hello</h1>
<el-button>hello</el-button>
<el-button type="primary">hello</el-button>
<el-button type="info">hello</el-button>
<el-button type="danger">hello</el-button>
<el-button type="success">hello</el-button>
</div>
修改app.vue
<template>
<div id="app">
<Home/>
</div>
</template>
<script>
import Home from './components/Home.vue'
export default {
name: 'App',
components: {
Home
}
}
</script>
最终效果
至此,elementui框架集成完毕
三.项目安装配置
3.1.axios安装
进入项目cmd,运行 npm i axios -S
打开main.js,配置 axios 全局引用
import axios from 'axios' //引入axios
Vue.prototype.axios = axios // 挂载到原型上,可以全局使用
3.2.路由安装
进入项目cmd,运行 npm i vue-router@3.5.3 -S
----- 因为用的是Vue2版本,所以定义一下router的版本
安装完进行配置,在src目录下新建文件夹 router,在该文件夹下创建 index.js
import Vue from 'vue'
import Router from 'vue-router'
import Home from '../components/Home.vue'
Vue.use(Router)
export default new Router({
routes:[
{
path:'/',
component:Home
}
],
mode:'history'
})
使用,在main.js里面导入并在渲染之前挂载一下
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import ElementUI from 'element-ui' //引入ElementUI
import 'element-ui/lib/theme-chalk/index.css' //导入样式
import axios from 'axios' //引入axios
Vue.prototype.axios = axios // 挂载到原型上,可以全局使用
Vue.use(ElementUI) //使用
Vue.config.productionTip = false
new Vue({
router,
render: h => h(App),
}).$mount('#app')
在app.vue里面加入路由出口, <router-view></router-view>
最终效果
3.3.路由升级-》路由懒加载
不需要 import Home...,只需要在 componet 后面加 import
import Vue from 'vue'
import Router from 'vue-router'
// import Home from '../components/Home.vue'
Vue.use(Router)
export default new Router({
routes:[
{
path:'/home',
// component:Home
component:()=> import('@/components/Home')
}
],
mode:'history'
})
3.4.路由升级-》异步组件
import Vue from 'vue'
import Router from 'vue-router'
// import Home from '../components/Home.vue'
Vue.use(Router)
export default new Router({
routes:[
{
path:'/home',
// component:Home
// component:()=> import('@/components/Home') //路由懒加载
component:resolve => require(['@/components/Home'],resolve) // 异步组件
}
],
mode:'history'
})
四.项目实战
4.1.通用登录页面实现
component文件夹创建Login.vue
<template>
<div class="login">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>XXXXXX后台管理系统</span>
</div>
<el-form label-width="80px" :model="form" ref="form">
<el-form-item label="用户名" prop="username"
:rules="[
{required:true,message:'请输入用户名', trigger:'blur'},
{min:4,max:10,message:'长度在4-10位字符之间', trigger:'blur'}
]">
<el-input v-model="form.username"></el-input>
</el-form-item>
<el-form-item label="密码" prop="password"
:rules="[
{required:true,message:'请输入密码', trigger:'blur'},
{min:6,max:12,message:'长度在6-12位字符', trigger:'blur'}
]">
<el-input type="password" v-model="form.password"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="login('form')">登录</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</template>
<script>
export default {
name: 'login',
data(){
return {
form:{
username:'',
password:''
}
}
},
methods:{
login(form){
this.$refs[form].validate((valid)=>{
if(valid){
console.log(this.form)
}else{
console.error(this.form)
}
})
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.login{
width: 100%;
height: 100%;
position: absolute;
background: #409EFF;
}
.box-card{
width: 450px;
margin:200px auto;
}
</style>
修改router文件夹下的index.js
import Vue from 'vue'
import Router from 'vue-router'
// import Home from '../components/Home.vue'
Vue.use(Router)
export default new Router({
routes:[
{
path:'/',
redirect:'/login',
hidden:true,
component:()=> import('@/components/Login') //路由懒加载
},
{
path:'/login',
name:'/Login',
hidden:true,
component:()=> import('@/components/Login') //路由懒加载
},
{
path:'/home',
// component:Home
// component:()=> import('@/components/Home') //路由懒加载
component:resolve => require(['@/components/Home'],resolve) // 异步组件
}
],
mode:'history'
})
4.2. 封装2个工具类
在src目录下新建文件夹 utils,新建2个工具类 setToken.js
// 设置
export function setToken(tokenKey,token){
return localStorage.setItem(tokenKey,token)
}
// 获取
export function getToken(tokenKey){
return localStorage.getItem(tokenKey)
}
// 删除
export function removeToken(tokenKey){
return localStorage.removeItem(tokenKey)
}
和 validate.js
// 用户名匹配
export function nameRule(rule,value,callback){
// 请输入4-10位的昵称
let reg = /(^[a-zA-Z0-9]{4,10}$)/;
if(value === ''){
callback(new Error('请输入用户名'))
}else if(!reg.test(value)){ // 校验没有通过
callback(new Error('请输入4-10位用户名'))
}else{
callback()
}
}
// 用户密码匹配
export function passRule(rule,value,callback){
// 请输入6-12位的密码,需要包含大小写字母和数字以及特殊符号
let pass = /^\S*(?=\S{6,12})(?=\S*\d)(?=\S*[A-Z])(?=\S*[a-z])(?=\S*[!@#$%^&*? ])\S*$/;
if(value === ''){
callback(new Error('请输入密码'))
}else if(!pass.test(value)){ // 校验没有通过
callback(new Error('6-12位密码需要包含大小写字母和数字及符号'))
}else{
callback()
}
}
在login.vue中使用
4.3.封装axios
在src目录下新建service.js
import axios from 'axios'
import {getToken} from '@/utils/setToken.js'
import { Message } from 'element-ui'
const service = axios.create({
baseURL:'/api', //baseURL会自动加在请求地址上
timeout:5000
})
// 添加请求拦截器
service.interceptors.request.use((config)=>{
// 在请求之前做些什么(获取并设置token)
config.headers['token'] = getToken('token')
return config
},(error)=>{
return Promise.reject(error)
})
// 添加响应拦截器
service.interceptors.response.use((response)=>{
// 在响应数据做些什么(获取并设置token)
let {code, msg} = response.data
if(code !== 200){
Message({message: msg || 'error',type:'warning'})
}
return response
},(error)=>{
return Promise.reject(error)
})
export default service
更改main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import ElementUI from 'element-ui' //引入ElementUI
import 'element-ui/lib/theme-chalk/index.css' //导入样式
//引入axios
//import axios from 'axios'
import service from './service'
// 挂载到原型上,可以全局使用
// Vue.prototype.axios = axios
Vue.prototype.service = service
Vue.use(ElementUI) //使用
Vue.config.productionTip = false
new Vue({
router,
render: h => h(App),
}).$mount('#app')
4.4.api请求封装
在src目录下新建api文件夹,创建api.js
// 项目中我们大多数时候都会把对应的接口请求都封装成api来调用
import service from '../service.js'
// 登录接口封装 后面再调试
export function login(data){
return service({
method: 'post',
url:'/login',
data
})
}
更改login.vue,引用封装
<template>
<div class="login">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>XXXXXX后台管理系统</span>
</div>
<el-form label-width="80px" :model="form" ref="form">
<el-form-item label="用户名" prop="username"
:rules="[
{required:true,message:'请输入用户名', trigger:'blur'},
{min:4,max:10,message:'长度在4-10位字符之间', trigger:'blur'}
]">
<el-input v-model="form.username"></el-input>
</el-form-item>
<el-form-item label="密码" prop="password"
:rules="[
{required:true,message:'请输入密码', trigger:'blur'},
{min:6,max:12,message:'长度在6-12位字符', trigger:'blur'}
]">
<el-input type="password" v-model="form.password"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="login('form')">登录</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</template>
<script>
import {nameRule,passRule } from '@/utils/validate.js'
import {setToken } from '@/utils/setToken.js'
import { login } from '@/api/api.js'
export default {
name: 'HelloWorld',
data(){
return {
form:{
username:'',
password:''
},
rules:{
username:[{validator:nameRule,trigger: 'blur'}],
password:[{validator:passRule,trigger: 'blur'}]
}
}
},
methods:{
login(form){
setToken('username',"test")
setToken('token',"-----")
this.$message({message:"res.data.msg", type:'success'})
this.$router.push('/home')
// this.$refs[form].validate((valid)=>{
// if(valid){
// console.log(this.form)
// let LoginParam = this.form
// login(LoginParam).then(res=>{
// if(res.data.code === 200){
// setToken('username',res.data.username)
// setToken('token',res.data.token)
// this.$message({message:res.data.msg, type:'success'})
// this.$router.push('/home')
// }
// })
// }else{
// console.error(this.form)
// }
// })
}
}
}
// import { setToken } from '@/utils/setToken'
// export default {
// name: 'login',
// data(){
// return {
// form:{
// username:'',
// password:''
// }
// }
// },
// methods:{
// login(form){
// this.$refs[form].validate((valid)=>{
// if(valid){
// console.log(this.form)
// setToken('username',this.form.username);
// this.$router.push('/home')
// }else{
// console.error(this.form)
// }
// })
// }
// }
// }
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.login{
width: 100%;
height: 100%;
position: absolute;
background: #409EFF;
}
.box-card{
width: 450px;
margin:200px auto;
}
</style>
4.5.创建404页面
在src->components文件夹下新建NotFound.vue
<template>
<div class="notfound">
<div class="wrapper">
<div class="big">页面不见了</div>
<div>去首页瞧瞧,点击<router-link to="/">这里</router-link>进入首页</div>
</div>
</div>
</template>
<script>
export default {
data(){
return {};
}
}
</script>
<style>
.notfpund{
height: 100%;
/* background-image: url('../assets/404.jpg'); */
background-position: right top, center center;
background-repeat: no-repeat repeat;
}
.notfound .wrapper .big{
font-size: 74px;
font-weight: 700;
line-height: 68px;
margin-bottom: 48px;
}
</style>
app.vue中设置样式
<style>
html,body{
width: 100%;
height: 100%;
}
#app {
width: 100%;
height: 100%;
font-family:Avenir,Helvetica, Arial, sans-serif;
-webkit-font-smoothing:antialiased;
-moz-osx-font-smoothing:grayscale;
text-align:center;
color:#2c3e50;
}
</style>
router文件夹下->index.js下配置404页面路由
{
path:'*',
name:'NotFound',
hidden:true,
component:()=> import('@/components/NotFound') //路由懒加载
},
最终效果如下
五。后台页面
5.1.后台布局页面
后台布局一般分头部,左边项目列表,尾部和项目内容,主页面包屑,
在src->components文件夹下创建common文件夹。然后新建
Header.vue,
<template>
<div class="header">
<el-header>
<div class="title">管理系统</div>
<div>{{name}}</div>
</el-header>
</div>
</template>
<script>
import { getToken } from '@/utils/setToken';
export default{
name:"Header_",
data(){
return{
name:''
}
},
created(){
this.name = getToken('username')
}
}
</script>
<style scoped>
.el-header{
background: #2578b5;
color:#fff;
line-height: 60px;
display: flex;
justify-content: space-between;
}
.title{
width: 200px;
font-size: 24px;
}
</style>
Menu.vue,
<template>
<div class="menu">
<el-aside width="200px">
<el-menu
router
default-active="2"
class="el-menu-vertical-demo"
background-color="#2578b5"
text-color="#fff"
active-text-color="#ffd04b">
<!-- 在这里遍历,进行绑定 -->
<template v-for="(item,index) in menus">
<el-submenu :index="index+''" :key="index" v-if="!item.hidden">
<template slot="title">
<i class="el-icon-location"></i>
<span>{{item.name}}</span>
</template>
<el-menu-item-group v-for="(child,index) in item.children" :key="index">
<el-menu-item :index="child.path">{{child.name}}</el-menu-item>
</el-menu-item-group>
</el-submenu>
</template>
</el-menu>
</el-aside>
</div>
</template>
<script>
export default{
name:"menu_",
data(){
return{
menus:[]
}
},
created(){
console.log(this.$router.options.routes)
this.menus = [...this.$router.options.routes]
}
}
</script>
<style scoped>
.el-aside{
height: 100%;
}
.el-menu{
height: 100%;
}
.el-submenu-item{
min-width: 0;
}
</style>
Footer.vue,
<template>
<div class="footer">
<el-card>
@瑞鹏软件工作室
</el-card>
</div>
</template>
<script>
export default{
name:"footer_",
data(){
return{}
}
}
</script>
Breadcrumb.vue
<template>
<div>
<el-card>
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
<el-breadcrumb-item
v-for="(item,index) in $route.matched"
:key="index"
>{{item.name}}</el-breadcrumb-item>
</el-breadcrumb>
</el-card>
</div>
</template>
<script>
export default{
name:"Breadcrumb_",
data(){
return{}
}
}
</script>
<style scoped>
</style>
在Home.vue中导入三个组件
<template>
<div class="home">
<!-- 头部 -->
<Header>Header</Header>
<el-container class="content">
<!-- 左侧菜单栏 -->
<Menu/>
<!-- 内容 -->
<el-container>
<el-main>Main</el-main>
<!-- 底部 -->
<el-footer><Footer/></el-footer>
</el-container>
</el-container>
</div>
</template>
<script>
import Header from './common/Header.vue'
import Footer from './common/Footer.vue'
import Menu from './common/Menu.vue'
export default {
name: 'HelloWorld',
components:{ //注册
Header,
Footer,
Menu
},
data(){
return {}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.home{
width:100%;
height:100%;
}
.content{
position: absolute;
width: 100%;
top:60px;
bottom: 0;
}
</style>
最终效果
5.2. 用户列表静态开发
在components文件夹下创建base文件夹。
新建sysuser.vue
<template>
<el-table
:data="tableData"
style="width: 100%">
<el-table-column
label="日期"
width="180">
<template slot-scope="scope">
<i class="el-icon-time"></i>
<span style="margin-left: 10px">{{ scope.row.date }}</span>
</template>
</el-table-column>
<el-table-column
label="姓名"
width="180">
<template slot-scope="scope">
<el-popover trigger="hover" placement="top">
<p>姓名: {{ scope.row.name }}</p>
<p>住址: {{ scope.row.address }}</p>
<div slot="reference" class="name-wrapper">
<el-tag size="medium">{{ scope.row.name }}</el-tag>
</div>
</el-popover>
</template>
</el-table-column>
<el-table-column label="操作">
<template slot-scope="scope">
<el-button
size="mini"
@click="handleEdit(scope.$index, scope.row)">编辑</el-button>
<el-button
size="mini"
type="danger"
@click="handleDelete(scope.$index, scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</template>
<script>
export default {
data() {
return {
tableData: [{
date: '2016-05-02',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
}, {
date: '2016-05-04',
name: '王小虎',
address: '上海市普陀区金沙江路 1517 弄'
}, {
date: '2016-05-01',
name: '王小虎',
address: '上海市普陀区金沙江路 1519 弄'
}, {
date: '2016-05-03',
name: '王小虎',
address: '上海市普陀区金沙江路 1516 弄'
}]
}
},
methods: {
handleEdit(index, row) {
console.log(index, row);
},
handleDelete(index, row) {
console.log(index, row);
}
}
}
</script>
修改路由,进入router->index.js
import Vue from 'vue'
import Router from 'vue-router'
// import Home from '../components/Home.vue'
Vue.use(Router)
export default new Router({
routes:[
{
path:'/',
redirect:'/login',
hidden:true,
component:()=> import('@/components/Login') //路由懒加载
},
{
path:'/login',
name:'/Login',
hidden:true,
component:()=> import('@/components/Login') //路由懒加载
},
{
path:'*',
name:'NotFound',
hidden:true,
component:()=> import('@/components/NotFound') //路由懒加载
},
{
path:'/home',
name:'基本信息管理',
redirect:'/home/sysuser', //默认重定向
component:()=> import('@/components/Home'),
children:[
{
path:'/home/sysuser',
name:'用户列表',
component:()=> import('@/components/base/sysuser'),//这里对应文件的名字
}
]
},
// {
// path:'/home',
// component:Home
// component:()=> import('@/components/Home') //路由懒加载
// component:resolve => require(['@/components/Home'],resolve) // 异步组件
// }
],
mode:'history'
})
设置路由出口,让Main区域跟着路由变化而显示不同的内容。在Home.vue中修改,把原始的Main改为router-view
<template>
<div class="home">
<!-- 头部 -->
<Header>Header</Header>
<el-container class="content">
<!-- 左侧菜单栏 -->
<Menu />
<!-- 内容 -->
<el-container>
<el-main>
<!-- 面包屑 -->
<Bread />
<div class="cont">
<router-view></router-view>
</div>
</el-main>
<!-- 底部 -->
<el-footer><Footer /></el-footer>
</el-container>
</el-container>
</div>
</template>
<script>
import Bread from './common/Breadcrumb'
import Header from './common/Header.vue'
import Footer from './common/Footer.vue'
import Menu from './common/Menu.vue'
export default {
name: 'HelloWorld',
components:{ //注册
Header,
Footer,
Menu,
Bread
},
data(){
return {}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.home{
width:100%;
height:100%;
}
.content{
position: absolute;
width: 100%;
top:60px;
bottom: 0;
}
</style>
最终效果