在vue中全局禁止按钮二次提交
八月更文第一弹, 开始啦! 🍦
最近在做后台管理系统中碰到一个问题,例如网络状态不好的情况下,用户可以连续点击保存按钮,最终造成数据多次提交的一个情况。
场景概述
处理场景:用户点击了保存按钮后,后端接口需要在长时间后才会返回数据。我们的目的就是要让用户在这段时间内无法点击按钮
处理方式:
- 1.在点击保存按钮后,弹出弹层,显示loading,等数据处理结束后关闭弹窗。
- 2.在点击保存按钮后,将按钮禁用,同时将保存改为保存中,并增加按钮内部loading(此种交互体验更好)
实现方式: - 1.在每个页面请求和获取到数据后单独去处理,这中方式简单,但却会很繁琐,并且造成大量冗余代码。
- 2.对按钮增加指令,全局统一处理逻辑。
开发实践
在我们vue项目中,具体的实现可以分为3步:axios拦截处理,指令文件编写引入,具体组件绑定。
axios拦截处理
当用户点击保存按钮之后,我们想要再次点击必然是得到响应结果之后,所以要在axios这里进行处理。
设置currentResState和currentResUrl两个参数,在请求拦截器中进行处理,并将参数存储到session中(可修改为存储到vuex中更好,因为此数据不需要持久化存储)
// 请求拦截器
instance.interceptors.request.use(
config => {
// 存储当前请求状态
sessionStorage.setItem('currentResState', false)
sessionStorage.setItem('currentResUrl', config.url)
// ...
return config
},
error => {
return Promise.reject(error)
}
)
在相应拦截器处进行获取url判断,当判断请求为同一个请求时,将currentResState状态改为true
// 响应拦截器,对返回数据进行预处理
instance.interceptors.response.use(
response => {
// 判断url参数一样时,就是当前currentResq中记录的请求返回数据,并将done设置为true
if (sessionStorage.getItem('currentResUrl') === response.config.url) {
sessionStorage.setItem('currentResState', true)
}
return response
},
error => {
return Promise.reject(error)
}
)
指令文件编写并引入
此处用到了vue的自定义指令,新建btnForbidden.js,在main.js中进行注册
import Vue from 'vue'
let forbidClick = null;
// 权限指令
Vue.directive('forbidClick', {
bind: function (el) {
let timer = null;
forbidClick = () => {
el.disabled = true;
el.classList.add('is-disabled');
timer = setInterval(() => {
if (sessionStorage.getItem('currentResState') == 'true') {
clearInterval(timer);
el.disabled = false;
el.classList.remove('is-disabled');
}
}, 500);
};
el.addEventListener('click', forbidClick);
},
unbind() {
document.removeEventListener('click', forbidClick);
}
})
main.js
// 按钮指令注册
import '@/utils/clickForbidden'
指令绑定
在需要使用的按钮上添加v-forbidClick指令即可,由于通常项目中保存按钮都是封装在全局组件中,所以我们只需要在组件上的保存按钮添加指令就可以实现全局拦截啦~
// 使用示例
<template>
<div class="layout-main__buttons">
<el-button
v-if="!!save"
id="layoutSaveButton"
v-forbidClick
:disabled="readonly"
icon="el-icon-check"
size="small"
type="primary"
@click="save"
>
保存
</el-button>
</div>
</template>
写在最后
本文中用到了vue指令的使用,如果对使用方式不了解的同学可以前往 vue自定义指令 了解。
本文到此结束,希望对你有帮助 😉