1426 lines
44 KiB
PHP
1426 lines
44 KiB
PHP
|
|
<?php
|
|||
|
|
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
|
|||
|
|
/**
|
|||
|
|
* 发展历史插件后台管理面板
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
// 获取配置
|
|||
|
|
$options = Typecho_Widget::widget('Widget_Options');
|
|||
|
|
$config = $options->plugin('DevelopmentHistory');
|
|||
|
|
|
|||
|
|
// 引入Action类
|
|||
|
|
require_once __DIR__ . '/Action.php';
|
|||
|
|
$action = new DevelopmentHistory_Action();
|
|||
|
|
|
|||
|
|
// 获取当前页码
|
|||
|
|
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
|
|||
|
|
$perPage = isset($config->perPage) ? intval($config->perPage) : 10;
|
|||
|
|
|
|||
|
|
// 处理表单提交
|
|||
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||
|
|
// 处理新增 - 关键:处理完后不重定向,刷新当前页面
|
|||
|
|
// 在manage-panel.php中修改发布处理部分(第29-38行):
|
|||
|
|
// 处理新增
|
|||
|
|
if (!empty($_POST['content'])) {
|
|||
|
|
try {
|
|||
|
|
$action->addHistory(array(
|
|||
|
|
'content' => $_POST['content'],
|
|||
|
|
'event_date' => $_POST['event_date'],
|
|||
|
|
'post_cids' => isset($_POST['post_cids']) ? $_POST['post_cids'] : ''
|
|||
|
|
));
|
|||
|
|
|
|||
|
|
$successMsg = '记录添加成功!';
|
|||
|
|
// 添加滚动到列表的锚点
|
|||
|
|
header('Location: ' . $_SERVER['PHP_SELF'] . '?panel=DevelopmentHistory/manage-panel.php&page=' . $page . '#history-list-anchor');
|
|||
|
|
exit;
|
|||
|
|
} catch (Exception $e) {
|
|||
|
|
// 如果数据已保存但出现错误,只显示警告
|
|||
|
|
$successMsg = '记录已添加,但出现警告:' . htmlspecialchars($e->getMessage());
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 处理批量删除
|
|||
|
|
if (!empty($_POST['delete_ids']) && is_array($_POST['delete_ids'])) {
|
|||
|
|
$action->deleteHistories($_POST['delete_ids']);
|
|||
|
|
$successMsg = '删除成功!';
|
|||
|
|
// 添加滚动到列表的锚点
|
|||
|
|
header('Location: ' . $_SERVER['PHP_SELF'] . '?panel=DevelopmentHistory/manage-panel.php&page=' . $page . '#history-list-anchor');
|
|||
|
|
exit;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 处理编辑
|
|||
|
|
if (!empty($_POST['edit_id'])) {
|
|||
|
|
$action->updateHistory(array(
|
|||
|
|
'edit_id' => $_POST['edit_id'],
|
|||
|
|
'edit_content' => $_POST['edit_content'],
|
|||
|
|
'edit_event_date' => $_POST['edit_event_date'],
|
|||
|
|
'edit_post_cids' => isset($_POST['edit_post_cids']) ? $_POST['edit_post_cids'] : ''
|
|||
|
|
));
|
|||
|
|
$successMsg = '记录更新成功!';
|
|||
|
|
// 添加滚动到列表的锚点
|
|||
|
|
header('Location: ' . $_SERVER['PHP_SELF'] . '?panel=DevelopmentHistory/manage-panel.php&page=' . $page . '#history-list-anchor');
|
|||
|
|
exit;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 处理导出
|
|||
|
|
if (isset($_POST['export'])) {
|
|||
|
|
$exportContent = $action->exportData();
|
|||
|
|
$filename = 'development_history_' . date('Ymd_His') . '.txt';
|
|||
|
|
|
|||
|
|
header('Content-Type: text/plain');
|
|||
|
|
header('Content-Disposition: attachment; filename="' . $filename . '"');
|
|||
|
|
header('Content-Length: ' . strlen($exportContent));
|
|||
|
|
echo $exportContent;
|
|||
|
|
exit;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 处理导入
|
|||
|
|
if (isset($_POST['import']) && !empty($_FILES['import_file']['tmp_name'])) {
|
|||
|
|
$fileContent = file_get_contents($_FILES['import_file']['tmp_name']);
|
|||
|
|
$result = $action->importData($fileContent);
|
|||
|
|
|
|||
|
|
if ($result['imported'] > 0) {
|
|||
|
|
$successMsg = '导入成功!成功导入 ' . $result['imported'] . ' 条记录';
|
|||
|
|
if ($result['failed'] > 0) {
|
|||
|
|
$successMsg .= ',失败 ' . $result['failed'] . ' 条记录';
|
|||
|
|
|
|||
|
|
// 显示失败原因(限制显示前10条)
|
|||
|
|
if (!empty($result['fail_reasons'])) {
|
|||
|
|
$errorDetails = '<strong>失败原因:</strong><br>';
|
|||
|
|
$displayCount = min(10, count($result['fail_reasons']));
|
|||
|
|
for ($i = 0; $i < $displayCount; $i++) {
|
|||
|
|
$errorDetails .= htmlspecialchars($result['fail_reasons'][$i]) . '<br>';
|
|||
|
|
}
|
|||
|
|
if (count($result['fail_reasons']) > 10) {
|
|||
|
|
$errorDetails .= '...还有 ' . (count($result['fail_reasons']) - 10) . ' 条失败记录';
|
|||
|
|
}
|
|||
|
|
$errorMsg = $errorDetails;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
$errorMsg = '导入失败!未找到可导入的记录或格式不正确';
|
|||
|
|
if (!empty($result['fail_reasons'])) {
|
|||
|
|
$errorMsg .= '<br><strong>失败原因:</strong><br>';
|
|||
|
|
$displayCount = min(10, count($result['fail_reasons']));
|
|||
|
|
for ($i = 0; $i < $displayCount; $i++) {
|
|||
|
|
$errorMsg .= htmlspecialchars($result['fail_reasons'][$i]) . '<br>';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取记录
|
|||
|
|
$histories = $action->getHistories($page, $perPage);
|
|||
|
|
$total = $action->getTotalCount();
|
|||
|
|
$totalPages = ceil($total / $perPage);
|
|||
|
|
?>
|
|||
|
|
|
|||
|
|
<?php include 'header.php'; ?>
|
|||
|
|
<?php include 'menu.php'; ?>
|
|||
|
|
|
|||
|
|
<style>
|
|||
|
|
/* 基础重置 */
|
|||
|
|
* {
|
|||
|
|
box-sizing: border-box;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 浮动消息提示 */
|
|||
|
|
.floating-message {
|
|||
|
|
position: fixed;
|
|||
|
|
top: 20px;
|
|||
|
|
right: 20px;
|
|||
|
|
z-index: 9999;
|
|||
|
|
max-width: 350px;
|
|||
|
|
opacity: 0;
|
|||
|
|
transform: translateY(-20px);
|
|||
|
|
transition: opacity 0.3s, transform 0.3s;
|
|||
|
|
padding: 14px 20px;
|
|||
|
|
border-radius: 6px;
|
|||
|
|
border: 1px solid transparent;
|
|||
|
|
font-size: 14px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.floating-message.show {
|
|||
|
|
opacity: 1;
|
|||
|
|
transform: translateY(0);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.floating-message.success {
|
|||
|
|
color: #0f5132;
|
|||
|
|
background-color: #d1e7dd;
|
|||
|
|
border-color: #badbcc;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.floating-message.error {
|
|||
|
|
color: #842029;
|
|||
|
|
background-color: #f8d7da;
|
|||
|
|
border-color: #f5c2c7;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 表格样式 */
|
|||
|
|
.data-table {
|
|||
|
|
width: 100%;
|
|||
|
|
border-collapse: separate;
|
|||
|
|
border-spacing: 0;
|
|||
|
|
margin: 0px 0;
|
|||
|
|
font-size: 14px;
|
|||
|
|
background-color: #fff;
|
|||
|
|
border-radius: 6px;
|
|||
|
|
overflow: hidden;
|
|||
|
|
table-layout: fixed;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.data-table th,
|
|||
|
|
.data-table td {
|
|||
|
|
padding: 14px 12px;
|
|||
|
|
border-bottom: 1px solid #f0f0f0;
|
|||
|
|
text-align: left;
|
|||
|
|
vertical-align: middle;
|
|||
|
|
line-height: 1.4;
|
|||
|
|
white-space: nowrap;
|
|||
|
|
overflow: hidden;
|
|||
|
|
text-overflow: ellipsis;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.data-table th {
|
|||
|
|
background-color: #fafbfc;
|
|||
|
|
font-weight: 600;
|
|||
|
|
color: #333;
|
|||
|
|
border-bottom: 2px solid #e0e0e0;
|
|||
|
|
font-size: 14px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.data-table tbody tr:hover {
|
|||
|
|
background-color: #f8f9fa;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.data-table tbody tr:last-child td {
|
|||
|
|
border-bottom: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 复选框列 */
|
|||
|
|
.data-table th:first-child,
|
|||
|
|
.data-table td:first-child {
|
|||
|
|
text-align: center;
|
|||
|
|
width: 50px;
|
|||
|
|
min-width: 50px;
|
|||
|
|
max-width: 50px;
|
|||
|
|
padding: 14px 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.data-table th:first-child input[type="checkbox"],
|
|||
|
|
.data-table td:first-child input[type="checkbox"] {
|
|||
|
|
margin: 0;
|
|||
|
|
vertical-align: middle;
|
|||
|
|
width: 16px;
|
|||
|
|
height: 16px;
|
|||
|
|
cursor: pointer;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* ID列样式 - 固定宽度 */
|
|||
|
|
.data-table th:nth-child(2),
|
|||
|
|
.data-table td:nth-child(2) {
|
|||
|
|
text-align: center;
|
|||
|
|
width: 70px;
|
|||
|
|
min-width: 70px;
|
|||
|
|
max-width: 70px;
|
|||
|
|
font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
|
|||
|
|
font-size: 13px;
|
|||
|
|
color: #666;
|
|||
|
|
white-space: nowrap;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 内容列 - 最大宽度,不换行 */
|
|||
|
|
.data-table th:nth-child(3),
|
|||
|
|
.data-table td:nth-child(3) {
|
|||
|
|
width: auto;
|
|||
|
|
min-width: 250px;
|
|||
|
|
max-width: none;
|
|||
|
|
white-space: nowrap;
|
|||
|
|
overflow: hidden;
|
|||
|
|
text-overflow: ellipsis;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 事件时间列 - 固定宽度,不换行 */
|
|||
|
|
.data-table th:nth-child(4),
|
|||
|
|
.data-table td:nth-child(4) {
|
|||
|
|
text-align: center;
|
|||
|
|
width: 110px;
|
|||
|
|
min-width: 110px;
|
|||
|
|
max-width: 110px;
|
|||
|
|
font-size: 13px;
|
|||
|
|
white-space: nowrap;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 发布时间列 - 固定宽度,不换行 - 修复:改为黑色 */
|
|||
|
|
.data-table th:nth-child(5),
|
|||
|
|
.data-table td:nth-child(5) {
|
|||
|
|
text-align: center;
|
|||
|
|
width: 110px;
|
|||
|
|
min-width: 110px;
|
|||
|
|
max-width: 110px;
|
|||
|
|
font-size: 13px;
|
|||
|
|
white-space: nowrap;
|
|||
|
|
color: #000; /* 改为黑色 */
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 文章关联列 - 固定宽度 */
|
|||
|
|
.data-table th:nth-child(6),
|
|||
|
|
.data-table td:nth-child(6) {
|
|||
|
|
text-align: left; /* 改为左对齐,以便显示多个CID */
|
|||
|
|
width: 180px; /* 增加宽度以便显示多个CID */
|
|||
|
|
min-width: 180px;
|
|||
|
|
max-width: 180px;
|
|||
|
|
font-size: 13px;
|
|||
|
|
white-space: normal; /* 允许多行显示 */
|
|||
|
|
overflow: visible; /* 允许内容溢出 */
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 操作列样式 */
|
|||
|
|
.data-table th:last-child,
|
|||
|
|
.data-table td:last-child {
|
|||
|
|
text-align: center;
|
|||
|
|
width: 80px;
|
|||
|
|
min-width: 80px;
|
|||
|
|
max-width: 80px;
|
|||
|
|
padding: 14px 8px;
|
|||
|
|
white-space: nowrap;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 日期样式 */
|
|||
|
|
.date-badge {
|
|||
|
|
display: inline-block;
|
|||
|
|
background-color: #f0f7ff;
|
|||
|
|
color: #0066cc;
|
|||
|
|
padding: 4px 10px;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
font-size: 12px;
|
|||
|
|
font-weight: 500;
|
|||
|
|
white-space: nowrap;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 发布时间样式 - 修复:改为普通黑色文字 */
|
|||
|
|
.publish-time {
|
|||
|
|
color: #000;
|
|||
|
|
font-size: 13px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* CID徽章样式 */
|
|||
|
|
.cid-badge {
|
|||
|
|
display: inline-block;
|
|||
|
|
background-color: #e8f5e8;
|
|||
|
|
color: #2e7d32;
|
|||
|
|
padding: 3px 8px;
|
|||
|
|
margin: 2px;
|
|||
|
|
border-radius: 3px;
|
|||
|
|
font-size: 11px;
|
|||
|
|
font-weight: 500;
|
|||
|
|
font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
|
|||
|
|
white-space: nowrap;
|
|||
|
|
border: 1px solid #c8e6c9;
|
|||
|
|
line-height: 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.cid-container {
|
|||
|
|
display: flex;
|
|||
|
|
flex-wrap: wrap; /* 允许多行显示 */
|
|||
|
|
gap: 3px;
|
|||
|
|
max-height: 60px;
|
|||
|
|
overflow-y: auto;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.no-cid {
|
|||
|
|
color: #999;
|
|||
|
|
font-size: 12px;
|
|||
|
|
font-style: italic;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 批量操作区域 */
|
|||
|
|
.bulk-actions-header, .bulk-actions-footer {
|
|||
|
|
padding: 15px;
|
|||
|
|
background-color: #f8f9fa;
|
|||
|
|
border: 1px solid #e0e0e0;
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
align-items: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.bulk-actions-footer {
|
|||
|
|
border-top: none;
|
|||
|
|
border-bottom: 2px solid #e0e0e0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 模态框样式 */
|
|||
|
|
.modal {
|
|||
|
|
display: none;
|
|||
|
|
position: fixed;
|
|||
|
|
z-index: 1000;
|
|||
|
|
left: 0;
|
|||
|
|
top: 0;
|
|||
|
|
width: 100%;
|
|||
|
|
height: 100%;
|
|||
|
|
background-color: rgba(0,0,0,0.5);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.modal-content {
|
|||
|
|
background-color: #fff;
|
|||
|
|
margin: 8% auto;
|
|||
|
|
padding: 0;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
width: 85%;
|
|||
|
|
max-width: 500px;
|
|||
|
|
box-shadow: 0 5px 25px rgba(0,0,0,0.15);
|
|||
|
|
position: relative;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.close {
|
|||
|
|
position: absolute;
|
|||
|
|
right: 20px;
|
|||
|
|
top: 18px;
|
|||
|
|
font-size: 22px;
|
|||
|
|
cursor: pointer;
|
|||
|
|
color: #999;
|
|||
|
|
background: none;
|
|||
|
|
border: none;
|
|||
|
|
padding: 0;
|
|||
|
|
width: 24px;
|
|||
|
|
height: 24px;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.close:hover {
|
|||
|
|
color: #333;
|
|||
|
|
background-color: #f5f5f5;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.modal-header {
|
|||
|
|
margin: 0;
|
|||
|
|
padding: 20px 25px;
|
|||
|
|
border-bottom: 1px solid #e0e0e0;
|
|||
|
|
background-color: #fafbfc;
|
|||
|
|
border-radius: 8px 8px 0 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.modal-header h3 {
|
|||
|
|
margin: 0;
|
|||
|
|
color: #333;
|
|||
|
|
font-size: 16px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.modal-content form {
|
|||
|
|
padding: 25px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 分页样式 */
|
|||
|
|
.pagination {
|
|||
|
|
margin-top: 25px;
|
|||
|
|
margin-bottom:25px;
|
|||
|
|
text-align: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.pagination ul {
|
|||
|
|
list-style: none;
|
|||
|
|
padding: 0;
|
|||
|
|
margin: 0;
|
|||
|
|
display: inline-flex;
|
|||
|
|
gap: 4px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.pagination li {
|
|||
|
|
display: inline;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.pagination a, .pagination span {
|
|||
|
|
display: inline-flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
padding: 8px 12px;
|
|||
|
|
background-color: #fff;
|
|||
|
|
color: #495057;
|
|||
|
|
text-decoration: none;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
border: 1px solid #dee2e6;
|
|||
|
|
min-width: 36px;
|
|||
|
|
height: 36px;
|
|||
|
|
font-size: 13px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.pagination a:hover {
|
|||
|
|
background-color: #f8f9fa;
|
|||
|
|
border-color: #dee2e6;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.pagination .active a, .pagination .active span {
|
|||
|
|
background-color: #007bff;
|
|||
|
|
color: white;
|
|||
|
|
border-color: #007bff;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 消息提示 */
|
|||
|
|
.message {
|
|||
|
|
padding: 14px 20px;
|
|||
|
|
margin: 18px 0;
|
|||
|
|
border-radius: 6px;
|
|||
|
|
border: 1px solid transparent;
|
|||
|
|
font-size: 14px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.message.success {
|
|||
|
|
color: #0f5132;
|
|||
|
|
background-color: #d1e7dd;
|
|||
|
|
border-color: #badbcc;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.message.error {
|
|||
|
|
color: #842029;
|
|||
|
|
background-color: #f8d7da;
|
|||
|
|
border-color: #f5c2c7;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 表单样式 */
|
|||
|
|
.form-section {
|
|||
|
|
background: #fff;
|
|||
|
|
padding: 25px;
|
|||
|
|
margin-bottom: 22px;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
border: 1px solid #e0e0e0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-section h2 {
|
|||
|
|
margin-top: 0;
|
|||
|
|
margin-bottom: 22px;
|
|||
|
|
padding-bottom: 16px;
|
|||
|
|
border-bottom: 1px solid #f0f0f0;
|
|||
|
|
color: #333;
|
|||
|
|
font-size: 18px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-group {
|
|||
|
|
margin-bottom: 20px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-group label {
|
|||
|
|
display: block;
|
|||
|
|
margin-bottom: 8px;
|
|||
|
|
font-weight: 500;
|
|||
|
|
color: #555;
|
|||
|
|
font-size: 14px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-control {
|
|||
|
|
width: 100%;
|
|||
|
|
padding: 10px 14px;
|
|||
|
|
border: 1px solid #ced4da;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
font-size: 14px;
|
|||
|
|
color: #333;
|
|||
|
|
background-color: #fff;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-control:focus {
|
|||
|
|
outline: none;
|
|||
|
|
border-color: #80bdff;
|
|||
|
|
box-shadow: 0 0 0 0.2rem rgba(0,123,255,.15);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
textarea.form-control {
|
|||
|
|
resize: vertical;
|
|||
|
|
min-height: 100px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.description {
|
|||
|
|
font-size: 12px;
|
|||
|
|
color: #6c757d;
|
|||
|
|
margin-top: 6px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 文章关联输入框样式 */
|
|||
|
|
.post-cids-input-wrapper {
|
|||
|
|
position: relative;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.post-cids-input {
|
|||
|
|
width: 100%;
|
|||
|
|
padding: 10px 12px;
|
|||
|
|
border: 1px solid #ced4da;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
font-size: 14px;
|
|||
|
|
color: #333;
|
|||
|
|
background-color: #fff;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.post-cids-input:focus {
|
|||
|
|
outline: none;
|
|||
|
|
border-color: #80bdff;
|
|||
|
|
box-shadow: 0 0 0 0.2rem rgba(0,123,255,.15);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.post-cids-display {
|
|||
|
|
margin-top: 8px;
|
|||
|
|
font-size: 12px;
|
|||
|
|
color: #6c757d;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 按钮样式 */
|
|||
|
|
.btn {
|
|||
|
|
display: inline-flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
padding: 10px 18px;
|
|||
|
|
font-size: 14px;
|
|||
|
|
font-weight: 500;
|
|||
|
|
text-align: center;
|
|||
|
|
cursor: pointer;
|
|||
|
|
border: 1px solid transparent;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
height: 40px;
|
|||
|
|
min-width: 80px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn-primary {
|
|||
|
|
color: #fff;
|
|||
|
|
background-color: #007bff;
|
|||
|
|
border-color: #007bff;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn-primary:hover {
|
|||
|
|
background-color: #0069d9;
|
|||
|
|
border-color: #0062cc;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn-secondary {
|
|||
|
|
color: #fff;
|
|||
|
|
background-color: #6c757d;
|
|||
|
|
border-color: #6c757d;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn-secondary:hover {
|
|||
|
|
background-color: #5a6268;
|
|||
|
|
border-color: #545b62;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn-danger {
|
|||
|
|
color: #fff;
|
|||
|
|
background-color: #dc3545;
|
|||
|
|
border-color: #dc3545;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn-danger:hover {
|
|||
|
|
background-color: #c82333;
|
|||
|
|
border-color: #bd2130;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn-success {
|
|||
|
|
color: #fff;
|
|||
|
|
background-color: #28a745;
|
|||
|
|
border-color: #28a745;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn-success:hover {
|
|||
|
|
background-color: #218838;
|
|||
|
|
border-color: #1e7e34;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn-info {
|
|||
|
|
color: #fff;
|
|||
|
|
background-color: #17a2b8;
|
|||
|
|
border-color: #17a2b8;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn-info:hover {
|
|||
|
|
background-color: #138496;
|
|||
|
|
border-color: #117a8b;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn-sm {
|
|||
|
|
padding: 6px 12px;
|
|||
|
|
font-size: 12px;
|
|||
|
|
height: 32px;
|
|||
|
|
min-width: 60px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn-group {
|
|||
|
|
display: flex;
|
|||
|
|
gap: 10px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 主容器样式 */
|
|||
|
|
.main {
|
|||
|
|
padding: 25px 0;
|
|||
|
|
min-height: calc(100vh - 60px);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.body.container {
|
|||
|
|
max-width: 1200px;
|
|||
|
|
margin: 0 auto;
|
|||
|
|
padding: 0 20px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.typecho-page-title {
|
|||
|
|
margin-bottom: 25px;
|
|||
|
|
padding-bottom: 18px;
|
|||
|
|
border-bottom: 1px solid #e0e0e0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.typecho-page-title h1 {
|
|||
|
|
margin: 0;
|
|||
|
|
color: #333;
|
|||
|
|
font-size: 24px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 全宽布局 */
|
|||
|
|
.typecho-page-main {
|
|||
|
|
display: block;
|
|||
|
|
width: 100%;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.table-section {
|
|||
|
|
background: #fff;
|
|||
|
|
padding: 0;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
border: 1px solid #e0e0e0;
|
|||
|
|
margin-bottom: 22px;
|
|||
|
|
overflow: hidden;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.table-section h2 {
|
|||
|
|
margin: 0;
|
|||
|
|
padding: 20px 25px;
|
|||
|
|
border-bottom: 1px solid #f0f0f0;
|
|||
|
|
color: #333;
|
|||
|
|
font-size: 18px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 统计信息盒子 */
|
|||
|
|
.stats-box {
|
|||
|
|
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
|
|||
|
|
padding: 22px;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
margin-bottom: 22px;
|
|||
|
|
border: 1px solid #dee2e6;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stats-box h3 {
|
|||
|
|
margin-top: 0;
|
|||
|
|
margin-bottom: 18px;
|
|||
|
|
color: #495057;
|
|||
|
|
font-size: 16px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stats-grid {
|
|||
|
|
display: grid;
|
|||
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|||
|
|
gap: 15px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stat-item {
|
|||
|
|
background: white;
|
|||
|
|
padding: 18px;
|
|||
|
|
border-radius: 6px;
|
|||
|
|
border-left: 4px solid #007bff;
|
|||
|
|
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stat-item .label {
|
|||
|
|
font-size: 12px;
|
|||
|
|
color: #6c757d;
|
|||
|
|
margin-bottom: 6px;
|
|||
|
|
text-transform: uppercase;
|
|||
|
|
letter-spacing: 0.5px;
|
|||
|
|
font-weight: 500;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stat-item .value {
|
|||
|
|
font-size: 20px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
color: #343a40;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 导入导出样式 */
|
|||
|
|
.import-export-section {
|
|||
|
|
margin-bottom: 22px;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.import-export-grid {
|
|||
|
|
display: grid;
|
|||
|
|
grid-template-columns: 1fr 1fr;
|
|||
|
|
gap: 22px;
|
|||
|
|
margin-bottom: 22px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.import-box, .export-box {
|
|||
|
|
background: #f8f9fa;
|
|||
|
|
padding: 22px;
|
|||
|
|
border-radius: 6px;
|
|||
|
|
border: 1px solid #dee2e6;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.import-box h4, .export-box h4 {
|
|||
|
|
margin-top: 0;
|
|||
|
|
margin-bottom: 16px;
|
|||
|
|
color: #495057;
|
|||
|
|
font-size: 15px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.format-example {
|
|||
|
|
background: #fff;
|
|||
|
|
padding: 16px;
|
|||
|
|
border-radius: 6px;
|
|||
|
|
border-left: 3px solid #007bff;
|
|||
|
|
margin-top: 18px;
|
|||
|
|
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
|
|||
|
|
font-size: 13px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.format-example pre {
|
|||
|
|
margin: 0;
|
|||
|
|
white-space: pre-wrap;
|
|||
|
|
font-size: 12px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.format-example code {
|
|||
|
|
color: #e83e8c;
|
|||
|
|
font-family: inherit;
|
|||
|
|
font-size: 12px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.format-example strong {
|
|||
|
|
color: #333;
|
|||
|
|
font-weight: 600;
|
|||
|
|
margin-bottom: 8px;
|
|||
|
|
display: block;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 锚点定位样式 */
|
|||
|
|
.anchor-target {
|
|||
|
|
scroll-margin-top: 20px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 响应式设计 */
|
|||
|
|
@media (max-width: 768px) {
|
|||
|
|
.body.container {
|
|||
|
|
padding: 0 15px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.import-export-grid {
|
|||
|
|
grid-template-columns: 1fr;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.bulk-actions-header, .bulk-actions-footer {
|
|||
|
|
flex-direction: column;
|
|||
|
|
gap: 10px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.floating-message {
|
|||
|
|
left: 20px;
|
|||
|
|
right: 20px;
|
|||
|
|
max-width: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 响应式调整关联文章列 */
|
|||
|
|
.data-table th:nth-child(6),
|
|||
|
|
.data-table td:nth-child(6) {
|
|||
|
|
width: 140px;
|
|||
|
|
min-width: 140px;
|
|||
|
|
max-width: 140px;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</style>
|
|||
|
|
|
|||
|
|
<div class="main">
|
|||
|
|
<div class="body container">
|
|||
|
|
|
|||
|
|
<!-- 浮动消息提示 -->
|
|||
|
|
<?php if (isset($successMsg)): ?>
|
|||
|
|
<div id="floatingMessage" class="floating-message success show">
|
|||
|
|
<div style="display: flex; justify-content: space-between; align-items: flex-start;">
|
|||
|
|
<div style="flex-grow: 1;">
|
|||
|
|
<strong>✓ 成功</strong>
|
|||
|
|
<div style="margin-top: 5px;"><?php echo $successMsg; ?></div>
|
|||
|
|
</div>
|
|||
|
|
<button type="button" onclick="hideMessage()" style="background: none; border: none; color: inherit; font-size: 18px; cursor: pointer; margin-left: 10px; padding: 0 5px;">×</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
|
|||
|
|
<?php if (isset($errorMsg)): ?>
|
|||
|
|
<div id="floatingMessage" class="floating-message error show">
|
|||
|
|
<div style="display: flex; justify-content: space-between; align-items: flex-start;">
|
|||
|
|
<div style="flex-grow: 1;">
|
|||
|
|
<strong>✗ 错误</strong>
|
|||
|
|
<div style="margin-top: 5px;"><?php echo $errorMsg; ?></div>
|
|||
|
|
</div>
|
|||
|
|
<button type="button" onclick="hideMessage()" style="background: none; border: none; color: inherit; font-size: 18px; cursor: pointer; margin-left: 10px; padding: 0 5px;">×</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
|
|||
|
|
<div class="typecho-page-main" role="main">
|
|||
|
|
<!-- 统计信息 -->
|
|||
|
|
<div class="stats-box">
|
|||
|
|
<div class="stats-grid">
|
|||
|
|
<div class="stat-item">
|
|||
|
|
<div class="label">总记录数</div>
|
|||
|
|
<div class="value"><?php echo $total; ?> 条</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="stat-item">
|
|||
|
|
<div class="label">当前页数</div>
|
|||
|
|
<div class="value"><?php echo $page; ?> / <?php echo $totalPages; ?></div>
|
|||
|
|
</div>
|
|||
|
|
<div class="stat-item">
|
|||
|
|
<div class="label">每页显示</div>
|
|||
|
|
<div class="value"><?php echo $perPage; ?> 条</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 导入导出功能 -->
|
|||
|
|
<div class="import-export-section">
|
|||
|
|
<div class="import-export-grid">
|
|||
|
|
<!-- 导出功能 -->
|
|||
|
|
<div class="export-box">
|
|||
|
|
<h4>导出数据</h4>
|
|||
|
|
<p style="margin-bottom: 15px;color:#000;">导出所有历史记录为文本文件格式</p>
|
|||
|
|
<form method="post" action="">
|
|||
|
|
<input type="hidden" name="action" value="development-history">
|
|||
|
|
<button type="submit" name="export" class="btn btn-success">
|
|||
|
|
导出为 TXT 文件
|
|||
|
|
</button>
|
|||
|
|
</form>
|
|||
|
|
<div class="format-example">
|
|||
|
|
<strong>导出格式示例:</strong>
|
|||
|
|
<pre>2025.12.21 网站正式上线
|
|||
|
|
2025.12.25 新增用户注册功能
|
|||
|
|
2025.12.28 优化了网站性能</pre>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 导入功能 -->
|
|||
|
|
<div class="import-box">
|
|||
|
|
<h4>导入数据</h4>
|
|||
|
|
<p style="margin-bottom: 15px;color:#000;">从文本文件导入历史记录</p>
|
|||
|
|
<form method="post" action="" enctype="multipart/form-data">
|
|||
|
|
<input type="hidden" name="action" value="development-history">
|
|||
|
|
<div class="form-group" style="margin-bottom: 15px;">
|
|||
|
|
<label for="import_file">选择文件</label>
|
|||
|
|
<input type="file" id="import_file" name="import_file" class="form-control" required accept=".txt,.text">
|
|||
|
|
<p class="description">支持 .txt 文本文件格式</p>
|
|||
|
|
</div>
|
|||
|
|
<button type="submit" name="import" class="btn btn-info">
|
|||
|
|
导入 TXT 文件
|
|||
|
|
</button>
|
|||
|
|
</form>
|
|||
|
|
<div class="format-example">
|
|||
|
|
<strong>导入格式要求:</strong>
|
|||
|
|
<pre>每行一条记录,格式为:<code>日期 内容</code>
|
|||
|
|
日期格式:YYYY.M[M].D[D](支持单数字月份和日期)
|
|||
|
|
例如:<code>2023.12.9 网站更新</code>
|
|||
|
|
例如:<code>2023.8.8 新功能上线</code></pre>
|
|||
|
|
<p style="margin-top: 10px; font-size: 12px; color: #666;">
|
|||
|
|
提示:请确保文件编码为 UTF-8,每行一条记录
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 发布表单 -->
|
|||
|
|
<div class="form-section">
|
|||
|
|
<h2>发布新历史</h2>
|
|||
|
|
<!-- 修复:表单action设为空字符串,提交到当前页面 -->
|
|||
|
|
<form method="post" action="" id="publishForm">
|
|||
|
|
<div class="form-group">
|
|||
|
|
<label for="content">内容 *</label>
|
|||
|
|
<textarea id="content" name="content" class="form-control" rows="4" required
|
|||
|
|
placeholder="请输入详细网站发展历史内容"></textarea>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="form-group">
|
|||
|
|
<label for="event_date">日期 *</label>
|
|||
|
|
<div class="btn-group">
|
|||
|
|
<input type="date" id="event_date" name="event_date" class="form-control"
|
|||
|
|
value="<?php echo date('Y-m-d'); ?>" required>
|
|||
|
|
<button type="button" class="btn btn-secondary" onclick="document.getElementById('event_date').value = '<?php echo date('Y-m-d'); ?>'">
|
|||
|
|
今天
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="form-group">
|
|||
|
|
<label for="post_cids">关联文章CID(可选)</label>
|
|||
|
|
<div class="post-cids-input-wrapper">
|
|||
|
|
<input type="text" id="post_cids" name="post_cids" class="form-control post-cids-input"
|
|||
|
|
placeholder="输入文章CID,多个用逗号分隔,如:123,456">
|
|||
|
|
</div>
|
|||
|
|
<p class="description post-cids-display">
|
|||
|
|
可关联多个文章,输入文章CID,用逗号分隔
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div style="text-align: center;">
|
|||
|
|
<button type="reset" class="btn btn-secondary" style="margin-right: 10px;">重置</button>
|
|||
|
|
<button type="submit" class="btn btn-primary" id="publishBtn">发布</button>
|
|||
|
|
</div>
|
|||
|
|
</form>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 锚点目标,用于滚动定位 -->
|
|||
|
|
<div id="history-list-anchor" class="anchor-target"></div>
|
|||
|
|
|
|||
|
|
<!-- 历史记录列表 -->
|
|||
|
|
<div class="table-section">
|
|||
|
|
<h2>历史记录列表</h2>
|
|||
|
|
|
|||
|
|
<?php if (empty($histories)): ?>
|
|||
|
|
<div class="message info" style="text-align: center; padding: 30px;">
|
|||
|
|
暂无历史记录,请先添加记录
|
|||
|
|
</div>
|
|||
|
|
<?php else: ?>
|
|||
|
|
<form method="post" action="" id="mainForm">
|
|||
|
|
<!-- 表头多选操作栏 -->
|
|||
|
|
<div class="bulk-actions-header">
|
|||
|
|
<div style="display: flex; align-items: center; gap: 15px;">
|
|||
|
|
<div style="display: flex; align-items: center;">
|
|||
|
|
<input type="checkbox" id="header-select-all" style="margin: 0; width: 16px; height: 16px;">
|
|||
|
|
<label for="header-select-all" style="color:#000;margin-left: 8px; font-weight: 500; font-size: 14px;">
|
|||
|
|
全选本页
|
|||
|
|
</label>
|
|||
|
|
</div>
|
|||
|
|
<div class="selected-info">
|
|||
|
|
<span class="description">
|
|||
|
|
已选择 <strong id="selected-count-text">0</strong> 条记录
|
|||
|
|
</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div>
|
|||
|
|
<button type="button" id="header-delete-btn" class="btn btn-danger btn-sm">
|
|||
|
|
删除选中
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<table class="data-table">
|
|||
|
|
<thead>
|
|||
|
|
<tr>
|
|||
|
|
<th>
|
|||
|
|
<input type="checkbox" id="thead-select-all" title="全选/全不选">
|
|||
|
|
</th>
|
|||
|
|
<th>ID</th>
|
|||
|
|
<th>内容</th>
|
|||
|
|
<th>历史时间</th>
|
|||
|
|
<th>发布时间</th>
|
|||
|
|
<th>文章关联</th>
|
|||
|
|
<th>操作</th>
|
|||
|
|
</tr>
|
|||
|
|
</thead>
|
|||
|
|
<tbody>
|
|||
|
|
<?php foreach ($histories as $history):
|
|||
|
|
$cids = !empty($history['post_cids']) ? explode(',', $history['post_cids']) : array();
|
|||
|
|
?>
|
|||
|
|
<tr>
|
|||
|
|
<td>
|
|||
|
|
<input type="checkbox" name="delete_ids[]" value="<?php echo $history['id']; ?>" class="row-checkbox">
|
|||
|
|
</td>
|
|||
|
|
<td>
|
|||
|
|
<span class="history-id">#<?php echo $history['id']; ?></span>
|
|||
|
|
</td>
|
|||
|
|
<td title="<?php echo htmlspecialchars($history['content']); ?>">
|
|||
|
|
<div style="font-size: 13px; color: #333; line-height: 1.4; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
|
|||
|
|
<?php echo htmlspecialchars($history['content']); ?>
|
|||
|
|
</div>
|
|||
|
|
</td>
|
|||
|
|
<td>
|
|||
|
|
<span class="date-badge">
|
|||
|
|
<?php echo date('Y-m-d', strtotime($history['event_date'])); ?>
|
|||
|
|
</span>
|
|||
|
|
</td>
|
|||
|
|
<td>
|
|||
|
|
<!-- 修复:发布时间改为黑色普通文字 -->
|
|||
|
|
<span class="publish-time">
|
|||
|
|
<?php echo date('Y-m-d', strtotime($history['created'])); ?>
|
|||
|
|
</span>
|
|||
|
|
</td>
|
|||
|
|
<td>
|
|||
|
|
<?php if (!empty($cids)): ?>
|
|||
|
|
<div class="cid-container">
|
|||
|
|
<?php foreach ($cids as $cid):
|
|||
|
|
$cid = trim($cid);
|
|||
|
|
if (!empty($cid)): ?>
|
|||
|
|
<span class="cid-badge" title="文章CID: <?php echo $cid; ?>">
|
|||
|
|
<?php echo $cid; ?>
|
|||
|
|
</span>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
<?php endforeach; ?>
|
|||
|
|
</div>
|
|||
|
|
<?php else: ?>
|
|||
|
|
<span class="no-cid">-</span>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
</td>
|
|||
|
|
<td>
|
|||
|
|
<button type="button" class="btn btn-sm btn-primary edit-btn"
|
|||
|
|
data-history='<?php echo htmlspecialchars(json_encode($history), ENT_QUOTES, 'UTF-8'); ?>'
|
|||
|
|
style="padding: 4px 10px; font-size: 12px;">
|
|||
|
|
编辑
|
|||
|
|
</button>
|
|||
|
|
</td>
|
|||
|
|
</tr>
|
|||
|
|
<?php endforeach; ?>
|
|||
|
|
</tbody>
|
|||
|
|
</table>
|
|||
|
|
|
|||
|
|
<!-- 表尾多选操作栏 -->
|
|||
|
|
<div class="bulk-actions-footer">
|
|||
|
|
<div style="display: flex; align-items: center; gap: 15px;">
|
|||
|
|
<div style="display: flex; align-items: center;">
|
|||
|
|
<input type="checkbox" id="footer-select-all" style="margin: 0; width: 16px; height: 16px;">
|
|||
|
|
<label for="footer-select-all" style="color:#000;margin-left: 8px; font-weight: 500; font-size: 14px;">
|
|||
|
|
全选本页
|
|||
|
|
</label>
|
|||
|
|
</div>
|
|||
|
|
<div class="selected-info">
|
|||
|
|
<span class="description">
|
|||
|
|
已选择 <strong id="footer-selected-count">0</strong> 条记录
|
|||
|
|
</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div>
|
|||
|
|
<button type="button" id="footer-delete-btn" class="btn btn-danger btn-sm">
|
|||
|
|
删除选中
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 隐藏的提交按钮 -->
|
|||
|
|
<input type="submit" id="hidden-submit" style="display: none;">
|
|||
|
|
</form>
|
|||
|
|
|
|||
|
|
<!-- 分页 -->
|
|||
|
|
<?php if ($totalPages > 1): ?>
|
|||
|
|
<div class="pagination">
|
|||
|
|
<ul>
|
|||
|
|
<?php if ($page > 1): ?>
|
|||
|
|
<li><a href="?panel=DevelopmentHistory/manage-panel.php&page=<?php echo $page-1; ?>#history-list-anchor">« 上一页</a></li>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
|
|||
|
|
<?php
|
|||
|
|
$start = max(1, $page - 2);
|
|||
|
|
$end = min($totalPages, $page + 2);
|
|||
|
|
|
|||
|
|
if ($start > 1) {
|
|||
|
|
echo '<li><a href="?panel=DevelopmentHistory/manage-panel.php&page=1#history-list-anchor">1</a></li>';
|
|||
|
|
if ($start > 2) echo '<li><span>...</span></li>';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for ($i = $start; $i <= $end; $i++): ?>
|
|||
|
|
<li <?php if ($i == $page) echo 'class="active"'; ?>>
|
|||
|
|
<a href="?panel=DevelopmentHistory/manage-panel.php&page=<?php echo $i; ?>#history-list-anchor"><?php echo $i; ?></a>
|
|||
|
|
</li>
|
|||
|
|
<?php endfor; ?>
|
|||
|
|
|
|||
|
|
<?php if ($end < $totalPages): ?>
|
|||
|
|
<?php if ($end < $totalPages - 1): ?>
|
|||
|
|
<li><span>...</span></li>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
<li><a href="?panel=DevelopmentHistory/manage-panel.php&page=<?php echo $totalPages; ?>#history-list-anchor"><?php echo $totalPages; ?></a></li>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
|
|||
|
|
<?php if ($page < $totalPages): ?>
|
|||
|
|
<li><a href="?panel=DevelopmentHistory/manage-panel.php&page=<?php echo $page+1; ?>#history-list-anchor">下一页 »</a></li>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
</ul>
|
|||
|
|
</div>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 编辑模态框 -->
|
|||
|
|
<div id="editModal" class="modal" style="display:none;">
|
|||
|
|
<div class="modal-content">
|
|||
|
|
<button type="button" class="close" onclick="closeModal()">×</button>
|
|||
|
|
<div class="modal-header">
|
|||
|
|
<h3>编辑历史记录</h3>
|
|||
|
|
</div>
|
|||
|
|
<form id="editForm" method="post" action="">
|
|||
|
|
<div class="form-group">
|
|||
|
|
<label for="edit_content">内容 *</label>
|
|||
|
|
<textarea id="edit_content" name="edit_content" class="form-control" rows="5" required></textarea>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="form-group">
|
|||
|
|
<label for="edit_event_date">日期 *</label>
|
|||
|
|
<input type="date" id="edit_event_date" name="edit_event_date" class="form-control" required>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="form-group">
|
|||
|
|
<label for="edit_post_cids">关联文章CID(可选)</label>
|
|||
|
|
<div class="post-cids-input-wrapper">
|
|||
|
|
<input type="text" id="edit_post_cids" name="edit_post_cids" class="form-control post-cids-input"
|
|||
|
|
placeholder="输入文章CID,多个用逗号分隔,如:123,456">
|
|||
|
|
</div>
|
|||
|
|
<p class="description post-cids-display">
|
|||
|
|
可关联多个文章,输入文章CID,用逗号分隔
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<input type="hidden" name="edit_id" id="edit_id">
|
|||
|
|
|
|||
|
|
<div style="text-align:right;margin-top:20px; padding-top: 15px; border-top: 1px solid #eee;">
|
|||
|
|
<button type="button" class="btn btn-secondary" onclick="closeModal()" style="margin-right:10px;">
|
|||
|
|
取消
|
|||
|
|
</button>
|
|||
|
|
<button type="submit" class="btn btn-primary">保存修改</button>
|
|||
|
|
</div>
|
|||
|
|
</form>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<script>
|
|||
|
|
// 页面加载后自动滚动到锚点
|
|||
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|||
|
|
if (window.location.hash === '#history-list-anchor') {
|
|||
|
|
setTimeout(function() {
|
|||
|
|
var anchor = document.getElementById('history-list-anchor');
|
|||
|
|
if (anchor) {
|
|||
|
|
anchor.scrollIntoView({ behavior: 'smooth' });
|
|||
|
|
}
|
|||
|
|
}, 100);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 自动隐藏浮动消息
|
|||
|
|
var floatingMessage = document.getElementById('floatingMessage');
|
|||
|
|
if (floatingMessage) {
|
|||
|
|
setTimeout(function() {
|
|||
|
|
hideMessage();
|
|||
|
|
}, 5000);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 隐藏浮动消息
|
|||
|
|
function hideMessage() {
|
|||
|
|
var message = document.getElementById('floatingMessage');
|
|||
|
|
if (message) {
|
|||
|
|
message.classList.remove('show');
|
|||
|
|
setTimeout(function() {
|
|||
|
|
if (message.parentNode) {
|
|||
|
|
message.parentNode.removeChild(message);
|
|||
|
|
}
|
|||
|
|
}, 300);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新选中数量函数
|
|||
|
|
function updateSelectedCount() {
|
|||
|
|
var checkboxes = document.querySelectorAll('.row-checkbox:checked');
|
|||
|
|
var count = checkboxes.length;
|
|||
|
|
|
|||
|
|
document.getElementById('selected-count-text').textContent = count;
|
|||
|
|
document.getElementById('footer-selected-count').textContent = count;
|
|||
|
|
|
|||
|
|
var headerBtn = document.getElementById('header-delete-btn');
|
|||
|
|
var footerBtn = document.getElementById('footer-delete-btn');
|
|||
|
|
|
|||
|
|
if (count > 0) {
|
|||
|
|
headerBtn.textContent = '删除选中(' + count + ')';
|
|||
|
|
footerBtn.textContent = '删除选中(' + count + ')';
|
|||
|
|
} else {
|
|||
|
|
headerBtn.textContent = '删除选中';
|
|||
|
|
footerBtn.textContent = '删除选中';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var allCheckboxes = document.querySelectorAll('.row-checkbox');
|
|||
|
|
var headerCheckboxes = document.querySelectorAll('#header-select-all, #thead-select-all, #footer-select-all');
|
|||
|
|
|
|||
|
|
if (allCheckboxes.length > 0) {
|
|||
|
|
var allChecked = count === allCheckboxes.length;
|
|||
|
|
var someChecked = count > 0 && count < allCheckboxes.length;
|
|||
|
|
|
|||
|
|
headerCheckboxes.forEach(function(checkbox) {
|
|||
|
|
checkbox.checked = allChecked;
|
|||
|
|
checkbox.indeterminate = someChecked;
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return count;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 全选/全不选功能
|
|||
|
|
function setupSelectAll(selector, targetSelector) {
|
|||
|
|
var selectAll = document.querySelector(selector);
|
|||
|
|
if (selectAll) {
|
|||
|
|
selectAll.addEventListener('change', function(e) {
|
|||
|
|
var checkboxes = document.querySelectorAll(targetSelector);
|
|||
|
|
checkboxes.forEach(function(checkbox) {
|
|||
|
|
checkbox.checked = e.target.checked;
|
|||
|
|
});
|
|||
|
|
updateSelectedCount();
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 为行复选框添加事件监听
|
|||
|
|
function setupRowCheckboxes() {
|
|||
|
|
var checkboxes = document.querySelectorAll('.row-checkbox');
|
|||
|
|
checkboxes.forEach(function(checkbox) {
|
|||
|
|
checkbox.addEventListener('change', updateSelectedCount);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 统一的删除确认函数
|
|||
|
|
function confirmDelete() {
|
|||
|
|
var checkboxes = document.querySelectorAll('.row-checkbox:checked');
|
|||
|
|
var count = checkboxes.length;
|
|||
|
|
|
|||
|
|
if (count === 0) {
|
|||
|
|
alert('请先选择要删除的记录!');
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var confirmMsg = '确定要删除选中的 ' + count + ' 条记录吗?此操作不可恢复!';
|
|||
|
|
|
|||
|
|
if (!confirm(confirmMsg)) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
document.getElementById('hidden-submit').click();
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 初始化选择功能
|
|||
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|||
|
|
setupSelectAll('#header-select-all', '.row-checkbox');
|
|||
|
|
setupSelectAll('#thead-select-all', '.row-checkbox');
|
|||
|
|
setupSelectAll('#footer-select-all', '.row-checkbox');
|
|||
|
|
|
|||
|
|
setupRowCheckboxes();
|
|||
|
|
|
|||
|
|
var headerDeleteBtn = document.getElementById('header-delete-btn');
|
|||
|
|
var footerDeleteBtn = document.getElementById('footer-delete-btn');
|
|||
|
|
|
|||
|
|
if (headerDeleteBtn) {
|
|||
|
|
headerDeleteBtn.addEventListener('click', function(e) {
|
|||
|
|
e.preventDefault();
|
|||
|
|
confirmDelete();
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (footerDeleteBtn) {
|
|||
|
|
footerDeleteBtn.addEventListener('click', function(e) {
|
|||
|
|
e.preventDefault();
|
|||
|
|
confirmDelete();
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
updateSelectedCount();
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 编辑历史记录
|
|||
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|||
|
|
var editButtons = document.querySelectorAll('.edit-btn');
|
|||
|
|
editButtons.forEach(function(button) {
|
|||
|
|
button.addEventListener('click', function() {
|
|||
|
|
try {
|
|||
|
|
var historyData = JSON.parse(this.getAttribute('data-history'));
|
|||
|
|
editHistory(historyData);
|
|||
|
|
} catch (e) {
|
|||
|
|
console.error('解析历史记录数据失败:', e);
|
|||
|
|
alert('编辑失败:数据格式错误');
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
function editHistory(history) {
|
|||
|
|
document.getElementById('edit_id').value = history.id;
|
|||
|
|
document.getElementById('edit_content').value = history.content;
|
|||
|
|
|
|||
|
|
var eventDate = new Date(history.event_date);
|
|||
|
|
var year = eventDate.getFullYear();
|
|||
|
|
var month = (eventDate.getMonth() + 1).toString().padStart(2, '0');
|
|||
|
|
var day = eventDate.getDate().toString().padStart(2, '0');
|
|||
|
|
|
|||
|
|
document.getElementById('edit_event_date').value = year + '-' + month + '-' + day;
|
|||
|
|
document.getElementById('edit_post_cids').value = history.post_cids || '';
|
|||
|
|
document.getElementById('editModal').style.display = 'block';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 关闭模态框
|
|||
|
|
function closeModal() {
|
|||
|
|
document.getElementById('editModal').style.display = 'none';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 点击模态框外部关闭
|
|||
|
|
window.onclick = function(event) {
|
|||
|
|
var modal = document.getElementById('editModal');
|
|||
|
|
if (event.target == modal) {
|
|||
|
|
closeModal();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 表单验证
|
|||
|
|
document.querySelectorAll('form').forEach(function(form) {
|
|||
|
|
if (form.querySelector('button[name="export"]')) return;
|
|||
|
|
|
|||
|
|
form.addEventListener('submit', function(e) {
|
|||
|
|
var requiredFields = form.querySelectorAll('[required]');
|
|||
|
|
var valid = true;
|
|||
|
|
|
|||
|
|
requiredFields.forEach(function(field) {
|
|||
|
|
if (!field.value.trim()) {
|
|||
|
|
valid = false;
|
|||
|
|
field.style.borderColor = '#dc3545';
|
|||
|
|
field.style.boxShadow = '0 0 0 0.2rem rgba(220,53,69,.25)';
|
|||
|
|
} else {
|
|||
|
|
field.style.borderColor = '#ced4da';
|
|||
|
|
field.style.boxShadow = 'none';
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (!valid) {
|
|||
|
|
e.preventDefault();
|
|||
|
|
alert('请填写所有必填字段!');
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 导入文件验证
|
|||
|
|
var importForm = document.querySelector('form[enctype="multipart/form-data"]');
|
|||
|
|
if (importForm) {
|
|||
|
|
importForm.addEventListener('submit', function(e) {
|
|||
|
|
var fileInput = document.getElementById('import_file');
|
|||
|
|
if (fileInput.files.length === 0) {
|
|||
|
|
e.preventDefault();
|
|||
|
|
alert('请选择要导入的文件!');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var file = fileInput.files[0];
|
|||
|
|
if (file.size > 5 * 1024 * 1024) {
|
|||
|
|
e.preventDefault();
|
|||
|
|
alert('文件大小不能超过5MB!');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!file.name.toLowerCase().endsWith('.txt')) {
|
|||
|
|
if (!confirm('文件不是.txt格式,确定要导入吗?')) {
|
|||
|
|
e.preventDefault();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<?php include 'footer.php'; ?>
|