2 changed files with 546 additions and 2 deletions
@ -0,0 +1,544 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="zh-CN"> |
|||
<head> |
|||
<meta charset="UTF-8"> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|||
<title>管理后台</title> |
|||
<style> |
|||
/* 基础样式 */ |
|||
* { |
|||
margin: 0; |
|||
padding: 0; |
|||
box-sizing: border-box; |
|||
font-family: "Microsoft YaHei", sans-serif; |
|||
} |
|||
body { |
|||
background-color: #f5f7fa; |
|||
color: #333; |
|||
} |
|||
.container { |
|||
display: flex; |
|||
min-height: 100vh; |
|||
} |
|||
|
|||
/* 侧边栏样式 */ |
|||
.sidebar { |
|||
width: 220px; |
|||
background-color: #2c3e50; |
|||
color: #fff; |
|||
padding: 20px 0; |
|||
} |
|||
.sidebar-logo { |
|||
text-align: center; |
|||
padding: 0 20px 20px; |
|||
border-bottom: 1px solid rgba(255,255,255,0.1); |
|||
margin-bottom: 20px; |
|||
} |
|||
.sidebar-logo h2 { |
|||
font-size: 18px; |
|||
margin-top: 10px; |
|||
} |
|||
.sidebar-menu { |
|||
list-style: none; |
|||
} |
|||
.sidebar-menu li a { |
|||
display: flex; |
|||
align-items: center; |
|||
padding: 12px 20px; |
|||
color: rgba(255,255,255,0.8); |
|||
text-decoration: none; |
|||
transition: all 0.3s; |
|||
} |
|||
.sidebar-menu li a:hover, |
|||
.sidebar-menu li a.active { |
|||
background-color: #34495e; |
|||
color: #fff; |
|||
} |
|||
.sidebar-menu li a i { |
|||
margin-right: 10px; |
|||
font-size: 16px; |
|||
} |
|||
|
|||
/* 主内容区样式 */ |
|||
.main-content { |
|||
flex: 1; |
|||
padding: 20px; |
|||
overflow-y: auto; |
|||
} |
|||
.page-header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
margin-bottom: 20px; |
|||
padding-bottom: 15px; |
|||
border-bottom: 1px solid #eee; |
|||
} |
|||
.page-header h2 { |
|||
font-size: 20px; |
|||
font-weight: 600; |
|||
color: #333; |
|||
} |
|||
.btn { |
|||
padding: 6px 12px; |
|||
border: 1px solid #ddd; |
|||
border-radius: 4px; |
|||
background-color: #fff; |
|||
cursor: pointer; |
|||
color: #333; |
|||
text-decoration: none; |
|||
} |
|||
.btn-primary { |
|||
background-color: #3498db; |
|||
color: #fff; |
|||
border-color: #3498db; |
|||
} |
|||
.btn-danger { |
|||
background-color: #e74c3c; |
|||
color: #fff; |
|||
border-color: #e74c3c; |
|||
} |
|||
|
|||
/* 查询区域样式 */ |
|||
.search-area { |
|||
display: flex; |
|||
align-items: center; |
|||
gap: 10px; |
|||
margin-bottom: 20px; |
|||
flex-wrap: wrap; /* 适配小屏幕 */ |
|||
} |
|||
.search-area input, |
|||
.search-area select { |
|||
padding: 6px 10px; |
|||
border: 1px solid #ddd; |
|||
border-radius: 4px; |
|||
outline: none; |
|||
} |
|||
.search-area .date-picker { |
|||
display: flex; |
|||
align-items: center; |
|||
gap: 5px; |
|||
} |
|||
|
|||
/* 表格样式 */ |
|||
.table-container { |
|||
background-color: #fff; |
|||
border-radius: 4px; |
|||
box-shadow: 0 2px 12px rgba(0,0,0,0.1); |
|||
overflow: hidden; |
|||
} |
|||
.data-table { |
|||
width: 100%; |
|||
border-collapse: collapse; |
|||
} |
|||
.data-table th, |
|||
.data-table td { |
|||
padding: 12px 15px; |
|||
text-align: left; |
|||
border-bottom: 1px solid #f0f0f0; |
|||
} |
|||
.data-table th { |
|||
background-color: #f9fafb; |
|||
font-weight: 600; |
|||
color: #666; |
|||
} |
|||
.data-table tbody tr:hover { |
|||
background-color: #f5f7fa; |
|||
} |
|||
|
|||
/* 分页样式 */ |
|||
.pagination { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
padding: 15px; |
|||
border-top: 1px solid #f0f0f0; |
|||
} |
|||
.pagination-info { |
|||
color: #666; |
|||
font-size: 14px; |
|||
} |
|||
.pagination-list { |
|||
display: flex; |
|||
list-style: none; |
|||
padding: 0; |
|||
margin: 0; |
|||
} |
|||
.pagination-list li { |
|||
margin: 0 2px; |
|||
} |
|||
.pagination-list a, |
|||
.pagination-list span { |
|||
display: inline-block; |
|||
width: 45px; |
|||
height: 45px; |
|||
line-height: 45px; |
|||
text-align: center; |
|||
border: 1px solid #ddd; |
|||
border-radius: 4px; |
|||
text-decoration: none; |
|||
color: #333; |
|||
font-size: 14px; |
|||
box-sizing: border-box; |
|||
} |
|||
.pagination-list a { |
|||
cursor: pointer; |
|||
} |
|||
.pagination-list a:hover, |
|||
.pagination-list a.active { |
|||
background-color: #3498db; |
|||
color: #fff; |
|||
border-color: #3498db; |
|||
} |
|||
.pagination-list span { |
|||
background-color: #f5f5f5; |
|||
color: #999; |
|||
cursor: not-allowed; |
|||
} |
|||
.pagination-jump { |
|||
display: flex; |
|||
align-items: center; |
|||
font-size: 14px; |
|||
gap: 5px; |
|||
} |
|||
.pagination-jump input { |
|||
width: 50px; |
|||
height: 32px; |
|||
margin: 0 5px; |
|||
padding: 0 5px; |
|||
border: 1px solid #ddd; |
|||
border-radius: 4px; |
|||
text-align: center; |
|||
box-sizing: border-box; |
|||
} |
|||
.pagination-jump button { |
|||
padding: 6px 12px; |
|||
border: 1px solid #ddd; |
|||
border-radius: 4px; |
|||
background-color: #fff; |
|||
cursor: pointer; |
|||
height: 32px; |
|||
box-sizing: border-box; |
|||
} |
|||
|
|||
/* 加载中样式 */ |
|||
.loading { |
|||
text-align: center; |
|||
padding: 50px 0; |
|||
color: #666; |
|||
} |
|||
.loading i { |
|||
font-size: 24px; |
|||
margin-bottom: 10px; |
|||
animation: spin 1s linear infinite; |
|||
} |
|||
@keyframes spin { |
|||
from { transform: rotate(0deg); } |
|||
to { transform: rotate(360deg); } |
|||
} |
|||
</style> |
|||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"> |
|||
</head> |
|||
<body> |
|||
<div class="container"> |
|||
<div class="sidebar"> |
|||
<div class="sidebar-logo"> |
|||
<i class="fa fa-line-chart" style="font-size: 24px;"></i> |
|||
<h2>后台管理系统</h2> |
|||
</div> |
|||
<ul class="sidebar-menu"> |
|||
<li><a href="#" class="active"><i class="fa fa-file-text-o"></i> 落地页管理</a></li> |
|||
</ul> |
|||
</div> |
|||
|
|||
<!-- 主内容区 --> |
|||
<div class="main-content"> |
|||
<div class="page-header"> |
|||
<div class="page-header h2">详情</div> |
|||
<a href="adminConfig.html" class="btn">返回上一级页面</a> |
|||
</div> |
|||
|
|||
<!-- 查询区域 --> |
|||
<div class="search-area"> |
|||
<label class="date-picker"> |
|||
打开网页时间 |
|||
<input type="date" id="startTime" placeholder="请选择开始时间" /> |
|||
至 |
|||
<input type="date" id="endTime" placeholder="请选择结束时间" /> |
|||
</label> |
|||
<div> |
|||
<label for="statusSelect">收下状态</label> |
|||
<select id="statusSelect"> |
|||
<option value="">请选择状态</option> |
|||
<option value="1">是</option> |
|||
<option value="0">否</option> |
|||
</select> |
|||
</div> |
|||
<button id="searchBtn" class="btn btn-primary">查询</button> |
|||
<button class="btn btn-danger">导出</button> |
|||
</div> |
|||
|
|||
<!-- 数据表格 --> |
|||
<div class="table-container"> |
|||
<table class="data-table"> |
|||
<thead> |
|||
<tr> |
|||
<th>序号</th> |
|||
<th>打开网页时间</th> |
|||
<th>收下状态</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody id="tableBody"> |
|||
<tr> |
|||
<td colspan="3" class="loading"> <!-- 注意:表格列数是3,colspan改为3 --> |
|||
<i class="fa fa-spinner"></i> |
|||
<p>加载中...</p> |
|||
</td> |
|||
</tr> |
|||
</tbody> |
|||
</table> |
|||
|
|||
<!-- 分页控件 --> |
|||
<div class="pagination" id="paginationContainer"> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<script type="module"> |
|||
// 导入API函数(假设接口定义) |
|||
import { getLandingDetailApi } from './src/api/member.js'; |
|||
|
|||
// DOM元素 |
|||
const tableBody = document.getElementById('tableBody'); |
|||
const paginationContainer = document.getElementById('paginationContainer'); |
|||
const searchBtn = document.getElementById('searchBtn'); |
|||
const startTimeInput = document.getElementById('startTime'); |
|||
const endTimeInput = document.getElementById('endTime'); |
|||
const statusSelect = document.getElementById('statusSelect'); |
|||
|
|||
// 分页参数 |
|||
let currentPage = 1; |
|||
const pageSize = 20; |
|||
let totalCount = 0; |
|||
let totalPages = 0; |
|||
|
|||
// 初始化页面:从URL获取页码,请求后端数据 |
|||
async function initPage() { |
|||
// 从URL参数获取当前页码(如 ?page=2) |
|||
const urlParams = new URLSearchParams(window.location.search); |
|||
const pageParam = urlParams.get('page'); |
|||
if (pageParam && !isNaN(pageParam)) { |
|||
currentPage = parseInt(pageParam); |
|||
} |
|||
|
|||
// 从后端请求数据(替换模拟数据) |
|||
await fetchDataFromBackend(); |
|||
} |
|||
|
|||
// 核心:从后端API请求数据 |
|||
async function fetchDataFromBackend() { |
|||
// 显示加载状态 |
|||
tableBody.innerHTML = ` |
|||
<tr> |
|||
<td colspan="3" class="loading"> |
|||
<i class="fa fa-spinner"></i> |
|||
<p>加载中...</p> |
|||
</td> |
|||
</tr> |
|||
`; |
|||
paginationContainer.innerHTML = ''; |
|||
|
|||
try { |
|||
// 1. 构造请求参数(分页+查询条件) |
|||
const requestParams = { |
|||
page: currentPage, // 当前页码 |
|||
size: pageSize, // 每页条数 |
|||
startTime: startTimeInput.value || '', // 开始时间(查询条件) |
|||
endTime: endTimeInput.value || '', // 结束时间(查询条件) |
|||
receiveStatus: statusSelect.value || '' // 收下状态(查询条件) |
|||
}; |
|||
|
|||
// 2. 调用导入的API函数 |
|||
const result = await getLandingDetailApi(requestParams); |
|||
|
|||
// 3. 处理API返回结果 |
|||
if (result.code === 200) { |
|||
const { list, total } = result.data; |
|||
totalCount = total; |
|||
totalPages = Math.ceil(totalCount / pageSize); |
|||
|
|||
renderTable(list); |
|||
renderPagination(); |
|||
} else { |
|||
renderEmptyState('获取数据失败'); |
|||
} |
|||
|
|||
} catch (error) { |
|||
// 捕获异常 |
|||
console.error('请求数据异常:', error); |
|||
renderEmptyState('网络错误,请稍后重试'); |
|||
} |
|||
} |
|||
|
|||
// 渲染表格数据 |
|||
function renderTable(dataList) { |
|||
if (!dataList || dataList.length === 0) { |
|||
renderEmptyState('暂无符合条件的数据'); |
|||
return; |
|||
} |
|||
|
|||
let html = ''; |
|||
dataList.forEach((item, index) => { |
|||
// 计算序号:(当前页-1)*每页条数 + 索引+1 |
|||
const serialNumber = (currentPage - 1) * pageSize + index + 1; |
|||
|
|||
// 注意:item的字段名需与后端返回的字段一致 |
|||
html += ` |
|||
<tr> |
|||
<td>${serialNumber}</td> |
|||
<td>${formatDate(item.openTime || '')}</td> |
|||
<td>${item.receiveStatus === '1' ? '是' : item.receiveStatus === '0' ? '否' : ''}</td> |
|||
</tr> |
|||
`; |
|||
}); |
|||
|
|||
tableBody.innerHTML = html; |
|||
} |
|||
|
|||
// 渲染空状态 |
|||
function renderEmptyState(message) { |
|||
tableBody.innerHTML = ` |
|||
<tr> |
|||
<td colspan="3" style="text-align: center; padding: 30px;"> |
|||
<i class="fa fa-inbox" style="font-size: 24px; color: #ddd; margin-bottom: 10px;"></i> |
|||
<p>${message}</p> |
|||
</td> |
|||
</tr> |
|||
`; |
|||
paginationContainer.innerHTML = ''; |
|||
} |
|||
|
|||
// 渲染分页控件 |
|||
function renderPagination() { |
|||
if (totalCount === 0) return; |
|||
|
|||
// 生成页码列表 |
|||
const pageNumbers = []; |
|||
let startPage = Math.max(1, currentPage - 2); |
|||
let endPage = Math.min(totalPages, currentPage + 2); |
|||
|
|||
if (endPage - startPage < 4 && totalPages >= 5) { |
|||
if (startPage === 1) endPage = 5; |
|||
else if (endPage === totalPages) startPage = totalPages - 4; |
|||
} |
|||
|
|||
for (let i = startPage; i <= endPage; i++) { |
|||
pageNumbers.push(i); |
|||
} |
|||
|
|||
// 分页HTML |
|||
const html = ` |
|||
<div class="pagination-info"> |
|||
共 ${totalCount} 条记录,当前第 ${currentPage} / ${totalPages} 页 |
|||
</div> |
|||
<ul class="pagination-list"> |
|||
<li> |
|||
${currentPage === 1 |
|||
? `<span>首页</span>` |
|||
: `<a href="?page=1">首页</a>` |
|||
} |
|||
</li> |
|||
<li> |
|||
${currentPage > 1 |
|||
? `<a href="?page=${currentPage - 1}">上一页</a>` |
|||
: `<span>上一页</span>` |
|||
} |
|||
</li> |
|||
${pageNumbers.map(num => ` |
|||
<li> |
|||
<a href="?page=${num}" class="${num === currentPage ? 'active' : ''}"> |
|||
${num} |
|||
</a> |
|||
</li> |
|||
`).join('')} |
|||
<li> |
|||
${currentPage < totalPages |
|||
? `<a href="?page=${currentPage + 1}">下一页</a>` |
|||
: `<span>下一页</span>` |
|||
} |
|||
</li> |
|||
<li> |
|||
${currentPage === totalPages |
|||
? `<span>尾页</span>` |
|||
: `<a href="?page=${totalPages}">尾页</a>` |
|||
} |
|||
</li> |
|||
</ul> |
|||
<div class="pagination-jump"> |
|||
<span>跳至</span> |
|||
<input type="number" min="1" max="${totalPages}" value="${currentPage}" id="jumpPageInput"> |
|||
<span>页</span> |
|||
<button id="jumpBtn">确定</button> |
|||
</div> |
|||
`; |
|||
|
|||
paginationContainer.innerHTML = html; |
|||
|
|||
// 绑定跳转事件 |
|||
document.getElementById('jumpBtn').addEventListener('click', handlePageJump); |
|||
document.getElementById('jumpPageInput').addEventListener('keypress', (e) => { |
|||
if (e.key === 'Enter') handlePageJump(); |
|||
}); |
|||
} |
|||
|
|||
// 处理页码跳转 |
|||
function handlePageJump() { |
|||
const pageInput = document.getElementById('jumpPageInput'); |
|||
const page = parseInt(pageInput.value); |
|||
|
|||
if (page && !isNaN(page) && page >= 1 && page <= totalPages && page !== currentPage) { |
|||
window.location.href = `?page=${page}`; |
|||
} else { |
|||
alert('请输入有效的页码'); |
|||
} |
|||
} |
|||
|
|||
// 查询按钮事件:点击后重新请求数据 |
|||
searchBtn.addEventListener('click', async () => { |
|||
currentPage = 1; // 重置为第一页 |
|||
await fetchDataFromBackend(); // 重新请求后端数据 |
|||
}); |
|||
|
|||
// 工具函数:格式化日期(适配后端返回的时间格式) |
|||
function formatDate(dateString) { |
|||
if (!dateString) return ''; |
|||
const date = new Date(dateString); |
|||
// 处理无效日期 |
|||
if (isNaN(date.getTime())) return dateString; |
|||
// 格式化显示:年-月-日 时:分:秒 |
|||
return date.toLocaleString('zh-CN', { |
|||
year: 'numeric', |
|||
month: '2-digit', |
|||
day: '2-digit', |
|||
hour: '2-digit', |
|||
minute: '2-digit', |
|||
second: '2-digit' |
|||
}).replace(/\//g, '-'); |
|||
} |
|||
|
|||
// 工具函数:HTML转义(防止XSS攻击) |
|||
function escapeHtml(str) { |
|||
if (!str) return ''; |
|||
return str |
|||
.replace(/&/g, '&') |
|||
.replace(/</g, '<') |
|||
.replace(/>/g, '>') |
|||
.replace(/"/g, '"') |
|||
.replace(/'/g, '''); |
|||
} |
|||
|
|||
// 页面加载时初始化 |
|||
window.addEventListener('DOMContentLoaded', initPage); |
|||
</script> |
|||
</body> |
|||
</html> |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue