Compare commits

...

38 Commits
0.6.3 ... main

Author SHA1 Message Date
浪子 0ed7e7b5e8 0.7.3
解决类名相同的问题
2025-06-11 17:15:57 +08:00
浪子 6f37575943 0.7.2
fix 翻页样式
2025-06-06 09:01:26 +08:00
浪子 6b8f758dcf 0.7.2
友好时间显示
2025-06-04 10:33:16 +08:00
浪子 1f535bb9d6 0.7.2
修复置顶文章问题
2025-05-24 18:55:25 +08:00
浪子 a42f5f41ca 0.7.2
更新预览
2025-05-22 19:24:20 +08:00
浪子 16e91b835e 0.7.2 2025-05-16 13:45:33 +08:00
浪子 9671712412 0.7.2 2025-05-16 13:43:59 +08:00
浪子 4df85ae062 0.7.2 2025-05-16 11:56:31 +08:00
浪子 a6883c5ce7 0.7.1
在后台撰写文章增加一个批量插入附件的按钮
2025-04-07 10:55:57 +08:00
浪子 4dce3365b6 0.7.1 2025-04-07 09:14:37 +08:00
浪子 26e2c5b40c 0.7.1 2025-04-02 11:37:35 +08:00
浪子 c5eb0aa112 0.7.1
更新说明
2025-03-27 10:03:28 +08:00
浪子 cd3933ea53 0.7.1 2025-03-27 09:36:15 +08:00
浪子 482e9a1e93 0.7.1
修复了一个<div>未闭合的bug

优化分页和加载更多逻辑
2025-03-26 09:14:45 +08:00
浪子 13c3587762 add
显示详细的时间
2025-03-25 18:43:19 +08:00
浪子 4d7652218f 0.7.0
判断文章是否是memos分类,如果是则显示memos分类的样式
2025-03-25 15:59:13 +08:00
浪子 8a7f6e5baa 0.7.0
删除了第三方评论的设置
2025-03-22 20:47:48 +08:00
浪子 ef3c0d44ed Merge branch 'main' of https://github.com/jkjoy/typecho-theme-farallon 2025-03-22 20:34:50 +08:00
浪子 4f24340993 0.7.0
自定义字段太多影响观感,注释掉了
2025-03-22 20:34:45 +08:00
浪子 cacb1ca01b fix 2025-03-22 08:14:56 +08:00
浪子 e4a4eed079 fix
mb_convert_encoding(): 已弃用
2025-03-21 21:28:35 +08:00
浪子 9f8976966c 0.7.0
注释
2025-03-21 12:16:37 +08:00
浪子 d4d2fd3c13 0.7.0
更新日志
2025-03-21 11:55:10 +08:00
浪子 ad347ce0d7 0.7.0
根据原版增加了足迹和memo的页面
指定分类显示
增加文章页面的自定义字段说明
2025-03-21 11:47:08 +08:00
浪子 5dc3ec7059 0.7.0
缩进
2025-03-21 09:39:14 +08:00
浪子 5ca790f631 0.7.0
1 把部分CSS移入style.css
2 加入点击加载的文章列表展示方式
3 修改了nopic.svg的大小
4 修复特定分类下的显示功能
2025-03-21 09:17:15 +08:00
浪子 077bd0f720 0.7.0
fix 头部bug
add loadmore
2025-03-20 23:49:27 +08:00
浪子 2189a0ec68 0.7.0
后台增加指定分类ID

显示指定样式
2025-03-20 23:37:23 +08:00
浪子 78cdfb6fe9 0.7.1 2025-03-20 20:07:01 +08:00
浪子 75773ee369 0.7.0
更新说明
2025-03-17 09:49:17 +08:00
浪子 7b1984c876 0.7.0
fix  分类图片的设置说明
2025-03-17 09:12:27 +08:00
浪子 ec49b125fe 0.7.0
fix 未开启复制链接时的强制检测
2025-03-17 09:12:20 +08:00
浪子 221f0ed1bd 0.7.0 2025-03-17 08:44:03 +08:00
浪子 5c91195745 0.7.0
重新整理了一下目录,更改了部分内容的实现方式
2025-03-17 08:13:35 +08:00
浪子 81af4b6590 and 更新文档 2025-03-16 16:49:08 +08:00
浪子 81b745ddb3 change 好物页面的实现方式 2025-03-16 16:49:03 +08:00
浪子 8aec57b257 fix 过滤svg格式 2025-03-16 16:16:57 +08:00
浪子 b5dfa7f09b add 记住评论信息 2025-03-16 15:31:11 +08:00
48 changed files with 1474 additions and 960 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
tooot.php
Parsedown.php
/assets/images/covers

107
README.md
View File

