/**
* SMS 验证码服务前端 JavaScript
* 主要功能:短信记录查询和验证码获取
*/
document.addEventListener("DOMContentLoaded", function() {
// 初始化常量和变量
const PAGE_SIZE = 10;
let currentPage = 1;
let totalRecords = 0;
// DOM 元素
const codeQueryForm = document.getElementById('codeQueryForm');
const queryResult = document.getElementById('queryResult');
const resultContent = document.getElementById('resultContent');
const smsRecordsTable = document.getElementById('smsRecordsTable');
const loadingIndicator = document.getElementById('loadingIndicator');
const noRecords = document.getElementById('noRecords');
const pagination = document.getElementById('pagination');
const refreshButton = document.querySelector('.refresh-records');
// 绑定事件监听器
if (codeQueryForm) {
codeQueryForm.addEventListener('submit', function(e) {
e.preventDefault();
queryVerificationCode();
});
}
if (refreshButton) {
refreshButton.addEventListener('click', function() {
loadSMSRecords(currentPage);
});
}
// 初始化工具提示
initializeTooltips();
// 加载短信记录
loadSMSRecords(currentPage);
// 监听窗口大小变化,调整表格显示
window.addEventListener('resize', function() {
adjustTableColumns();
});
/**
* 初始化工具提示
*/
function initializeTooltips() {
// 如果Bootstrap tooltip可用
if (typeof bootstrap !== 'undefined' && bootstrap.Tooltip) {
const tooltips = document.querySelectorAll('[data-bs-toggle="tooltip"]');
tooltips.forEach(tooltip => {
new bootstrap.Tooltip(tooltip);
});
}
}
/**
* 加载短信记录
* @param {number} page - 页码
* @param {number} limit - 每页记录数
*/
function loadSMSRecords(page = 1, limit = PAGE_SIZE) {
const offset = (page - 1) * limit;
// 显示加载指示器
if (loadingIndicator) {
loadingIndicator.classList.remove('d-none');
}
// 隐藏无记录提示
if (noRecords) {
noRecords.classList.add('d-none');
}
// 清空表格内容
if (smsRecordsTable) {
const tableBody = smsRecordsTable.querySelector('tbody');
if (tableBody) {
tableBody.innerHTML = '';
}
}
// 获取短信记录
fetch(`/v1/sms/history?limit=${limit}&offset=${offset}`)
.then(response => {
if (!response.ok) {
throw new Error('网络响应异常');
}
return response.json();
})
.then(records => {
// 隐藏加载指示器
if (loadingIndicator) {
loadingIndicator.classList.add('d-none');
}
if (records.length === 0) {
// 显示无记录提示
if (noRecords) {
noRecords.classList.remove('d-none');
}
if (pagination) {
pagination.innerHTML = '';
}
} else {
// 更新记录总数
totalRecords = records.length === limit ? -1 : offset + records.length;
// 渲染记录
renderRecords(records);
// 生成分页
generatePagination(page, limit, records.length === limit);
// 调整表格列
adjustTableColumns();
}
})
.catch(error => {
console.error('获取短信记录失败:', error);
showToast('error', '获取短信记录失败', error.message);
// 隐藏加载指示器
if (loadingIndicator) {
loadingIndicator.classList.add('d-none');
}
if (smsRecordsTable) {
const tableBody = smsRecordsTable.querySelector('tbody');
if (tableBody) {
tableBody.innerHTML = `
加载失败: ${error.message}
|
`;
}
}
});
}
/**
* 渲染短信记录到表格
* @param {Array} records - 短信记录数组
*/
function renderRecords(records) {
const tableBody = smsRecordsTable.querySelector('tbody');
if (!tableBody) return;
tableBody.innerHTML = '';
records.forEach(record => {
const row = document.createElement('tr');
row.classList.add('fade-in');
// 格式化日期时间
const date = new Date(record.receive_time);
const formattedDate = formatDateTime(date);
// 应用敏感信息掩码
const maskedSMS = maskSensitiveInfo(record.sms);
const maskedFrom = maskSensitiveInfo(record.from_ || record.from);
const maskedSimSlot = record.sim_slot ? maskSensitiveInfo(record.sim_slot) : '';
// 为验证码创建适当的徽章
const codeElement = record.extracted_code
? `${record.extracted_code}`
: `未提取`;
row.innerHTML = `
${record.id} |
${maskedFrom} |
${maskedSMS} |
${codeElement} |
${formattedDate} |
|
`;
tableBody.appendChild(row);
});
// 添加查看按钮事件监听
tableBody.querySelectorAll('.view-sms').forEach(button => {
button.addEventListener('click', function() {
const smsId = this.getAttribute('data-id');
viewSMSDetail(smsId);
});
});
// 重新初始化工具提示
initializeTooltips();
}
/**
* 生成分页控件
* @param {number} currentPage - 当前页码
* @param {number} limit - 每页记录数
* @param {boolean} hasMore - 是否有更多记录
*/
function generatePagination(currentPage, limit, hasMore) {
if (!pagination) return;
pagination.innerHTML = '';
// 上一页按钮
const prevItem = document.createElement('li');
prevItem.className = `page-item ${currentPage === 1 ? 'disabled' : ''}`;
prevItem.innerHTML = `«`;
if (currentPage > 1) {
prevItem.querySelector('a').addEventListener('click', (e) => {
e.preventDefault();
loadSMSRecords(currentPage - 1, limit);
});
}
pagination.appendChild(prevItem);
// 当前页
const currentItem = document.createElement('li');
currentItem.className = 'page-item active';
currentItem.innerHTML = `${currentPage}`;
pagination.appendChild(currentItem);
// 下一页按钮
const nextItem = document.createElement('li');
nextItem.className = `page-item ${!hasMore ? 'disabled' : ''}`;
nextItem.innerHTML = `»`;
if (hasMore) {
nextItem.querySelector('a').addEventListener('click', (e) => {
e.preventDefault();
loadSMSRecords(currentPage + 1, limit);
});
}
pagination.appendChild(nextItem);
}
/**
* 查询验证码
*/
function queryVerificationCode() {
const phoneNumber = document.getElementById('phoneNumber').value;
const platformKeyword = document.getElementById('platformKeyword').value;
const timeout = document.getElementById('timeout').value;
if (!phoneNumber) {
showToast('warning', '请输入手机号码', '请输入有效的手机号码以查询验证码');
return;
}
// 显示查询结果区域
if (queryResult) {
queryResult.classList.remove('d-none');
}
// 显示加载状态
if (resultContent) {
resultContent.innerHTML = `
`;
}
// 构建请求URL
let url = `/v1/sms/code?phone_number=${encodeURIComponent(phoneNumber)}&wait_timeout=${timeout}`;
if (platformKeyword) {
url += `&platform_keyword=${encodeURIComponent(platformKeyword)}`;
}
// 发送请求
fetch(url)
.then(response => {
if (!response.ok) {
throw new Error('网络响应异常');
}
return response.json();
})
.then(data => {
// 显示查询结果
if (resultContent) {
if (data.result === 'ok') {
const maskedSmsExcerpt = maskSensitiveInfo(data.data.sms_excerpt);
resultContent.innerHTML = `
验证码获取成功!
接收时间:
${formatDateTime(new Date(data.data.received_time))}
短信内容:
${maskedSmsExcerpt}...
`;
// 添加复制验证码功能
const copyButton = resultContent.querySelector('.copy-code');
if (copyButton) {
copyButton.addEventListener('click', function() {
const code = this.getAttribute('data-code');
copyToClipboard(code);
showToast('success', '已复制验证码', `验证码 ${code} 已复制到剪贴板`);
});
}
} else {
resultContent.innerHTML = `
未找到验证码
${data.message}
请检查手机号码是否正确,或稍后再试。
`;
}
}
// 刷新短信记录,以便查看最新记录
loadSMSRecords(1);
})
.catch(error => {
console.error('查询验证码失败:', error);
if (resultContent) {
resultContent.innerHTML = `
查询失败
发生错误: ${escapeHtml(error.message)}
请检查网络连接或稍后再试。
`;
}
});
}
/**
* 查看短信详情
* @param {string} smsId - 短信ID
*/
function viewSMSDetail(smsId) {
// 显示加载中模态框
showLoadingModal('正在加载短信详情...');
fetch(`/v1/sms/${smsId}`)
.then(response => {
if (!response.ok) {
throw new Error('网络响应异常');
}
return response.json();
})
.then(data => {
// 关闭加载中模态框
closeModal('loadingModal');
// 应用敏感信息掩码
const maskedSMS = maskSensitiveInfo(data.sms);
const maskedFrom = maskSensitiveInfo(data.from_ || data.from);
const maskedSimSlot = data.sim_slot ? maskSensitiveInfo(data.sim_slot) : '未知';
const maskedPhoneNumber = data.phone_number ? maskSensitiveInfo(data.phone_number) : '未提取';
// 创建详情模态框
const modalHtml = `
${escapeHtml(data.contact_name || '未知')}
${maskedSMS.replace(/\n/g, '
')}
${data.extracted_code ?
`${data.extracted_code}` :
'未提取'}
${formatDateTime(new Date(data.receive_time))}
`;
// 添加到DOM并显示
const modalContainer = document.createElement('div');
modalContainer.innerHTML = modalHtml;
document.body.appendChild(modalContainer);
const modal = new bootstrap.Modal(document.getElementById('smsDetailModal'));
modal.show();
// 添加复制验证码功能
const copyButton = document.querySelector('.copy-code');
if (copyButton) {
copyButton.addEventListener('click', function() {
const code = this.getAttribute('data-code');
copyToClipboard(code);
showToast('success', '已复制验证码', `验证码 ${code} 已复制到剪贴板`);
});
}
// 模态框关闭后从DOM中移除
document.getElementById('smsDetailModal').addEventListener('hidden.bs.modal', function () {
this.remove();
});
})
.catch(error => {
// 关闭加载中模态框
closeModal('loadingModal');
console.error('获取短信详情失败:', error);
showToast('error', '获取短信详情失败', error.message);
});
}
/**
* 显示加载中模态框
* @param {string} message - 加载提示信息
*/
function showLoadingModal(message) {
const modalHtml = `
`;
const modalContainer = document.createElement('div');
modalContainer.innerHTML = modalHtml;
document.body.appendChild(modalContainer);
const modal = new bootstrap.Modal(document.getElementById('loadingModal'));
modal.show();
}
/**
* 关闭模态框
* @param {string} modalId - 模态框ID
*/
function closeModal(modalId) {
const modalElement = document.getElementById(modalId);
if (modalElement) {
const modal = bootstrap.Modal.getInstance(modalElement);
if (modal) {
modal.hide();
modalElement.addEventListener('hidden.bs.modal', function() {
this.remove();
});
} else {
modalElement.remove();
}
}
}
/**
* 复制文本到剪贴板
* @param {string} text - 要复制的文本
*/
function copyToClipboard(text) {
// 创建临时文本区域
const textArea = document.createElement('textarea');
textArea.value = text;
textArea.style.position = 'fixed';
textArea.style.left = '-999999px';
textArea.style.top = '-999999px';
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
// 执行复制命令
try {
document.execCommand('copy');
} catch (err) {
console.error('复制失败:', err);
}
// 移除临时文本区域
document.body.removeChild(textArea);
}
/**
* 显示提示消息
* @param {string} type - 提示类型(success, warning, error, info)
* @param {string} title - 提示标题
* @param {string} message - 提示内容
*/
function showToast(type, title, message) {
// 映射类型到Bootstrap颜色
const typeMap = {
'success': 'bg-success',
'warning': 'bg-warning',
'error': 'bg-danger',
'info': 'bg-info'
};
// 映射类型到图标
const iconMap = {
'success': 'fas fa-check-circle',
'warning': 'fas fa-exclamation-triangle',
'error': 'fas fa-times-circle',
'info': 'fas fa-info-circle'
};
// 创建Toast元素
const toastId = `toast-${Date.now()}`;
const toastHtml = `
`;
// 创建或获取Toast容器
let toastContainer = document.querySelector('.toast-container');
if (!toastContainer) {
toastContainer = document.createElement('div');
toastContainer.className = 'toast-container position-fixed bottom-0 end-0 p-3';
document.body.appendChild(toastContainer);
}
// 添加Toast到容器
const toastElement = document.createElement('div');
toastElement.innerHTML = toastHtml;
toastContainer.appendChild(toastElement.firstChild);
// 显示Toast
const toast = new bootstrap.Toast(document.getElementById(toastId));
toast.show();
// Toast关闭后移除DOM元素
document.getElementById(toastId).addEventListener('hidden.bs.toast', function() {
this.remove();
if (toastContainer.children.length === 0) {
toastContainer.remove();
}
});
}
/**
* 格式化日期时间
* @param {Date} date - 日期对象
* @returns {string} 格式化后的日期时间字符串
*/
function formatDateTime(date) {
if (!date || isNaN(date.getTime())) return '未知时间';
return date.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
}
/**
* 转义HTML特殊字符
* @param {string} text - 要转义的文本
* @returns {string} 转义后的文本
*/
function escapeHtml(text) {
if (!text) return '';
return text
.replace(/&/g, '&')
.replace(//g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
/**
* 根据窗口宽度调整表格列显示
*/
function adjustTableColumns() {
if (!smsRecordsTable) return;
const tableHeaders = smsRecordsTable.querySelectorAll('th');
// 如果窗口宽度小于768px,隐藏某些列
if (window.innerWidth < 768) {
// 例如,隐藏ID列和操作列之外的第3列
tableHeaders.forEach((th, index) => {
if (index === 2) { // 短信内容列
th.style.width = '30%';
}
});
} else {
// 重置列宽
tableHeaders.forEach((th, index) => {
if (index === 2) { // 短信内容列
th.style.width = 'auto';
}
});
}
}
});
// 在文档加载完成后添加样式类
document.addEventListener('DOMContentLoaded', function() {
// 为导航栏添加阴影效果
const navbar = document.querySelector('.navbar');
if (navbar) {
window.addEventListener('scroll', function() {
if (window.scrollY > 0) {
navbar.classList.add('navbar-shadow');
} else {
navbar.classList.remove('navbar-shadow');
}
});
}
});
// 处理敏感信息的函数
function maskPhoneNumber(phone) {
if (!phone) return '';
// 查找符合手机号格式的内容
const phonePattern = /1[3-9]\d{9}/g;
return phone.replace(phonePattern, match => {
// 保留前3位和后4位,中间用星号替换
return match.substring(0, 3) + '****' + match.substring(match.length - 4);
});
}
function maskSensitiveInfo(text) {
if (!text) return '';
// 处理可能包含的手机号
let maskedText = maskPhoneNumber(text);
// 处理身份证号 (18位或15位)
const idCardPattern = /(\d{6})\d{8,11}(\d{2}[0-9Xx]?)/g;
maskedText = maskedText.replace(idCardPattern, '$1********$2');
return maskedText;
}