- 新增「简记memo」一体化小程序产品原型设计文档 - 新增简记memo完整版UI视觉设计规范和界面细节 - 添加IDEA项目配置文件.gitignore - 创建404页面HTML文件,包含响应式布局和错误提示 - 添加关于页面HTML文件,展示品牌介绍和团队信息 - 实现AES加解密工具函数,支持请求体加密 - 添加用户协议页面基础框架
164 lines
5.0 KiB
JavaScript
164 lines
5.0 KiB
JavaScript
// static/js/request-guard.js
|
|
// 前端请求安全工具:防抖、节流、重复提交防护
|
|
(function() {
|
|
'use strict';
|
|
|
|
// ==================== 防抖函数 ====================
|
|
/**
|
|
* 防抖函数 - 在事件触发n秒后再执行,n秒内再次触发则重新计时
|
|
* @param {Function} func - 要执行的函数
|
|
* @param {number} wait - 等待时间(毫秒)
|
|
* @param {boolean} immediate - 是否立即执行
|
|
*/
|
|
window.debounce = function(func, wait, immediate) {
|
|
var timeout;
|
|
return function() {
|
|
var context = this;
|
|
var args = arguments;
|
|
clearTimeout(timeout);
|
|
timeout = setTimeout(function() {
|
|
func.apply(context, args);
|
|
}, wait);
|
|
};
|
|
};
|
|
|
|
// ==================== 节流函数 ====================
|
|
/**
|
|
* 节流函数 - 在规定时间内只执行一次
|
|
* @param {Function} func - 要执行的函数
|
|
* @param {number} wait - 时间间隔(毫秒)
|
|
*/
|
|
window.throttle = function(func, wait) {
|
|
var timeout;
|
|
var previous = 0;
|
|
return function() {
|
|
var context = this;
|
|
var args = arguments;
|
|
var now = Date.now();
|
|
var remaining = wait - (now - previous);
|
|
|
|
if (remaining <= 0 || remaining > wait) {
|
|
if (timeout) {
|
|
clearTimeout(timeout);
|
|
timeout = null;
|
|
}
|
|
previous = now;
|
|
func.apply(context, args);
|
|
} else if (!timeout) {
|
|
timeout = setTimeout(function() {
|
|
previous = Date.now();
|
|
timeout = null;
|
|
func.apply(context, args);
|
|
}, remaining);
|
|
}
|
|
};
|
|
};
|
|
|
|
// ==================== 请求锁(防止重复提交) ====================
|
|
var RequestLock = {
|
|
locks: {},
|
|
|
|
/**
|
|
* 锁定指定key的请求
|
|
* @param {string} key - 锁的标识
|
|
* @param {number} duration - 锁定时长(毫秒),默认3秒
|
|
* @returns {boolean} - 是否成功锁定
|
|
*/
|
|
lock: function(key, duration) {
|
|
duration = duration || 3000;
|
|
if (this.locks[key]) {
|
|
return false; // 已存在锁
|
|
}
|
|
this.locks[key] = true;
|
|
setTimeout(function() {
|
|
delete RequestLock.locks[key];
|
|
}, duration);
|
|
return true;
|
|
},
|
|
|
|
/**
|
|
* 检查是否存在锁
|
|
* @param {string} key - 锁的标识
|
|
* @returns {boolean}
|
|
*/
|
|
isLocked: function(key) {
|
|
return !!this.locks[key];
|
|
},
|
|
|
|
/**
|
|
* 解锁
|
|
* @param {string} key - 锁的标识
|
|
*/
|
|
unlock: function(key) {
|
|
delete this.locks[key];
|
|
},
|
|
|
|
/**
|
|
* 带锁执行请求
|
|
* @param {string} key - 锁的标识
|
|
* @param {Function} requestFn - 请求函数(可选,传null则只做锁检查)
|
|
* @param {number} duration - 锁定时长
|
|
* @returns {boolean} - 是否执行了请求
|
|
*/
|
|
executeWithLock: function(key, requestFn, duration) {
|
|
if (this.isLocked(key)) {
|
|
if (typeof $toast !== 'undefined') {
|
|
$toast.warning('操作太频繁,请稍后再试');
|
|
}
|
|
return false;
|
|
}
|
|
this.lock(key, duration);
|
|
if (typeof requestFn === 'function') {
|
|
requestFn();
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
window.RequestLock = RequestLock;
|
|
|
|
// ==================== 请求ID生成器(防重复提交) ====================
|
|
var RequestID = {
|
|
cache: {},
|
|
|
|
/**
|
|
* 生成请求ID
|
|
* @param {string} userId - 用户ID
|
|
* @param {string} action - 操作类型
|
|
* @returns {string}
|
|
*/
|
|
generate: function(userId, action) {
|
|
return userId + '_' + action + '_' + Date.now();
|
|
},
|
|
|
|
/**
|
|
* 记录请求ID(用于检测重复)
|
|
* @param {string} id - 请求ID
|
|
* @param {number} ttl - 有效期(毫秒)
|
|
* @returns {boolean} - 是否是新请求
|
|
*/
|
|
record: function(id, ttl) {
|
|
ttl = ttl || 5000; // 默认5秒内不允许重复
|
|
if (this.cache[id]) {
|
|
return false;
|
|
}
|
|
this.cache[id] = true;
|
|
var self = this;
|
|
setTimeout(function() {
|
|
delete self.cache[id];
|
|
}, ttl);
|
|
return true;
|
|
},
|
|
|
|
/**
|
|
* 检查请求是否重复
|
|
* @param {string} id - 请求ID
|
|
* @returns {boolean}
|
|
*/
|
|
isDuplicate: function(id) {
|
|
return !!this.cache[id];
|
|
}
|
|
};
|
|
window.RequestID = RequestID;
|
|
|
|
})();
|