之前写过一篇关vue项目框架搭建的文章,主要是讲项目里需要用到哪些js包,这篇文章主要是讲一个初始vue项目需要具备哪些基本的功能模块,希望对刚开始学习vue的小伙伴有帮助。
ajax编程js
前后端分离之后,前端mock开发
配合mockjs使用
ui框架
一套icon图表css框架,此框架也可不引人,element-ui/iview-ui两个ui框架都自带icon图表,如满足不了再考虑此框架
安装命令: npm install 包名称
如: npm install axios
main.js 引入
import Vue from 'vue'
import App from './App'
import router from './router'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import Mock from './mock'
import axios from './axios'
import './assets/css/custom.css'
import 'font-awesome/css/font-awesome.min.css'
import MyPlugin from './assets/js/plugin'
Vue.use(ElementUI)
Vue.use(MyPlugin)
Vue.config.productionTip = false
if (process.env.NODE_ENV === 'development') {
Mock.bootstrap()
}
/* eslint-disable no-new */
var vm = new Vue({
el: '#app',
router,
template: ' ',
components: { App }
})
export default vm
资源目录
自定义css的文件目录
图片目录
自定义js的文件目录
ajax编程框架主配置目录
axios配置文件及出口
mock开发主配置目录
mock配置文件及出口
路由目录
router配置文件及出口
全局组件目录
组件配置文件及出口
所有业务模块目录
业务模块目录
封装业务模块对应的后端api ajax请求的目录
api出口文件
封装业务模块对应的后端api的mock请求的
mock出口文件
业务模块出口文件
业务模块的vue文件
顶级父组件
主配置文件
{
code: 0, // 状态码,0代表后端逻辑正常,通过此状态码可做统一提示
msg: "提示信息", // 后端逻辑错误时,可将提示信息写在此用于提示用户
result: {} 或者 [] // 逻辑正常情况下,返给前端的具体数据
}
一个总入口配置文件,各业务模块有自己mock配置文件,每次新增业务模块,一定要在总入配置文件中引入业务模块自己的mock配置文件,该业务模块的mock功能才能生效
开启和关闭mock在main.js中
if (process.env.NODE_ENV === 'development') {
Mock.bootstrap() // 与后端联调时注释掉即可
}
总入口配置文件,即mock目录下index.js
import axios from 'axios'
import MockAdapter from 'axios-mock-adapter'
import Login from '../pages/login/mock' // 引入各个业务模块自己的mock文件
export default {
/**
* mock 入口
*/
bootstrap () {
let mock = new MockAdapter(axios)
Login.bootstrap(mock)
},
/**
* mock服务的返回结果集,并打印日志,
* @param requestType 请求类型
* @param apiName 接口名称
* @param apiUrl 接口url
* @param apiParam 接口参数
* @param apiResult 接口返回结果的result属性或者是接口返回的完整json对象{code:,msg:,result:}
* @returns {{code: number, msg: string, result: *}}
*/
apiResponseJson (requestType, apiName, apiUrl, apiParam, apiResult) {
let res = {
code: 0,
msg: 'success',
result: apiResult
}
if (apiResult.code !== undefined && apiResult.msg !== undefined) {
res = apiResult
}
console.log(apiName + ',url:' + apiUrl + ', 请求类型:' + requestType + ',参数:', apiParam)
console.log(apiName + ', 返回:', res)
return res
},
/**
* 打印mock的接口格式,包括接口名称,接口url,接口参数,接口返回,方法会返回mock测试数据json格式
* @param requestType
* @param apiName
* @param apiUrl
* @param apiParam
* @param apiResult
* @returns {[number,*]}
*/
resolve200 (requestType, apiName, apiUrl, apiParam, apiResult) {
return [200, this.apiResponseJson(requestType, apiName, apiUrl, apiParam, apiResult)]
},
/**
*
* @param config
* @param requestType
* @param apiName
* @param apiUrl
* @param apiResultFunc
* @returns {Promise}
*/
replyFunc (config, requestType, apiName, apiUrl, apiResultFunc) {
let data = config.data
if (typeof (data) === 'string') {
data = JSON.parse(data)
}
return new Promise((resolve, reject) => {
resolve(this.resolve200(requestType, apiName, apiUrl, data, apiResultFunc(data)))
})
},
/**
* mock,post请求,模拟后端正常成功的逻辑,code=0
* @param mock mock对象
* @param apiName 接口名称
* @param apiUrl 接口url
* @param apiResultFunc 获取接口返回对象的result属性值的回调方法,回调方法的参数就是接口参数,是一个json对象
*/
post (mock, apiName, apiUrl, apiResultFunc) {
mock.onPost(apiUrl).reply(config => {
return this.replyFunc(config, 'post', apiName, apiUrl, apiResultFunc)
})
},
/**
* mock,get,模拟后端正常成功的逻辑,code=0
* @param mock mock对象
* @param apiName 接口名称
* @param apiUrl 接口url
* @param apiResultFunc 获取接口返回对象的result属性值的回调方法,回调方法的参数就是接口参数,是一个json对象
*/
get (mock, apiName, apiUrl, apiResultFunc) {
mock.onGet(apiUrl).reply(config => {
return this.replyFunc(config, 'get', apiName, apiUrl, apiResultFunc)
})
}
}
业务模块自己的mock文件,即业务模块目录/mock/index.js
import Mock from '../../../mock'
import {LogUrl} from '../api'
const AdminUser = {
userId: 1,
userName: 'admin',
password: '123456',
name: '某某某',
sysRouters: [], // 所有是资源的路由
authRouters: [] // 用户有权限的路由资源
}
export default {
bootstrap (mock) {
Mock.post(mock, '登录系统', LogUrl.Login, function (params) {
console.log('ddd')
debugger
if (params.userName !== AdminUser.userName || params.password !== AdminUser.password) {
debugger
return {code: 100, msg: '账号或者密码不正确!'}
}
return AdminUser
})
Mock.post(mock, '退出系统', LogUrl.Logout, function (params) {
return true
})
}
}
权限分两种:登录权限和资源权限
我的实现方式是登录成功之后就将权限信息加载到前端,然后在前端判断,这样简单些,也可以实时请求后端来判断
拦截路由校验登录信息,在router/index.js中
/**
* 不做登录校验的路由集合
* @type {[*]}
*/
const unLoginCheckRoutes = ['/noauth', '/test', '/report-design']
/**
* 拦截路由跳转,校验用户的路由权限,或者做流量监控
*/
router.beforeEach(function (to, from, next) {
// 是否要做登录及权限校验
let loginCheck = true
unLoginCheckRoutes.forEach(r => {
if (to.path === '/' || (r !== '/' && to.path.startsWith(r))) {
loginCheck = false
}
})
if (loginCheck) {
let user = LoginUtil.getLoginUser()
if (user === null) { // 登录失效,跳转登录页
VueGlobal.goLoginPage(to.path)
return
}
// 校验路由是否可访问
if (user.sysRouters.includes(to.path)) { // 判断路由是不是一个资源
if (user.authRouters.includes(to.path)) { // 有此路由权限的登录用户才能访问
next()
} else {
next('/noauth')
}
} else { // 登录用户都能访问的路由资源
next()
}
} else { // 所有用户都能访问的路由资源
next()
}
})
拦截后端请求response,校验后端返回状态,判断登录是否失效,在axios/index.js中
// 拦截请求response,在此可做统一的错误提示
axios.interceptors.response.use(function (response) {
if (response.data && response.status === 200) {
if (response.data.code === 0) {
return response
} else {
if (response.data.code === 400 || response.data.code === 401) { // 登录token失效,转向登录页
VueGlobal.goLoginPage()
return response
} else {
VueGlobal.notifyWarn(response.data.msg)
return Promise.reject(response.data)
}
}
} else {
VueGlobal.notifyWarn(response.statusText)
return Promise.reject(new Error(response))
}
}, function (error) {
VueGlobal.notifyWarn(error.message)
return Promise.reject(error)
})
<template>
<PLayout>
<PMenu>
<PMenuItem path="/sys/user" icon="address-book" name="用户管理" v-if="getLoginUser().authDom && getLoginUser().authDom.sys_user"/>
<PMenuItem path="/sys/role" icon="address-book" name="角色管理" v-if="getLoginUser().authDom && getLoginUser().authDom.sys_role"/>
PMenu>
PLayout>
template>
<script>
export default {}
script>
<style>
style>
/**
* 不做登录校验的路由集合
* @type {[*]}
*/
const unLoginCheckRoutes = ['/noauth', '/test', '/report-design']
/**
* 拦截路由跳转,校验用户的路由权限,或者做流量监控
*/
router.beforeEach(function (to, from, next) {
// 是否要做登录及权限校验
let loginCheck = true
unLoginCheckRoutes.forEach(r => {
if (to.path === '/' || (r !== '/' && to.path.startsWith(r))) {
loginCheck = false
}
})
if (loginCheck) {
let user = LoginUtil.getLoginUser()
if (user === null) { // 登录失效,跳转登录页
VueGlobal.goLoginPage(to.path)
return
}
// 校验路由是否可访问
if (user.sysRouters.includes(to.path)) { // 判断路由是不是一个资源
if (user.authRouters.includes(to.path)) { // 有此路由权限的登录用户才能访问
next()
} else {
next('/noauth')
}
} else { // 登录用户都能访问的路由资源
next()
}
} else { // 所有用户都能访问的路由资源
next()
}
})
利用axios拦截后端api请求response,根据后端返回数据的code状态码判断
// 拦截请求response,在此可做统一的错误提示
axios.interceptors.response.use(function (response) {
if (response.data && response.status === 200) {
if (response.data.code === 0) { // 正常逻辑
return response
} else {
if (response.data.code === 400 || response.data.code === 401) { // 登录token失效,转向登录页
VueGlobal.goLoginPage()
return response
} else {
VueGlobal.notifyWarn(response.data.msg) // 错误提示
return Promise.reject(response.data)
}
}
} else {
VueGlobal.notifyWarn(response.statusText) // 错误提示
return Promise.reject(new Error(response))
}
}, function (error) {
VueGlobal.notifyWarn(error.message) // 错误提示
return Promise.reject(error)
})
import axios from 'axios'
import qs from 'qs'
import LoginUtil from '../assets/js/loginUtils'
import VueGlobal from '../assets/js/vueGlobal'
/**
* get请求
* @param url
* @param params
*/
export const get = (url, params) => { return axios.get(url + qs.stringify(params)).then(res => res.data) }
/**
* post请求
* @param url
* @param params
*/
export const post = (url, params) => { return axios.post(url, params).then(res => res.data) }
/**
* 下载请求
* @param url
* @param params
*/
export const download = (url, params) => { window.location.href = axios.defaults.baseURL + url + '?token=' + LoginUtil.getLoginUser().token + '&' + qs.stringify(params) }
// 1.设置请求头的默认配置
// 前后端联调的时候,后端服务地址
axios.defaults.baseURL = ''
if (process.env.NODE_ENV === 'development') {
// axios.defaults.baseURL = 'http://127.0.0.1:8080'
}
// axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'
// 拦截request请求
axios.interceptors.request.use(function (config) {
// 添加登录token或者其他公共参数
let user = LoginUtil.getLoginUser()
if (user) {
if (config.data) {
config.data.token = user.token
} else {
config.data = {token: user.token}
}
}
return config
}, function (error) {
return Promise.reject(error)
})
// 拦截请求response,在此可做统一的错误提示
axios.interceptors.response.use(function (response) {
if (response.data && response.status === 200) {
if (response.data.code === 0) {
return response
} else {
if (response.data.code === 400 || response.data.code === 401) { // 登录token失效
VueGlobal.goLoginPage()
return response
} else {
VueGlobal.notifyWarn(response.data.msg)
return Promise.reject(response.data)
}
}
} else {
VueGlobal.notifyWarn(response.statusText)
return Promise.reject(new Error(response))
}
}, function (error) {
VueGlobal.notifyWarn(error.message)
return Promise.reject(error)
})
export default axios
业务模块自己的api配置文件,在业务模块目录/api/index.js中
import {get, post} from '../../../axios'
const LogApiPre = '/api'
export const LogUrl = {
Login: LogApiPre + '/login', // 登录
Logout: LogApiPre + '/logout' // 退出
}
/**
* 登录
* @param params
*/
export const login = (params) => { return post(LogUrl.Login, params) }
/**
* 退出
* @param params
*/
export const logout = (params) => { return get(LogUrl.Logout, params) }
各个模块都可能用到的功能,就可以封装成全局组件或者全局方法
全局组件和方法需要安装成vue插件,然后再main.js中引入并use
import Component from '../../components'
import VueGlobal from './vueGlobal'
import LoginUtil from './loginUtils'
const PComponents = {
PHead: Component.PHead,
PLayout: Component.PLayout,
PMenu: Component.PNavside.PMenu,
PMenuItem: Component.PNavside.PMenuItem
}
export default {
install (Vue) {
Vue.prototype.goBack = () => { VueGlobal.goBack() }
Vue.prototype.getLoginUser = () => { return LoginUtil.getLoginUser() }
// 消息提示
Vue.prototype.message = (msg) => { VueGlobal.messageInfo(msg) }
Vue.prototype.messageSuccess = (msg) => { VueGlobal.messageSuccess(msg) }
Vue.prototype.messageWarn = (msg) => { VueGlobal.messageWarn(msg) }
Vue.prototype.messageError = (msg) => { VueGlobal.messageError(msg) }
// 通知提示
Vue.prototype.notify = (msg) => { VueGlobal.notifyInfo(msg) }
Vue.prototype.notifySuccess = (msg) => { VueGlobal.notifySuccess(msg) }
Vue.prototype.notifyWarn = (msg) => { VueGlobal.notifyWarn(msg) }
Vue.prototype.notifyError = (msg) => { VueGlobal.notifyError(msg) }
// 弹框提示
Vue.prototype.alertInfo = (msg) => { VueGlobal.alertInfo(msg) }
Vue.prototype.alertSuccess = (msg) => { VueGlobal.alertSuccess(msg) }
Vue.prototype.alertWarn = (msg) => { VueGlobal.alertWarn(msg) }
Vue.prototype.alertError = (msg) => { VueGlobal.alertError(msg) }
/**
* 统一处理promise catch的错误
* @param error 错误信息
*/
Vue.prototype.handleError = (error) => { console.warn(error) }
Object.keys(PComponents).forEach((key) => {
Vue.component(key, PComponents[key])
})
}
}
项目github地址: https://github.com/xiaoping1988/vue-template-proj