国内市场双十一活动仓库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

583 lines
15 KiB

3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>落地页管理系统</title>
  7. <style>
  8. /* 基础样式 */
  9. * {
  10. margin: 0;
  11. padding: 0;
  12. box-sizing: border-box;
  13. font-family: "Microsoft YaHei", sans-serif;
  14. }
  15. body {
  16. background-color: #f5f7fa;
  17. color: #333;
  18. }
  19. .container {
  20. display: flex;
  21. min-height: 100vh;
  22. }
  23. /* 侧边栏样式 */
  24. .sidebar {
  25. width: 220px;
  26. background-color: #2c3e50;
  27. color: #fff;
  28. padding: 20px 0;
  29. }
  30. .sidebar-logo {
  31. text-align: center;
  32. padding: 0 20px 20px;
  33. border-bottom: 1px solid rgba(255,255,255,0.1);
  34. margin-bottom: 20px;
  35. }
  36. .sidebar-logo h2 {
  37. font-size: 18px;
  38. margin-top: 10px;
  39. }
  40. .sidebar-menu {
  41. list-style: none;
  42. }
  43. .sidebar-menu li a {
  44. display: flex;
  45. align-items: center;
  46. padding: 12px 20px;
  47. color: rgba(255,255,255,0.8);
  48. text-decoration: none;
  49. transition: all 0.3s;
  50. }
  51. .sidebar-menu li a:hover,
  52. .sidebar-menu li a.active {
  53. background-color: #34495e;
  54. color: #fff;
  55. }
  56. .sidebar-menu li a i {
  57. margin-right: 10px;
  58. font-size: 16px;
  59. }
  60. /* 主内容区样式 */
  61. .main-content {
  62. flex: 1;
  63. padding: 20px;
  64. overflow-y: auto;
  65. }
  66. .header {
  67. display: flex;
  68. justify-content: space-between;
  69. align-items: center;
  70. margin-bottom: 20px;
  71. padding-bottom: 15px;
  72. border-bottom: 1px solid #eee;
  73. }
  74. .header-title {
  75. font-size: 20px;
  76. font-weight: 600;
  77. }
  78. .user-info {
  79. display: flex;
  80. align-items: center;
  81. }
  82. .user-info img {
  83. width: 36px;
  84. height: 36px;
  85. border-radius: 50%;
  86. margin-right: 10px;
  87. }
  88. /* 功能区样式 */
  89. .function-bar {
  90. display: flex;
  91. justify-content: space-between;
  92. align-items: center;
  93. margin-bottom: 20px;
  94. }
  95. .search-box {
  96. display: flex;
  97. }
  98. .search-box input {
  99. width: 300px;
  100. padding: 8px 15px;
  101. border: 1px solid #ddd;
  102. border-radius: 4px 0 0 4px;
  103. outline: none;
  104. }
  105. .search-box button {
  106. background-color: #3498db;
  107. color: #fff;
  108. border: none;
  109. padding: 0 15px;
  110. border-radius: 0 4px 4px 0;
  111. cursor: pointer;
  112. }
  113. .add-btn {
  114. background-color: #2ecc71;
  115. color: #fff;
  116. border: none;
  117. padding: 8px 20px;
  118. border-radius: 4px;
  119. cursor: pointer;
  120. display: flex;
  121. align-items: center;
  122. }
  123. .add-btn i {
  124. margin-right: 5px;
  125. }
  126. .add-btn:hover {
  127. background-color: #27ae60;
  128. }
  129. /* 表格样式 */
  130. .table-container {
  131. background-color: #fff;
  132. border-radius: 4px;
  133. box-shadow: 0 2px 12px rgba(0,0,0,0.1);
  134. overflow: hidden;
  135. }
  136. .data-table {
  137. width: 100%;
  138. border-collapse: collapse;
  139. }
  140. .data-table th,
  141. .data-table td {
  142. padding: 12px 15px;
  143. text-align: left;
  144. border-bottom: 1px solid #f0f0f0;
  145. }
  146. .data-table th {
  147. background-color: #f9fafb;
  148. font-weight: 600;
  149. color: #666;
  150. }
  151. .data-table tbody tr:hover {
  152. background-color: #f5f7fa;
  153. }
  154. .status {
  155. display: inline-block;
  156. padding: 3px 8px;
  157. border-radius: 4px;
  158. font-size: 12px;
  159. }
  160. .status-active {
  161. background-color: rgba(46, 204, 113, 0.1);
  162. color: #2ecc71;
  163. }
  164. .status-inactive {
  165. background-color: rgba(231, 76, 60, 0.1);
  166. color: #e74c3c;
  167. }
  168. .action-btn {
  169. margin-right: 8px;
  170. color: #3498db;
  171. text-decoration: none;
  172. font-size: 14px;
  173. cursor: pointer;
  174. }
  175. .action-btn.delete {
  176. color: #e74c3c;
  177. }
  178. .action-btn:hover {
  179. text-decoration: underline;
  180. }
  181. /* 分页样式 */
  182. .pagination {
  183. display: flex;
  184. justify-content: space-between;
  185. align-items: center;
  186. padding: 15px;
  187. border-top: 1px solid #f0f0f0;
  188. }
  189. .pagination-info {
  190. color: #666;
  191. font-size: 14px;
  192. }
  193. .pagination-list {
  194. display: flex;
  195. list-style: none;
  196. padding: 0;
  197. margin: 0;
  198. }
  199. .pagination-list li {
  200. margin: 0 2px;
  201. }
  202. /* 核心:确保a和span统一尺寸和居中 */
  203. .pagination-list a,
  204. .pagination-list span {
  205. display: inline-block;
  206. width: 45px;
  207. height: 45px;
  208. line-height: 45px;
  209. text-align: center;
  210. border: 1px solid #ddd;
  211. border-radius: 4px;
  212. text-decoration: none;
  213. color: #333;
  214. font-size: 14px;
  215. box-sizing: border-box;
  216. }
  217. .pagination-list a {
  218. cursor: pointer;
  219. }
  220. /* 激活和hover状态 */
  221. .pagination-list a:hover,
  222. .pagination-list a.active {
  223. background-color: #3498db;
  224. color: #fff;
  225. border-color: #3498db;
  226. }
  227. /* 禁用状态(span标签) */
  228. .pagination-list span {
  229. background-color: #f5f5f5;
  230. color: #999;
  231. cursor: not-allowed;
  232. }
  233. /* 跳转区域样式 */
  234. .pagination-jump {
  235. display: flex;
  236. align-items: center;
  237. font-size: 14px;
  238. gap: 5px;
  239. }
  240. .pagination-jump input {
  241. width: 50px;
  242. height: 32px;
  243. margin: 0 5px;
  244. padding: 0 5px;
  245. border: 1px solid #ddd;
  246. border-radius: 4px;
  247. text-align: center;
  248. box-sizing: border-box;
  249. }
  250. .pagination-jump button {
  251. padding: 6px 12px;
  252. border: 1px solid #ddd;
  253. border-radius: 4px;
  254. background-color: #fff;
  255. cursor: pointer;
  256. height: 32px;
  257. box-sizing: border-box;
  258. }
  259. /* 加载中样式 */
  260. .loading {
  261. text-align: center;
  262. padding: 50px 0;
  263. color: #666;
  264. }
  265. .loading i {
  266. font-size: 24px;
  267. margin-bottom: 10px;
  268. animation: spin 1s linear infinite;
  269. }
  270. @keyframes spin {
  271. from { transform: rotate(0deg); }
  272. to { transform: rotate(360deg); }
  273. }
  274. </style>
  275. <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
  276. </head>
  277. <body>
  278. <div class="container">
  279. <!-- 侧边栏 -->
  280. <div class="sidebar">
  281. <div class="sidebar-logo">
  282. <i class="fa fa-line-chart" style="font-size: 24px;"></i>
  283. <h2>后台管理系统</h2>
  284. </div>
  285. <ul class="sidebar-menu">
  286. <li><a href="#" class="active"><i class="fa fa-file-text-o"></i> 落地页管理</a></li>
  287. </ul>
  288. </div>
  289. <!-- 主内容区 -->
  290. <div class="main-content">
  291. <div class="header">
  292. <div class="header-title">落地页管理</div>
  293. </div>
  294. <!-- 功能区 -->
  295. <div class="function-bar">
  296. <button class="add-btn">
  297. <i class="fa fa-plus"></i> 新增落地页
  298. </button>
  299. </div>
  300. <!-- 数据表格 -->
  301. <div class="table-container">
  302. <table class="data-table">
  303. <thead>
  304. <tr>
  305. <th>序号</th>
  306. <th>活动名称</th>
  307. <th>活动简介</th>
  308. <th>活动时间</th>
  309. <th>编辑时间</th>
  310. <th>操作</th>
  311. </tr>
  312. </thead>
  313. <tbody id="tableBody">
  314. <tr>
  315. <td colspan="6" class="loading">
  316. <i class="fa fa-spinner"></i>
  317. <p>加载中...</p>
  318. </td>
  319. </tr>
  320. </tbody>
  321. </table>
  322. <!-- 分页控件 -->
  323. <div class="pagination" id="paginationContainer">
  324. </div>
  325. </div>
  326. </div>
  327. </div>
  328. <script type="module">
  329. // 导入API函数
  330. import { getLandingListApi } from './src/api/member.js';
  331. // DOM元素
  332. const tableBody = document.getElementById('tableBody');
  333. const paginationContainer = document.getElementById('paginationContainer');
  334. // 分页参数
  335. let currentPage = 1;
  336. const pageSize = 10;
  337. let totalCount = 0;
  338. let totalPages = 0;
  339. // 模拟数据
  340. const mockData = Array.from({ length: 15 }, (_, index) => ({
  341. id: index + 1,
  342. title: `双十${index + 1}促销活动`,
  343. desc: `这是第${index + 1}个落地页活动,主要推广新品上市和优惠折扣`,
  344. activityTime: `2023-11-${(index % 20) + 10} 00:00:00 - 2023-11-${(index % 20) + 20} 23:59:59`,
  345. updateTime: `2023-11-${(index % 20) + 5} ${10 + index}:${index * 5}:00`
  346. }));
  347. // 初始化页面
  348. async function initPage() {
  349. const urlParams = new URLSearchParams(window.location.search);
  350. const pageParam = urlParams.get('page');
  351. if (pageParam && !isNaN(pageParam)) {
  352. currentPage = parseInt(pageParam);
  353. }
  354. // 获取列表数据
  355. // await fetchLandingList();
  356. await fetchMockLandingList();
  357. }
  358. // 新增:使用模拟数据获取列表(替代真实API调用)
  359. async function fetchMockLandingList() {
  360. // 模拟网络延迟
  361. await new Promise(resolve => setTimeout(resolve, 800));
  362. // 计算分页数据
  363. totalCount = mockData.length;
  364. totalPages = Math.ceil(totalCount / pageSize);
  365. // 截取当前页的数据(数组切片)
  366. const startIndex = (currentPage - 1) * pageSize;
  367. const endIndex = startIndex + pageSize;
  368. const currentPageData = mockData.slice(startIndex, endIndex);
  369. // 渲染表格数据
  370. renderTable(currentPageData);
  371. // 渲染分页控件
  372. renderPagination();
  373. }
  374. // 从后端获取落地页列表
  375. async function fetchLandingList() {
  376. try {
  377. // 调用API接口,传递分页参数
  378. const response = await getLandingListApi({
  379. page: currentPage,
  380. size: pageSize
  381. });
  382. if (response.code === 200 && response.data) {
  383. const { list: landingPages, total } = response.data;
  384. totalCount = total;
  385. totalPages = Math.ceil(totalCount / pageSize);
  386. // 渲染表格数据
  387. renderTable(landingPages);
  388. // 渲染分页控件
  389. renderPagination();
  390. } else {
  391. renderEmptyState('获取数据失败');
  392. }
  393. } catch (error) {
  394. console.error('获取列表失败:', error);
  395. renderEmptyState('网络错误,请重试');
  396. }
  397. }
  398. // 渲染表格数据
  399. function renderTable(landingPages) {
  400. if (!landingPages || landingPages.length === 0) {
  401. renderEmptyState('暂无数据');
  402. return;
  403. }
  404. let html = '';
  405. landingPages.forEach((page, index) => {
  406. // 计算序号(当前页-1)*每页条数 + 索引+1
  407. const serialNumber = (currentPage - 1) * pageSize + index + 1;
  408. html += `
  409. <tr>
  410. <td>${serialNumber}</td>
  411. <td>${escapeHtml(page.title || '')}</td>
  412. <td>${escapeHtml(page.desc || '无简介')}</td>
  413. <td>${formatDate(page.activityTime || '')}</td>
  414. <td>${formatDate(page.updateTime || '')}</td>
  415. <td>
  416. <a class="action-btn">编辑</a>
  417. <a href="adminDetail.html?id=${page.id}" class="action-btn">详情</a>
  418. </td>
  419. </tr>
  420. `;
  421. });
  422. tableBody.innerHTML = html;
  423. }
  424. // 渲染空状态
  425. function renderEmptyState(message) {
  426. tableBody.innerHTML = `
  427. <tr>
  428. <td colspan="6" style="text-align: center; padding: 30px;">
  429. <i class="fa fa-inbox" style="font-size: 24px; color: #ddd; margin-bottom: 10px;"></i>
  430. <p>${message}</p>
  431. </td>
  432. </tr>
  433. `;
  434. // 清空分页
  435. paginationContainer.innerHTML = '';
  436. }
  437. // 渲染分页控件
  438. function renderPagination() {
  439. if (totalCount === 0) return;
  440. // 生成页码列表(显示当前页前后2页)
  441. const pageNumbers = [];
  442. let startPage = Math.max(1, currentPage - 2);
  443. let endPage = Math.min(totalPages, currentPage + 2);
  444. // 确保至少显示5个页码(如果总页数够的话)
  445. if (endPage - startPage < 4 && totalPages >= 5) {
  446. if (startPage === 1) {
  447. endPage = 5;
  448. } else if (endPage === totalPages) {
  449. startPage = totalPages - 4;
  450. }
  451. }
  452. for (let i = startPage; i <= endPage; i++) {
  453. pageNumbers.push(i);
  454. }
  455. // 分页HTML
  456. const html = `
  457. <div class="pagination-info">
  458. 共 ${totalCount} 条记录,当前第 ${currentPage} / ${totalPages} 页
  459. </div>
  460. <ul class="pagination-list">
  461. <li>
  462. ${currentPage === 1
  463. ? `<span>首页</span>`
  464. : `<a href="?page=1">首页</a>`
  465. }
  466. </li>
  467. <li>
  468. ${currentPage > 1
  469. ? `<a href="?page=${currentPage - 1}">上一页</a>`
  470. : `<span>上一页</span>`
  471. }
  472. </li>
  473. ${pageNumbers.map(num => `
  474. <li>
  475. <a href="?page=${num}" class="${num === currentPage ? 'active' : ''}">
  476. ${num}
  477. </a>
  478. </li>
  479. `).join('')}
  480. <li>
  481. ${currentPage < totalPages
  482. ? `<a href="?page=${currentPage + 1}">下一页</a>`
  483. : `<span>下一页</span>`
  484. }
  485. </li>
  486. <li>
  487. ${currentPage === totalPages
  488. ? `<span>尾页</span>`
  489. : `<a href="?page=${totalPages}">尾页</a>`
  490. }
  491. </li>
  492. </ul>
  493. <div class="pagination-jump">
  494. <span>跳至</span>
  495. <input type="number" min="1" max="${totalPages}" value="${currentPage}" id="jumpPageInput">
  496. <span></span>
  497. <button id="jumpBtn">确定</button>
  498. </div>
  499. `;
  500. paginationContainer.innerHTML = html;
  501. // 绑定跳转事件
  502. document.getElementById('jumpBtn').addEventListener('click', handlePageJump);
  503. document.getElementById('jumpPageInput').addEventListener('keypress', (e) => {
  504. if (e.key === 'Enter') handlePageJump();
  505. });
  506. }
  507. // 处理页码跳转
  508. function handlePageJump() {
  509. const pageInput = document.getElementById('jumpPageInput');
  510. const page = parseInt(pageInput.value);
  511. if (page && !isNaN(page) && page >= 1 && page <= totalPages && page !== currentPage) {
  512. window.location.href = `?page=${page}`;
  513. } else {
  514. alert('请输入有效的页码');
  515. }
  516. }
  517. // 工具函数:格式化日期
  518. function formatDate(dateString) {
  519. if (!dateString) return '';
  520. const date = new Date(dateString);
  521. return date.toLocaleString('zh-CN', {
  522. year: 'numeric',
  523. month: '2-digit',
  524. day: '2-digit',
  525. hour: '2-digit',
  526. minute: '2-digit',
  527. second: '2-digit'
  528. }).replace(/\//g, '-');
  529. }
  530. // 工具函数:HTML转义(防止XSS)
  531. function escapeHtml(str) {
  532. if (!str) return '';
  533. return str
  534. .replace(/&/g, '&amp;')
  535. .replace(/</g, '&lt;')
  536. .replace(/>/g, '&gt;')
  537. .replace(/"/g, '&quot;')
  538. .replace(/'/g, '&#039;');
  539. }
  540. // 页面加载时初始化
  541. window.addEventListener('DOMContentLoaded', initPage);
  542. </script>
  543. </body>
  544. </html>