const Message = (function() {
class Mes {
constructor() {
this.ele = document.createElement('div')
this.ele.className = 'mes-box'
document.body.appendChild(this.ele)
}
setContent(opts) {
this.ele.classList.add('fadeIn')
const type = opts.type || 'default'
let iconClassList = ''
switch (type) {
case 'default':
iconClassList = 'iconfont icon-loading rotate default inBlock'
break
case 'success':
iconClassList = 'iconfont icon-success success'
break
case 'warning':
iconClassList = 'iconfont icon-warning warning'
break
case 'error':
iconClassList = 'iconfont icon-error error'
break
default:
iconClassList = 'iconfont icon-loading inBlock ratate'
}
this.ele.innerHTML = `
<div class="mes-icon-box ${type}">
<i class="${iconClassList}"></i>
</div>
<div class="mes-cont">${opts.text || '加载中,请稍后...'}</div>
`
if (opts.duration) {
setTimeout(() => {
this.ele.classList.remove('fadeIn')
}, opts.duration)
}
}
close() {
this.ele.classList.remove('fadeIn')
}
}
let instance = null
return function(options = { duration: '', icon: '', text: '', type: 'default' }) {
if (!instance) instance = new Mes()
instance.setContent(options)
return instance
}
})()
export default Message
我们实现一个message弹窗,将在axios封装中替换第三方组件库提供的message
-----------------------
message.js
当然message的样式我们需要在全局的css中去做相关设置,我们新建css样式在main.js中引入
.mes-box {
display: flex;
position: fixed;
top: 50%;
left: 50%;
padding: 16px;
min-width: 220px;
transform: translate(-50%, -50%);
box-shadow: 0 0 10px 4px #ccc;
transition: all 0.2s ease-out;
font-size: 14px;
z-index: 3000;
opacity: 0;
background: #fff;
}
.inBlock {
display: inline-block;
}
.fadeIn {
opacity: 1;
}
.mes-icon-box {
width: 60px;
text-align: center;
}
.mes-cont {
flex: 1
}
.rotate {
animation: ani 1s ease-in infinite;
}
@keyframes ani {
0% {
-webkit-transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
}
}
.slide-enter-active,
.slide-leave-active {
transition: all 0.2s;
top: 100px;
opacity: 1;
}
.slide-enter,
.slide-leave-active {
opacity: 0;
top: -100px;
}
.default {
color: #409EFF
}
.error {
color: #f00
}
.warning {
color: #ff7a33
}
.success {
color: #63deaa
}
第三步,我们将在axios的封装中用到它
import axios from 'axios'
import router from '@/router'
import qs from 'qs'
import Message from '@/plugins/message'
let loadingInstance = null
console.log('process.env:', process.env)
const config = {
timeout: 5000,
baseURL: process.env.VUE_APP_BASE_API
// baseURL: ''
}
const Http = axios.create(config)
// 添加请求拦截器
Http.interceptors.request.use((config) => {
const Token = localStorage.getItem("token")
if (Token) {
config.headers.token = Token
}
if (config.method === 'post' || config.method === 'put') {
if (config.data.loading) {
console.log('loadingInstance:', loadingInstance)
loadingInstance = new Message()
}
} else {
if (config.params.loading) {
loadingInstance = new Message()
}
}
return config
})
// 添加响应拦截器
Http.interceptors.response.use(
(response) => {
if (response.data.code === 0) {
if (loadingInstance) {
loadingInstance.close()
}
} else {
loadingInstance = new Message({
type: 'error', duration: 3000,
text: '程序异常,请联系管理员'
})
}
return response.data
},
(error) => {
console.log('error:', error)
if (
error.code === 'ECONNABORTED' ||
error.message === 'Network Error' ||
error.message.includes('timeout')
) {
new Message({ type: 'error', text: '当前网络错误', duration: 3000 })
}
// 错误处理
const code = error.response.status
if (code === 200) {
} else if (code === 403) {
// 处理token过期问题
loadingInstance = new Message({
type: 'error', duration: 3000,
text: '登录已过期,请重新登录'
})
localStorage.clear()
router.replace('/')
} else {
loadingInstance = new Message({
type: 'error', duration: 3000,
text: error.response.data
})
}
return Promise.reject(error)
}
)
// loading: true:请求接口时出现加载提示,false反之
function request(url, method = 'GET', params = {}, loading = false, headerConfig = {}) {
const Method = method.toLowerCase()
if (Method === 'get' || Method === 'delete') {
params = { ...params, loading }
return Http[Method](url, { params })
} else if (Method === 'post' || Method === 'put') {
let data = {}
if (Object.keys(headerConfig).length > 0) {
data = qs.stringify({ ...params, loading })
} else {
data = Object.assign(params, { loading }, headerConfig)
}
return Http[Method](url, data, headerConfig)
}
}
export default request
效果图如下:
为什么需要单例模式,因为我们在axios拦截中请求成功,需要关闭这个message,所以所有的打开与关闭,针对的都是同一个实例对象。这样就可以防止页面中多个message出现干扰
----------------------------------------------------------------------------
更新:
最近在使用的时候发现有一个问题。因为我们的dom中一直都会存在这个mes-box,而且我们对它的隐藏实际是是通过opactity:0来实现的,但是它的z-index很大,导致虽然在视图上看不见它,但是它会干扰到我们正常的点击。也就是如果有一个按钮的位置刚好和messagebox重合,但是由于层级关系,messagebox会遮挡住页面内容导致无法点击。其实解决办法也很简单:我们给messagebox默认设置一个很低的层级比如z-index:-400,在fadeIn的时候给它把层级提上去。
在fadeIn的css里做如下设置:
.fadeIn{
opacity:1;
z-index:3000
}
即可解决这个问题