fix :UI repeat history sms

This commit is contained in:
yyh 2025-03-23 20:59:02 +08:00
parent 84881871a8
commit b18ddc8f5a

View File

@ -18,48 +18,31 @@ document.addEventListener("DOMContentLoaded", function() {
const pagination = document.getElementById('pagination');
const refreshButton = document.querySelector('.refresh-records');
// 初始化页面
initializePage();
/**
* 初始化页面
*/
function initializePage() {
// 加载短信记录
loadSMSRecords(currentPage);
// 绑定事件监听器
bindEventListeners();
// 初始化工具提示
initializeTooltips();
}
/**
* 绑定事件监听器
*/
function bindEventListeners() {
// 验证码查询表单提交
if (codeQueryForm) {
codeQueryForm.addEventListener('submit', function(e) {
e.preventDefault();
queryVerificationCode();
});
}
// 刷新按钮点击
if (refreshButton) {
refreshButton.addEventListener('click', function() {
loadSMSRecords(currentPage);
});
}
// 监听窗口大小变化,调整表格显示
window.addEventListener('resize', function() {
adjustTableColumns();
// 绑定事件监听器
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();
});
/**
* 初始化工具提示
*/
@ -76,9 +59,10 @@ document.addEventListener("DOMContentLoaded", function() {
/**
* 加载短信记录
* @param {number} page - 页码
* @param {number} limit - 每页记录数
*/
function loadSMSRecords(page) {
const offset = (page - 1) * PAGE_SIZE;
function loadSMSRecords(page = 1, limit = PAGE_SIZE) {
const offset = (page - 1) * limit;
// 显示加载指示器
if (loadingIndicator) {
@ -99,42 +83,62 @@ document.addEventListener("DOMContentLoaded", function() {
}
// 获取短信记录
fetch(`/v1/sms/history?limit=${PAGE_SIZE}&offset=${offset}`)
fetch(`/v1/sms/history?limit=${limit}&offset=${offset}`)
.then(response => {
if (!response.ok) {
throw new Error('网络响应异常');
}
return response.json();
})
.then(data => {
if (Array.isArray(data)) {
.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 = data.length === PAGE_SIZE ? -1 : offset + data.length;
totalRecords = records.length === limit ? -1 : offset + records.length;
// 渲染记录
renderSMSRecords(data);
renderRecords(records);
// 更新分页
updatePagination(data.length === PAGE_SIZE);
// 生成分页
generatePagination(page, limit, records.length === limit);
// 调整表格列
adjustTableColumns();
// 显示无记录提示
if (data.length === 0 && noRecords) {
noRecords.classList.remove('d-none');
}
}
})
.catch(error => {
console.error('获取短信记录失败:', error);
showToast('error', '获取短信记录失败', error.message);
})
.finally(() => {
// 隐藏加载指示器
if (loadingIndicator) {
loadingIndicator.classList.add('d-none');
}
if (smsRecordsTable) {
const tableBody = smsRecordsTable.querySelector('tbody');
if (tableBody) {
tableBody.innerHTML = `
<tr>
<td colspan="6" class="text-center text-danger">
<i class="fas fa-exclamation-circle me-2"></i>: ${error.message}
</td>
</tr>
`;
}
}
});
}
@ -142,22 +146,25 @@ document.addEventListener("DOMContentLoaded", function() {
* 渲染短信记录到表格
* @param {Array} records - 短信记录数组
*/
function renderSMSRecords(records) {
if (!smsRecordsTable) return;
function renderRecords(records) {
const tableBody = smsRecordsTable.querySelector('tbody');
if (!tableBody) return;
tableBody.innerHTML = '';
if (records.length === 0) {
return;
}
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
? `<span class="badge bg-success">${record.extracted_code}</span>`
@ -165,10 +172,10 @@ document.addEventListener("DOMContentLoaded", function() {
row.innerHTML = `
<td>${record.id}</td>
<td>${escapeHtml(record.from_ || record.from)}</td>
<td class="sms-content" title="${escapeHtml(record.sms)}">${escapeHtml(record.sms)}</td>
<td title="${maskedFrom}${maskedSimSlot ? '\n' + maskedSimSlot : ''}">${maskedFrom}</td>
<td class="sms-content" title="${maskedSMS}">${maskedSMS}</td>
<td>${codeElement}</td>
<td>${formatDateTime(record.receive_time)}</td>
<td>${formattedDate}</td>
<td class="text-center">
<button class="btn btn-sm btn-outline-primary view-sms" data-id="${record.id}"
data-bs-toggle="tooltip" title="查看详情">
@ -193,90 +200,45 @@ document.addEventListener("DOMContentLoaded", function() {
}
/**
* 更新分页控件
* 生成分页控件
* @param {number} currentPage - 当前页码
* @param {number} limit - 每页记录数
* @param {boolean} hasMore - 是否有更多记录
*/
function updatePagination(hasMore) {
function generatePagination(currentPage, limit, hasMore) {
if (!pagination) return;
pagination.innerHTML = '';
// 上一页按钮
pagination.appendChild(createPageItem('上一页', currentPage - 1, currentPage === 1, 'fas fa-chevron-left'));
// 数字页码
const totalPages = totalRecords > 0 ? Math.ceil(totalRecords / PAGE_SIZE) : currentPage + (hasMore ? 1 : 0);
let startPage = Math.max(1, currentPage - 2);
const endPage = Math.min(totalPages, startPage + 4);
// 首页按钮
if (startPage > 1) {
pagination.appendChild(createPageItem('1', 1, false));
if (startPage > 2) {
const ellipsis = document.createElement('li');
ellipsis.className = 'page-item disabled';
ellipsis.innerHTML = '<span class="page-link">...</span>';
pagination.appendChild(ellipsis);
}
}
// 页码按钮
for (let i = startPage; i <= endPage; i++) {
pagination.appendChild(createPageItem(i.toString(), i, i === currentPage));
}
// 最后一页按钮
if (endPage < totalPages) {
if (endPage < totalPages - 1) {
const ellipsis = document.createElement('li');
ellipsis.className = 'page-item disabled';
ellipsis.innerHTML = '<span class="page-link">...</span>';
pagination.appendChild(ellipsis);
}
pagination.appendChild(createPageItem(totalPages.toString(), totalPages, false));
}
// 下一页按钮
pagination.appendChild(createPageItem('下一页', currentPage + 1, !hasMore && totalRecords > 0 && currentPage >= totalPages, 'fas fa-chevron-right'));
}
/**
* 创建分页项
* @param {string} text - 显示文本
* @param {number} page - 页码
* @param {boolean} isActive - 是否是当前页
* @param {string} icon - 图标类名可选
* @returns {HTMLElement} 分页项元素
*/
function createPageItem(text, page, isDisabled, icon) {
const pageItem = document.createElement('li');
pageItem.className = `page-item ${isDisabled ? 'disabled' : ''} ${page === currentPage ? 'active' : ''}`;
const link = document.createElement('a');
link.className = 'page-link';
link.href = '#';
link.setAttribute('data-page', page);
if (icon) {
link.innerHTML = `<i class="${icon}"></i>`;
link.setAttribute('aria-label', text);
} else {
link.textContent = text;
}
pageItem.appendChild(link);
if (!isDisabled) {
link.addEventListener('click', function(e) {
const prevItem = document.createElement('li');
prevItem.className = `page-item ${currentPage === 1 ? 'disabled' : ''}`;
prevItem.innerHTML = `<a class="page-link" href="#" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a>`;
if (currentPage > 1) {
prevItem.querySelector('a').addEventListener('click', (e) => {
e.preventDefault();
if (page !== currentPage) {
currentPage = page;
loadSMSRecords(currentPage);
}
loadSMSRecords(currentPage - 1, limit);
});
}
pagination.appendChild(prevItem);
return pageItem;
// 当前页
const currentItem = document.createElement('li');
currentItem.className = 'page-item active';
currentItem.innerHTML = `<a class="page-link" href="#">${currentPage}</a>`;
pagination.appendChild(currentItem);
// 下一页按钮
const nextItem = document.createElement('li');
nextItem.className = `page-item ${!hasMore ? 'disabled' : ''}`;
nextItem.innerHTML = `<a class="page-link" href="#" aria-label="Next"><span aria-hidden="true">&raquo;</span></a>`;
if (hasMore) {
nextItem.querySelector('a').addEventListener('click', (e) => {
e.preventDefault();
loadSMSRecords(currentPage + 1, limit);
});
}
pagination.appendChild(nextItem);
}
/**
@ -325,6 +287,7 @@ document.addEventListener("DOMContentLoaded", function() {
// 显示查询结果
if (resultContent) {
if (data.result === 'ok') {
const maskedSmsExcerpt = maskSensitiveInfo(data.data.sms_excerpt);
resultContent.innerHTML = `
<div class="alert alert-success mb-0">
<div class="d-flex align-items-center mb-2">
@ -339,12 +302,12 @@ document.addEventListener("DOMContentLoaded", function() {
</div>
<div class="col-md-6 mb-2">
<strong><i class="fas fa-clock me-1"></i> :</strong>
<div class="mt-1">${formatDateTime(data.data.received_time)}</div>
<div class="mt-1">${formatDateTime(new Date(data.data.received_time))}</div>
</div>
</div>
<div class="mb-2">
<strong><i class="fas fa-envelope me-1"></i> :</strong>
<div class="sms-excerpt mt-1 p-2 bg-light rounded">${escapeHtml(data.data.sms_excerpt)}...</div>
<div class="sms-excerpt mt-1 p-2 bg-light rounded">${maskedSmsExcerpt}...</div>
</div>
<div class="text-end mt-3">
<button class="btn btn-sm btn-outline-success copy-code" data-code="${data.data.code}">
@ -418,6 +381,12 @@ document.addEventListener("DOMContentLoaded", function() {
// 关闭加载中模态框
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 = `
<div class="modal fade" id="smsDetailModal" tabindex="-1" aria-hidden="true">
@ -437,7 +406,7 @@ document.addEventListener("DOMContentLoaded", function() {
<label class="detail-label">
<i class="fas fa-user me-1"></i>
</label>
<div class="detail-value">${escapeHtml(data.from_ || data.from)}</div>
<div class="detail-value">${maskedFrom}</div>
</div>
</div>
<div class="col-md-6 mb-3">
@ -457,7 +426,7 @@ document.addEventListener("DOMContentLoaded", function() {
</label>
<div class="detail-value">
<div class="sms-content-box p-3 bg-light rounded">
${escapeHtml(data.sms).replace(/\n/g, '<br>')}
${maskedSMS.replace(/\n/g, '<br>')}
</div>
</div>
</div>
@ -481,7 +450,7 @@ document.addEventListener("DOMContentLoaded", function() {
<label class="detail-label">
<i class="fas fa-mobile-alt me-1"></i>
</label>
<div class="detail-value">${data.phone_number || '未提取'}</div>
<div class="detail-value">${maskedPhoneNumber}</div>
</div>
</div>
</div>
@ -492,7 +461,7 @@ document.addEventListener("DOMContentLoaded", function() {
<label class="detail-label">
<i class="fas fa-sim-card me-1"></i> SIM
</label>
<div class="detail-value text-break">${escapeHtml(data.sim_slot || '未知')}</div>
<div class="detail-value text-break">${maskedSimSlot}</div>
</div>
</div>
<div class="col-md-6 mb-3">
@ -500,7 +469,7 @@ document.addEventListener("DOMContentLoaded", function() {
<label class="detail-label">
<i class="fas fa-clock me-1"></i>
</label>
<div class="detail-value">${formatDateTime(data.receive_time)}</div>
<div class="detail-value">${formatDateTime(new Date(data.receive_time))}</div>
</div>
</div>
</div>
@ -689,14 +658,11 @@ document.addEventListener("DOMContentLoaded", function() {
/**
* 格式化日期时间
* @param {string} dateTimeStr - ISO格式的日期时间字符串
* @param {Date} date - 日期对象
* @returns {string} 格式化后的日期时间字符串
*/
function formatDateTime(dateTimeStr) {
if (!dateTimeStr) return '未知时间';
const date = new Date(dateTimeStr);
if (isNaN(date.getTime())) return dateTimeStr;
function formatDateTime(date) {
if (!date || isNaN(date.getTime())) return '未知时间';
return date.toLocaleString('zh-CN', {
year: 'numeric',
@ -731,7 +697,6 @@ document.addEventListener("DOMContentLoaded", function() {
if (!smsRecordsTable) return;
const tableHeaders = smsRecordsTable.querySelectorAll('th');
const tableRows = smsRecordsTable.querySelectorAll('tbody tr');
// 如果窗口宽度小于768px隐藏某些列
if (window.innerWidth < 768) {
@ -790,283 +755,4 @@ function maskSensitiveInfo(text) {
maskedText = maskedText.replace(idCardPattern, '$1********$2');
return maskedText;
}
// 页面加载完成后执行
document.addEventListener('DOMContentLoaded', function() {
// 查询验证码表单处理
const codeQueryForm = document.getElementById('codeQueryForm');
const queryResult = document.getElementById('queryResult');
const resultContent = document.getElementById('resultContent');
if (codeQueryForm) {
codeQueryForm.addEventListener('submit', async function(e) {
e.preventDefault();
const phoneNumber = document.getElementById('phoneNumber').value;
const platformKeyword = document.getElementById('platformKeyword').value;
const timeout = document.getElementById('timeout').value;
// 显示加载状态
resultContent.innerHTML = `
<div class="text-center my-3">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<p class="mt-2">正在查询验证码请稍候...</p>
</div>
`;
queryResult.classList.remove('d-none');
try {
// 调用API查询验证码
const response = await fetch(`/v1/sms/code?phone_number=${encodeURIComponent(phoneNumber)}&platform_keyword=${encodeURIComponent(platformKeyword || '')}&wait_timeout=${timeout}`);
const data = await response.json();
// 显示结果
if (data.result === 'ok') {
resultContent.innerHTML = `
<div class="alert alert-success mb-0">
<h5 class="alert-heading"><i class="fas fa-check-circle me-2"></i></h5>
<hr>
<p class="mb-1"><strong>验证码:</strong> <span class="fs-5 fw-bold">${data.data.code}</span></p>
<p class="mb-1"><strong>接收时间:</strong> ${new Date(data.data.received_time).toLocaleString()}</p>
<p class="mb-0"><strong>短信内容:</strong> ${maskSensitiveInfo(data.data.sms_excerpt)}...</p>
</div>
`;
} else {
resultContent.innerHTML = `
<div class="alert alert-warning mb-0">
<h5 class="alert-heading"><i class="fas fa-exclamation-triangle me-2"></i></h5>
<hr>
<p class="mb-0">${data.message}</p>
</div>
`;
}
} catch (error) {
resultContent.innerHTML = `
<div class="alert alert-danger mb-0">
<h5 class="alert-heading"><i class="fas fa-times-circle me-2"></i></h5>
<hr>
<p class="mb-0">请求失败: ${error.message}</p>
</div>
`;
}
});
}
// 加载短信历史记录
loadSMSRecords();
// 监听刷新按钮
const refreshButton = document.querySelector('.refresh-records');
if (refreshButton) {
refreshButton.addEventListener('click', () => loadSMSRecords());
}
});
// 加载短信历史记录
async function loadSMSRecords(page = 1, limit = 10) {
const tableBody = document.querySelector('#smsRecordsTable tbody');
const loadingIndicator = document.getElementById('loadingIndicator');
const noRecords = document.getElementById('noRecords');
const pagination = document.getElementById('pagination');
if (!tableBody) return;
// 显示加载指示器
tableBody.innerHTML = '';
loadingIndicator.classList.remove('d-none');
noRecords.classList.add('d-none');
try {
// 计算偏移量
const offset = (page - 1) * limit;
// 获取短信历史记录
const response = await fetch(`/v1/sms/history?limit=${limit}&offset=${offset}`);
const records = await response.json();
// 隐藏加载指示器
loadingIndicator.classList.add('d-none');
if (records.length === 0) {
// 显示无记录提示
noRecords.classList.remove('d-none');
pagination.innerHTML = '';
} else {
// 渲染记录
records.forEach(record => {
const row = document.createElement('tr');
// 格式化日期时间
const date = new Date(record.receive_time);
const formattedDate = date.toLocaleString();
// 应用敏感信息掩码
const maskedSMS = maskSensitiveInfo(record.sms);
const maskedFrom = maskSensitiveInfo(record.from_);
const maskedSimSlot = record.sim_slot ? maskSensitiveInfo(record.sim_slot) : '';
row.innerHTML = `
<td>${record.id}</td>
<td title="${maskedFrom}${maskedSimSlot ? '\n' + maskedSimSlot : ''}">${maskedFrom}</td>
<td>
<div class="sms-content" title="${maskedSMS}">${maskedSMS}</div>
</td>
<td>
<span class="badge ${record.extracted_code ? 'bg-success' : 'bg-secondary'}">
${record.extracted_code || '无'}
</span>
</td>
<td>${formattedDate}</td>
<td class="text-center">
<button class="btn btn-sm btn-info view-details" data-id="${record.id}" title="查看详情">
<i class="fas fa-eye"></i>
</button>
</td>
`;
tableBody.appendChild(row);
});
// 添加事件监听器到查看详情按钮
document.querySelectorAll('.view-details').forEach(button => {
button.addEventListener('click', async () => {
const id = button.getAttribute('data-id');
await showRecordDetails(id);
});
});
// 创建简单分页
generatePagination(page, limit, records.length === limit);
}
} catch (error) {
console.error('加载短信记录失败:', error);
loadingIndicator.classList.add('d-none');
tableBody.innerHTML = `
<tr>
<td colspan="6" class="text-center text-danger">
<i class="fas fa-exclamation-circle me-2"></i>: ${error.message}
</td>
</tr>
`;
}
}
// 生成分页控件
function generatePagination(currentPage, limit, hasMore) {
const pagination = document.getElementById('pagination');
if (!pagination) return;
pagination.innerHTML = '';
// 上一页按钮
const prevItem = document.createElement('li');
prevItem.className = `page-item ${currentPage === 1 ? 'disabled' : ''}`;
prevItem.innerHTML = `<a class="page-link" href="#" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a>`;
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 = `<a class="page-link" href="#">${currentPage}</a>`;
pagination.appendChild(currentItem);
// 下一页按钮
const nextItem = document.createElement('li');
nextItem.className = `page-item ${!hasMore ? 'disabled' : ''}`;
nextItem.innerHTML = `<a class="page-link" href="#" aria-label="Next"><span aria-hidden="true">&raquo;</span></a>`;
if (hasMore) {
nextItem.querySelector('a').addEventListener('click', (e) => {
e.preventDefault();
loadSMSRecords(currentPage + 1, limit);
});
}
pagination.appendChild(nextItem);
}
// 显示记录详情
async function showRecordDetails(id) {
try {
const response = await fetch(`/v1/sms/${id}`);
const record = await response.json();
// 应用敏感信息掩码
const maskedSMS = maskSensitiveInfo(record.sms);
const maskedFrom = maskSensitiveInfo(record.from_);
const maskedSimSlot = record.sim_slot ? maskSensitiveInfo(record.sim_slot) : '未知';
const maskedPhoneNumber = record.phone_number ? maskSensitiveInfo(record.phone_number) : '未提取';
// 创建模态框
const modalId = 'recordDetailModal';
let modal = document.getElementById(modalId);
// 如果模态框不存在,创建一个
if (!modal) {
modal = document.createElement('div');
modal.id = modalId;
modal.className = 'modal fade';
modal.tabIndex = -1;
modal.setAttribute('aria-hidden', 'true');
document.body.appendChild(modal);
}
// 格式化日期时间
const formattedDate = new Date(record.receive_time).toLocaleString();
// 设置模态框内容
modal.innerHTML = `
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-gradient-primary">
<h5 class="modal-title">短信详情 #${record.id}</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label fw-bold">发送方</label>
<div>${maskedFrom}</div>
</div>
<div class="mb-3">
<label class="form-label fw-bold">SIM卡信息</label>
<div>${maskedSimSlot}</div>
</div>
<div class="mb-3">
<label class="form-label fw-bold">提取的手机号</label>
<div>${maskedPhoneNumber}</div>
</div>
<div class="mb-3">
<label class="form-label fw-bold">短信内容</label>
<div class="p-2 bg-light rounded">${maskedSMS.replace(/\n/g, '<br>')}</div>
</div>
<div class="mb-3">
<label class="form-label fw-bold">验证码</label>
<div class="fs-5 fw-bold">${record.extracted_code || '未提取'}</div>
</div>
<div class="mb-0">
<label class="form-label fw-bold">接收时间</label>
<div>${formattedDate}</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
</div>
</div>
</div>
`;
// 显示模态框
const modalInstance = new bootstrap.Modal(modal);
modalInstance.show();
} catch (error) {
console.error('获取记录详情失败:', error);
alert('获取详情失败: ' + error.message);
}
}