Skip to content

防重复提交

防重复提交是保障数据一致性的重要措施。通过前端防抖和后端唯一性校验双重保护,防止用户重复点击导致的数据重复提交。

前端防抖

useLockFn

前端提供 useLockFn 钩子函数,实现请求级别的防重复提交:

javascript
import { useLockFn } from '@/utils/lock'

// 包装提交函数,防止重复点击
const handleSubmit = useLockFn(async () => {
    await axios.post('/level/add', formData)
    ElMessage.success('添加成功')
})

useLockFn 原理:

javascript
function useLockFn(fn) {
    let isLocked = false
    return async (...args) => {
        if (isLocked) return
        isLocked = true
        try {
            return await fn(...args)
        } finally {
            isLocked = false
        }
    }
}

按钮 loading

ElementPlus 的按钮组件支持 loading 属性,请求期间禁用按钮:

vue
<el-button
    type="primary"
    :loading="submitting"
    @click="handleSubmit"
>
    提交
</el-button>

<script setup>
const submitting = ref(false)

async function handleSubmit() {
    submitting.value = true
    try {
        await axios.post('/level/add', formData)
        ElMessage.success('添加成功')
    } finally {
        submitting.value = false
    }
}
</script>

后端唯一性校验

Django Form 唯一性

在 service 层查询数据库校验唯一性:

python
# application/level/services.py
def add_level(request):
    data = parse_request_body(request)
    form = LevelForm(data)

    if not form.is_valid():
        return R.failed(msg=regular.get_err(form))

    # 唯一性校验
    name = data.get('name')
    if Level.objects.filter(name=name, is_delete=False).exists():
        return R.failed("职级名称不能重复")

    # 保存数据...
    return R.ok(msg="添加成功")

软删除 + 唯一索引

软删除场景下,唯一索引需要包含 is_delete 字段:

sql
-- MySQL: 唯一索引包含 is_delete 字段
ALTER TABLE django_level ADD UNIQUE INDEX uk_name (name, is_delete);

最佳实践

1. 前端防抖:useLockFn 或按钮 loading,防止用户重复点击
2. 后端校验:service 层查询数据库校验唯一性
3. 数据库约束:唯一索引兜底,防止并发重复插入
4. 错误提示:重复提交时返回明确的错误提示

总结

防重复提交方案具备以下特点:

1. 前端防抖:useLockFn 函数级防抖,按钮 loading 状态控制
2. 后端校验:service 层查询数据库校验唯一性
3. 数据库约束:唯一索引兜底,防止并发重复插入
4. 双重保护:前端防抖 + 后端校验,确保数据一致性
5. 用户体验:重复点击时按钮禁用,返回明确提示

小蚂蚁云团队 · 提供技术支持