Files

1371 lines
45 KiB
PHP
Raw Permalink Normal View History

2026-02-23 17:23:48 +08:00
<?php
/**
* 发展历史插件
*
* @package DevelopmentHistory
* @author 石头厝
* @version 1.1.0
* @link https://www.shitoucuo.com/
*/
class DevelopmentHistory_Plugin implements Typecho_Plugin_Interface
{
/**
* 激活插件
*
* @return string
* @throws Typecho_Plugin_Exception
*/
public static function activate()
{
// 创建数据表
$db = Typecho_Db::get();
$prefix = $db->getPrefix();
$tableName = $prefix . 'development_history';
// 检查主表是否存在
$tables = $db->fetchAll($db->query("SHOW TABLES LIKE '{$tableName}'"));
if (empty($tables)) {
// 表不存在创建新表不带title字段
$sql = "CREATE TABLE `{$tableName}` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
`content` TEXT NOT NULL COMMENT '内容',
`event_date` DATE NOT NULL COMMENT '事件日期',
`post_cids` VARCHAR(255) DEFAULT '' COMMENT '关联文章CID多个用逗号分隔',
`created` DATETIME NOT NULL COMMENT '创建时间',
`modified` DATETIME NOT NULL COMMENT '修改时间',
`status` TINYINT(1) DEFAULT 1 COMMENT '状态1正常'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='发展历史记录表';";
try {
$db->query($sql);
} catch (Typecho_Db_Exception $e) {
throw new Typecho_Plugin_Exception('创建数据表失败: ' . $e->getMessage());
}
} else {
// 表已存在检查是否有title字段如果有则删除
$columns = $db->fetchAll($db->query("SHOW COLUMNS FROM `{$tableName}`"));
$hasTitle = false;
foreach ($columns as $column) {
if ($column['Field'] == 'title') {
$hasTitle = true;
break;
}
}
if ($hasTitle) {
// 删除title字段
try {
$db->query("ALTER TABLE `{$tableName}` DROP COLUMN `title`");
} catch (Typecho_Db_Exception $e) {
// 如果删除字段失败,可能是表结构问题,继续执行
}
}
// 检查是否有post_cids字段如果没有则添加
$hasPostCids = false;
foreach ($columns as $column) {
if ($column['Field'] == 'post_cids') {
$hasPostCids = true;
break;
}
}
if (!$hasPostCids) {
try {
$db->query("ALTER TABLE `{$tableName}` ADD `post_cids` VARCHAR(255) DEFAULT '' COMMENT '关联文章CID多个用逗号分隔' AFTER `event_date`");
} catch (Typecho_Db_Exception $e) {
// 如果添加字段失败,继续执行
}
}
}
// 添加管理菜单
Helper::addPanel(3, 'DevelopmentHistory/manage-panel.php', '发展历史', '发展历史管理', 'administrator');
return _t('发展历史插件已激活,请到插件设置中进行配置');
}
/**
* 禁用插件
*
* @return string
*/
public static function deactivate()
{
Helper::removePanel(3, 'DevelopmentHistory/manage-panel.php');
return _t('发展历史插件已禁用');
}
/**
* 插件配置面板
*
* @param Typecho_Widget_Helper_Form $form
*/
public static function config(Typecho_Widget_Helper_Form $form)
{
echo '<h2>发展历史插件配置</h2>';
echo '<p>版本1.1.0 | 作者:您的名字 | <a href="https://您的网站.com" target="_blank">访问官网</a></p>';
echo '<hr>';
// 每页显示数量
$perPage = new Typecho_Widget_Helper_Form_Element_Text(
'perPage',
NULL,
'10',
_t('后台每页显示数量'),
_t('后台管理页面每页显示的历史记录数量默认10')
);
$perPage->input->setAttribute('class', 'mini');
$form->addInput($perPage->addRule('isInteger', _t('请输入整数')));
// 前端每页显示数量
$frontPerPage = new Typecho_Widget_Helper_Form_Element_Text(
'frontPerPage',
NULL,
'20',
_t('主题调用每页显示数量'),
_t('在主题中调用时每页显示的历史记录数量默认20')
);
$frontPerPage->input->setAttribute('class', 'mini');
$form->addInput($frontPerPage->addRule('isInteger', _t('请输入整数')));
// 时间格式
$dateFormat = new Typecho_Widget_Helper_Form_Element_Text(
'dateFormat',
NULL,
'Y.m.d',
_t('日期显示格式'),
_t('PHP日期格式Y.m.d、Y-m-d、Y年m月d日等')
);
$form->addInput($dateFormat);
// 是否显示关联文章
$showRelatedPosts = new Typecho_Widget_Helper_Form_Element_Radio(
'showRelatedPosts',
array(
'1' => _t('显示'),
'0' => _t('不显示')
),
'1',
_t('显示关联文章'),
_t('是否在历史记录下方显示关联的文章链接')
);
$form->addInput($showRelatedPosts);
// 关联文章标题
$relatedPostsTitle = new Typecho_Widget_Helper_Form_Element_Text(
'relatedPostsTitle',
NULL,
'相关文章',
_t('关联文章标题'),
_t('关联文章部分的标题文本')
);
$form->addInput($relatedPostsTitle);
}
/**
* 个人用户配置
*
* @param Typecho_Widget_Helper_Form $form
*/
public static function personalConfig(Typecho_Widget_Helper_Form $form)
{
// 个人配置(如果需要)
}
/**
* 插件配置面板
*
* @param string $panel
*/
public static function configPanel($panel)
{
// 配置面板
}
/**
* 渲染方法(用于在主题模板中调用)
*/
public static function render()
{
// 获取配置
$options = Typecho_Widget::widget('Widget_Options');
$config = $options->plugin('DevelopmentHistory');
// 数据库操作
$db = Typecho_Db::get();
$prefix = $db->getPrefix();
// 获取当前页面文件名
$currentFile = basename($_SERVER['PHP_SELF']);
// 统一使用 'y' 参数
$selectedYear = 0;
$currentPage = 1;
$searchKeyword = '';
// 读取 'y' 参数
if (isset($_GET['y']) && is_numeric($_GET['y'])) {
$selectedYear = intval($_GET['y']);
}
// 保持向后兼容:如果旧链接使用了 'year' 参数,也支持
if ($selectedYear == 0 && isset($_GET['year']) && is_numeric($_GET['year'])) {
$selectedYear = intval($_GET['year']);
}
if (isset($_GET['page']) && is_numeric($_GET['page']) && $_GET['page'] > 0) {
$currentPage = intval($_GET['page']);
}
// 读取搜索关键词参数 - 正确处理URL编码
if (isset($_GET['search']) && !empty($_GET['search'])) {
$searchKeyword = trim($_GET['search']);
// 解码URL编码的参数
$searchKeyword = urldecode($searchKeyword);
}
// URL构建函数 - 正确处理编码
function buildUrl($year = 0, $page = 1, $search = '') {
$currentFile = basename($_SERVER['PHP_SELF']);
$params = array();
if ($page > 1) {
$params['page'] = $page;
}
if ($year > 0) {
$params['y'] = $year;
}
if (!empty($search)) {
// 这里使用原始搜索词http_build_query会自动编码
$params['search'] = $search;
}
if (empty($params)) {
return $currentFile;
}
return $currentFile . '?' . http_build_query($params);
}
// 年份筛选URL
function buildYearUrl($year, $search = '') {
return buildUrl($year, 1, $search);
}
$perPage = isset($config->frontPerPage) ? intval($config->frontPerPage) : 20;
$offset = ($currentPage - 1) * $perPage;
// 构建查询条件
$where = 'status = ?';
$whereParams = array(1);
if ($selectedYear > 0) {
$where .= ' AND YEAR(event_date) = ?';
$whereParams[] = $selectedYear;
}
if (!empty($searchKeyword)) {
$where .= ' AND content LIKE ?';
$whereParams[] = '%' . $searchKeyword . '%';
}
// 查询数据
$query = $db->select()
->from($prefix . 'development_history')
->where($where, ...$whereParams)
->order('id', Typecho_Db::SORT_DESC)
->limit($perPage)
->offset($offset);
$histories = $db->fetchAll($query);
// 为每条记录获取关联的文章信息 - 修复URL生成
foreach ($histories as &$history) {
if (!empty($history['post_cids'])) {
$cids = array_filter(array_map('trim', explode(',', $history['post_cids'])));
$posts = array();
foreach ($cids as $cid) {
if (is_numeric($cid)) {
$postQuery = $db->select('cid, title, slug, created')
->from($prefix . 'contents')
->where('cid = ?', intval($cid))
->where('type = ?', 'post')
->where('status = ?', 'publish')
->limit(1);
$post = $db->fetchRow($postQuery);
if ($post) {
// 修复直接使用slug变量值
if (!empty($post['slug'])) {
$post['url'] = Typecho_Common::url($post['slug'] . '.html', $options->index);
} else {
$post['url'] = Typecho_Common::url('archives/' . $post['cid'], $options->index);
}
$posts[] = $post;
}
}
}
$history['posts'] = $posts;
} else {
$history['posts'] = array();
}
}
// 获取总数
$countQuery = $db->select('COUNT(*) as count')
->from($prefix . 'development_history')
->where($where, ...$whereParams);
$totalResult = $db->fetchRow($countQuery);
$total = $totalResult['count'];
$totalPages = ceil($total / $perPage);
// 获取所有年份及记录数(考虑搜索条件)
$yearsWhere = 'status = ?';
$yearsParams = array(1);
if (!empty($searchKeyword)) {
$yearsWhere .= ' AND content LIKE ?';
$yearsParams[] = '%' . $searchKeyword . '%';
}
$yearsQuery = $db->select('YEAR(event_date) as year, COUNT(*) as count')
->from($prefix . 'development_history')
->where($yearsWhere, ...$yearsParams)
->group('YEAR(event_date)')
->order('year', Typecho_Db::SORT_DESC);
$yearsData = $db->fetchAll($yearsQuery);
// 获取全部记录数(考虑搜索条件)
$totalAllWhere = 'status = ?';
$totalAllParams = array(1);
if (!empty($searchKeyword)) {
$totalAllWhere .= ' AND content LIKE ?';
$totalAllParams[] = '%' . $searchKeyword . '%';
}
$totalAllQuery = $db->select('COUNT(*) as count')
->from($prefix . 'development_history')
->where($totalAllWhere, ...$totalAllParams);
$totalAllResult = $db->fetchRow($totalAllQuery);
$totalAll = $totalAllResult['count'];
// 输出内容
if (empty($histories) && empty($searchKeyword) && $selectedYear == 0) {
echo '<div class="development-history-empty">暂无发展历史记录</div>';
return;
}
?>
<!-- 关联文章样式 -->
<style>
.related-posts {
margin-top: 15px;
padding: 12px 15px;
background: #f8f9fa;
border-radius: 6px;
border-left: 3px solid #007bff;
}
.related-posts-title {
font-size: 13px;
font-weight: 600;
color: #495057;
margin-bottom: 8px;
display: flex;
align-items: center;
gap: 6px;
}
.related-posts ul li{
list-style: none;
}
.related-posts-list {
list-style: none;
padding: 0;
margin: 0;
}
.related-posts ul{
padding-left: 0px!important;
}
.related-post-item {
margin-bottom: 6px;
padding: 6px 10px;
background: white;
border-radius: 4px;
border: 1px solid #dee2e6;
transition: all 0.2s ease;
}
.related-post-item:last-child {
margin-bottom: 0;
}
.related-post-item:hover {
background-color: #e9ecef;
border-color: #ced4da;
}
.related-post-link {
display: flex;
align-items: center;
gap: 8px;
text-decoration: none;
color: #495057;
font-size: 13px;
}
.related-post-link:hover {
color: #007bff;
text-decoration: none;
}
/* 深色模式适配 */
.dark .related-posts {
background: #2d2d2d;
border-left-color: #dc2626;
}
.dark .related-posts-title {
color: #b0b0b0;
}
.dark .related-post-item {
background: #1e1e1e;
border-color: #444;
}
.dark .related-post-item:hover {
background-color: #3d3d3d;
border-color: #555;
}
.dark .related-post-link {
color: #b0b0b0;
}
.dark .related-post-link:hover {
color: #dc2626;
}
.dark .related-post-link:before {
color: #888;
}
</style>
<!-- 搜索框样式 - 同步优化版 -->
<style>
/* 搜索框样式 - 优化版 */
.history-search-box {
margin: 0 auto 20px;
}
.search-form {
display: flex;
flex-direction: column;
gap: 18px;
max-width: 400px;
margin: 0 auto;
}
.search-input-row {
display: flex;
gap: 10px;
width: 100%;
}
.search-input {
flex-grow: 1;
padding: 12px 16px;
border: 1px solid #dee2e6;
border-radius: 6px;
font-size: 15px;
transition: all 0.3s ease;
background: white;
color: #333;
}
.search-input:focus {
outline: none;
border-color: #007bff;
box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.1);
}
.search-input::placeholder {
color: #6c757d;
opacity: 0.8;
}
.search-button-row {
display: flex;
justify-content: center;
gap: 10px;
width: 100%;
}
.search-button {
padding: 8px 30px;
background: linear-gradient(135deg, #007bff, #0056b3);
color: white;
border: none;
border-radius: 10px;
font-size: 15px;
font-weight: 600;
cursor: pointer;
white-space: nowrap;
}
.search-button:hover {
background: linear-gradient(135deg, #0056b3, #004494);
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 123, 255, 0.3);
}
.search-button:active {
transform: translateY(0);
box-shadow: 0 1px 2px rgba(0, 123, 255, 0.2);
}
.clear-search-link {
padding: 12px 25px;
background: linear-gradient(135deg, #6c757d, #545b62);
color: white;
text-decoration: none;
border-radius: 6px;
font-size: 15px;
font-weight: 500;
transition: all 0.3s ease;
white-space: nowrap;
box-shadow: 0 2px 4px rgba(108, 117, 125, 0.2);
}
.clear-search-link:hover {
background: linear-gradient(135deg, #545b62, #3d4246);
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(108, 117, 125, 0.3);
color: white;
text-decoration: none;
}
.clear-search-link:active {
transform: translateY(0);
box-shadow: 0 1px 2px rgba(108, 117, 125, 0.2);
}
.search-results-info {
margin-top: 15px;
text-align: center;
font-size: 14px;
color: #495057;
padding: 8px 12px;
background: rgba(0, 123, 255, 0.05);
border-radius: 4px;
border-left: 3px solid #007bff;
}
.search-results-info strong {
color: #0056b3;
font-weight: 600;
}
/* 深色模式适配 */
.dark .search-input {
background: #1e1e1e;
color: #000;
border-color: #444;
}
.dark .search-input:focus {
border-color: #dc2626;
box-shadow: 0 0 0 3px rgba(77, 171, 247, 0.1);
}
.dark .search-input::placeholder {
color: #aaa;
}
.dark .search-button {
background: linear-gradient(135deg, #dc2626, #1c7ed6);
}
.dark .search-button:hover {
background: linear-gradient(135deg, #1c7ed6, #1864ab);
}
.dark .clear-search-link {
background: linear-gradient(135deg, #6c757d, #545b62);
box-shadow: 0 2px 4px rgba(108, 117, 125, 0.2);
}
.dark .clear-search-link:hover {
background: linear-gradient(135deg, #545b62, #3d4246);
box-shadow: 0 4px 8px rgba(108, 117, 125, 0.3);
}
.dark .search-results-info {
color: #e0e0e0;
background: rgba(77, 171, 247, 0.05);
border-left-color: #dc2626;
}
.dark .search-results-info strong {
color: #dc2626;
}
@media (max-width: 768px) {
.search-input-row {
flex-direction: column;
}
.search-button-row {
flex-direction: column;
align-items: stretch;
}
.search-button,
.clear-search-link {
width: 100%;
text-align: center;
}
.search-form {
gap: 15px;
}
}
@media (min-width: 769px) {
.search-button-row {
justify-content: center;
}
}
</style>
<!-- 主CSS样式 - 同步优化版 -->
<style>
.development-history-widget {
max-width: 100%;
background: #fff;
border-radius: 8px;
}
.dark .development-history-widget h3{
margin-top:0px;
}
.widget-title {
text-align: center;
margin-bottom: 20px;
color: #333;
font-size: 20px;
padding-bottom: 10px;
border-bottom: 2px solid #007bff;
transition: all 0.3s ease;
}
.dark .year-list li+li{margin-top:0em;}
.year-list li+li{margin-top:0em;}
.dark .year-filter ul{padding-left:0rem;margin-bottom:0px;}
.year-filter ul{padding-left:0rem;margin-bottom:0px;}
.year-filter {
margin:10px auto;
padding: 15px;
background: #f8f9fa;
border-radius: 6px;
border: 1px solid #e9ecef;
}
.year-filter-title {
font-size: 16px;
font-weight: 600;
color: #495057;
margin-bottom: 12px;
display: flex;
align-items: center;
gap: 8px;
}
.year-filter-title:before {
content: "📅";
font-size: 14px;
}
.year-list {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin: 0;
padding: 0;
justify-content: center;
list-style: none;
}
.year-item {
display: inline-block;
}
.year-link {
display: inline-block;
padding: 6px 14px;
background-color: #fff;
color: #495057;
text-decoration: none;
border-radius: 10px;
border: 1px solid #dee2e6;
font-size: 13px;
font-weight: 500;
transition: all 0.2s ease;
cursor: pointer;
}
.year-link:hover {
background-color: #e9ecef;
border-color: #ced4da;
transform: translateY(-1px);
}
.year-link.active {
background-color: #007bff;
color: white;
border-color: #007bff;
font-weight: 600;
}
.year-count {
margin-left: 4px;
font-size: 11px;
opacity: 0.8;
font-weight: normal;
}
.history-list {
list-style: none;
padding: 0;
margin: 0;
}
.history-item {
padding: 12px 12px;
display: flex;
align-items: flex-start;
border-radius: 10px;
transition: background-color 0.3s ease;
}
.history-item:first-child {
border-bottom: none;
margin-top: 20px;
}
.history-content p{margin-bottom:0px;}
.dark .history-content p{margin-bottom:0px;}
.history-date {
font-weight: bold;
color: #007bff;
font-size: 14px;
min-width: 90px;
flex-shrink: 0;
text-align: right;
transition: all 0.3s ease;
}
.history-content {
color: #555;
line-height: 1.6;
font-size: 14px;
word-wrap: break-word;
flex-grow: 1;
}
.history-content p {
margin: 0;
}
.history-date::after {
content: "";
color: #666;
font-weight: normal;
padding-left: 2px;
}
.development-history-empty {
text-align: center;
padding: 30px;
color: #999;
font-size: 14px;
border: 1px dashed #ddd;
border-radius: 8px;
background-color: #fafafa;
}
.history-item:last-child{margin-bottom:10px;}
.history-pagination {
text-align: center;
padding-top: 40px;
transition: all 0.3s ease;
border-top:1px solid #333;
padding-bottom:20px;
}
.pagination-container {
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
gap: 8px;
}
.pagination-info {
font-size: 13px;
color: #666;
margin: 0 10px;
flex-shrink: 0;
}
.pagination-nav {
display: flex;
align-items: center;
gap: 5px;
}
.pagination-page {
display: flex;
align-items: center;
gap: 8px;
}
.pagination-link {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 36px;
height: 36px;
padding: 0 10px;
background: #fff;
color: #333;
text-decoration: none;
border: 1px solid #ddd;
border-radius: 50px;
font-size: 14px;
transition: all 0.2s ease;
line-height: 1;
}
.pagination-link:hover {
background: #007bff;
color: white;
border-color: #007bff;
transform: translateY(-1px);
}
.pagination-link.active {
background: #007bff;
color: white;
border-color: #007bff;
font-weight: 600;
}
.pagination-link.disabled {
color: #999;
background: #f5f5f5;
border-color: #ddd;
cursor: not-allowed;
opacity: 0.6;
}
.pagination-link.disabled:hover {
background: #f5f5f5;
color: #999;
border-color: #ddd;
transform: none;
}
.pagination-ellipsis {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 36px;
height: 36px;
padding: 0 10px;
color: #666;
font-size: 14px;
}
/* 深色模式适配 */
.dark .development-history-widget {
background: #1e1e1e;
}
.dark .development-history-widget h2 {
margin-top:0px;
border-bottom-width: 0px;
}
.development-history-widget h2 {
margin-top:0px;
}
.dark .widget-title {
color: #e0e0e0;
border-bottom-color: #dc2626;
}
.dark .year-filter {
background: #2d2d2d;
border-color: #333;
}
.dark .year-filter-title {
color: #e0e0e0;
}
.dark .year-link {
background-color: #2d2d2d;
color: #e0e0e0;
border-color: #444;
}
.dark .year-link:hover {
background-color: #3d3d3d;
border-color: #555;
}
.dark .year-link.active {
background-color: #dc2626;
color: #fff;
border-color: #dc2626;
}
.dark .history-item {
border-bottom-color: #333;
}
.dark .history-item:hover {
background-color: #2d2d2d;
}
.dark .history-date {
color: #dc2626;
}
.dark .history-content {
color: #b0b0b0;
}
.dark .history-date::after {
color: #888;
}
.dark .development-history-empty {
color: #888;
border-color: #444;
background-color: #2d2d2d;
}
.dark .history-pagination {
border-top: 1px solid #333;
}
.dark .pagination-info {
color: #aaa;
}
.dark .pagination-link {
background: #2d2d2d;
color: #e0e0e0;
border-color: #444;
}
.dark .pagination-link:hover {
background: #dc2626;
color: #fff;
border-color: #dc2626;
}
.dark .pagination-link.active {
background: #dc2626;
color: #fff;
border-color: #dc2626;
}
.dark .pagination-link.disabled {
color: #666;
background: #2d2d2d;
border-color: #444;
}
.dark .pagination-link.disabled:hover {
background: #2d2d2d;
color: #666;
border-color: #444;
}
.dark .pagination-ellipsis {
color: #888;
}
@media (max-width: 768px) {
.development-history-widget {
padding: 12px;
}
.widget-title {
font-size: 18px;
}
.history-item {
padding: 10px 0;
flex-direction: column;
}
.history-date {
min-width: auto;
text-align: left;
margin-bottom: 5px;
width: 100%;
}
.history-date::after {
content: "";
}
.pagination-container {
flex-direction: column;
gap: 10px;
}
.pagination-nav {
width: 100%;
justify-content: center;
}
.pagination-page {
flex-wrap: wrap;
justify-content: center;
margin: 5px 0;
}
.year-list {
justify-content: center;
}
}
@media (max-width: 480px) {
.pagination-link {
min-width: 32px;
height: 32px;
padding: 0 8px;
font-size: 13px;
}
.pagination-ellipsis {
min-width: 32px;
height: 32px;
font-size: 13px;
}
.year-link {
padding: 5px 10px;
font-size: 12px;
}
}
</style>
<div class="development-history-widget">
<h2 class="widget-title">网站发展历史</h2>
<!-- 搜索框 -->
<div class="history-search-box">
<form method="get" action="<?php echo $currentFile; ?>" class="search-form">
<!-- 隐藏字段,保持当前年份筛选 -->
<?php if ($selectedYear > 0): ?>
<input type="hidden" name="y" value="<?php echo $selectedYear; ?>">
<?php endif; ?>
<div class="search-input-row">
<input type="text"
name="search"
class="search-input"
placeholder="输入关键词搜索历史记录..."
value="<?php echo htmlspecialchars($searchKeyword); ?>"
maxlength="100"
aria-label="搜索历史记录">
</div>
<div class="search-button-row">
<button type="submit" class="search-button">
搜索
</button>
<?php if (!empty($searchKeyword) || $selectedYear > 0): ?>
<a href="<?php echo $currentFile; ?>" class="clear-search-link">
清除筛选
</a>
<?php endif; ?>
</div>
</form>
<?php if (!empty($searchKeyword)): ?>
<div class="search-results-info">
搜索 "<strong><?php echo htmlspecialchars($searchKeyword); ?></strong>" 找到 <?php echo $total; ?> 条记录
<?php if ($selectedYear > 0): ?>
(在 <?php echo $selectedYear; ?> 年中)
<?php endif; ?>
</div>
<?php endif; ?>
</div>
<!-- 年份筛选器 -->
<?php if (!empty($yearsData)): ?>
<div class="year-filter">
<ul class="year-list">
<!-- 全部年份 -->
<li class="year-item">
<a href="<?php echo buildYearUrl(0, $searchKeyword); ?>"
class="year-link <?php echo $selectedYear == 0 ? 'active' : ''; ?>">
全部
<span class="year-count">(<?php echo $totalAll; ?>)</span>
</a>
</li>
<!-- 各年份 -->
<?php foreach ($yearsData as $yearData): ?>
<li class="year-item">
<?php
$year = $yearData['year'];
$hasData = $yearData['count'] > 0;
$url = buildYearUrl($year, $searchKeyword);
?>
<a href="<?php echo $url; ?>"
class="year-link <?php echo $selectedYear == $year ? 'active' : ''; ?>"
<?php if (!$hasData): ?>style="opacity:0.5; cursor:not-allowed;" onclick="return false;"<?php endif; ?>
title="<?php echo $hasData ? "查看{$year}年记录" : "{$year}年暂无记录"; ?>">
<?php echo $year; ?>
<span class="year-count">(<?php echo $yearData['count']; ?>)</span>
</a>
</li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
<!-- 当前筛选信息 -->
<?php if ($selectedYear > 0 && empty($searchKeyword)): ?>
<div style="text-align:center;margin-bottom: 15px; padding: 8px 12px; background: #e7f3ff; border-radius: 4px; ">
<span style="font-size: 13px; color: #0066cc;">
当前筛选:<strong><?php echo $selectedYear; ?></strong> 年
<small>(<?php echo $total; ?> 条记录)</small>
<a href="<?php echo buildYearUrl(0, $searchKeyword); ?>" style="margin-left: 10px; font-size: 12px; color: #666; text-decoration: none;">
清除筛选
</a>
</span>
</div>
<?php endif; ?>
<!-- 搜索结果为空提示 -->
<?php if (empty($histories) && (!empty($searchKeyword) || $selectedYear > 0)): ?>
<div class="development-history-empty">
<?php if (!empty($searchKeyword) && $selectedYear > 0): ?>
未找到包含 "<strong><?php echo htmlspecialchars($searchKeyword); ?></strong>" <?php echo $selectedYear; ?> 年历史记录
<?php elseif (!empty($searchKeyword)): ?>
未找到包含 "<strong><?php echo htmlspecialchars($searchKeyword); ?></strong>" 的历史记录
<?php elseif ($selectedYear > 0): ?>
<?php echo $selectedYear; ?> 年暂无历史记录
<?php endif; ?>
<div style="margin-top: 10px;">
<a href="<?php echo $currentFile; ?>" style="color: #007bff; text-decoration: none;">查看全部记录</a>
</div>
</div>
<?php endif; ?>
<!-- 历史记录列表 -->
<?php if (!empty($histories)): ?>
<div class="history-list">
<?php foreach ($histories as $history): ?>
<div class="history-item">
<div class="history-date">
<?php echo date(isset($config->dateFormat) ? $config->dateFormat : 'Y.m.d', strtotime($history['event_date'])); ?>
</div>
<div class="history-content">
<p>
<?php
$content = htmlspecialchars($history['content']);
// 如果有关键词,高亮显示
if (!empty($searchKeyword)) {
$content = preg_replace(
'/(' . preg_quote($searchKeyword, '/') . ')/i',
'<mark style="background-color: #fff3cd;color:#000; padding: 1px 3px; border-radius: 2px;">$1</mark>',
$content
);
}
echo nl2br($content);
?>
</p>
<?php
// 显示关联文章
if (isset($config->showRelatedPosts) && $config->showRelatedPosts == 1 && !empty($history['posts'])):
?>
<div class="related-posts">
<div class="related-posts-title">
<?php echo isset($config->relatedPostsTitle) ? $config->relatedPostsTitle : '相关文章'; ?>
</div>
<ul class="related-posts-list">
<?php foreach ($history['posts'] as $post): ?>
<li class="related-post-item">
<a href="<?php echo $post['url']; ?>" class="related-post-link" target="_blank">
<?php echo htmlspecialchars($post['title']); ?>
</a>
</li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
</div>
<?php if ($totalPages > 1): ?>
<div class="history-pagination">
<div class="pagination-container">
<!--<div class="pagination-info">
<?php echo $currentPage; ?> / <?php echo $totalPages; ?> 页
</div>-->
<div class="pagination-nav">
<?php if ($currentPage > 1): ?>
<!--<a href="<?php echo buildUrl($selectedYear, 1, $searchKeyword); ?>"
class="pagination-link" title="第一页">
首页
</a>-->
<a href="<?php echo buildUrl($selectedYear, $currentPage-1, $searchKeyword); ?>"
class="pagination-link" title="上一页">
上一页
</a>
<?php else: ?>
<!--<span class="pagination-link disabled" title="第一页">首页</span>-->
<span class="pagination-link disabled" title="上一页">上一页</span>
<?php endif; ?>
</div>
<div class="pagination-page">
<?php
// 显示页码逻辑
$start = max(1, $currentPage - 2);
$end = min($totalPages, $currentPage + 2);
// 显示第一页和省略号
if ($start > 1) {
echo '<a href="' . buildUrl($selectedYear, 1, $searchKeyword) . '" class="pagination-link">1</a>';
if ($start > 2) {
echo '<span class="pagination-ellipsis">...</span>';
}
}
// 显示页码
for ($i = $start; $i <= $end; $i++) {
if ($i == $currentPage) {
echo '<span class="pagination-link active">' . $i . '</span>';
} else {
echo '<a href="' . buildUrl($selectedYear, $i, $searchKeyword) . '" class="pagination-link">' . $i . '</a>';
}
}
// 显示最后页和省略号
if ($end < $totalPages) {
if ($end < $totalPages - 1) {
echo '<span class="pagination-ellipsis">...</span>';
}
echo '<a href="' . buildUrl($selectedYear, $totalPages, $searchKeyword) . '" class="pagination-link">' . $totalPages . '</a>';
}
?>
</div>
<div class="pagination-nav">
<?php if ($currentPage < $totalPages): ?>
<a href="<?php echo buildUrl($selectedYear, $currentPage+1, $searchKeyword); ?>"
class="pagination-link" title="下一页">
下一页
</a>
<!--<a href="<?php echo buildUrl($selectedYear, $totalPages, $searchKeyword); ?>"
class="pagination-link" title="最后一页">
末页
</a>-->
<?php else: ?>
<span class="pagination-link disabled" title="下一页">下一页</span>
<!--<span class="pagination-link disabled" title="最后一页">末页</span>-->
<?php endif; ?>
</div>
</div>
</div>
<?php endif; ?>
<?php endif; ?>
</div>
<script>
// 调试信息
console.log('当前URL:', window.location.href);
console.log('当前年份参数 (y):', <?php echo $selectedYear; ?>);
console.log('搜索关键词 (原始):', '<?php echo $searchKeyword; ?>');
console.log('搜索关键词 (编码):', '<?php echo urlencode($searchKeyword); ?>');
// 自动聚焦搜索框
document.addEventListener('DOMContentLoaded', function() {
var searchInput = document.querySelector('.search-input');
if (searchInput && !searchInput.value) {
searchInput.focus();
}
});
// 搜索表单提交时验证
var searchForm = document.querySelector('.search-form');
if (searchForm) {
searchForm.addEventListener('submit', function(e) {
var searchInput = this.querySelector('.search-input');
if (searchInput.value.trim() === '') {
e.preventDefault();
// 如果已经有年份筛选,保留年份参数
var yearParam = <?php echo $selectedYear > 0 ? "'?y=" . $selectedYear . "'" : "''"; ?>;
window.location.href = '<?php echo $currentFile; ?>' + yearParam;
}
});
}
// 确保年份链接正确传递搜索参数
document.addEventListener('DOMContentLoaded', function() {
var searchKeyword = '<?php echo $searchKeyword; ?>';
var yearLinks = document.querySelectorAll('.year-link[href]');
yearLinks.forEach(function(link) {
var href = link.getAttribute('href');
if (searchKeyword && href.indexOf('search=') === -1) {
// 如果链接中没有搜索参数,但当前有搜索关键词,需要添加
var separator = href.indexOf('?') === -1 ? '?' : '&';
var newHref = href + separator + 'search=' + encodeURIComponent(searchKeyword);
link.setAttribute('href', newHref);
}
});
});
// 搜索框动画效果
document.addEventListener('DOMContentLoaded', function() {
var searchInput = document.querySelector('.search-input');
if (searchInput) {
searchInput.addEventListener('focus', function() {
this.parentElement.style.transform = 'scale(1.02)';
});
searchInput.addEventListener('blur', function() {
this.parentElement.style.transform = 'scale(1)';
});
}
});
</script>
<?php
}
}