2 changed files with 591 additions and 12 deletions
@ -1,14 +1,584 @@ |
|||||
<!DOCTYPE html> |
<!DOCTYPE html> |
||||
<html lang=""> |
|
||||
<head> |
|
||||
<meta charset="UTF-8"> |
|
||||
<link rel="icon" href="/favicon.ico"> |
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
||||
<title>Vite App</title> |
|
||||
</head> |
|
||||
<body> |
|
||||
<h1>mzy</h1> |
|
||||
<script type="module" src="/src/main.js"> |
|
||||
</script> |
|
||||
</body> |
|
||||
|
<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; |
||||
|
} |
||||
|
.header { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
margin-bottom: 20px; |
||||
|
padding-bottom: 15px; |
||||
|
border-bottom: 1px solid #eee; |
||||
|
} |
||||
|
.header-title { |
||||
|
font-size: 20px; |
||||
|
font-weight: 600; |
||||
|
} |
||||
|
.user-info { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
} |
||||
|
.user-info img { |
||||
|
width: 36px; |
||||
|
height: 36px; |
||||
|
border-radius: 50%; |
||||
|
margin-right: 10px; |
||||
|
} |
||||
|
|
||||
|
/* 功能区样式 */ |
||||
|
.function-bar { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
margin-bottom: 20px; |
||||
|
} |
||||
|
.search-box { |
||||
|
display: flex; |
||||
|
} |
||||
|
.search-box input { |
||||
|
width: 300px; |
||||
|
padding: 8px 15px; |
||||
|
border: 1px solid #ddd; |
||||
|
border-radius: 4px 0 0 4px; |
||||
|
outline: none; |
||||
|
} |
||||
|
.search-box button { |
||||
|
background-color: #3498db; |
||||
|
color: #fff; |
||||
|
border: none; |
||||
|
padding: 0 15px; |
||||
|
border-radius: 0 4px 4px 0; |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
.add-btn { |
||||
|
background-color: #2ecc71; |
||||
|
color: #fff; |
||||
|
border: none; |
||||
|
padding: 8px 20px; |
||||
|
border-radius: 4px; |
||||
|
cursor: pointer; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
} |
||||
|
.add-btn i { |
||||
|
margin-right: 5px; |
||||
|
} |
||||
|
.add-btn:hover { |
||||
|
background-color: #27ae60; |
||||
|
} |
||||
|
|
||||
|
/* 表格样式 */ |
||||
|
.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; |
||||
|
} |
||||
|
.status { |
||||
|
display: inline-block; |
||||
|
padding: 3px 8px; |
||||
|
border-radius: 4px; |
||||
|
font-size: 12px; |
||||
|
} |
||||
|
.status-active { |
||||
|
background-color: rgba(46, 204, 113, 0.1); |
||||
|
color: #2ecc71; |
||||
|
} |
||||
|
.status-inactive { |
||||
|
background-color: rgba(231, 76, 60, 0.1); |
||||
|
color: #e74c3c; |
||||
|
} |
||||
|
.action-btn { |
||||
|
margin-right: 8px; |
||||
|
color: #3498db; |
||||
|
text-decoration: none; |
||||
|
font-size: 14px; |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
.action-btn.delete { |
||||
|
color: #e74c3c; |
||||
|
} |
||||
|
.action-btn:hover { |
||||
|
text-decoration: underline; |
||||
|
} |
||||
|
|
||||
|
/* 分页样式 */ |
||||
|
.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; |
||||
|
} |
||||
|
/* 核心:确保a和span统一尺寸和居中 */ |
||||
|
.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; |
||||
|
} |
||||
|
/* 激活和hover状态 */ |
||||
|
.pagination-list a:hover, |
||||
|
.pagination-list a.active { |
||||
|
background-color: #3498db; |
||||
|
color: #fff; |
||||
|
border-color: #3498db; |
||||
|
} |
||||
|
/* 禁用状态(span标签) */ |
||||
|
.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="header"> |
||||
|
<div class="header-title">落地页管理</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 功能区 --> |
||||
|
<div class="function-bar"> |
||||
|
<button class="add-btn"> |
||||
|
<i class="fa fa-plus"></i> 新增落地页 |
||||
|
</button> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 数据表格 --> |
||||
|
<div class="table-container"> |
||||
|
<table class="data-table"> |
||||
|
<thead> |
||||
|
<tr> |
||||
|
<th>序号</th> |
||||
|
<th>活动名称</th> |
||||
|
<th>活动简介</th> |
||||
|
<th>活动时间</th> |
||||
|
<th>编辑时间</th> |
||||
|
<th>操作</th> |
||||
|
</tr> |
||||
|
</thead> |
||||
|
<tbody id="tableBody"> |
||||
|
<tr> |
||||
|
<td colspan="6" class="loading"> |
||||
|
<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 { getLandingListApi } from './src/api/member.js'; |
||||
|
|
||||
|
// DOM元素 |
||||
|
const tableBody = document.getElementById('tableBody'); |
||||
|
const paginationContainer = document.getElementById('paginationContainer'); |
||||
|
|
||||
|
// 分页参数 |
||||
|
let currentPage = 1; |
||||
|
const pageSize = 10; |
||||
|
let totalCount = 0; |
||||
|
let totalPages = 0; |
||||
|
|
||||
|
// 模拟数据 |
||||
|
const mockData = Array.from({ length: 15 }, (_, index) => ({ |
||||
|
id: index + 1, |
||||
|
title: `双十${index + 1}促销活动`, |
||||
|
desc: `这是第${index + 1}个落地页活动,主要推广新品上市和优惠折扣`, |
||||
|
activityTime: `2023-11-${(index % 20) + 10} 00:00:00 - 2023-11-${(index % 20) + 20} 23:59:59`, |
||||
|
updateTime: `2023-11-${(index % 20) + 5} ${10 + index}:${index * 5}:00` |
||||
|
})); |
||||
|
|
||||
|
// 初始化页面 |
||||
|
async function initPage() { |
||||
|
const urlParams = new URLSearchParams(window.location.search); |
||||
|
const pageParam = urlParams.get('page'); |
||||
|
if (pageParam && !isNaN(pageParam)) { |
||||
|
currentPage = parseInt(pageParam); |
||||
|
} |
||||
|
|
||||
|
// 获取列表数据 |
||||
|
// await fetchLandingList(); |
||||
|
await fetchMockLandingList(); |
||||
|
} |
||||
|
|
||||
|
// 新增:使用模拟数据获取列表(替代真实API调用) |
||||
|
async function fetchMockLandingList() { |
||||
|
// 模拟网络延迟 |
||||
|
await new Promise(resolve => setTimeout(resolve, 800)); |
||||
|
|
||||
|
// 计算分页数据 |
||||
|
totalCount = mockData.length; |
||||
|
totalPages = Math.ceil(totalCount / pageSize); |
||||
|
|
||||
|
// 截取当前页的数据(数组切片) |
||||
|
const startIndex = (currentPage - 1) * pageSize; |
||||
|
const endIndex = startIndex + pageSize; |
||||
|
const currentPageData = mockData.slice(startIndex, endIndex); |
||||
|
|
||||
|
// 渲染表格数据 |
||||
|
renderTable(currentPageData); |
||||
|
// 渲染分页控件 |
||||
|
renderPagination(); |
||||
|
} |
||||
|
|
||||
|
// 从后端获取落地页列表 |
||||
|
async function fetchLandingList() { |
||||
|
try { |
||||
|
// 调用API接口,传递分页参数 |
||||
|
const response = await getLandingListApi({ |
||||
|
page: currentPage, |
||||
|
size: pageSize |
||||
|
}); |
||||
|
|
||||
|
if (response.code === 200 && response.data) { |
||||
|
const { list: landingPages, total } = response.data; |
||||
|
totalCount = total; |
||||
|
totalPages = Math.ceil(totalCount / pageSize); |
||||
|
|
||||
|
// 渲染表格数据 |
||||
|
renderTable(landingPages); |
||||
|
// 渲染分页控件 |
||||
|
renderPagination(); |
||||
|
} else { |
||||
|
renderEmptyState('获取数据失败'); |
||||
|
} |
||||
|
} catch (error) { |
||||
|
console.error('获取列表失败:', error); |
||||
|
renderEmptyState('网络错误,请重试'); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 渲染表格数据 |
||||
|
function renderTable(landingPages) { |
||||
|
if (!landingPages || landingPages.length === 0) { |
||||
|
renderEmptyState('暂无数据'); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
let html = ''; |
||||
|
landingPages.forEach((page, index) => { |
||||
|
// 计算序号(当前页-1)*每页条数 + 索引+1 |
||||
|
const serialNumber = (currentPage - 1) * pageSize + index + 1; |
||||
|
|
||||
|
html += ` |
||||
|
<tr> |
||||
|
<td>${serialNumber}</td> |
||||
|
<td>${escapeHtml(page.title || '')}</td> |
||||
|
<td>${escapeHtml(page.desc || '无简介')}</td> |
||||
|
<td>${formatDate(page.activityTime || '')}</td> |
||||
|
<td>${formatDate(page.updateTime || '')}</td> |
||||
|
<td> |
||||
|
<a class="action-btn">编辑</a> |
||||
|
<a href="adminDetail.html" class="action-btn">详情</a> |
||||
|
</td> |
||||
|
</tr> |
||||
|
`; |
||||
|
}); |
||||
|
|
||||
|
tableBody.innerHTML = html; |
||||
|
} |
||||
|
|
||||
|
// 渲染空状态 |
||||
|
function renderEmptyState(message) { |
||||
|
tableBody.innerHTML = ` |
||||
|
<tr> |
||||
|
<td colspan="6" 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; |
||||
|
|
||||
|
// 生成页码列表(显示当前页前后2页) |
||||
|
const pageNumbers = []; |
||||
|
let startPage = Math.max(1, currentPage - 2); |
||||
|
let endPage = Math.min(totalPages, currentPage + 2); |
||||
|
|
||||
|
// 确保至少显示5个页码(如果总页数够的话) |
||||
|
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('请输入有效的页码'); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 工具函数:格式化日期 |
||||
|
function formatDate(dateString) { |
||||
|
if (!dateString) return ''; |
||||
|
const date = new Date(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> |
</html> |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue