自从网易博客玩球后,改用 Typecho 写日志已经有一段时间。可能我要求比较低,一直满意。

心血来潮想加个日历插件在右侧栏上, 搜了下现有的插件emmm ...只有一个2016年的版本,功能比较简陋就真的只显示个月历而已。
眼看博客程序已经3年没有更新,估计是指望不上别人自己动手改造一个。
看了一下 Typecho的插件开发说明,嫌麻烦。还是按自己熟悉的粗暴手法来强行嫁接吧!

最终效果如图(如果没出错的话应该在页面右边已经看到实物了)

QQ截图20201010034140.png

我要实现的功能不多

  1. 要能显示月历,能前后翻月
  2. 样式要简洁,不突兀,与界面低调融合在一起
  3. 要标记有博客的日期,点上去的话要切换到当天
  4. 如果从下面按月归档的链接点进去的话,日历要跟随切换到当前月份

想了下,我用过的layDate日历插件应该能满足这个需求,也是一个已经3、4年没有更新的项目正好凑一对。
下面开干!

1.编辑外观文件 sidebar.php

进入博客后台 -> 控制台 -> 外观 -> 编辑当前外观 -> 选择 sidebar.php

在对应的位置插入以下代码(对blog程序做最小化改动,选择外部引用脚本),然后点保存文件

<!--//自定义控件:日历-->
<section class="widget">
    <h3 class="widget-title">一个土土的日历</h3>
<?php
require(dirname(__FILE__)."/../../../../blogplugins/plugin_typecho_laydate.php");
?>
</section>

大概看起来就是这样

QQ截图20201010040419.png

当中的路径根据自己的实际情况调整, 我放到博客路径同级的另一个文件夹 blogplugins 里,。
另外额外说一句,在下文的js中使用了jQuery语法, 如果博客之前没有用到的话建议这里也引用一下,也可以用 zepto 代替。

准备依赖的日历控件 layDate.js

按照layDate的文档说明放到任意路径即可,我的例子是放到了 /js/layDate.js路径下。注意有个 theme的文件夹要一起放进去不要遗漏。

插件脚本 plugin_typecho_laydate.php

不啰嗦了,直接上源码。
本例博客主程序路径在 /blog/,
插件的路径在 /blogplugins/plugin_typecho_laydate.php
日历插件的路径在 /js/laydate.js

<?php

/**
 * plugin_typecho_laydate
 * @author f77@outlook.com
 * @copyright 2020
 */

define("DB_HOST","localhost");
define("DB_PORT","3306");
define("DB_DATABASE","blog");
define("DB_USER","***账号***");
define("DB_PASS","***密码***");
include("../class/fmysql.class.php"); #mysql组件,源码原理略

$uri = $_SERVER['REQUEST_URI'];
$init_date = date("Y-m-d");//默认定位日历在今天
$ts_now = strtotime($init_date);
//分析是否日期,是否需要初始化日期
$parts = explode('/',$uri);
$p_count = count($parts);
if($p_count>=4){ //月份归档有/结尾,所以是
    //带日期blog会自动补地址栏最后的/所以还是要转换成offset计算
    if($parts[$p_count-4]=='index.php')
        $p_offset = $p_count;
    elseif($parts[$p_count-5]=='index.php')
        $p_offset = $p_count-1;
    else
        $p_offset = 0;
            
    if($p_offset){
        $str_date = $parts[$p_offset-3]."-".$parts[$p_offset-2]."-".($parts[$p_offset-1]?$parts[$p_offset-1]:'01'); //月份总览不带日期,所以定位在1号
        if($new_init_ts = strtotime($str_date)){ //返回false如果不是合法日期
            $init_date = date("Y-m-d",$new_init_ts);
        }
    }
}

//取出有日志的所有日期合集,定一个时间范围,避免数组太大
//今日取之前半年
//历史日期取前后各3个月
//因为博客程序用的是数据类型 timestamp int(10) 所以range值入乡随俗
$ts_init = strtotime($init_date);
if($ts_init<$ts_now){ //取前后各3月
    $range_s = $ts_init-3600*24*92;
    $range_e = $ts_init+3600*24*92;
}else{ //取之前6个月    
    $range_s = $ts_init-3600*24*183;
    $range_e = $ts_init+3600*24;//把今天包括进去
}
$s = new FMYSQL();
$sql = "SELECT FROM_UNIXTIME(`created`, '%Y-%m-%d') AS `blogDate` 
        FROM `typecho_contents` 
        WHERE `type` = 'post' 
        AND `created` BETWEEN {$range_s} AND {$range_e} 
        GROUP BY FROM_UNIXTIME(`created`, '%Y-%m-%d');";
if(!$dates = $s->getData($sql)) $dates = [];        
$str_arr_dates = '';//组装输出的日期
foreach($dates as $ditem){
    $str_arr_dates .= "'".$ditem["blogDate"]."',";
}
if($str_arr_dates) $str_arr_dates = substr($str_arr_dates,0,-1); //去掉末尾的,

?>
<div id="cald"></div>
<script src="/js/laydate.js"></script> <!-- laydate.js的路径 -->
<script>

//日志的mapper
var blog_dates = [<?php echo $str_arr_dates;?>];

//mDate 参数 YYYY-MM-DD 格式
function hasBlog(mDate){
    var result = false;
    $(blog_dates).each(function(n,bDate){
        if(bDate==mDate){
            console.log("date match!",bDate,mDate);
            result = true;
            return;
        }
    });
    console.log("date no match",mDate);
    return result;
}

//laydate实例
laydate.render({
    elem: '#cald' //指定元素
    ,position: 'static'
    ,theme: 'grid'
    ,calendar: true
    ,showBottom: false
    ,mark: { //标记有日志的日期,空字符串,只显示角标
        <?php
        
        $firstObj = true;
        foreach($dates as $d){
            if(!$firstObj){
                echo ","; //第一行以外其余行补个,在最前面 
            }else{
                $firstObj = false;
            }            
            echo "'".$d["blogDate"]."': ''\n";
        }        
        ?>
    }
    ,value: '<?php echo $init_date;?>' //初始日期。默认是当前日期
    ,done: function(value, date){ //点击日期交互回调
      console.log("date click:",date.year, date.month, date.date);
      //月份和日期是个位数时,需要手动补个0,保证日期格式的一致,下文同
      var mDate = date.year +'-'+ (date.month>=10?date.month:('0'+date.month.toString())) +'-'+ (date.date>=10?date.date:('0'+date.date.toString()));
      if(hasBlog(mDate)){ //跳转到当天
        //注意根据自己的博客路径调整下面的跳转URL
        location.href = "/blog/index.php/"+date.year +'/'+ (date.month>=10?date.month:('0'+date.month.toString())) +'/'+ (date.date>=10?date.date:('0'+date.date.toString()));        
      }else{}//什么也不做,这一天没有日志
    }
});
</script>

小结

其实没什么特别的地方,就在于一条 sql 把日志按日期分组查询出来。
然后用 laydate 渲染一个日历组件,对特定的日期做一定的交互。两句说说完了。

相关文档

标签: Typecho, 插件, 日历, layDate

添加新评论