@ -1,56 +1,121 @@
## 说明
移植自 `bigfa `大大的 `hugo-theme-farallon`
移植自 `bigfa `大大的 `hugo-theme-farallon` 主题,并在此基础上进行了一些修改.
由于是自用,有很多主观上的修改,可能不适合所有人.
## 更新日志 & 预览
### 0.7.2
- 优化页面加载速度,自动裁切封面,并压缩为`webp`格式,并保存在主题目录`assets`-`images`-`covers`下,使用前必须开启`php`的`GD拓展`和主题目录的写入权限
- 修复了置顶文章和翻页的问题
### 0.7.1
- 调整了赞赏的样式,同时填写支付宝和微信收款码的图片地址即可显示
- 调整文章列表的加载方式
- 调整了文件的结构
### 0.7.0
根据原版更新,修复了一些问题,并添加了一些功能
- 增加了文章列表的加载方式选择
- 增加了足迹分类的显示
- 增加了说说页面的显示
- 增加了编辑页面的自定义字段的说明
- 调整了文件夹的结构
- 调整了一些样式
- 移除了评论功能对links插件的关联,避免出现错误
- 移除了一些不必要的文件
- 移除了评论QQ通知的功能
后续会继续更新
- 可能会删除Memos说说的功能,因为memos的API不稳定
- 逐渐移除依靠API实现的功能
[预览](https://blog.imsun.org/)
https://www.imsun.org/archives/1640.html
## 功能
### 豆瓣观影
### 封面
更新豆瓣API获取方式
优先使用自定义字段`cover`的值作为封面,若没有则使用文章内的第一张图片作为封面
### 摘要
优先使用自定义字段`summary`的值作为摘要,若没有则显示默认字数摘要
### 观影
- 更新豆瓣API获取方式
[Docker 自动同步豆瓣书影音记录](https://fatesinger.com/103483)
在新建页面选择`豆瓣`, 在自定义字段设置`douban`值为API地址. (默认为`https://db.imsun.org`)
在新建页面选择模板`豆瓣页面`, 在自定义字段设置`douban`值为API地址. (默认为`https://db.imsun.org`)
- neodb 方式获取
参照[使用 NeoDB API 构建观影页面](https://www.imsun.org/archives/1688.html)
在新建页面选择模板`NEODB页面`, 在自定义字段设置`neodb`值为API地址. (默认为`https://neodb.imsun.org`)
### 友情链接
基于 `links` 插件实现
本功能基于 `links` 插件实现,必须安装 `links` 插件才能使用.
否则报错`Class "Links_Plugin" not found`
可使用 `寒泥` 大佬制作的版本或者其他版本
### 首页摘要
若使用AI摘要插件则显示AI摘要,不使用则显示默认字数摘要
*可使用 `寒泥` 大佬制作的版本或者其他版本*
### 说说 by Memos
使用自定义字段设置memos
基于`memos`的API获取,注意该功能要求`memos`的版本为 v0.18.0
在自定义字段中填入`memos`值为memos地址,不带`/`
自定义页面使用模板`说说页面 by memos`在自定义字段设置
在自定义字段中填入`memosID`默认值为`1`,不为`1`时才需要设置
- `memos`值为memos地址,结尾不带`/`
在自定义字段中填入`memosnum`默认值为`20`,默认获取20条最近的memo
- `memosID`默认值为`1`,不为`1`时需要设置
- `memosnum`默认值为`20`,默认获取20条最近的memo
### 说说 by Mastodon
根据 https://www.imsun.org/archives/1643.html
兼容`Mastodon`,`Pleroma`,`GotoSocial`的API.
根据 [如何获得Access Tokens](https://www.imsun.org/archives/1643.html)
获得API地址
在自定义字段中填入`tooot`值为Mastodon API 地址
在自定义字段中填入`tooot`值为 Mastodon API 地址
### 好物 by memos
### 好物页面
在自定义字段中填入`memos`值为memos地址
在自定义字段中填入`memosID`默认值为`1`,不为`1`时才需要设置
在自定义字段中填入`memostag`默认值为`好物`,不为`好物`时才需要设置
~~在自定义字段中填入`memos`值为memos地址~~
~~在自定义字段中填入`memosID`默认值为`1`,不为`1`时才需要设置~~
~~在自定义字段中填入`memostag`默认值为`好物`,不为`好物`时才需要设置~~
~~memos的写法可以参照~~
在自定义页面选择模板`好物页面`
在编辑器中使用表格的方式进行书写,以下为示例
```markdown
| 图片链接 | 商品名称 | 价格 | 商品链接 | 推荐理由 |
|---------|---------|------|----------|----------|
| https://example.com/image1.jpg | 商品A | ¥99 | https://example.com/product1 | 这是一个很好的产品 |
| https://example.com/image2.jpg | 商品B | ¥199 | https://example.com/product2 | 非常推荐购买 |
```
memos的写法可以参照
## 版权

View File

@ -1,77 +1,46 @@
<?php if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
<?php $this->need('header.php'); ?>
<main class="site--main">
<header class="archive--header"><?php if ($this->have()): ?>
<h2 class="post--single__title"><?php $this->archiveTitle(array(
<header class="archive--header">
<h2 class="post--single__title">
<?php $this->archiveTitle(array(
'category' => _t(' <span> %s </span> '),
'search' => _t('包含关键字<span> %s </span>的文章'),
'date' => _t('在 <span> %s </span>发布的文章'),
'tag' => _t('标签 <span> %s </span>下的文章'),
'author' => _t('作者 <span>%s </span>发布的文章')
), '', ''); ?></h2>
<h3 class="post--single__subtitle"><?php $this->categorydescription(); ?></h3>
<?php while($this->next()): ?>
</header>
<article class="post--item">
<div class="content">
<h2 class="post--title">
<a href="<?php $this->permalink() ?>"><?php $this->title() ?></a>
</h2>
<div class="description">
<?php
// 判断是否存在自定义字段summary并输出否则输出自动生成的摘要
if($this->fields->summary){echo $this->fields->summary;} else {$this->excerpt(180); }
?>
</div>
<div class="meta">
<svg class="icon" viewBox="0 0 1024 1024" width="16" height="16">
<path
d="M512 97.52381c228.912762 0 414.47619 185.563429 414.47619 414.47619s-185.563429 414.47619-414.47619 414.47619S97.52381 740.912762 97.52381 512 283.087238 97.52381 512 97.52381z m0 73.142857C323.486476 170.666667 170.666667 323.486476 170.666667 512s152.81981 341.333333 341.333333 341.333333 341.333333-152.81981 341.333333-341.333333S700.513524 170.666667 512 170.666667z m36.571429 89.697523v229.86362h160.865523v73.142857H512a36.571429 36.571429 0 0 1-36.571429-36.571429V260.388571h73.142858z">
</path>
</svg><time><?php $this->date('Y-m-d'); ?></time>
<svg class="icon" viewBox="0 0 1024 1024" width="16" height="16">
<path
d="M408.551619 97.52381a73.142857 73.142857 0 0 1 51.736381 21.430857L539.306667 197.973333A73.142857 73.142857 0 0 0 591.067429 219.428571H804.571429a73.142857 73.142857 0 0 1 73.142857 73.142858v560.761904a73.142857 73.142857 0 0 1-73.142857 73.142857H219.428571a73.142857 73.142857 0 0 1-73.142857-73.142857V170.666667a73.142857 73.142857 0 0 1 73.142857-73.142857h189.123048z m0 73.142857H219.428571v682.666666h585.142858V292.571429h-213.504a146.285714 146.285714 0 0 1-98.499048-38.13181L487.619048 249.734095 408.551619 170.666667zM365.714286 633.904762v73.142857h-73.142857v-73.142857h73.142857z m365.714285 0v73.142857H414.47619v-73.142857h316.952381z m-365.714285-195.047619v73.142857h-73.142857v-73.142857h73.142857z m365.714285 0v73.142857H414.47619v-73.142857h316.952381z">
</path>
</svg><?php $this->category(','); ?>
<svg viewBox="0 0 24 24" class="icon" aria-hidden="true" width="16" height="16">
<g>
<path
d="M1.751 10c0-4.42 3.584-8 8.005-8h4.366c4.49 0 8.129 3.64 8.129 8.13 0 2.96-1.607 5.68-4.196 7.11l-8.054 4.46v-3.69h-.067c-4.49.1-8.183-3.51-8.183-8.01zm8.005-6c-3.317 0-6.005 2.69-6.005 6 0 3.37 2.77 6.08 6.138 6.01l.351-.01h1.761v2.3l5.087-2.81c1.951-1.08 3.163-3.13 3.163-5.36 0-3.39-2.744-6.13-6.129-6.13H9.756z">
</path>
</g>
</svg><a href="<?php $this->permalink() ?>#comments"><?php $this->commentsNum('0 ', '1 ', '%d '); ?></a>
</div>
</div>
</article>
<?php endwhile; ?>
</main>
<?php $this->pageNav(
' ',
' ',
1,
'...',
array(
'wrapTag' => 'nav',
'wrapClass' => 'nav-links nav-links__comment',
'itemTag' => '',
'textTag' => 'span',
'itemClass' => 'page-numbers',
'currentClass' => 'page-numbers current',
'prevClass' => 'hidden',
'nextClass' => 'hidden'
)
);
?>
<!-- 搜索结果 -->
), '', ''); ?>
</h2>
<div class="taxonomy--description">
<?php echo $this->getDescription(); ?>
</div>
</header>
<div class="site--main">
<?php
// 获取分类ID配置
$travelId = Helper::options()->travel;
$memosId = Helper::options()->memos;
// 安全地获取当前分类 mid
$currentCategory = isset($this->categories[0]['mid']) ? intval($this->categories[0]['mid']) : null;
// 转换为整型(如果需要)
$travelId = is_numeric($travelId) ? intval($travelId) : null;
$memosId = is_numeric($memosId) ? intval($memosId) : null;
?>
<?php if ($this->have()): ?>
<?php if ($currentCategory === $travelId): ?>
<!-- 旅行分类模板 -->
<?php $this->need('module/travel.php'); ?>
<?php elseif ($currentCategory === $memosId): ?>
<!-- 说说分类模板 -->
<?php $this->need('module/memos.php'); ?>
<?php else: ?>
<main class="site--main">
<header class="archive-header archive-header__search">
<div class="pagination">
<h2>Sorry</h2>
<p>很遗憾,未找到您期待的内容</p>
</div>
</header>
</main>
<?php endif; ?>
<!-- 默认文章列表 -->
<?php $this->need('module/postlist.php'); ?>
<?php endif; ?>
<!-- 分页导航 -->
<?php $this->need('module/paging.php'); ?>
<?php else: ?>
<!-- 无结果 -->
<?php $this->need('module/notfound.php'); ?>
<?php endif; ?>
</div>
<?php $this->need('footer.php'); ?>

View File

@ -1 +1 @@
.neodb-container{--db-item-width:150px;--db-item-height:180px;--db-music-width:150px;--db-music-height:150px;--db-primary-color:var(--farallon-hover-color);--db-background-white:var(--farallon-background-white);--db-background-gray:var(--farallon-background-gray);--db-border-color:var(--farallon-border-color);--db-text-light:var(--farallon-text-light);}.neodb-nav{padding:30px 0 20px;display:flex;align-items:center;flex-wrap:wrap;}.neodb-navItem{font-size:20px;cursor:pointer;border-bottom:1px solid rgba(0,0,0,0);transition:0.5s border-color;display:flex;align-items:center;text-transform:capitalize;}.neodb-navItem.current,.neodb-navItem:hover{border-color:inherit;}.neodb-navItem{margin-right:20px;}.neodb-score svg{fill:#f5c518;margin-right:5px;}.neodb-list{display:flex;align-items:flex-start;flex-wrap:wrap;}.neodb-image{width:var(--db-item-width);height:var(--db-item-height);object-fit:cover;border-radius:4px;}.neodb-image:hover{box-shadow:0 0 10px var(--db-border-color);}.neodb-title{margin-top:2px;font-size:14px;line-height:1.4;}.neodb-title a:hover{color:var(--db-primary-color);text-decoration:underline;}.neodb-genreItem{background:var(--db-background-gray);font-size:12px;padding:5px 12px;border-radius:4px;margin-right:6px;margin-bottom:10px;line-height:1.4;cursor:pointer;}.neodb-genreItem.is-active,.neodb-genreItem:hover{background-color:var(--db-primary-color);color:var(--db-background-white);}.neodb-genres{padding-bottom:15px;display:flex;flex-wrap:wrap;}.neodb-genres.u-hide + .neodb-list{padding-top:10px;}.neodb-score{display:flex;align-items:center;font-size:14px;color:var(--db-text-light);}.neodb-item{width:var(--db-item-width);margin-right:20px;margin-bottom:20px;position:relative;}.neodb-item__music img{width:var(--db-music-width);height:var(--db-music-height);object-fit:cover;}.neodb-date{position:relative;font-size:20px;color:var(--farallon-text-light);font-weight:900;line-height:1;}.neodb-date::before{content:"";position:absolute;top:0.5em;bottom:-2px;left:-10px;width:3.4em;z-index:-1;background:var(--farallon-hover-color);opacity:0.3;transform:skew(-35deg);transition:opacity 0.2s ease;border-radius:3px 8px 10px 6px;}.neodb-date{margin-top:30px;margin-bottom:10px;}.neodb-dateList{padding-left:15px;padding-top:5px;padding-right:15px;}.neodb-card__list{display:flex;align-items:center;padding:15px 0;border-bottom:1px dotted var(--farallon-border-color);font-size:14px;color:rgba(0,0,0,0.55);}.neodb-card__list:last-child{border-bottom:0;}.neodb-card__list .title{font-size:18px;margin-bottom:5px;}.neodb-card__list .rating{margin:0 0 0px;font-size:14px;line-height:1;display:flex;align-items:center;}.neodb-card__list .rating .allstardark{position:relative;color:#f99b01;height:16px;width:80px;background-repeat:repeat;background-image:url("../images/star.svg");background-size:auto 100%;margin-right:5px;}.neodb-card__list .rating .allstarlight{position:absolute;left:0;color:#f99b01;height:16px;overflow:hidden;background-repeat:repeat;background-image:url("../images/star-fill.svg");background-size:auto 100%;}.neodb-card__list img{width:80px;border-radius:4px;height:80px;object-fit:cover;flex:0 0 auto;margin-right:15px;}.neodb-titleDate{display:flex;flex-direction:column;line-height:1.1;margin-bottom:10px;flex:0 0 auto;margin-right:15px;align-items:center;}.neodb-titleDate__day{font-weight:900;font-size:44px;}.neodb-titleDate__month{font-size:14px;color:var(--farallon-text-light);font-weight:900;}.neodb-list__card{display:block;}.neodb-dateList__card{display:flex;flex-wrap:wrap;align-items:flex-start;}.neodb-listBydate{display:flex;align-items:flex-start;margin-top:15px;}@media (max-width:600px){.neodb-listBydate{flex-direction:column;}}
.neodb-container{--db-item-width:140px;--db-item-height:180px;--db-music-width:150px;--db-music-height:150px;--db-primary-color:var(--farallon-hover-color);--db-background-white:var(--farallon-background-white);--db-background-gray:var(--farallon-background-gray);--db-border-color:var(--farallon-border-color);--db-text-light:var(--farallon-text-light);}.neodb-nav{padding:30px 0 20px;display:flex;align-items:center;flex-wrap:wrap;}.neodb-navItem{font-size:20px;cursor:pointer;border-bottom:1px solid rgba(0,0,0,0);transition:0.5s border-color;display:flex;align-items:center;text-transform:capitalize;}.neodb-navItem.current,.neodb-navItem:hover{border-color:inherit;}.neodb-navItem{margin-right:20px;}.neodb-score svg{fill:#f5c518;margin-right:5px;}.neodb-list{display:flex;align-items:flex-start;flex-wrap:wrap;}.neodb-image{width:var(--db-item-width);height:var(--db-item-height);object-fit:cover;border-radius:4px;}.neodb-image:hover{box-shadow:0 0 10px var(--db-border-color);}.neodb-title{margin-top:2px;font-size:14px;line-height:1.4;}.neodb-title a:hover{color:var(--db-primary-color);text-decoration:underline;}.neodb-genreItem{background:var(--db-background-gray);font-size:12px;padding:5px 12px;border-radius:4px;margin-right:6px;margin-bottom:10px;line-height:1.4;cursor:pointer;}.neodb-genreItem.is-active,.neodb-genreItem:hover{background-color:var(--db-primary-color);color:var(--db-background-white);}.neodb-genres{padding-bottom:15px;display:flex;flex-wrap:wrap;}.neodb-genres.u-hide + .neodb-list{padding-top:10px;}.neodb-score{display:flex;align-items:center;font-size:14px;color:var(--db-text-light);}.neodb-item{width:var(--db-item-width);margin-right:20px;margin-bottom:20px;position:relative;}.neodb-item__music img{width:var(--db-music-width);height:var(--db-music-height);object-fit:cover;}.neodb-date{position:relative;font-size:20px;color:var(--farallon-text-light);font-weight:900;line-height:1;}.neodb-date::before{content:"";position:absolute;top:0.5em;bottom:-2px;left:-10px;width:3.4em;z-index:-1;background:var(--farallon-hover-color);opacity:0.3;transform:skew(-35deg);transition:opacity 0.2s ease;border-radius:3px 8px 10px 6px;}.neodb-date{margin-top:30px;margin-bottom:10px;}.neodb-dateList{padding-left:15px;padding-top:5px;padding-right:15px;}.neodb-card__list{display:flex;align-items:center;padding:15px 0;border-bottom:1px dotted var(--farallon-border-color);font-size:14px;color:rgba(0,0,0,0.55);}.neodb-card__list:last-child{border-bottom:0;}.neodb-card__list .title{font-size:18px;margin-bottom:5px;}.neodb-card__list .rating{margin:0 0 0px;font-size:14px;line-height:1;display:flex;align-items:center;}.neodb-card__list .rating .allstardark{position:relative;color:#f99b01;height:16px;width:80px;background-repeat:repeat;background-image:url("../images/star.svg");background-size:auto 100%;margin-right:5px;}.neodb-card__list .rating .allstarlight{position:absolute;left:0;color:#f99b01;height:16px;overflow:hidden;background-repeat:repeat;background-image:url("../images/star-fill.svg");background-size:auto 100%;}.neodb-card__list img{width:80px;border-radius:4px;height:80px;object-fit:cover;flex:0 0 auto;margin-right:15px;}.neodb-titleDate{display:flex;flex-direction:column;line-height:1.1;margin-bottom:10px;flex:0 0 auto;margin-right:15px;align-items:center;}.neodb-titleDate__day{font-weight:900;font-size:44px;}.neodb-titleDate__month{font-size:14px;color:var(--farallon-text-light);font-weight:900;}.neodb-list__card{display:block;}.neodb-dateList__card{display:flex;flex-wrap:wrap;align-items:flex-start;}.neodb-listBydate{display:flex;align-items:flex-start;margin-top:15px;}@media (max-width:600px){.neodb-listBydate{flex-direction:column;}}

File diff suppressed because one or more lines are too long

BIN
assets/images/close.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 B

BIN
assets/images/loading.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

BIN
assets/images/next.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

1
assets/images/nopic.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.6 KiB

BIN
assets/images/prev.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -17,7 +17,7 @@
is_single = false;
post_id = 0;
is_archive = false;
VERSION = "0.6.3";
VERSION = "0.7.2";
constructor() {
super();
this.initCopyright();
@ -46,7 +46,7 @@
}
initCopyright() {
const copyright = `<div class="site--footer__info">由<a href="https://www.typecho.org" target="_blank">Typecho</a> 驱动 <br>
Theme <a href="https://fatesinger.com/101971" target="_blank">farallon</a> by bigfa &nbsp;<br>Made with<a href="https://www.imsun.org" target="_blank"> Sun</a> / version ${this.VERSION}
Theme <a href="https://fatesinger.com/101971" target="_blank">farallon</a> by bigfa &nbsp;<br>Made with&nbsp;<a href="https://www.imsun.org" target="_blank">老孙博客</a> / version ${this.VERSION}
</div>`;
document.querySelector(".site--footer__content").insertAdjacentHTML("afterend", copyright);
document.querySelector(".icon--copryrights").addEventListener("click", () => {

66
assets/js/loadmore.js Normal file
View File

@ -0,0 +1,66 @@
document.addEventListener('click', function (e) {
// 检查点击的元素是否是 .loadmore a
if (e.target.closest('.loadmore a')) {
e.preventDefault();
var btn = e.target.closest('.loadmore a');
var nextPage = btn.getAttribute('href');
// 防止重复点击
if (btn.classList.contains('loading')) return false;
btn.classList.add('loading');
btn.textContent = '加载中...';
// 发起 AJAX 请求
fetch(nextPage)
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.text();
})
.then(data => {
// 创建一个临时的 DOM 元素来解析返回的 HTML
var parser = new DOMParser();
var htmlDoc = parser.parseFromString(data, 'text/html');
// 调试代码:检查选择器
console.log('Searching for #loadpost:', htmlDoc.querySelectorAll('#loadpost'));
console.log('Searching for .nav-links:', htmlDoc.querySelector('.nav-links'));
// 找到新的文章和按钮
var newPosts = htmlDoc.querySelectorAll('#loadpost');
var newBtn = htmlDoc.querySelector('.nav-links a');
// 更健壮的元素选择
var articleList = document.querySelector('#loadposts') ||
document.querySelector('.posts-container') ||
document.body;
var postReadMore = document.querySelector('.nav-links');
if (newPosts.length > 0) {
newPosts.forEach(post => {
// 使用 appendChild 替代 insertBefore
articleList.appendChild(post);
});
// 新文章淡入效果
Array.from(newPosts).forEach(post => {
post.style.opacity = 0;
setTimeout(() => {
post.style.transition = 'opacity 0.5s';
post.style.opacity = 1;
}, 10);
});
}
// 更新"加载更多"按钮或移除它
if (newBtn) {
btn.setAttribute('href', newBtn.getAttribute('href'));
btn.classList.remove('loading');
btn.textContent = '加载更多';
} else {
// 如果没有更多的按钮,移除 .post-read-more
if (postReadMore) {
postReadMore.remove();
}
}
})
.catch(error => {
console.error("AJAX Error:", error);
btn.classList.remove('loading');
btn.textContent = '加载失败,点击重试';
});
}
});

186
assets/js/post.js Normal file
View File

@ -0,0 +1,186 @@
document.addEventListener('DOMContentLoaded', function() {
const tooltip = document.getElementById('copyTooltip');
let timeoutId = null;
// 确保初始状态下提示框是隐藏的
//tooltip.style.display = 'none';
// 复制函数
function copyToClipboard(text) {
navigator.clipboard.writeText(text).then(() => {
// 显示提示
tooltip.style.display = 'block';
tooltip.style.opacity = '1';
// 清除之前的定时器(如果存在)
if (timeoutId) clearTimeout(timeoutId);
// 设置新的定时器
timeoutId = setTimeout(() => {
tooltip.style.opacity = '0';
setTimeout(() => {
tooltip.style.display = 'none';
}, 300); // 等待淡出动画完成后再隐藏
}, 1500);
}).catch(err => {
tooltip.textContent = '复制失败,请重试';
tooltip.style.display = 'block';
tooltip.style.opacity = '1';
if (timeoutId) clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
tooltip.style.opacity = '0';
setTimeout(() => {
tooltip.style.display = 'none';
tooltip.textContent = '复制成功!'; // 重置文本
}, 300);
}, 1500);
console.error('复制失败:', err);
});
}
// 给所有复制链接添加点击事件
document.querySelectorAll('.copy').forEach(link => {
link.addEventListener('click', function(e) {
e.preventDefault();
const textToCopy = this.getAttribute('data-copy') || this.textContent;
copyToClipboard(textToCopy);
});
});
});
document.addEventListener('DOMContentLoaded', (event) => {
const targetClassElement = document.querySelector('.post--single__title');
const postContent = document.querySelector('.post--single__content');
if (!postContent) return;
let found = false;
for (let i = 1; i <= 6 &&!found; i++) {
if (postContent.querySelector(`h${i}`)) {
found = true;
}
}
if (!found) return;
const heads = postContent.querySelectorAll('h1, h2, h3, h4, h5, h6');
const toc = document.createElement('div');
toc.id = 'toc';
toc.innerHTML = '<details class="toc" open><summary class="toc-title">目录</summary><nav id="TableOfContents"><ul></ul></nav></details>';
// 插入到指定 class 元素之后
if (targetClassElement) {
targetClassElement.parentNode.insertBefore(toc, targetClassElement.nextSibling);
}
let currentLevel = 0;
let currentList = toc.querySelector('ul');
let levelCounts = [0];
heads.forEach((head, index) => {
const level = parseInt(head.tagName.substring(1));
if (levelCounts[level] === undefined) {
levelCounts[level] = 1;
} else {
levelCounts[level]++;
}
// 重置下级标题的计数器
levelCounts = levelCounts.slice(0, level + 1);
if (currentLevel === 0) {
currentLevel = level;
}
while (level > currentLevel) {
let newList = document.createElement('ul');
if (!currentList.lastElementChild) {
currentList.appendChild(newList);
} else {
currentList.lastElementChild.appendChild(newList);
}
currentList = newList;
currentLevel++;
levelCounts[currentLevel] = 1;
}
while (level < currentLevel) {
currentList = currentList.parentElement;
if (currentList.tagName.toLowerCase() === 'li') {
currentList = currentList.parentElement;
}
currentLevel--;
}
const anchor = head.textContent.trim().replace(/\s+/g, '-');
head.id = anchor;
const item = document.createElement('li');
const link = document.createElement('a');
link.href = `#${anchor}`;
link.textContent = `${head.textContent}`;
link.style.textDecoration = 'none';
item.appendChild(link);
currentList.appendChild(item);
});
});
function fetchWithRetry(url, retries = 3) {
return fetch(url)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.text(); // 首先获取文本响应
})
.then(text => {
try {
return JSON.parse(text); // 尝试解析 JSON
} catch (e) {
console.error('Invalid JSON:', text);
throw new Error('Invalid JSON response');
}
})
.catch(error => {
if (retries > 0) {
console.log(`Retrying... (${retries} attempts left)`);
return new Promise(resolve => setTimeout(resolve, 1000)) // 等待1秒
.then(() => fetchWithRetry(url, retries - 1));
}
throw error;
});
}
document.addEventListener('DOMContentLoaded', function() {
const doubanLinks = document.querySelectorAll('a[href^="https://movie.douban.com/subject/"]');
doubanLinks.forEach(link => {
const url = link.href;
const movieId = url.match(/subject\/(\d+)/)[1];
link.innerHTML += ' <span class="loading">(加载中...)</span>';
fetchWithRetry(`https://api.loliko.cn/movies/${movieId}`)
.then(data => {
const movieInfo = createMovieInfoHTML(data, url);
const wrapper = document.createElement('div');
wrapper.innerHTML = movieInfo;
link.parentNode.replaceChild(wrapper, link);
})
.catch(error => {
console.error('Error fetching movie data:', error);
// 显示错误消息给用户
link.innerHTML = `<span style="color: red;">加载失败</span> <a href="${url}" target="_blank">查看豆瓣电影详情</a>`;
})
.finally(() => {
const loadingSpan = link.querySelector('.loading');
if (loadingSpan) {
loadingSpan.remove();
}
});
});
});
function createMovieInfoHTML(data, originalUrl) {
if (!data || data.error || Object.keys(data).length === 0) {
return `<a href="${originalUrl}" target="_blank">查看豆瓣电影详情</a>`;
}
return `
<div class=doulist-item>
<div class=doulist-subject>
<div class=doulist-post>
<img decoding=async referrerpolicy=no-referrer src=${data.img}>
</div>
<div class=doulist-content>
<div class=doulist-title>
<a href="${originalUrl}" class=cute target="_blank" rel="external nofollow"> ${data.name} </a>
</div>
<div class=rating>
<span class=rating_nums>豆瓣评分 : ${data.rating}</span>
</div>
<div class=abstract>
${data.year} · ${data.country} · ${data.genre} · 导演: ${data.director} · 演员 : ${data.actor}
</div>
</div>
</div>
</div>
`;
}

View File

@ -1,136 +0,0 @@
<?php if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
<div class="post--ingle__comments">
<?php $this->comments()->to($comments); ?>
<?php $language = isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) ? $_SERVER['HTTP_ACCEPT_LANGUAGE'] : '';
if($this->allow('comment') && stripos($language, 'zh') > -1): ?>
<?php if ($this->is('attachment')) : ?>
<?php else: ?>
<h3 class="comments--title" id="comments">
<svg viewBox="0 0 24 24" class="icon" aria-hidden="true" width="16" height="16">
<g>
<path
d="M1.751 10c0-4.42 3.584-8 8.005-8h4.366c4.49 0 8.129 3.64 8.129 8.13 0 2.96-1.607 5.68-4.196 7.11l-8.054 4.46v-3.69h-.067c-4.49.1-8.183-3.51-8.183-8.01zm8.005-6c-3.317 0-6.005 2.69-6.005 6 0 3.37 2.77 6.08 6.138 6.01l.351-.01h1.761v2.3l5.087-2.81c1.951-1.08 3.163-3.13 3.163-5.36 0-3.39-2.744-6.13-6.129-6.13H9.756z">
</path>
</g>
</svg>
<?php $this->commentsNum(_t('0'), _t('1'), _t('%d')); ?>
</h3>
<ol class="commentlist sulliComment--list"></ol>
<div id="<?php $this->respondId(); ?>" class="comment-respond">
<div class="cancel-comment-reply cancel-comment-reply-link"><?php $comments->cancelReply(); ?></div>
<form method="post" action="<?php $this->commentUrl() ?>" id="comment-form" role="form" class="comment-form">
<?php if($this->user->hasLogin()): ?>
<p><?php _e('登录身份: '); ?>
<a href="<?php $this->options->profileUrl(); ?>">
<?php $this->user->screenName(); ?></a>.
<a href="<?php $this->options->logoutUrl(); ?>" title="Logout"><?php _e('退出'); ?> &raquo;</a></p>
<?php else: ?>
<p class="comment-form-author">
<input placeholder="称呼 *" type="text" name="author" id="author" class="text" value="" required />
</p>
<p class="comment-notes">
<input placeholder="邮箱<?php if ($this->options->commentsRequireMail): ?> *<?php endif; ?>" type="email" name="mail" id="mail" class="text" value=""<?php if ($this->options->commentsRequireMail): ?> required<?php endif; ?> />
</p>
<p class="comment-form-url">
<input type="url" name="url" id="url" class="text" placeholder="http(s)://<?php if ($this->options->commentsRequireURL): ?> *<?php endif; ?>" value=""<?php if ($this->options->commentsRequireURL): ?> required<?php endif; ?> />
</p>
<?php endif; ?>
<p class="comment-form-comment">
<textarea rows="8" cols="50" name="text" id="textarea" class="textarea" onkeydown="if(event.ctrlKey&&event.keyCode==13){document.getElementById('misubmit').click();return false};" placeholder="<?php _e('评论审核后显示,请勿重复提交...'); ?>" required ><?php $this->remember('text'); ?></textarea>
</p>
<p class="form-submit">
<button type="submit" class="submit" id="misubmit"><?php _e('提交评论Ctrl+Enter'); ?></button>
</p>
</form>
</div>
<?php endif; ?>
<?php else: ?>
<?php endif; ?>
<?php if ($comments->have()): ?>
<?php $comments->listComments(); ?>
<?php $comments->pageNav(
' ',
' ',
1,
'...',
array(
'wrapTag' => 'nav',
'wrapClass' => 'nav-links nav-links__comment',
'itemTag' => '',
'textTag' => 'span',
'itemClass' => 'page-numbers',
'currentClass' => 'page-numbers current',
'prevClass' => 'hidden',
'nextClass' => 'hidden'
)
);
?>
<?php else: ?>
<center><h3></h3></center>
<?php endif; ?>
<?php $this->options->twikoo(); ?>
</div>
<?php
function threadedComments($comments, $options) {
$commentClass = '';
if ($comments->authorId) {
if ($comments->authorId == $comments->ownerId) {
$commentClass .= ' comment-by-author';
} else {
$commentClass .= ' comment-by-user';
}
}
$depth = $comments->levels + 1;
?>
<li id="li-<?php $comments->theId(); ?>" class="<?php
if ($comments->levels == 0) {
echo 'comment parent';
} else {
echo 'comment child';
}
echo $commentClass;
?>">
<?php $commentApprove = commentApprove($comments, $comments->mail); ?>
<div class="comment-body" id="<?php $comments->theId(); ?>">
<div class="comment-meta">
<div class="comment--avatar">
<?php if ($comments->url): ?>
<a href="<?php echo $comments->url ?>" target="_blank" rel="external nofollow" title=" <?php echo $commentApprove['userDesc']; ?> ">
<?php echo $comments->gravatar('40', ''); ?>
</a>
<?php else: ?>
<?php echo $comments->gravatar('40', ''); ?>
<?php endif; ?>
</div>
<div class="comment--meta">
<div class="comment--author">
<span style="color: <?php echo $commentApprove['bgColor']; ?>;">
<p class="tooltip" data-tooltip=" <?php echo $commentApprove['userLevel']; ?> ">
<?php echo $comments->author; ?>
</p>
</span>
<span class="dot"></span>
<div class="comment--time"><?php $comments->date('Y-m-d H:i'); ?></div>
<!-- <span class="dot"></span>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="12" height="12" fill="currentColor"><path d="M12 20.8995L16.9497 15.9497C19.6834 13.2161 19.6834 8.78392 16.9497 6.05025C14.2161 3.31658 9.78392 3.31658 7.05025 6.05025C4.31658 8.78392 4.31658 13.2161 7.05025 15.9497L12 20.8995ZM12 23.7279L5.63604 17.364C2.12132 13.8492 2.12132 8.15076 5.63604 4.63604C9.15076 1.12132 14.8492 1.12132 18.364 4.63604C21.8787 8.15076 21.8787 13.8492 18.364 17.364L12 23.7279ZM12 13C13.1046 13 14 12.1046 14 11C14 9.89543 13.1046 9 12 9C10.8954 9 10 9.89543 10 11C10 12.1046 10.8954 13 12 13ZM12 15C9.79086 15 8 13.2091 8 11C8 8.79086 9.79086 7 12 7C14.2091 7 16 8.79086 16 11C16 13.2091 14.2091 15 12 15Z"></path></svg>
-->
<?php //$ip = $comments->ip; $location = get_ip_location($ip); display_location($location); ?>
<span class="comment-reply-link u-cursorPointer">
<?php $comments->reply('<svg viewBox="0 0 24 24" width="14" height="14" aria-hidden="true" class="" ><g><path d="M12 3.786c-4.556 0-8.25 3.694-8.25 8.25s3.694 8.25 8.25 8.25c1.595 0 3.081-.451 4.341-1.233l1.054 1.7c-1.568.972-3.418 1.534-5.395 1.534-5.661 0-10.25-4.589-10.25-10.25S6.339 1.786 12 1.786s10.25 4.589 10.25 10.25c0 .901-.21 1.77-.452 2.477-.592 1.731-2.343 2.477-3.917 2.334-1.242-.113-2.307-.74-3.013-1.647-.961 1.253-2.45 2.011-4.092 1.78-2.581-.363-4.127-2.971-3.76-5.578.366-2.606 2.571-4.688 5.152-4.325 1.019.143 1.877.637 2.519 1.342l1.803.258-.507 3.549c-.187 1.31.761 2.509 2.079 2.629.915.083 1.627-.356 1.843-.99.2-.585.345-1.224.345-1.83 0-4.556-3.694-8.25-8.25-8.25zm-.111 5.274c-1.247-.175-2.645.854-2.893 2.623-.249 1.769.811 3.143 2.058 3.319 1.247.175 2.645-.854 2.893-2.623.249-1.769-.811-3.144-2.058-3.319z"></path></g></svg>'); ?>
</span>
</div>
</div>
</div>
<div class="comment-content">
<?php if ($comments->parent) {echo getPermalinkFromCoid($comments->parent);} $comments->content();?>
</div>
</div>
<?php if ($comments->children) { ?>
<ol class="children">
<?php $comments->threadedComments($options); ?>
</ol>
<?php } ?>
</li>
<?php } ?>

1
dist/css/donate.css vendored
View File

@ -1 +0,0 @@
#donate-btn,.donate-panel{position:relative;text-align:center}#donate-btn,#donate-close,input[name=pay]{cursor:pointer}#donate-btn{width:50px;height:50px;color:#fff;font-size:20px;font-weight:600;border-radius:50%;line-height:50px;display:inline-block}#qrcode-panel,.qrcode-body{background:#fff;border-radius:5px}#qrcode-panel{position:absolute;width:300px;height:320px;top:0;left:0;box-shadow:0 2px 4px rgba(0,0,0,.12),0 0 6px rgba(0,0,0,.04)}.qrcode-body{width:100%;height:100%;position:relative}.donate-memo{padding:10px;font-size:14px;color:#999;text-align:center}#donate-close{float:right;padding:0 5px;font-size:12px}#donate-close:hover{color:#bd4b4b}.donate-qrpay img{width:280px;height:280px}

File diff suppressed because one or more lines are too long

30
dist/js/donate.js vendored
View File

@ -1,30 +0,0 @@
setTimeout(function(){
initDonate()
}, 1000);
function initDonate(){
var donateBtn = document.getElementById('donate-btn');
var donatePopup = document.getElementById('qrcode-panel');
if(!donateBtn && !donatePopup){
return
}
var l = donateBtn.offsetLeft-125;
var t = donateBtn.offsetTop-330;
donatePopup.style.left=l+'px'
donatePopup.style.top=t+'px'
donateBtn.addEventListener('click',function(){
donatePopup.style.display='';
event.stopPropagation()
})
document.getElementById('donate-close').addEventListener('click',function(){
donatePopup.style.display='none';
event.stopPropagation()
})
document.querySelector('body').addEventListener('click',function() {
donatePopup.style.display='none';
event.stopPropagation()
});
donatePopup.addEventListener('click',function() {
event.stopPropagation()
});
}

File diff suppressed because one or more lines are too long

6
dist/js/vue.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -2,7 +2,7 @@
<footer class="site--footer">
<div class="site--footer__content">
<div class=site--footer__sns>
<?php $this->need('sns.php'); ?>
<?php $this->need('./module/sns.php'); ?>
<?php //sitemap填入
if($this->options->sitemapurl): ?>
<a href="<?php $this->options->sitemapurl() ?>" target="_blank" aria-label="网站地图">💗</a>
@ -30,7 +30,7 @@
</path>
</svg>
</div>
<script src="<?php $this->options->themeUrl('/dist/js/bundle.js'); ?>"></script>
<script src="<?php $this->options->themeUrl('assets/js/bundle.js'); ?>"></script>
<?php $this->footer(); ?>
</div>
</body>

View File

@ -1,5 +1,6 @@
<?php
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
//主题设置
function themeConfig($form) {
$logoUrl = new Typecho_Widget_Helper_Form_Element_Text('logoUrl', NULL, NULL, _t('站点 LOGO 地址'));
$form->addInput($logoUrl);
@ -7,51 +8,81 @@ function themeConfig($form) {
$form->addInput($icoUrl);
$sticky = new Typecho_Widget_Helper_Form_Element_Text('sticky', NULL, NULL, _t('置顶文章cid'), _t('多篇文章以`|`符号隔开'), _t('会在首页展示置顶文章。'));
$form->addInput($sticky);
$instagramurl = new Typecho_Widget_Helper_Form_Element_Text('instagramurl', NULL, 'https://Instagram.com/', _t('Instagram'), _t('会在个人信息显示'));
$travel = new Typecho_Widget_Helper_Form_Element_Text('travel', NULL, NULL, _t('travel分类 Mid'), _t('填写分类的mid'), _t('指定分类ID用于足迹分类展示'));
$form->addInput($travel);
$memos = new Typecho_Widget_Helper_Form_Element_Text('memos', NULL, NULL, _t('说说分类 Mid'), _t('填写分类的mid'), _t('指定分类ID用于说说分类展示'));
$form->addInput($memos);
$instagramurl = new Typecho_Widget_Helper_Form_Element_Text('instagramurl', NULL, NULL, _t('Instagram'), _t('会在个人信息显示'));
$form->addInput($instagramurl);
$telegramurl = new Typecho_Widget_Helper_Form_Element_Text('telegramurl', NULL, 'https://t.me/', _t('电报'), _t('会在个人信息显示'));
$form->addInput($telegramurl);
$githuburl = new Typecho_Widget_Helper_Form_Element_Text('githuburl', NULL, 'https://github.com/', _t('github'), _t('会在个人信息显示'));
$form->addInput($githuburl);
$twitterurl = new Typecho_Widget_Helper_Form_Element_Text('twitterurl', NULL, 'https://x.com/', _t('twitter'), _t('会在个人信息显示'));
$twitterurl = new Typecho_Widget_Helper_Form_Element_Text('twitterurl', NULL, NULL, _t('twitter'), _t('会在个人信息显示'));
$form->addInput($twitterurl);
$mastodonurl = new Typecho_Widget_Helper_Form_Element_Text('mastodonurl', NULL,'https://jiong.us/', _t('mastodon'), _t('会在个人信息显示'));
$mastodonurl = new Typecho_Widget_Helper_Form_Element_Text('mastodonurl', NULL, NULL, _t('mastodon'), _t('会在个人信息显示'));
$form->addInput($mastodonurl);
$sitemapurl = new Typecho_Widget_Helper_Form_Element_Text('sitemapurl', NULL, NULL, _t('sitemap'), _t('会在页脚显示'));
$form->addInput($sitemapurl);
$cnavatar = new Typecho_Widget_Helper_Form_Element_Text('cnavatar', NULL, 'https://cravatar.cn/avatar/', _t('Gravatar镜像'), _t('默认https://cravatar.cn/avatar/,建议保持默认'));
$form->addInput($cnavatar);
$midimg = new Typecho_Widget_Helper_Form_Element_Text('midimg', NULL, './img/', _t('填写分类图片路径,以"/"结尾'), _t('可以使用本地目录或者CDN地址,自动匹配路径下以mid.jpg格式的图片,使用分类页面时需要设置'));
$form->addInput($midimg);
$donate = new Typecho_Widget_Helper_Form_Element_Text('donate', NULL, 'https://blogcdn.loliko.cn/donate/wx.png', _t('赞赏二维码'), _t('不填写则不显示'));
$form->addInput($donate);
$twikoo = new Typecho_Widget_Helper_Form_Element_Textarea('twikoo', NULL, NULL, _t('引用第三方评论'), _t('不填写则不显示'));
$form->addInput($twikoo);
$addhead = new Typecho_Widget_Helper_Form_Element_Textarea('addhead', NULL, NULL, _t('添加head'), _t('支持HTML'));
$form->addInput($addhead);
$tongji = new Typecho_Widget_Helper_Form_Element_Textarea('tongji', NULL, NULL, _t('统计代码'), _t('支持HTML'));
$form->addInput($tongji);
$friendlyTime = new Typecho_Widget_Helper_Form_Element_Radio('friendlyTime',
array('0' => _t('否'),
'1' => _t('是')),
'0', _t('是否显示友好时间'), _t('默认不显示友好时间,显示标准时间格式'));
$form->addInput($friendlyTime);
$showProfile = new Typecho_Widget_Helper_Form_Element_Radio('showProfile',
array('0'=> _t('否'), '1'=> _t('是')),
'0', _t('是否在文章页面显示作者信息'), _t('选择“是”将在文章页面包含显示作者信息。'));
'0', _t('是否在文章页面显示作者信息'), _t('选择"是"将在文章页面包含显示作者信息。'));
$form->addInput($showProfile);
$showcate = new Typecho_Widget_Helper_Form_Element_Radio('showcate',
array('0'=> _t('否'), '1'=> _t('是')),
'0', _t('是否在文章页面显示文章分类'), _t('选择“是”将在文章页面显示文章的分类信息。'));
'0', _t('是否在文章页面显示文章分类'), _t('选择"是"将在文章页面显示文章的分类信息。'));
$form->addInput($showcate);
$showrelated = new Typecho_Widget_Helper_Form_Element_Radio('showrelated',
array('0'=> _t('否'), '1'=> _t('是')),
'0', _t('是否显示相关文章'), _t('选择“是”将在文章页面显示相关文章。'));
'0', _t('是否显示相关文章'), _t('选择"是"将在文章页面显示相关文章。'));
$form->addInput($showrelated);
$showshare = new Typecho_Widget_Helper_Form_Element_Radio('showshare',
array('0'=> _t('否'), '1'=> _t('是')),
'0', _t('是否显示复制链接'), _t('选择“是”将在文章页面显示复制链接。'));
'0', _t('是否显示复制链接'), _t('选择"是"将在文章页面显示复制链接。'));
$form->addInput($showshare);
$showtime = new Typecho_Widget_Helper_Form_Element_Radio('showtime',
array('0'=> _t('否'), '1'=> _t('是')),
'0', _t('是否显示页面加载时间'), _t('选择“是”将在页脚显示加载时间。'));
'0', _t('是否显示页面加载时间'), _t('选择"是"将在页脚显示加载时间。'));
$form->addInput($showtime);
}
$loadmore = new Typecho_Widget_Helper_Form_Element_Radio('loadmore',
array('0'=> _t('加载更多'), '1'=> _t('页码模式')),
'0', _t('加载文章列表方式'), _t('加载更多将在文章列表底部显示加载更多按钮'));
$form->addInput($loadmore);
$sitemapurl = new Typecho_Widget_Helper_Form_Element_Text('sitemapurl', NULL, NULL, _t('sitemap'), _t('网站地图链接'));
$form->addInput($sitemapurl);
$cnavatar = new Typecho_Widget_Helper_Form_Element_Text('cnavatar', NULL, NULL , _t('Gravatar镜像'), _t('默认https://cravatar.cn/avatar/'));
$form->addInput($cnavatar);
$midimg = new Typecho_Widget_Helper_Form_Element_Text('midimg', NULL, '/img/', _t('填写分类图片路径,以"/"结尾'), _t('默认使用网站根目录下的img文件夹,也可以填写绝对或者CDN地址,自动匹配目录下以分类ID为文件名的mid.jpg格式的图片'));
$form->addInput($midimg);
$wxpay = new Typecho_Widget_Helper_Form_Element_Text('wxpay', NULL, 'https://blog.loliko.cn/images/wechatpay.png', _t('微信收款码'), _t('赞赏二维码'));
$form->addInput($wxpay);
$alipay= new Typecho_Widget_Helper_Form_Element_Text('alipay', NULL, 'https://blog.loliko.cn/images/alipay.png', _t('支付宝收款码'), _t('赞赏二维码'));
$form->addInput($alipay);
$addhead = new Typecho_Widget_Helper_Form_Element_Textarea('addhead', NULL, NULL, _t('Head内代码用于网站验证等'), _t('支持HTML'));
$form->addInput($addhead);
$tongji = new Typecho_Widget_Helper_Form_Element_Textarea('tongji', NULL, NULL, _t('统计代码'), _t('支持HTML'));
$form->addInput($tongji);
}
function saveThemeConfig($config) {
// 可以在这里添加额外的验证或处理逻辑
return $config;
}
// 自定义字段
function themeFields($layout) {
$summary= new Typecho_Widget_Helper_Form_Element_Textarea('summary', NULL, NULL, _t('文章摘要'), _t('自定义摘要'));
$layout->addItem($summary);
$cover= new Typecho_Widget_Helper_Form_Element_Text('cover', NULL, NULL, _t('文章封面'), _t('自定义文章封面'));
$layout->addItem($cover);
}
/*
* 文章浏览数统计
*/
function get_post_view($archive) {
$cid = $archive->cid;
$db = Typecho_Db::get();
@ -79,12 +110,23 @@ function get_post_view($archive) {
}
echo $row['views'];
}
// 获取Typecho的选项
/** 头像镜像 */
$options = Typecho_Widget::widget('Widget_Options');
// 检查cnavatar是否已设置如果未设置或为空则使用默认的Gravatar前缀
$gravatarPrefix = empty($options->cnavatar) ? 'https://cravatar.cn/avatar/' : $options->cnavatar;
// 定义全局常量__TYPECHO_GRAVATAR_PREFIX__用于存储Gravatar前缀
define('__TYPECHO_GRAVATAR_PREFIX__', $gravatarPrefix);
// 初始化主题
function init_theme() {
// 检查并创建封面图片目录
$coversDir = dirname(__FILE__) . '/assets/images/covers';
if (!is_dir($coversDir)) {
@mkdir($coversDir, 0755, true);
}
}
// 在主题加载时执行初始化
init_theme();
/**
* 页面加载时间
*/
@ -107,8 +149,24 @@ function timer_start() {
return $r;
}
/**
* 获取文章第一张图片
*/
function img_postthumb($cid) {
$db = Typecho_Db::get();
// 首先检查是否设置了自定义封面
$cover = $db->fetchRow($db->select('str_value')
->from('table.fields')
->where('cid = ?', $cid)
->where('name = ?', 'cover'));
// 如果找到自定义封面,直接返回
if ($cover && !empty($cover['str_value'])) {
return $cover['str_value'];
}
// 否则尝试从文章内容中获取第一张图片
$rs = $db->fetchRow($db->select('table.contents.text')
->from('table.contents')
->where('table.contents.cid=?', $cid)
@ -126,6 +184,7 @@ function img_postthumb($cid) {
return ""; // 没有匹配到图片URL返回空字符串
}
}
//回复加上@
function getPermalinkFromCoid($coid) {
$db = Typecho_Db::get();
@ -138,64 +197,48 @@ function getPermalinkFromCoid($coid) {
* 图片灯箱
*/
class ImageStructureProcessor {
public static function processContent($content, $widget) {
// 首先检查内容是否为空
public static function processContent($content, $widget, $lastResult = null) {
$content = empty($lastResult) ? $content : $lastResult;
if (empty($content) || !is_string($content)) {
return $content;
}
if ($widget instanceof Widget_Archive) {
try {
// 使用 DOM 操作确保结构完整性
$dom = new DOMDocument('1.0', 'UTF-8');
// 添加错误处理
libxml_use_internal_errors(true);
// 添加基础 HTML 结构以确保正确解析
$content = '<div>' . $content . '</div>';
// 转换编码并加载内容
$content = mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8');
$content = '<!DOCTYPE html><html><head><meta charset="UTF-8"></head><body><div>' . $content . '</div></body></html>';
$dom->loadHTML($content,
LIBXML_HTML_NOIMPLIED |
LIBXML_HTML_NODEFDTD |
LIBXML_NOERROR |
LIBXML_NOWARNING
);
$xpath = new DOMXPath($dom);
// 查找所有没有父 figure 的图片
$images = $xpath->query("//img[not(ancestor::figure)]");
// 查找所有没有父 figure 的图片,排除 SVG
$images = $xpath->query("//img[not(ancestor::figure) and not(contains(@src, '.svg'))]");
if ($images->length > 0) {
foreach ($images as $img) {
// 获取必要的属性
$src = $img->getAttribute('src');
$alt = $img->getAttribute('alt');
if (empty($src)) {
continue; // 跳过没有 src 的图片
// 跳过没有 src 的图片或 SVG 格式的图片
if (empty($src) || stripos($src, '.svg') !== false) {
continue;
}
// 创建容器元素
$figure = $dom->createElement('figure');
$figure->setAttribute('class', 'grap--figure');
// 创建链接元素用于lightbox
// 创建链接元素用于 lightbox
$link = $dom->createElement('a');
$link->setAttribute('href', $src);
$link->setAttribute('data-lightbox', 'image-set');
$link->setAttribute('data-title', $alt);
$link->setAttribute('class', 'no-style-link');
// 只有在有 alt 属性时才创建 figcaption
if (!empty($alt)) {
$caption = $dom->createElement('figcaption', $alt);
$caption->setAttribute('class', 'imageCaption');
}
// 重组 DOM 结构
if ($img->parentNode) {
$img->parentNode->replaceChild($figure, $img);
@ -207,71 +250,203 @@ class ImageStructureProcessor {
}
}
}
// 获取处理后的内容
$content = $dom->saveHTML();
// 提取 body 部分的内容
$content = preg_replace('/^.*<body>(.*)<\/body>.*$/is', '$1', $content);
// 清理临时添加的 div 标签
$content = preg_replace('/^<div>|<\/div>$/i', '', $content);
// 清理 libxml 错误
libxml_clear_errors();
} catch (Exception $e) {
// 记录错误但返回原始内容
error_log('Image processing error: ' . $e->getMessage());
return $content;
// 如果发生错误,返回上一个过滤器结果或原始内容
return empty($lastResult) ? $content : $lastResult;
}
}
return $content;
}
}
Typecho_Plugin::factory('Widget_Abstract_Contents')->contentEx = function($content, $widget) {
return ImageStructureProcessor::processContent($content, $widget);
};
/**
* 处理图片为封面图裁剪为5:3最大宽度500px转换为webp
*
* @param string $imageUrl 原始图片URL
* @return string 处理后的图片URL
*/
function process_cover_image($imageUrl) {
// 检查GD库是否可用
if (!function_exists('imagecreatetruecolor')) {
return $imageUrl; // 如果GD库不可用返回原图
}
// 分析URL
$parsed = parse_url($imageUrl);
// 如果图片是外部链接,需要下载
$isExternalUrl = !empty($parsed['host']) && $parsed['host'] !== $_SERVER['HTTP_HOST'];
// 生成唯一的文件名使用MD5哈希
$filename = md5($imageUrl) . '.webp';
// 处理后图片的保存路径
$themeDir = dirname(__FILE__);
$savePath = $themeDir . '/assets/images/covers/' . $filename;
$webPath = Helper::options()->themeUrl . '/assets/images/covers/' . $filename;
// 如果缓存文件已存在,直接返回
if (file_exists($savePath)) {
return $webPath;
}
// 获取原始图片内容
if ($isExternalUrl) {
// 外部图片,需要下载
$imageContent = @file_get_contents($imageUrl);
if (!$imageContent) {
return $imageUrl; // 无法下载,返回原图
}
} else {
// 本地图片
$localPath = $_SERVER['DOCUMENT_ROOT'] . $parsed['path'];
if (!file_exists($localPath)) {
return $imageUrl; // 无法找到本地文件,返回原图
}
$imageContent = @file_get_contents($localPath);
}
// 创建图像资源
$originalImage = @imagecreatefromstring($imageContent);
if (!$originalImage) {
return $imageUrl; // 无法创建图像资源,返回原图
}
// 获取原始图片尺寸
$originalWidth = imagesx($originalImage);
$originalHeight = imagesy($originalImage);
// 计算目标尺寸5:3比例最大宽度500px
$targetWidth = min(500, $originalWidth);
$targetHeight = intval($targetWidth * 3 / 5);
// 计算裁剪坐标(居中裁剪)
$cropX = 0;
$cropY = 0;
$cropWidth = $originalWidth;
$cropHeight = $originalHeight;
// 计算比例
$originalRatio = $originalWidth / $originalHeight;
$targetRatio = 5 / 3;
if ($originalRatio > $targetRatio) {
// 原图过宽,需要裁剪宽度
$cropWidth = intval($originalHeight * $targetRatio);
$cropX = intval(($originalWidth - $cropWidth) / 2);
} else {
// 原图过高,需要裁剪高度
$cropHeight = intval($originalWidth / $targetRatio);
$cropY = intval(($originalHeight - $cropHeight) / 2);
}
// 创建目标图像
$targetImage = imagecreatetruecolor($targetWidth, $targetHeight);
// 裁剪并调整大小
imagecopyresampled(
$targetImage,
$originalImage,
0, 0,
$cropX, $cropY,
$targetWidth, $targetHeight,
$cropWidth, $cropHeight
);
// 保存为webp格式
if (!function_exists('imagewebp')) {
// 如果不支持webp保存为png
$filename = md5($imageUrl) . '.png';
$savePath = $themeDir . '/assets/images/covers/' . $filename;
$webPath = Helper::options()->themeUrl . '/assets/images/covers/' . $filename;
imagepng($targetImage, $savePath, 9); // 9是最高压缩质量
} else {
// 保存为webp
imagewebp($targetImage, $savePath, 80); // 80是质量参数
}
// 释放资源
imagedestroy($originalImage);
imagedestroy($targetImage);
return $webPath;
}
//获取文章卡片
/**
* *获取文章卡片
* **
*
*/
function get_article_summary($post) {
// 首先尝试从自定义字段获取摘要
$db = Typecho_Db::get();
// 查询自定义字段表
$row = $db->fetchRow($db->select()
->from('table.fields')
->where('cid = ?', $post['cid'])
->where('name = ?', 'summary'));
// 如果找到自定义摘要字段
if ($row && !empty($row['str_value'])) {
return $row['str_value'];
}
// 如果没有自定义摘要,截取文章内容
// 去除HTML标签
$text = strip_tags($post['text']);
// 截取指定长度的摘要
return Typecho_Common::subStr($text, 0, 100, '...');
}
// 在原函数中使用
function get_article_info($atts) {
$default_atts = array(
'id' => '',
'url' => ''
);
$atts = array_merge($default_atts, $atts);
$db = Typecho_Db::get();
// 根据 ID获取文章
if (!empty($atts['id'])) {
$post = $db->fetchRow($db->select()->from('table.contents')
->where('cid = ?', $atts['id'])
->limit(1));
} elseif (!empty($atts['url'])) {
$post = $db->fetchRow($db->select()->from('table.contents')
->where('permalink = ?', $atts['url'])
->limit(1));
} else {
return '请提供文章ID或URL';
return '请提供文章ID';
}
if (!$post) {
return '未找到文章';
}
// 将文章数据推送到抽象内容小部件中
$post = Typecho_Widget::widget('Widget_Abstract_Contents')->push($post);
// 获取摘要
$summary = get_article_summary($post);
// 获取缩略图
$default_thumbnail = Helper::options()->themeUrl . '/assets/images/nopic.svg';
$imageToDisplay = img_postthumb($post['cid']);
if (empty($imageToDisplay)) {
$imageToDisplay = 'https://pic.0tz.top/img/nopic.png'; // 设置一个默认图片路径
$imageToDisplay = $default_thumbnail;
} else {
// 处理封面图片
$imageToDisplay = process_cover_image($imageToDisplay);
}
// 构建输出
$output = '<div class="graph--mixtapeEmbed">';
$output .= '<a class="mixtapeContent" href="' . $post['permalink'] . '" target="_blank">';
$output .= '<span class="markup--strong markup--mixtapeEmbed-strong">' . $post['title'] . '</span>';
$output .= '<em class="markup--em markup--mixtapeEmbed-em">' . Typecho_Common::subStr(strip_tags($post['text']), 0, 100, '...') . '</em>';
$output .= '<span class="markup--strong markup--mixtapeEmbed-strong">' . htmlspecialchars($post['title']) . '</span>';
$output .= '<em class="markup--em markup--mixtapeEmbed-em">' . htmlspecialchars($summary) . '</em>';
$output .= '</a>';
$output .= '<a class="mixtapeImage" href="' . $post['permalink'] . '" target="_blank" style="background-image:url(' . $imageToDisplay . ')"></a>';
$output .= '<a class="mixtapeImage" href="' . $post['permalink'] . '" target="_blank" style="background-image:url(' . htmlspecialchars($imageToDisplay) . ')"></a>';
$output .= '</div>';
return $output;
}
// 创建一个新的类来处理内容过滤
class ContentFilter
{
@ -279,16 +454,18 @@ class ContentFilter
{
// 首先运行之前的过滤器结果
$content = empty($lastResult) ? $content : $lastResult;
// 处理图片灯箱
$content = ImageStructureProcessor::processContent($content, $widget, $content);
// 然后处理我们的文章短代码
$content = preg_replace_callback('/\[article\s+([^\]]+)\]/', function($matches) {
$atts = self::parse_atts($matches[1]);
return get_article_info($atts);
}, $content);
return $content;
}
// 解析短代码属性
private static function parse_atts($text) {
$atts = array();
@ -311,10 +488,8 @@ class ContentFilter
return $atts;
}
}
// 注册钩子
Typecho_Plugin::factory('Widget_Abstract_Contents')->contentEx = array('ContentFilter', 'filterContent');
// 编辑器按钮类
class EditorButton {
public static function render()
@ -332,13 +507,10 @@ $(document).ready(function() {
var start = textarea.selectionStart;
var end = textarea.selectionEnd;
var value = textarea.value;
textarea.value = value.substring(0, start) + text + value.substring(end);
// 将光标移动到插入的文本之后
textarea.setSelectionRange(start + text.length, start + text.length);
textarea.focus();
// 触发change事件确保编辑器更新
$('#text').trigger('change');
}
@ -348,10 +520,12 @@ $(document).ready(function() {
EOF;
}
}
// 注册编辑器按钮钩子
Typecho_Plugin::factory('admin/write-post.php')->bottom = array('EditorButton', 'render');
Typecho_Plugin::factory('admin/write-page.php')->bottom = array('EditorButton', 'render');
// 避免重复注册,在最后执行
if (!Typecho_Plugin::exists('Widget_Abstract_Contents', 'editor')) {
Typecho_Plugin::factory('admin/write-post.php')->bottom = array('EditorButton', 'render');
Typecho_Plugin::factory('admin/write-page.php')->bottom = array('EditorButton', 'render');
}
/**
* 评论者认证等级 + 身份
@ -385,8 +559,8 @@ function commentApprove($widget, $email = NULL)
->from('table.comments')
->where('mail = ?', $email));
$commentNum = $commentNumSql[0]['commentNum'];
$linkSql = $db->fetchAll($db->select()->from('table.links')
->where('user = ?',$email));
//$linkSql = $db->fetchAll($db->select()->from('table.links')
// ->where('user = ?',$email));
if($commentNum==1){
$result['userLevel'] = '初识';
$result['bgColor'] = '#999999';
@ -413,14 +587,197 @@ function commentApprove($widget, $email = NULL)
}
$userDesc = '已有'.$commentNum.'条评论';
}
if($linkSql){
$result['userLevel'] = '博友';
$result['bgColor'] = '#21b9bb';
$userDesc = '🔗'.$linkSql[0]['description'].'&#10;✌️'.$userDesc;
}
// if($linkSql){
// $result['userLevel'] = '博友';
// $result['bgColor'] = '#21b9bb';
// $userDesc = '🔗'.$linkSql[0]['description'].'&#10;✌️'.$userDesc;
// }
$result['userDesc'] = $userDesc;
$result['commentNum'] = $commentNum;
}
return $result;
}
/**
* Typecho后台附件增强图片预览、批量插入、保留官方删除按钮与逻辑
* @author 老孙博客
* @date 2025-04-25
*/
Typecho_Plugin::factory('admin/write-post.php')->bottom = array('AttachmentHelper', 'addEnhancedFeatures');
Typecho_Plugin::factory('admin/write-page.php')->bottom = array('AttachmentHelper', 'addEnhancedFeatures');
class AttachmentHelper {
public static function addEnhancedFeatures() {
?>
<style>
#file-list{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:15px;padding:15px;list-style:none;margin:0;}
#file-list li{position:relative;border:1px solid #e0e0e0;border-radius:4px;padding:10px;background:#fff;transition:all 0.3s ease;list-style:none;margin:0;}
#file-list li:hover{box-shadow:0 2px 8px rgba(0,0,0,0.1);}
#file-list li.loading{opacity:0.7;pointer-events:none;}
.att-enhanced-thumb{position:relative;width:100%;height:150px;margin-bottom:8px;background:#f5f5f5;overflow:hidden;border-radius:3px;display:flex;align-items:center;justify-content:center;}
.att-enhanced-thumb img{width:100%;height:100%;object-fit:contain;display:block;}
.att-enhanced-thumb .file-icon{display:flex;align-items:center;justify-content:center;width:100%;height:100%;font-size:40px;color:#999;}
.att-enhanced-finfo{padding:5px 0;}
.att-enhanced-fname{font-size:13px;margin-bottom:5px;word-break:break-all;color:#333;}
.att-enhanced-fsize{font-size:12px;color:#999;}
.att-enhanced-factions{display:flex;justify-content:space-between;align-items:center;margin-top:8px;gap:8px;}
.att-enhanced-factions button{flex:1;padding:4px 8px;border:none;border-radius:3px;background:#e0e0e0;color:#333;cursor:pointer;font-size:12px;transition:all 0.2s ease;}
.att-enhanced-factions button:hover{background:#d0d0d0;}
.att-enhanced-factions .btn-insert{background:#467B96;color:white;}
.att-enhanced-factions .btn-insert:hover{background:#3c6a81;}
.att-enhanced-checkbox{position:absolute;top:5px;right:5px;z-index:2;width:18px;height:18px;cursor:pointer;}
.batch-actions{margin:15px;display:flex;gap:10px;align-items:center;}
.btn-batch{padding:8px 15px;border-radius:4px;border:none;cursor:pointer;transition:all 0.3s ease;font-size:10px;display:inline-flex;align-items:center;justify-content:center;}
.btn-batch.primary{background:#467B96;color:white;}
.btn-batch.primary:hover{background:#3c6a81;}
.btn-batch.secondary{background:#e0e0e0;color:#333;}
.btn-batch.secondary:hover{background:#d0d0d0;}
.upload-progress{position:absolute;bottom:0;left:0;width:100%;height:2px;background:#467B96;transition:width 0.3s ease;}
</style>
<script>
$(document).ready(function() {
// 批量操作UI按钮
var $batchActions = $('<div class="batch-actions"></div>')
.append('<button type="button" class="btn-batch primary" id="batch-insert">批量插入</button>')
.append('<button type="button" class="btn-batch secondary" id="select-all">全选</button>')
.append('<button type="button" class="btn-batch secondary" id="unselect-all">取消全选</button>');
$('#file-list').before($batchActions);
// 插入格式
Typecho.insertFileToEditor = function(title, url, isImage) {
var textarea = $('#text'),
sel = textarea.getSelection(),
insertContent = isImage ? '![' + title + '](' + url + ')' :
'[' + title + '](' + url + ')';
textarea.replaceSelection(insertContent + '\n');
textarea.focus();
};
// 批量插入
$('#batch-insert').on('click', function(e) {
e.preventDefault();
e.stopPropagation();
var content = '';
$('#file-list li').each(function() {
if ($(this).find('.att-enhanced-checkbox').is(':checked')) {
var $li = $(this);
var title = $li.find('.att-enhanced-fname').text();
var url = $li.data('url');
var isImage = $li.data('image') == 1;
content += isImage ? '![' + title + '](' + url + ')\n' : '[' + title + '](' + url + ')\n';
}
});
if (content) {
var textarea = $('#text');
var pos = textarea.getSelection();
var newContent = textarea.val();
newContent = newContent.substring(0, pos.start) + content + newContent.substring(pos.end);
textarea.val(newContent);
textarea.focus();
}
});
$('#select-all').on('click', function(e) {
e.preventDefault();
e.stopPropagation();
$('#file-list .att-enhanced-checkbox').prop('checked', true);
return false;
});
$('#unselect-all').on('click', function(e) {
e.preventDefault();
e.stopPropagation();
$('#file-list .att-enhanced-checkbox').prop('checked', false);
return false;
});
// 防止复选框冒泡
$(document).on('click', '.att-enhanced-checkbox', function(e) {e.stopPropagation();});
// 增强文件列表样式但不破坏li原结构和官方按钮
function enhanceFileList() {
$('#file-list li').each(function() {
var $li = $(this);
if ($li.hasClass('att-enhanced')) return;
$li.addClass('att-enhanced');
// 只增强不清空li
// 增加批量选择框
if ($li.find('.att-enhanced-checkbox').length === 0) {
$li.prepend('<input type="checkbox" class="att-enhanced-checkbox" />');
}
// 增加图片预览(如已有则不重复加)
if ($li.find('.att-enhanced-thumb').length === 0) {
var url = $li.data('url');
var isImage = $li.data('image') == 1;
var fileName = $li.find('.insert').text();
var $thumbContainer = $('<div class="att-enhanced-thumb"></div>');
if (isImage) {
var $img = $('<img src="' + url + '" alt="' + fileName + '" />');
$img.on('error', function() {
$(this).replaceWith('<div class="file-icon">🖼️</div>');
});
$thumbContainer.append($img);
} else {
$thumbContainer.append('<div class="file-icon">📄</div>');
}
// 插到插入按钮之前
$li.find('.insert').before($thumbContainer);
}
});
}
// 插入按钮事件
$(document).on('click', '.btn-insert', function(e) {
e.preventDefault();
e.stopPropagation();
var $li = $(this).closest('li');
var title = $li.find('.att-enhanced-fname').text();
Typecho.insertFileToEditor(title, $li.data('url'), $li.data('image') == 1);
});
// 上传完成后增强新项
var originalUploadComplete = Typecho.uploadComplete;
Typecho.uploadComplete = function(attachment) {
setTimeout(function() {
enhanceFileList();
}, 200);
if (typeof originalUploadComplete === 'function') {
originalUploadComplete(attachment);
}
};
// 首次增强
enhanceFileList();
});
</script>
<?php
}
}
?>
<?php
/**
* 友好时间显示函数
* @param int $time 时间戳
* @param int $threshold 阈值超过此值则显示标准日期格式Y-m-d
* @return string
*/
function time_ago($time, $threshold = 31536000) { // 31536000秒 = 1年
$now = time();
$difference = $now - $time;
// 如果时间差超过阈值默认1年则返回标准日期格式不带时间
if ($difference >= $threshold) {
return date('Y-m-d', $time);
}
// 1年以内的时间返回友好格式如 "3天前"
$periods = array("", "分钟", "小时", "", "", "个月", "");
$lengths = array("60", "60", "24", "7", "4.35", "12");
for ($j = 0; $difference >= $lengths[$j] && $j < count($lengths); $j++) {
$difference /= $lengths[$j];
}
$difference = round($difference);
return $difference . $periods[$j] . "";
}

View File

@ -13,8 +13,11 @@ if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
'tag' => _t('标签 %s 下的文章'),
'date' => _t('在<span> %s </span>发布的文章'),
'author' => _t('%s 发布的文章')
), '', ' - '); ?><?php if ($this->is('post')) $this->category(',', false);?><?php if ($this->is('post')) echo ' - ';?><?php $this->options->title(); ?><?php if ($this->is('index')) echo ' - '; ?><?php if ($this->is('index')) $this->options->description() ?></title>
<link rel="stylesheet" href="<?php $this->options->themeUrl('/dist/css/style.min.css'); ?>">
), '', ' - '); ?>
<?php $this->options->title(); ?><?php if ($this->is('index')) echo ' - '; ?>
<?php if ($this->is('index')) $this->options->description() ?>
</title>
<link rel="stylesheet" href="<?php $this->options->themeUrl('assets/css/style.min.css'); ?>">
<?php if ($this->options->icoUrl): ?>
<link rel='icon' href='<?php $this->options->icoUrl() ?>' type='image/x-icon' />
<?php endif; ?>
@ -52,10 +55,10 @@ if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
<div class="inner">
<nav>
<ul>
<?php $this->widget('Widget_Contents_Page_List')->to($pages); ?>
<?php while($pages->next()): ?>
<li><a <?php if($this->is('page', $pages->slug)): ?> class="current"<?php endif; ?> href="<?php $pages->permalink(); ?>" title="<?php $pages->title(); ?>"><?php $pages->title(); ?></a></li>
<?php endwhile; ?>
<?php $this->widget('Widget_Contents_Page_List')->to($pages); ?>
<?php while($pages->next()): ?>
<li><a <?php if($this->is('page', $pages->slug)): ?> class="current"<?php endif; ?> href="<?php $pages->permalink(); ?>" title="<?php $pages->title(); ?>"><?php $pages->title(); ?></a></li>
<?php endwhile; ?>
</ul>
</nav>
<!-- 这年头谁会用站内的搜索啊 -->
@ -74,4 +77,4 @@ if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
d="M20.067 18.933l-4.157-4.157a6 6 0 1 0-.884.884l4.157 4.157a.624.624 0 1 0 .884-.884zM6.5 11c0-2.62 2.13-4.75 4.75-4.75S16 8.38 16 11s-2.13 4.75-4.75 4.75S6.5 13.62 6.5 11z">
</path>
</svg>
</header>
</header>

View File

@ -3,15 +3,16 @@
* 一款单栏主题. 移植自HUGO主题 Farallon 原作者 bigfa
* @package Farallon
* @author 老孙
* @version 0.6.3
* @version 0.7.3
* @link https://www.imsun.org
*/
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
$this->need('header.php');
?>
if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
<?php $this->need('module/sticky.php'); ?>
<?php $this->need('header.php');?>
<main class="site--main">
<div class="articleList">
<?php $this->need('postlist.php'); ?>
</div>
<div class="articleList">
<?php $this->need('module/postlist.php'); ?>
<?php $this->need('module/paging.php'); ?>
</div>
</main>
<?php $this->need('footer.php'); ?>

163
module/comments.php Normal file
View File

@ -0,0 +1,163 @@
<?php if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
<div class="post--ingle__comments">
<?php $this->comments()->to($comments); ?>
<?php
// 获取之前评论者的信息
$previousAuthor = isset($_COOKIE['__typecho_remember_author']) ? htmlspecialchars($_COOKIE['__typecho_remember_author']) : '';
$previousEmail = isset($_COOKIE['__typecho_remember_mail']) ? htmlspecialchars($_COOKIE['__typecho_remember_mail']) : '';
$previousUrl = isset($_COOKIE['__typecho_remember_url']) ? htmlspecialchars($_COOKIE['__typecho_remember_url']) : '';
$language = isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) ? $_SERVER['HTTP_ACCEPT_LANGUAGE'] : '';
if($this->allow('comment') && stripos($language, 'zh') > -1): ?>
<?php if ($this->is('attachment')) : ?>
<?php else: ?>
<h3 class="comments--title" id="comments">
<svg viewBox="0 0 24 24" class="icon" aria-hidden="true" width="16" height="16">
<g>
<path d="M1.751 10c0-4.42 3.584-8 8.005-8h4.366c4.49 0 8.129 3.64 8.129 8.13 0 2.96-1.607 5.68-4.196 7.11l-8.054 4.46v-3.69h-.067c-4.49.1-8.183-3.51-8.183-8.01zm8.005-6c-3.317 0-6.005 2.69-6.005 6 0 3.37 2.77 6.08 6.138 6.01l.351-.01h1.761v2.3l5.087-2.81c1.951-1.08 3.163-3.13 3.163-5.36 0-3.39-2.744-6.13-6.129-6.13H9.756z">
</path>
</g>
</svg>
<?php $this->commentsNum(_t('0'), _t('1'), _t('%d')); ?>
</h3>
<ol class="commentlist sulliComment--list"></ol>
<div id="<?php $this->respondId(); ?>" class="comment-respond">
<div class="cancel-comment-reply cancel-comment-reply-link"><?php $comments->cancelReply(); ?></div>
<form method="post" action="<?php $this->commentUrl() ?>" id="comment-form" role="form" class="comment-form">
<?php if($this->user->hasLogin()): ?>
<p><?php _e('登录身份: '); ?>
<a href="<?php $this->options->profileUrl(); ?>">
<?php $this->user->screenName(); ?></a>.
<a href="<?php $this->options->logoutUrl(); ?>" title="Logout"><?php _e('退出'); ?> &raquo;</a></p>
<?php else: ?>
<p class="comment-form-author">
<input placeholder="称呼 *" type="text" name="author" id="author" class="text" value="<?php echo $previousAuthor; ?>" required />
</p>
<p class="comment-notes">
<input placeholder="邮箱<?php if ($this->options->commentsRequireMail): ?> *<?php endif; ?>" type="email" name="mail" id="mail" class="text" value="<?php echo $previousEmail; ?>"<?php if ($this->options->commentsRequireMail): ?> required<?php endif; ?> />
</p>
<p class="comment-form-url">
<input type="url" name="url" id="url" class="text" placeholder="http(s)://<?php if ($this->options->commentsRequireURL): ?> *<?php endif; ?>" value="<?php echo $previousUrl; ?>"<?php if ($this->options->commentsRequireURL): ?> required<?php endif; ?> />
</p>
<?php endif; ?>
<p class="comment-form-comment">
<textarea rows="8" cols="50" name="text" id="textarea" class="textarea" onkeydown="if(event.ctrlKey&&event.keyCode==13){document.getElementById('misubmit').click();return false};" placeholder="<?php _e('评论审核后显示,请勿重复提交...'); ?>" required ><?php $this->remember('text'); ?></textarea>
</p>
<p class="form-submit">
<button type="submit" class="submit" id="misubmit"><?php _e('提交评论Ctrl+Enter'); ?></button>
</p>
</form>
</div>
<?php endif; ?>
<?php else: ?>
<?php endif; ?>
<?php if ($comments->have()): ?>
<?php $comments->listComments(); ?>
<?php $comments->pageNav(
' ',
' ',
1,
'...',
array(
'wrapTag' => 'nav',
'wrapClass' => 'nav-links nav-links__comment',
'itemTag' => '',
'textTag' => 'span',
'itemClass' => 'page-numbers',
'currentClass' => 'page-numbers current',
'prevClass' => 'hidden',
'nextClass' => 'hidden'
)
);
?>
<?php else: ?>
<center><h3></h3></center>
<?php endif; ?>
<?php $this->options->twikoo(); ?>
</div>
<!-- 添加JavaScript代码 -->
<script>
document.getElementById('comment-form').addEventListener('submit', function() {
// 获取表单数据
var author = document.getElementById('author');
var mail = document.getElementById('mail');
var url = document.getElementById('url');
// 如果不是登录用户才保存cookie
if (author && mail && url) {
// 设置cookie有效期30天
setCookie('__typecho_remember_author', author.value, 30);
setCookie('__typecho_remember_mail', mail.value, 30);
setCookie('__typecho_remember_url', url.value, 30);
}
});
// Cookie设置函数
function setCookie(name, value, days) {
var expires = '';
if (days) {
var date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
expires = '; expires=' + date.toUTCString();
}
document.cookie = name + '=' + encodeURIComponent(value) + expires + '; path=/';
}
</script>
<?php
function threadedComments($comments, $options) {
$commentClass = '';
if ($comments->authorId) {
if ($comments->authorId == $comments->ownerId) {
$commentClass .= ' comment-by-author';
} else {
$commentClass .= ' comment-by-user';
}
}
$depth = $comments->levels + 1;
?>
<li id="li-<?php $comments->theId(); ?>" class="<?php
if ($comments->levels == 0) {
echo 'comment parent';
} else {
echo 'comment child';
}
echo $commentClass;
?>">
<?php $commentApprove = commentApprove($comments, $comments->mail); ?>
<div class="comment-body" id="<?php $comments->theId(); ?>">
<div class="comment-meta">
<div class="comment--avatar">
<?php if ($comments->url): ?>
<a href="<?php echo $comments->url ?>" target="_blank" rel="external nofollow" title=" <?php echo $commentApprove['userDesc']; ?> ">
<?php echo $comments->gravatar('40', ''); ?>
</a>
<?php else: ?>
<?php echo $comments->gravatar('40', ''); ?>
<?php endif; ?>
</div>
<div class="comment--meta">
<div class="comment--author">
<span style="color: <?php echo $commentApprove['bgColor']; ?>;">
<p class="tooltip" data-tooltip=" <?php echo $commentApprove['userLevel']; ?> ">
<?php echo $comments->author; ?>
</p>
</span>
<span class="dot"></span>
<div class="comment--time"><?php $comments->date('Y-m-d H:i'); ?></div>
<span class="comment-reply-link u-cursorPointer">
<?php $comments->reply('<svg viewBox="0 0 24 24" width="14" height="14" aria-hidden="true" class="" ><g><path d="M12 3.786c-4.556 0-8.25 3.694-8.25 8.25s3.694 8.25 8.25 8.25c1.595 0 3.081-.451 4.341-1.233l1.054 1.7c-1.568.972-3.418 1.534-5.395 1.534-5.661 0-10.25-4.589-10.25-10.25S6.339 1.786 12 1.786s10.25 4.589 10.25 10.25c0 .901-.21 1.77-.452 2.477-.592 1.731-2.343 2.477-3.917 2.334-1.242-.113-2.307-.74-3.013-1.647-.961 1.253-2.45 2.011-4.092 1.78-2.581-.363-4.127-2.971-3.76-5.578.366-2.606 2.571-4.688 5.152-4.325 1.019.143 1.877.637 2.519 1.342l1.803.258-.507 3.549c-.187 1.31.761 2.509 2.079 2.629.915.083 1.627-.356 1.843-.99.2-.585.345-1.224.345-1.83 0-4.556-3.694-8.25-8.25-8.25zm-.111 5.274c-1.247-.175-2.645.854-2.893 2.623-.249 1.769.811 3.143 2.058 3.319 1.247.175 2.645-.854 2.893-2.623.249-1.769-.811-3.144-2.058-3.319z"></path></g></svg>'); ?>
</span>
</div>
</div>
</div>
<div class="comment-content">
<?php if ($comments->parent) {echo getPermalinkFromCoid($comments->parent);} $comments->content();?>
</div>
</div>
<?php if ($comments->children) { ?>
<ol class="children">
<?php $comments->threadedComments($options); ?>
</ol>
<?php } ?>
</li>
<?php } ?>

19
module/memos.php Normal file
View File

@ -0,0 +1,19 @@
<?php if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
<div id="loadposts">
<?php while($this->next()): ?>
<article id="loadpost" class="post--item post--item__status" itemtype="http://schema.org/Article" itemscope="itemscope">
<div class="content">
<header>
<img src="<?php $this->options->logoUrl() ?>" class="avatar" width="48" height="48" />
<a datetime='<?php $this->date('Y-m-d'); ?>' class="humane--time" href="<?php $this->permalink() ?>"
itemprop="datePublished">
<?php $options = Helper::options();if ($options->friendlyTime == '1') {echo time_ago($this->created);} else {$this->date('Y-m-d H:i'); }?>
</a>
</header>
<div class="description" itemprop="about">
<?php $this->excerpt(200, '...'); ?>
</div>
</div>
</article>
<?php endwhile; ?>
</div>

8
module/notfound.php Normal file
View File

@ -0,0 +1,8 @@
<main class="site--main">
<header class="archive-header archive-header__search">
<div class="pagination">
<h2>Sorry</h2>
<p>很遗憾,未找到您期待的内容</p>
</div>
</header>
</main>

31
module/paging.php Normal file
View File

@ -0,0 +1,31 @@
<?php if ($this->options->loadmore): ?>
<?php
$this->pageNav(
' ',
' ',
1,
'...',
array(
'wrapTag' => 'nav',
'wrapClass' => 'nav-links',
'itemTag' => '',
'textTag' => 'span',
'itemClass' => 'page-numbers',
'currentClass' => 'page-numbers current',
'prevClass' => 'hidden',
'nextClass' => 'hidden'
)
);
?>
<?php else:?>
<?php
$nextPage = $this->_currentPage + 1;
$totalPages = ceil($this->getTotal() / $this->parameter->pageSize);
if ($this->_currentPage < $totalPages):
?>
<div class="nav-links">
<span class="loadmore"><?php $this->pageLink('加载更多', 'next'); ?></span>
</div>
<script src="<?php $this->options->themeUrl('assets/js/loadmore.js'); ?>"></script>
<?php endif; ?>
<?php endif; ?>

88
module/postlist.php Normal file
View File

@ -0,0 +1,88 @@
<div id="loadposts">
<?php while($this->next()): ?>
<?php
// 获取当前文章的分类
$categories = $this->categories;
$memosMid = $this->options->memos; // 获取主题设置中的说说分类 mid
$isMemos = false;
// 检查当前文章是否属于说说分类
foreach ($categories as $category) {
if ($category['mid'] == $memosMid) {
$isMemos = true;
break;
}
}
// 根据是否为说说分类使用不同的显示模板
if ($isMemos):
?>
<div id="loadpost">
<article class="post--item post--item__status" itemtype="http://schema.org/Article" itemscope="itemscope">
<div class="content">
<header>
<img src="<?php $this->options->logoUrl() ?>" class="avatar" width="48" height="48" />
<a datetime='<?php $this->date('Y-m-d'); ?>' class="humane--time" href="<?php $this->permalink() ?>"
itemprop="datePublished">
<?php $options = Helper::options();if ($options->friendlyTime == '1') {echo time_ago($this->created);} else {$this->date('Y-m-d H:i'); }?>
</a>
</header>
<div class="description" itemprop="about">
<?php $this->excerpt(200, '...'); ?>
</div>
</div>
</article>
</div>
<?php else: ?>
<div id="loadpost">
<article class="post--item" id="loadpost">
<div class="content">
<h2 class="post--title">
<a href="<?php $this->permalink() ?>">
<?php $this->title() ?><?php if (isset($this->isSticky) && $this->isSticky): ?><?php echo $this->stickyHtml; ?><?php endif; ?>
<?php if((time() - $this->created) < 60*60*24*3): ?>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
class="icon--sticky" stroke="currentColor" stroke-width="2" stroke-linecap="round"
stroke-linejoin="round" class="w-4 h-4 text-red-400">
<path d="M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z"></path>
</svg>
<?php endif; ?>
</a>
</h2>
<div class="description">
<?php
if($this->fields->summary){
echo $this->fields->summary;
} else {
$this->excerpt(180);
}?>
</div>
<div class="meta">
<svg class="icon" viewBox="0 0 1024 1024" width="16" height="16"><path d="M512 97.52381c228.912762 0 414.47619 185.563429 414.47619 414.47619s-185.563429 414.47619-414.47619 414.47619S97.52381 740.912762 97.52381 512 283.087238 97.52381 512 97.52381z m0 73.142857C323.486476 170.666667 170.666667 323.486476 170.666667 512s152.81981 341.333333 341.333333 341.333333 341.333333-152.81981 341.333333-341.333333S700.513524 170.666667 512 170.666667z m36.571429 89.697523v229.86362h160.865523v73.142857H512a36.571429 36.571429 0 0 1-36.571429-36.571429V260.388571h73.142858z"></path></svg>
<time>
<?php $options = Helper::options();if ($options->friendlyTime == '1') {echo time_ago($this->created);} else {$this->date('Y-m-d H:i'); }?>
</time>
<svg class="icon" viewBox="0 0 1024 1024" width="16" height="16"><path d="M408.551619 97.52381a73.142857 73.142857 0 0 1 51.736381 21.430857L539.306667 197.973333A73.142857 73.142857 0 0 0 591.067429 219.428571H804.571429a73.142857 73.142857 0 0 1 73.142857 73.142858v560.761904a73.142857 73.142857 0 0 1-73.142857 73.142857H219.428571a73.142857 73.142857 0 0 1-73.142857-73.142857V170.666667a73.142857 73.142857 0 0 1 73.142857-73.142857h189.123048z m0 73.142857H219.428571v682.666666h585.142858V292.571429h-213.504a146.285714 146.285714 0 0 1-98.499048-38.13181L487.619048 249.734095 408.551619 170.666667zM365.714286 633.904762v73.142857h-73.142857v-73.142857h73.142857z m365.714285 0v73.142857H414.47619v-73.142857h316.952381z m-365.714285-195.047619v73.142857h-73.142857v-73.142857h73.142857z m365.714285 0v73.142857H414.47619v-73.142857h316.952381z"></path></svg>
<?php $this->category(','); ?>
<svg class="icon" viewBox="0 0 24 24" width="16" height="16" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M12 9C10.3431 9 9 10.3431 9 12C9 13.6569 10.3431 15 12 15C13.6569 15 15 13.6569 15 12C15 10.3431 13.6569 9 12 9ZM11 12C11 11.4477 11.4477 11 12 11C12.5523 11 13 11.4477 13 12C13 12.5523 12.5523 13 12 13C11.4477 13 11 12.5523 11 12Z" /><path fill-rule="evenodd" clip-rule="evenodd" d="M21.83 11.2807C19.542 7.15186 15.8122 5 12 5C8.18777 5 4.45796 7.15186 2.17003 11.2807C1.94637 11.6844 1.94361 12.1821 2.16029 12.5876C4.41183 16.8013 8.1628 19 12 19C15.8372 19 19.5882 16.8013 21.8397 12.5876C22.0564 12.1821 22.0536 11.6844 21.83 11.2807ZM12 17C9.06097 17 6.04052 15.3724 4.09173 11.9487C6.06862 8.59614 9.07319 7 12 7C14.9268 7 17.9314 8.59614 19.9083 11.9487C17.9595 15.3724 14.939 17 12 17Z" /></svg>
<span class="article--views">
<?php get_post_view($this) ?>
</span>
<svg viewBox="0 0 24 24" class="icon" aria-hidden="true" width="16" height="16"><g><path d="M1.751 10c0-4.42 3.584-8 8.005-8h4.366c4.49 0 8.129 3.64 8.129 8.13 0 2.96-1.607 5.68-4.196 7.11l-8.054 4.46v-3.69h-.067c-4.49.1-8.183-3.51-8.183-8.01zm8.005-6c-3.317 0-6.005 2.69-6.005 6 0 3.37 2.77 6.08 6.138 6.01l.351-.01h1.761v2.3l5.087-2.81c1.951-1.08 3.163-3.13 3.163-5.36 0-3.39-2.744-6.13-6.129-6.13H9.756z"></path></g></svg>
<a href="<?php $this->permalink() ?>#comments"><?php $this->commentsNum('0 ', '1 ', '%d '); ?></a>
</div>
</div>
<?php
$firstImage = img_postthumb($this->cid);
$cover = $this->fields->cover;
$imageToDisplay = !empty($cover) ? $cover : $firstImage;
if($imageToDisplay):
$imageToDisplay = process_cover_image($imageToDisplay);
?>
<a href="<?php $this->permalink() ?>" class="cover--link">
<img src="<?php echo $imageToDisplay; ?>" alt="<?php $this->title() ?>" class="cover" itemprop="image"/>
</a>
<?php endif; ?>
</article>
</div>
<?php endif; ?>
<?php endwhile; ?>
</div>

View File

@ -8,6 +8,6 @@
<?php $this->options->description() ?>
</div>
<div class="author--sns">
<?php $this->need('sns.php'); ?>
<?php $this->need('./module/sns.php'); ?>
</div>
</div>

View File

@ -1,8 +1,9 @@
<?php if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
<div class="related--content">
<?php $this->related(6)->to($relatedPosts); ?>
<?php if ($relatedPosts->have()): ?>
<h3 class="related--posts__title">相关文章</h3>
<div class="post--single__related">
<?php $this->related(6)->to($relatedPosts); ?>
<?php while ($relatedPosts->next()): ?>
<div class="post--single__related__item">
<a href="<?php $relatedPosts->permalink(); ?>">
@ -18,4 +19,5 @@
</div>
<?php endwhile; ?>
</div>
<?php endif; ?>
</div>

124
module/sticky.php Normal file
View File

@ -0,0 +1,124 @@
<?php if (!defined('__TYPECHO_ROOT_DIR__')) exit;
$sticky = $this->options->sticky;
$db = Typecho_Db::get();
$pageSize = $this->options->pageSize;
if ($sticky && !empty(trim($sticky))) {
$sticky_cids = array_filter(explode('|', $sticky));
if (!empty($sticky_cids)) {
$sticky_html = " <span class='sticky--post'> 置顶 </span>";
// 保存原始对象状态
$originalRows = $this->row;
$originalStack = $this->stack;
$originalLength = $this->length;
$totalOriginal = $this->getTotal();
// 重置当前对象状态
$this->row = [];
$this->stack = [];
$this->length = 0;
// 关键修改:不再减少总文章数
// 保持原始总数,这样分页逻辑不会受影响
// $this->setTotal(max($totalOriginal - $stickyCount, 0));
if (isset($this->currentPage) && $this->currentPage == 1) {
// 查询置顶文章
$selectSticky = $this->select()->where('type = ?', 'post');
foreach ($sticky_cids as $i => $cid) {
if ($i == 0)
$selectSticky->where('cid = ?', $cid);
else
$selectSticky->orWhere('cid = ?', $cid);
}
$stickyPosts = $db->fetchAll($selectSticky);
// 添加置顶文章到结果集
foreach ($stickyPosts as &$stickyPost) {
$stickyPost['isSticky'] = true;
$stickyPost['stickyHtml'] = $sticky_html;
$this->push($stickyPost);
}
// 计算当前页应显示的普通文章数量
$standardPageSize = $pageSize - count($stickyPosts);
// 确保第一页不会显示太多文章
if ($standardPageSize <= 0) {
$standardPageSize = 0; // 如果置顶文章已经填满或超过一页,则不显示普通文章
}
} else {
// 非第一页显示正常数量的文章
$standardPageSize = $pageSize;
}
// 查询普通文章
if ($this->currentPage == 1) {
// 第一页需要排除置顶文章并限制数量
$selectNormal = $this->select()
->where('type = ?', 'post')
->where('status = ?', 'publish')
->where('created < ?', time());
// 排除所有置顶文章
foreach ($sticky_cids as $cid) {
$selectNormal->where('table.contents.cid != ?', $cid);
}
$selectNormal->order('created', Typecho_Db::SORT_DESC)
->limit($standardPageSize)
->offset(0);
} else {
// 非第一页的查询
// 计算正确的偏移量:(当前页码-1) * 每页数量 - 置顶文章数
// 这样可以确保不会漏掉文章
$offset = ($this->currentPage - 1) * $pageSize - count($sticky_cids);
$offset = max($offset, 0); // 确保偏移量不为负
$selectNormal = $this->select()
->where('type = ?', 'post')
->where('status = ?', 'publish')
->where('created < ?', time());
// 排除所有置顶文章
foreach ($sticky_cids as $cid) {
$selectNormal->where('table.contents.cid != ?', $cid);
}
$selectNormal->order('created', Typecho_Db::SORT_DESC)
->limit($pageSize)
->offset($offset);
}
} else {
// 没有有效的置顶文章ID正常查询
$selectNormal = $this->select()
->where('type = ?', 'post')
->where('status = ?', 'publish')
->where('created < ?', time())
->order('created', Typecho_Db::SORT_DESC)
->page(isset($this->currentPage) ? $this->currentPage : 1, $pageSize);
}
} else {
// 没有设置置顶文章,正常查询
$selectNormal = $this->select()
->where('type = ?', 'post')
->where('status = ?', 'publish')
->where('created < ?', time())
->order('created', Typecho_Db::SORT_DESC)
->page(isset($this->currentPage) ? $this->currentPage : 1, $pageSize);
}
// 添加私有文章查询条件
if ($this->user->hasLogin()) {
$uid = $this->user->uid;
if ($uid) {
$selectNormal->orWhere('authorId = ? AND status = ?', $uid, 'private');
}
}
// 获取普通文章
$normalPosts = $db->fetchAll($selectNormal);
// 如果没有置顶文章或在前面的代码中没有重置对象状态,则在这里重置
if (empty($sticky) || empty(trim($sticky)) || empty($sticky_cids)) {
$this->row = [];
$this->stack = [];
$this->length = 0;
}
// 将普通文章添加到结果集
foreach ($normalPosts as $normalPost) {
$this->push($normalPost);
}
?>

41
module/travel.php Normal file
View File

@ -0,0 +1,41 @@
<?php if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
<div class="post--cards" id="loadposts">
<?php while($this->next()): ?>
<?php // 获取文章图片
$default_thumbnail = Helper::options()->themeUrl . '/assets/images/nopic.svg';
$firstImage = img_postthumb($this->cid);
if (empty($firstImage)) {
$firstImage = $default_thumbnail;
}
$cover = $this->fields->cover;
$imageToDisplay = $cover;
if (empty($imageToDisplay)) {
$imageToDisplay = $firstImage;
}
if (!empty($imageToDisplay) && $imageToDisplay != $default_thumbnail) {
$imageToDisplay = process_cover_image($imageToDisplay);
}
?>
<article class="post--card" id="loadpost">
<img src="<?php echo $imageToDisplay; ?>" alt="<?php $this->title() ?>" class="cover" itemprop="image"/>
<div class="content">
<h2 class="post--title">
<a href="<?php $this->permalink() ?>">
<?php $this->title() ?>
</a>
</h2>
<div class="description">
<?php $this->excerpt(20, '...'); ?>
</div>
<div class="meta">
<svg class="icon" viewBox="0 0 1024 1024" width="16" height="16">
<path d="M512 97.52381c228.912762 0 414.47619 185.563429 414.47619 414.47619s-185.563429 414.47619-414.47619 414.47619S97.52381 740.912762 97.52381 512 283.087238 97.52381 512 97.52381z m0 73.142857C323.486476 170.666667 170.666667 323.486476 170.666667 512s152.81981 341.333333 341.333333 341.333333 341.333333-152.81981 341.333333-341.333333S700.513524 170.666667 512 170.666667z m36.571429 89.697523v229.86362h160.865523v73.142857H512a36.571429 36.571429 0 0 1-36.571429-36.571429V260.388571h73.142858z"></path>
</svg>
<time datetime='<?php $this->date('Y-m-d'); ?>' class="humane--time">
<?php $options = Helper::options();if ($options->friendlyTime == '1') {echo time_ago($this->created);} else {$this->date('Y-m-d H:i'); }?>
</time>
</div>
</div>
</article>
<?php endwhile; ?>
</div>

View File

@ -6,7 +6,7 @@
*/
if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
<?php $this->need('header.php'); ?>
<script src="<?php $this->options->themeUrl('/dist/js/db.js'); ?>"></script>
<script src="<?php $this->options->themeUrl('assets/js/db.js'); ?>"></script>
<section class="site--main">
<header class="archive--header">
<h1 class="post--single__title"><?php $this->title() ?></h1>

View File

@ -1,64 +1,87 @@
<?php
<?php
/**
* 好物页面
*
* @package custom
*/
if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
<?php $this->need('header.php'); ?>
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
$this->need('header.php');
?>
<div class="site--main site--main__gears">
<header class="archive--header">
<h1 class="post--single__title"><?php $this->title() ?></h1>
<h2 class="post--single__subtitle"><?php $this->content(); ?></h2>
</header>
<div id=goods class="good--list"></div>
</div>
<style>
.img40 {
height: 137px;
width: auto;
}
</style>
<?php
// 检查是否存在自定义字段 'memos' 和 'memosID'
$memos = $this->fields->memos ? $this->fields->memos : 'https://memos.imsun.org';
$memosID = $this->fields->memosID ? $this->fields->memosID : '1';
$memostag = $this->fields->memostag ? $this->fields->memostag : '好物';
?>
<script>
document.addEventListener("DOMContentLoaded", () => {
memoGoods();
});
function memoGoods(e) {
let t = e || 12;
var n = "<?php echo $memos; ?>",
s = n + "/api/v1/memo?creatorId=<?php echo $memosID; ?>&limit=" + t + "&tag=<?php echo $memostag; ?>";
let i = 1;
const o = /\n/;
fetch(s).then(e => e.json()).then(e => {
let c = "";
for (var t, s, i, a, d, u, h, m, r = 0; r < e.length; r++) {
a = e[r].content.replace(`#好物 \n`, ""),
t = a.split(o),
i = t[0].replace(/!\[.*?\]\((.*?)\)/g, "$1"), // 图片链接
s = t[0].replace(/!\[(.*?)\]\(.*?\)/g, "$1"),
d = s.split(",")[0], // 商品名称
u = s.split(",")[1], // 价格
h = t[1].replace(/\[.*?\]\((.*?)\)/g, "$1"), // 商品链接
m = t[1].replace(/\[(.*?)\]\(.*?\)/g, "$1"), // 推荐理由
c +=
'<div class="good--item"><div class="img-spacer"><a href="'
+ h +
'" target="_blank"><img src="'
+ i +
'" class="img40"></a></div><div class="good--name"><div class="brand">'
+ d + '·' + m +'</div>'
+ u +
'</div></div>';
<header class="archive--header">
<h1 class="post--single__title"><?php $this->title() ?></h1>
</header>
<div id="goods" class="good--list">
<?php
// 获取内容并解析
$content = $this->content;
$goods = parseGoodsTable($content);
if (!empty($goods)) {
foreach ($goods as $item): ?>
<div class="good--item">
<div class="img-spacer">
<a href="<?php echo htmlspecialchars($item['link']); ?>" target="_blank" rel="noopener noreferrer">
<img src="<?php echo htmlspecialchars($item['image']); ?>" class="img40" alt="<?php echo htmlspecialchars($item['name']); ?>">
</a>
</div>
<div class="good--name">
<div class="brand">
<?php echo htmlspecialchars($item['name']); ?>·<?php echo htmlspecialchars($item['description']); ?>
</div>
<?php echo htmlspecialchars($item['price']); ?>
</div>
</div>
<?php endforeach;
} else {
echo '<div class="no-goods">暂无商品数据,请按照格式填写商品信息。</div>';
}
let f = document.querySelector("#goods");
f.innerHTML = c;
});
?>
</div>
<?php if ($this->allow('comment')): ?>
<?php $this->need('./module/comments.php'); ?>
<?php endif; ?>
</div>
<style>.img40{height:137px;width:auto;object-fit:cover;}.img-spacer{width:100%;aspect-ratio:1;overflow:hidden;}.brand{font-weight:500;margin-bottom:5px;}.no-goods{grid-column:1 / -1;text-align:center;padding:20px;background:#f5f5f5;border-radius:8px;}</style>
<?php
/**
* 解析商品表格数据
* @param string $content 页面内容
* @return array 解析后的商品数据
*/
function parseGoodsTable($content) {
$goods = array();
// 创建DOM对象
$dom = new DOMDocument();
libxml_use_internal_errors(true); // 禁用libxml错误
$dom->loadHTML(mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8'), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
libxml_clear_errors();
// 查找表格
$tables = $dom->getElementsByTagName('table');
if ($tables->length > 0) {
$table = $tables->item(0); // 获取第一个表格
$rows = $table->getElementsByTagName('tr');
// 跳过表头行
for ($i = 1; $i < $rows->length; $i++) {
$row = $rows->item($i);
$cells = $row->getElementsByTagName('td');
// 确保有足够的单元格
if ($cells->length >= 5) {
$item = array(
'image' => trim($cells->item(0)->textContent),
'name' => trim($cells->item(1)->textContent),
'price' => trim($cells->item(2)->textContent),
'link' => trim($cells->item(3)->textContent),
'description' => trim($cells->item(4)->textContent)
);
// 确保必要字段不为空
if (!empty($item['image']) && !empty($item['name'])) {
$goods[] = $item;
}
}
}
}
return $goods;
}
</script>
<?php $this->need('footer.php'); ?>
$this->need('footer.php');
?>

View File

@ -13,17 +13,13 @@ if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
</header>
<div class="template--linksWrap">
<ul class="link-items">
<?php
Links_Plugin::output('<li class="link-item"><a class="link-item-inner effect-apollo" href="{url}" target="_blank">
<span class="sitename"><strong>{name}</strong>{title}</span>
</a></li>');
?>
</ul>
<?php Links_Plugin::output('<li class="link-item"><a class="link-item-inner effect-apollo" href="{url}" target="_blank"><span class="sitename"><strong>{name}</strong>{title}</span></a></li>'); ?>
</ul>
</div>
<article class="post--single">
<?php if ($this->allow('comment')): ?>
<?php $this->need('comments.php'); ?>
<?php endif; ?>
<?php if ($this->allow('comment')): ?>
<?php $this->need('module/comments.php'); ?>
<?php endif; ?>
</article>
</section>
<?php $this->need('footer.php'); ?>

View File

@ -13,9 +13,9 @@ if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
</header>
<article class="post--single">
<?php $tooot = $this->fields->tooot ? $this->fields->tooot : 'https://bbapi.ima.cm'; ?>
<script src="<?php $this->options->themeUrl('/dist/js/marked.min.js'); ?>"></script>
<link rel="stylesheet" href="<?php $this->options->themeUrl('/dist/css/lightbox.min.css'); ?>">
<script src="<?php $this->options->themeUrl('/dist/js/lightbox-plus-jquery.min.js'); ?>"></script>
<script src="<?php $this->options->themeUrl('assets/js/marked.min.js'); ?>"></script>
<link rel="stylesheet" href="<?php $this->options->themeUrl('assets/css/lightbox.min.css'); ?>">
<script src="<?php $this->options->themeUrl('assets/js/lightbox-plus-jquery.min.js'); ?>"></script>
<div id="tooot"></div>
<div class="nav-links" id="loadmore">
<span class="loadmore">加载更多</span>
@ -146,33 +146,6 @@ img {
height: 480px;
}
}
.load-more-btn {
display: block;
margin: 20px auto;
padding: 10px 20px;
background-color: #007bff;
color: white;
border: none;
cursor: pointer;
border-radius: 5px;
}
.load-more-btn:hover {
background-color: #0056b3;
}
.nav-links .loadmore {
border: 1px solid var(--farallon-border-color);
cursor: pointer;
position: relative;
padding: 5px 30px;
border-radius: 8px;
font-size: 14px;
color: var(--farallon-text-gray)
}
.nav-links .loadmore:hover {
border-color: var(--farallon-hover-color);
color: var(--farallon-hover-color)
}
</style>
</div>
<?php $this->need('footer.php'); ?>

View File

@ -16,11 +16,11 @@ if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
$memos = $this->fields->memos ? $this->fields->memos : 'https://memos.imsun.org';
$memosID = $this->fields->memosID ? $this->fields->memosID : '1';
$memosnum = $this->fields->memosnum ? $this->fields->memosnum : '20';
?>
?>
<article class="post--single">
<script src="<?php $this->options->themeUrl('/dist/js/marked.min.js'); ?>"></script>
<link rel="stylesheet" href="<?php $this->options->themeUrl('/dist/css/lightbox.min.css'); ?>">
<script src="<?php $this->options->themeUrl('/dist/js/lightbox-plus-jquery.min.js'); ?>"></script>
<script src="<?php $this->options->themeUrl('assets/js/marked.min.js'); ?>"></script>
<link rel="stylesheet" href="<?php $this->options->themeUrl('assets/css/lightbox.min.css'); ?>">
<script src="<?php $this->options->themeUrl('assets/js/lightbox-plus-jquery.min.js'); ?>"></script>
<div id="talk"></div>
<div class="nav-links" id="loadmore">
<span class="loadmore">加载更多</span>
@ -32,7 +32,6 @@ const limit = 10; // 每页条数
let url = '<?php echo $memos; ?>';
let memosID = '<?php echo $memosID; ?>';
let memosnum = '<?php echo $memosnum; ?>';
function loadMemos(page) {
fetch(`${url}/api/v1/memo?creatorId=${memosID}&rowStatus=NORMAL&limit=${limit}&offset=${(page - 1) * limit}`)
.then(res => res.json())
@ -64,7 +63,6 @@ function loadMemos(page) {
// 这里可以添加一些用户提示错误发生的 HTML 更新
});
}
function Format(item) {
let date = getTime(new Date(item.createdTs * 1000).toString()),
content = item.content,
@ -94,7 +92,6 @@ function Format(item) {
text: text.replace(/\[(.*?)\]\((.*?)\)/g, '[链接]' + `${imgs?'[图片]':''}`)
}
}
// 页面时间格式化
function getTime(time) {
let d = new Date(time),
@ -105,16 +102,13 @@ function getTime(time) {
if (new Date().getFullYear() == ls[0]) return ls[1] + '月' + ls[2] + '日 ' + ls[3] +':'+ ls[4]
else return ls[0] + '年' + ls[1] + '月' + ls[2] + '日 ' + ls[3] +':'+ ls[4]
}
// 初始加载第一页
loadMemos(currentPage);
// 点击“加载更多”按钮时加载下一页
document.getElementById('loadmore').addEventListener('click', function() {
currentPage++;
loadMemos(currentPage);
});
</script>
<style>
div pre code {
@ -166,34 +160,7 @@ img {
.resimg {
grid-template-columns: 1fr; /* 修改为一列 */
}
}
.load-more-btn {
display: block;
margin: 20px auto;
padding: 10px 20px;
background-color: #007bff;
color: white;
border: none;
cursor: pointer;
border-radius: 5px;
}
.load-more-btn:hover {
background-color: #0056b3;
}
.nav-links .loadmore {
border: 1px solid var(--farallon-border-color);
cursor: pointer;
position: relative;
padding: 5px 30px;
border-radius: 8px;
font-size: 14px;
color: var(--farallon-text-gray)
}
.nav-links .loadmore:hover {
border-color: var(--farallon-hover-color);
color: var(--farallon-hover-color)
}
</style>
</div>
<?php $this->need('footer.php'); ?>

View File

@ -6,23 +6,23 @@
*/
if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
<?php $this->need('header.php'); ?>
<link rel="stylesheet" href="<?php $this->options->themeUrl('/dist/css/neodb.css'); ?>">
<script src="<?php $this->options->themeUrl('/dist/js/neodb.js'); ?>"></script>
<link rel="stylesheet" href="<?php $this->options->themeUrl('assets/css/neodb.css'); ?>">
<script src="<?php $this->options->themeUrl('assets/js/neodb.js'); ?>"></script>
<section class="site--main">
<header class="archive--header">
<h1 class="post--single__title"><?php $this->title() ?></h1>
<h2 class="post--single__subtitle"><?php $this->content(); ?></h2>
</header>
<div class="site--main">
<div class="neodb-container"></div>
<?php $neodb = $this->fields->neodb ? $this->fields->neodb : 'https://neodb.imsun.org'; ?>
<script>
const neodb = new NeoDB({
container: ".neodb-container",
baseAPI: "<?php echo $neodb; ?>/api",
types: ["book", "movie", "tv", "music", "game"],
});
</script>
</div>
<div class="neodb-container"></div>
<?php $neodb = $this->fields->neodb ? $this->fields->neodb : 'https://neodb.imsun.org'; ?>
<script>
const neodb = new NeoDB({
container: ".neodb-container",
baseAPI: "<?php echo $neodb; ?>/api",
types: ["book", "movie", "tv", "music", "game"],
});
</script>
</div>
</section>
<?php $this->need('footer.php'); ?>

View File

@ -24,7 +24,9 @@ if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
<?php endwhile; ?>
</div>
<?php else: ?>
<p class="text-center pb-2"><?php _e('没有任何标签'); ?></p>
<div style="display: grid; place-items: center;">
<p style="padding-bottom: 2px;"><?php _e('暂无标签'); ?></p>
</div>
<?php endif; ?>
</div>
</section>

View File

@ -10,10 +10,8 @@
</div>
<!-- 判断如果禁止评论则不显示评论的div -->
<?php if ($this->allow('comment')): ?>
<?php $this->need('comments.php'); ?>
<?php $this->need('./module/comments.php'); ?>
<?php endif; ?>
<!-- 可以使用第三方评论-->
<?php $this->options->twikoo(); ?>
</article>
</section>
<?php $this->need('footer.php'); ?>

279
post.php
View File

@ -1,13 +1,6 @@
<?php if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
<?php $this->need('header.php'); ?>
<style>
#toc {font-size:14px;padding:10px 15px;background-color:var(--farallon-background-gray);border-radius:10px;margin-bottom:20px}
#toc summary{cursor:pointer}
#toc toc-title{font-weight:600}
#toc ul{padding-left:10px;margin-bottom:10px}
#toc ul li::before{content:"·";margin-right:5px}
#toc ul li>ul{margin-left:10px;font-size:12px}
</style>
<style>#toc{font-size:14px;padding:10px 15px;background-color:var(--farallon-background-gray);border-radius:10px;margin-bottom:20px}#toc summary{cursor:pointer}#toc toc-title{font-weight:600}#toc ul{padding-left:10px;margin-bottom:10px}#toc ul li::before{content:"·";margin-right:5px}#toc ul li>ul{margin-left:10px;font-size:12px}</style>
<main class="site--main">
<article class="post--single">
<ul class="meta">
@ -16,7 +9,7 @@
<path
d="M512 97.52381c228.912762 0 414.47619 185.563429 414.47619 414.47619s-185.563429 414.47619-414.47619 414.47619S97.52381 740.912762 97.52381 512 283.087238 97.52381 512 97.52381z m0 73.142857C323.486476 170.666667 170.666667 323.486476 170.666667 512s152.81981 341.333333 341.333333 341.333333 341.333333-152.81981 341.333333-341.333333S700.513524 170.666667 512 170.666667z m36.571429 89.697523v229.86362h160.865523v73.142857H512a36.571429 36.571429 0 0 1-36.571429-36.571429V260.388571h73.142858z">
</path>
</svg><time class="humane--time"><?php $this->date('Y-m-d'); ?></time>
</svg><time class="humane--time"><?php $options = Helper::options();if ($options->friendlyTime == '1') {echo time_ago($this->created);} else {$this->date('Y-m-d H:i'); }?></time>
<svg class="icon" viewBox="0 0 1024 1024" width="16" height="16">
<path
d="M408.551619 97.52381a73.142857 73.142857 0 0 1 51.736381 21.430857L539.306667 197.973333A73.142857 73.142857 0 0 0 591.067429 219.428571H804.571429a73.142857 73.142857 0 0 1 73.142857 73.142858v560.761904a73.142857 73.142857 0 0 1-73.142857 73.142857H219.428571a73.142857 73.142857 0 0 1-73.142857-73.142857V170.666667a73.142857 73.142857 0 0 1 73.142857-73.142857h189.123048z m0 73.142857H219.428571v682.666666h585.142858V292.571429h-213.504a146.285714 146.285714 0 0 1-98.499048-38.13181L487.619048 249.734095 408.551619 170.666667zM365.714286 633.904762v73.142857h-73.142857v-73.142857h73.142857z m365.714285 0v73.142857H414.47619v-73.142857h316.952381z m-365.714285-195.047619v73.142857h-73.142857v-73.142857h73.142857z m365.714285 0v73.142857H414.47619v-73.142857h316.952381z">
@ -45,97 +38,44 @@
</div>
<h2 class="post--single__title"><?php $this->title() ?></h2>
<div class="post--single__content graph" ><?php $this->content(); ?></div>
<?php if($this->options->donate): ?>
<?php if($this->options->wxpay): ?>
<!--打赏 -->
<div class="post--single__action">
<link rel="stylesheet" href="<?php $this->options->themeUrl('/dist/css/donate.css'); ?>">
<script type="text/javascript" src="<?php $this->options->themeUrl('/dist/js/donate.js'); ?>"></script>
<div class="donate-panel">
<div id="donate-btn">
<button class="button--like">
<svg class="icon--default" viewBox="0 0 1024 1024" width="32" height="32">
<path
d="M332.8 249.6c38.4 0 83.2 19.2 108.8 44.8L467.2 320 512 364.8 556.8 320l25.6-25.6c32-32 70.4-44.8 108.8-44.8 19.2 0 38.4 6.4 57.6 12.8 44.8 25.6 70.4 57.6 76.8 108.8 6.4 44.8-6.4 89.6-38.4 121.6L512 774.4 236.8 492.8C204.8 460.8 185.6 416 192 371.2c6.4-44.8 38.4-83.2 76.8-108.8C288 256 313.6 249.6 332.8 249.6L332.8 249.6M332.8 185.6C300.8 185.6 268.8 192 243.2 204.8 108.8 275.2 89.6 441.6 185.6 537.6l281.6 281.6C480 832 499.2 838.4 512 838.4s32-6.4 38.4-19.2l281.6-281.6c96-96 76.8-262.4-57.6-332.8-25.6-12.8-57.6-19.2-89.6-19.2-57.6 0-115.2 25.6-153.6 64L512 275.2 486.4 249.6C448 211.2 390.4 185.6 332.8 185.6L332.8 185.6z">
</path>
</svg>
</button>
</div>
<div id="qrcode-panel" style="display: none;">
<div class="qrcode-body">
<div class="donate-memo">
<span id="donate-close">关闭</span>
</div>
<div class="donate-qrpay">
<img id="wxqr" src="<?php $this->options->donate() ?>" />
</div>
</div>
</div>
</div>
<button class="button--like" onclick="openModal()">
<svg class="icon--default" viewBox="0 0 1024 1024" width="32" height="32"><path d="M332.8 249.6c38.4 0 83.2 19.2 108.8 44.8L467.2 320 512 364.8 556.8 320l25.6-25.6c32-32 70.4-44.8 108.8-44.8 19.2 0 38.4 6.4 57.6 12.8 44.8 25.6 70.4 57.6 76.8 108.8 6.4 44.8-6.4 89.6-38.4 121.6L512 774.4 236.8 492.8C204.8 460.8 185.6 416 192 371.2c6.4-44.8 38.4-83.2 76.8-108.8C288 256 313.6 249.6 332.8 249.6L332.8 249.6M332.8 185.6C300.8 185.6 268.8 192 243.2 204.8 108.8 275.2 89.6 441.6 185.6 537.6l281.6 281.6C480 832 499.2 838.4 512 838.4s32-6.4 38.4-19.2l281.6-281.6c96-96 76.8-262.4-57.6-332.8-25.6-12.8-57.6-19.2-89.6-19.2-57.6 0-115.2 25.6-153.6 64L512 275.2 486.4 249.6C448 211.2 390.4 185.6 332.8 185.6L332.8 185.6z"></path></svg>
</button>
</div>
<!-- 弹窗 -->
<div class="modal" id="modal" onclick="closeModal()">
<div class="modal-content" onclick="event.stopPropagation()">
<span class="close-btn" onclick="closeModal()">&times;</span>
<p>如果觉得文章对你有帮助,可以请作者喝杯咖啡 ☕️</p>
<img src="<?php $this->options->wxpay() ?>" alt="微信二维码">
<img src="<?php $this->options->alipay() ?>" alt="支付宝二维码">
</div>
</div>
<script>function openModal(){document.getElementById('modal').style.display='flex'}function closeModal(){document.getElementById('modal').style.display='none'}</script>
<style>.button--like{background:none;border:none;padding:0;cursor:pointer;transition:transform 0.3s ease;}.button--like:hover{transform:scale(1.1);}.icon--default{fill:#666;transition:fill 0.3s ease;}.button--like:hover .icon--default{fill:#ff4081;}.modal{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background-color:rgba(0,0,0,0.1);justify-content:center;align-items:center;z-index:1000;}.modal-content{background-color:white;padding:20px;border-radius:10px;text-align:center;position:relative;}.modal-content img{width:200px;height:200px;margin-top:10px;}.close-btn{position:absolute;top:10px;right:10px;cursor:pointer;font-size:20px;color:#888;}</style>
<?php endif; ?>
<!-- 复制链接 -->
<?php if($this->options->showshare): ?>
<div class="post--share">
<svg viewBox="0 0 24 24" aria-hidden="true">
<g>
<path d="M18.36 5.64c-1.95-1.96-5.11-1.96-7.07 0L9.88 7.05 8.46 5.64l1.42-1.42c2.73-2.73 7.16-2.73 9.9 0 2.73 2.74 2.73 7.17 0 9.9l-1.42 1.42-1.41-1.42 1.41-1.41c1.96-1.96 1.96-5.12 0-7.07zm-2.12 3.53l-7.07 7.07-1.41-1.41 7.07-7.07 1.41 1.41zm-12.02.71l1.42-1.42 1.41 1.42-1.41 1.41c-1.96 1.96-1.96 5.12 0 7.07 1.95 1.96 5.11 1.96 7.07 0l1.41-1.41 1.42 1.41-1.42 1.42c-2.73 2.73-7.16 2.73-9.9 0-2.73-2.74-2.73-7.17 0-9.9z"></path>
</g>
</svg>
<span class="text">复制链接</span>
<span class="link" @click="copy" data-link="<?php $this->permalink(); ?>"><?php $this->permalink(); ?></span>
<script src="<?php $this->options->themeUrl('/dist/js/vue.min.js'); ?>"></script>
<script src="<?php $this->options->themeUrl('/dist/js/clipboard.min.js'); ?>"></script>
<script>
var app = new Vue({
el: '.post--share',
data() {
return {
msg: "<?php $this->permalink(); ?>",
};
},
methods: {
//复制方法
copy: function () {
var that = this;
var clipboard = new ClipboardJS(".link",{
text: function (trigger) {
//返回字符串
return that.msg;
}});
clipboard.on("success", (e) => {
//复制成功,显示提示
this.showCopySuccessToast();
clipboard.destroy();
});
clipboard.on("error", (e) => {
//复制失败
clipboard.destroy();
});
},
showCopySuccessToast: function() {
const toast = document.createElement("div");
toast.textContent = "复制成功!";
toast.className = "notice--wrapper";
document.body.appendChild(toast);
setTimeout(() => {
document.body.removeChild(toast);
}, 3000);
}
}
});
</script>
</div>
<?php endif; ?>
<!-- TAG -->
<svg viewBox="0 0 24 24" aria-hidden="true"><g><path d="M18.36 5.64c-1.95-1.96-5.11-1.96-7.07 0L9.88 7.05 8.46 5.64l1.42-1.42c2.73-2.73 7.16-2.73 9.9 0 2.73 2.74 2.73 7.17 0 9.9l-1.42 1.42-1.41-1.42 1.41-1.41c1.96-1.96 1.96-5.12 0-7.07zm-2.12 3.53l-7.07 7.07-1.41-1.41 7.07-7.07 1.41 1.41zm-12.02.71l1.42-1.42 1.41 1.42-1.41 1.41c-1.96 1.96-1.96 5.12 0 7.07 1.95 1.96 5.11 1.96 7.07 0l1.41-1.41 1.42 1.41-1.42 1.42c-2.73 2.73-7.16 2.73-9.9 0-2.73-2.74-2.73-7.17 0-9.9z"></path></g></svg>
<span class="cpoy" data-copy="<?php $this->permalink(); ?>">复制链接</span>
<span class="link copy"><?php $this->permalink(); ?></span>
<div class="notice--wrapper" id="copyTooltip" style="display: none;">复制成功!</div>
</div>
<?php endif; ?>
<!-- TAG -->
<div class="tag--list artile--tag">
<?php $this->tags(' ', true, ' '); ?>
</div>
<!-- 个人信息-->
<?php if ($this->options->showProfile): ?>
<?php $this->need('profile.php'); ?>
<?php endif; ?>
<!-- 分类-->
<?php if ($this->options->showcate): ?>
<!-- 个人信息-->
<?php if ($this->options->showProfile): ?>
<?php $this->need('module/profile.php'); ?>
<?php endif; ?>
<!-- 分类-->
<?php if ($this->options->showcate): ?>
<?php
// 初始化分类图片地址为空
$categoryImage = '';
@ -169,14 +109,11 @@
<?php endif; ?>
<!-- 相关文章-->
<?php if ($this->options->showrelated): ?>
<?php $this->need('related.php'); ?>
<?php $this->need('module/related.php'); ?>
<?php endif; ?>
<?php if ($this->allow('comment')): ?>
<?php $this->need('module/comments.php'); ?>
<?php endif; ?>
<!-- 如果设置了第三方评论系统则使用第三方评论 -->
<?php if($this->options->twikoo): ?>
<?php $this->options->twikoo(); ?>
<?php else: ?>
<?php $this->need('comments.php'); ?>
<?php endif; ?>
<!--翻页-->
<nav class="navigation post-navigation is-active">
<div class="nav-links">
@ -191,149 +128,7 @@
</ul>
</article>
</main>
<script>
document.addEventListener('DOMContentLoaded', (event) => {
const targetClassElement = document.querySelector('.post--single__title');
const postContent = document.querySelector('.post--single__content');
if (!postContent) return;
let found = false;
for (let i = 1; i <= 6 &&!found; i++) {
if (postContent.querySelector(`h${i}`)) {
found = true;
}
}
if (!found) return;
const heads = postContent.querySelectorAll('h1, h2, h3, h4, h5, h6');
const toc = document.createElement('div');
toc.id = 'toc';
toc.innerHTML = '<details class="toc" open><summary class="toc-title">目录</summary><nav id="TableOfContents"><ul></ul></nav></details>';
// 插入到指定 class 元素之后
if (targetClassElement) {
targetClassElement.parentNode.insertBefore(toc, targetClassElement.nextSibling);
}
let currentLevel = 0;
let currentList = toc.querySelector('ul');
let levelCounts = [0];
heads.forEach((head, index) => {
const level = parseInt(head.tagName.substring(1));
if (levelCounts[level] === undefined) {
levelCounts[level] = 1;
} else {
levelCounts[level]++;
}
// 重置下级标题的计数器
levelCounts = levelCounts.slice(0, level + 1);
if (currentLevel === 0) {
currentLevel = level;
}
while (level > currentLevel) {
let newList = document.createElement('ul');
if (!currentList.lastElementChild) {
currentList.appendChild(newList);
} else {
currentList.lastElementChild.appendChild(newList);
}
currentList = newList;
currentLevel++;
levelCounts[currentLevel] = 1;
}
while (level < currentLevel) {
currentList = currentList.parentElement;
if (currentList.tagName.toLowerCase() === 'li') {
currentList = currentList.parentElement;
}
currentLevel--;
}
const anchor = head.textContent.trim().replace(/\s+/g, '-');
head.id = anchor;
const item = document.createElement('li');
const link = document.createElement('a');
link.href = `#${anchor}`;
link.textContent = `${head.textContent}`;
link.style.textDecoration = 'none';
item.appendChild(link);
currentList.appendChild(item);
});
});
</script>
<script>
function fetchWithRetry(url, retries = 3) {
return fetch(url)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.text(); // 首先获取文本响应
})
.then(text => {
try {
return JSON.parse(text); // 尝试解析 JSON
} catch (e) {
console.error('Invalid JSON:', text);
throw new Error('Invalid JSON response');
}
})
.catch(error => {
if (retries > 0) {
console.log(`Retrying... (${retries} attempts left)`);
return new Promise(resolve => setTimeout(resolve, 1000)) // 等待1秒
.then(() => fetchWithRetry(url, retries - 1));
}
throw error;
});
}
document.addEventListener('DOMContentLoaded', function() {
const doubanLinks = document.querySelectorAll('a[href^="https://movie.douban.com/subject/"]');
doubanLinks.forEach(link => {
const url = link.href;
const movieId = url.match(/subject\/(\d+)/)[1];
link.innerHTML += ' <span class="loading">(加载中...)</span>';
fetchWithRetry(`https://api.loliko.cn/movies/${movieId}`)
.then(data => {
const movieInfo = createMovieInfoHTML(data, url);
const wrapper = document.createElement('div');
wrapper.innerHTML = movieInfo;
link.parentNode.replaceChild(wrapper, link);
})
.catch(error => {
console.error('Error fetching movie data:', error);
// 显示错误消息给用户
link.innerHTML = `<span style="color: red;">加载失败</span> <a href="${url}" target="_blank">查看豆瓣电影详情</a>`;
})
.finally(() => {
const loadingSpan = link.querySelector('.loading');
if (loadingSpan) {
loadingSpan.remove();
}
});
});
});
function createMovieInfoHTML(data, originalUrl) {
if (!data || data.error || Object.keys(data).length === 0) {
return `<a href="${originalUrl}" target="_blank">查看豆瓣电影详情</a>`;
}
return `
<div class=doulist-item>
<div class=doulist-subject>
<div class=doulist-post>
<img decoding=async referrerpolicy=no-referrer src=${data.img}>
</div>
<div class=doulist-content>
<div class=doulist-title>
<a href="${originalUrl}" class=cute target="_blank" rel="external nofollow"> ${data.name} </a>
</div>
<div class=rating>
<span class=rating_nums>豆瓣评分 : ${data.rating}</span>
</div>
<div class=abstract>
${data.year} · ${data.country} · ${data.genre} · 导演: ${data.director} · 演员 : ${data.actor}
</div>
</div>
</div>
</div>
`;
}
</script>
<link rel="stylesheet" href="<?php $this->options->themeUrl('/dist/css/lightbox.min.css'); ?>">
<script src="<?php $this->options->themeUrl('/dist/js/lightbox-plus-jquery.min.js'); ?>"></script>
<script src="<?php $this->options->themeUrl('assets/js/post.js'); ?>"></script>
<link rel="stylesheet" href="<?php $this->options->themeUrl('assets/css/lightbox.min.css'); ?>">
<script src="<?php $this->options->themeUrl('assets/js/lightbox-plus-jquery.min.js'); ?>"></script>
<?php $this->need('footer.php'); ?>

View File

@ -1,184 +0,0 @@
<?php
// 确保退出安全
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
/** 文章置顶 */
$sticky = $this->options->sticky; // 置顶的文章id多个用|隔开
$db = Typecho_Db::get();
$pageSize = $this->options->pageSize;
if ($sticky && !empty(trim($sticky))) {
$sticky_cids = array_filter(explode('|', $sticky)); // 分割文本并过滤空值
if (!empty($sticky_cids)) {
$sticky_html = " <span class='sticky--post'> 置顶 </span> "; // 置顶标题的 html
// 清空原有文章的队列
$this->row = [];
$this->stack = [];
$this->length = 0;
// 获取总数,并排除置顶文章数量
if (isset($this->currentPage) && $this->currentPage == 1) {
$totalOriginal = $this->getTotal();
$stickyCount = count($sticky_cids);
$this->setTotal(max($totalOriginal - $stickyCount, 0));
// 构建置顶文章的查询
$selectSticky = $this->select()->where('type = ?', 'post');
foreach ($sticky_cids as $i => $cid) {
if ($i == 0)
$selectSticky->where('cid = ?', $cid);
else
$selectSticky->orWhere('cid = ?', $cid);
}
// 获取置顶文章
$stickyPosts = $db->fetchAll($selectSticky);
// 压入置顶文章到文章队列
foreach ($stickyPosts as &$stickyPost) {
$stickyPost['title'] .= $sticky_html;
$this->push($stickyPost);
}
$standardPageSize = $pageSize - count($stickyPosts);
} else {
$standardPageSize = $pageSize;
}
// 构建正常文章查询,排除置顶文章
$selectNormal = $this->select()
->where('type = ?', 'post')
->where('status = ?', 'publish')
->where('created < ?', time())
->order('created', Typecho_Db::SORT_DESC)
->page(isset($this->currentPage) ? $this->currentPage : 1, $standardPageSize);
foreach ($sticky_cids as $cid) {
$selectNormal->where('table.contents.cid != ?', $cid);
}
} else {
// 如果sticky_cids为空使用默认查询
$selectNormal = $this->select()
->where('type = ?', 'post')
->where('status = ?', 'publish')
->where('created < ?', time())
->order('created', Typecho_Db::SORT_DESC)
->page(isset($this->currentPage) ? $this->currentPage : 1, $pageSize);
}
} else {
// 如果没有置顶文章,使用默认查询
$selectNormal = $this->select()
->where('type = ?', 'post')
->where('status = ?', 'publish')
->where('created < ?', time())
->order('created', Typecho_Db::SORT_DESC)
->page(isset($this->currentPage) ? $this->currentPage : 1, $pageSize);
}
// 登录用户显示私密文章
if ($this->user->hasLogin()) {
$uid = $this->user->uid;
if ($uid) {
$selectNormal->orWhere('authorId = ? AND status = ?', $uid, 'private');
}
}
$normalPosts = $db->fetchAll($selectNormal);
// 如果之前没有清空队列(没有置顶文章的情况),现在清空
if (empty($sticky) || empty(trim($sticky)) || empty($sticky_cids)) {
$this->row = [];
$this->stack = [];
$this->length = 0;
}
// 压入正常文章到文章队列
foreach ($normalPosts as $normalPost) {
$this->push($normalPost);
}
?>
<?php while($this->next()): ?>
<article class="post--item">
<div class="content">
<h2 class="post--title">
<a href="<?php $this->permalink() ?>">
<?php $this->title() ?>
<?php if((time() - $this->created) < 60*60*24*3): ?>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
class="icon--sticky" stroke="currentColor" stroke-width="2" stroke-linecap="round"
stroke-linejoin="round" class="w-4 h-4 text-red-400">
<path
d="M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z">
</path>
</svg>
<?php endif; ?>
</a>
</h2>
<div class="description">
<?php
if($this->fields->summary){
echo $this->fields->summary;
} else {
$this->excerpt(180);
}?>
</div>
<div class="meta">
<svg class="icon" viewBox="0 0 1024 1024" width="16" height="16">
<path
d="M512 97.52381c228.912762 0 414.47619 185.563429 414.47619 414.47619s-185.563429 414.47619-414.47619 414.47619S97.52381 740.912762 97.52381 512 283.087238 97.52381 512 97.52381z m0 73.142857C323.486476 170.666667 170.666667 323.486476 170.666667 512s152.81981 341.333333 341.333333 341.333333 341.333333-152.81981 341.333333-341.333333S700.513524 170.666667 512 170.666667z m36.571429 89.697523v229.86362h160.865523v73.142857H512a36.571429 36.571429 0 0 1-36.571429-36.571429V260.388571h73.142858z">
</path>
</svg><time><?php $this->date('Y-m-d'); ?></time>
<svg class="icon" viewBox="0 0 1024 1024" width="16" height="16">
<path
d="M408.551619 97.52381a73.142857 73.142857 0 0 1 51.736381 21.430857L539.306667 197.973333A73.142857 73.142857 0 0 0 591.067429 219.428571H804.571429a73.142857 73.142857 0 0 1 73.142857 73.142858v560.761904a73.142857 73.142857 0 0 1-73.142857 73.142857H219.428571a73.142857 73.142857 0 0 1-73.142857-73.142857V170.666667a73.142857 73.142857 0 0 1 73.142857-73.142857h189.123048z m0 73.142857H219.428571v682.666666h585.142858V292.571429h-213.504a146.285714 146.285714 0 0 1-98.499048-38.13181L487.619048 249.734095 408.551619 170.666667zM365.714286 633.904762v73.142857h-73.142857v-73.142857h73.142857z m365.714285 0v73.142857H414.47619v-73.142857h316.952381z m-365.714285-195.047619v73.142857h-73.142857v-73.142857h73.142857z m365.714285 0v73.142857H414.47619v-73.142857h316.952381z">
</path>
</svg><?php $this->category(','); ?>
<svg class="icon" viewBox="0 0 24 24" width="16" height="16" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M12 9C10.3431 9 9 10.3431 9 12C9 13.6569 10.3431 15 12 15C13.6569 15 15 13.6569 15 12C15 10.3431 13.6569 9 12 9ZM11 12C11 11.4477 11.4477 11 12 11C12.5523 11 13 11.4477 13 12C13 12.5523 12.5523 13 12 13C11.4477 13 11 12.5523 11 12Z" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M21.83 11.2807C19.542 7.15186 15.8122 5 12 5C8.18777 5 4.45796 7.15186 2.17003 11.2807C1.94637 11.6844 1.94361 12.1821 2.16029 12.5876C4.41183 16.8013 8.1628 19 12 19C15.8372 19 19.5882 16.8013 21.8397 12.5876C22.0564 12.1821 22.0536 11.6844 21.83 11.2807ZM12 17C9.06097 17 6.04052 15.3724 4.09173 11.9487C6.06862 8.59614 9.07319 7 12 7C14.9268 7 17.9314 8.59614 19.9083 11.9487C17.9595 15.3724 14.939 17 12 17Z" />
</svg>
<span class="article--views"><?php get_post_view($this) ?></span>
<svg viewBox="0 0 24 24" class="icon" aria-hidden="true" width="16" height="16">
<g>
<path
d="M1.751 10c0-4.42 3.584-8 8.005-8h4.366c4.49 0 8.129 3.64 8.129 8.13 0 2.96-1.607 5.68-4.196 7.11l-8.054 4.46v-3.69h-.067c-4.49.1-8.183-3.51-8.183-8.01zm8.005-6c-3.317 0-6.005 2.69-6.005 6 0 3.37 2.77 6.08 6.138 6.01l.351-.01h1.761v2.3l5.087-2.81c1.951-1.08 3.163-3.13 3.163-5.36 0-3.39-2.744-6.13-6.129-6.13H9.756z">
</path>
</g>
</svg>
<a href="<?php $this->permalink() ?>#comments"><?php $this->commentsNum('0 ', '1 ', '%d '); ?></a>
</div>
</div>
<?php
$firstImage = img_postthumb($this->cid);
$cover = $this->fields->cover;
$imageToDisplay = !empty($cover) ? $cover : $firstImage;
if($imageToDisplay):
?>
<a href="<?php $this->permalink() ?>" class="cover--link">
<img src="<?php echo $imageToDisplay; ?>" alt="<?php $this->title() ?>" class="cover" itemprop="image"/>
</a>
<?php endif; ?>
</article>
<?php endwhile; ?>
<?php
$this->pageNav(
' ',
' ',
1,
'...',
array(
'wrapTag' => 'nav',
'wrapClass' => 'nav-links nav-links__comment',
'itemTag' => '',
'textTag' => 'span',
'itemClass' => 'page-numbers',
'currentClass' => 'page-numbers current',
'prevClass' => 'hidden',
'nextClass' => 'hidden'
)
);
?>