封装评论组件 内置【Twikoo、Waline】

This commit is contained in:
Han 2025-03-25 17:27:12 +08:00
parent bf2bdf1720
commit 3bf613946c
16 changed files with 215 additions and 43 deletions

View File

@ -25,7 +25,7 @@
- [x] LivePhoto
- [x] LaTex 数学公式
- [x] 赞赏功能
- [x] Twikoo 评论
- [x] 评论 - 内置【Twikoo、Waline】
- [x] 本地搜索
- [x] 标签
- [x] 分类

View File

@ -7,7 +7,9 @@
"build": "astro build",
"preview": "astro preview",
"astro": "astro",
"newpost": "node ./script/newpost.js"
"newpost": "node ./script/newpost.js",
"offdev": "astro preferences disable devToolbar",
"ondev": "astro preferences enable devToolbar"
},
"dependencies": {
"@astrojs/mdx": "^4.2.1",
@ -15,7 +17,7 @@
"@astrojs/sitemap": "^3.3.0",
"@swup/astro": "^1.5.0",
"aplayer": "^1.10.1",
"astro": "^5.5.3",
"astro": "^5.5.4",
"overlayscrollbars": "^2.11.1",
"vanilla-lazyload": "^19.1.3",
"vh-plugin": "^1.2.2"
@ -24,6 +26,7 @@
"@playform/compress": "^0.1.7",
"@types/dplayer": "^1.25.5",
"@types/nprogress": "^0.2.3",
"@waline/client": "^3.5.6",
"cheerio": "^1.0.0",
"dayjs": "^1.11.13",
"less": "^4.2.2",

View File

@ -6,6 +6,7 @@ section.article-list {
grid-template-columns: repeat(3, 1fr);
gap: 1.6rem;
width: 100%;
min-width: 0;
height: max-content;
overflow: hidden;

View File

@ -319,4 +319,108 @@
}
}
}
div[data-waline] {
// 头像尺寸和圆角
--waline-avatar-size: 2.56rem;
--waline-avatar-radius: 0.58rem;
font-size: var(--vh-size-h2);
.wl-cards {
&>.wl-card-item {
padding-left: 0;
.wl-card {
padding-bottom: 0;
&>.wl-head {
.wl-nick {
color: var(--vh-black-100);
font-size: var(--vh-size-h2);
line-height: 1.18;
&::after {
position: absolute;
content: "";
right: 0;
left: auto;
bottom: 0;
width: 0;
height: 1px;
background-color: var(--vh-black-88);
transition: all 0.28s;
}
&:hover {
&::after {
left: 0;
right: auto;
width: 100%;
}
}
}
}
// &>.wl-meta {}
&>.wl-content {
padding: 0;
p {
padding: 0.18rem 0;
color: var(--vh-black-100);
font-size: var(--vh-size-span);
line-height: 1.75rem;
}
&>p {
&:nth-of-type(1) {
padding: 0;
}
a {
color: var(--vh-black-100);
font-size: 0.76rem;
font-weight: 700;
}
}
}
&>.wl-quote {
border-left: solid 0.25rem var(--vh-main-color-38);
}
}
// item 中 wl-card 虚线间距
&>.wl-card {
padding-bottom: 1.25rem;
border-bottom-color: var(--vh-black-16);
}
}
}
// 图片
img {
&:not(.wl-user-avatar, .wl-reaction-list img) {
max-height: 66px;
object-fit: contain;
cursor: zoom-in;
cursor: -moz-zoom-in;
cursor: -webkit-zoom-in;
}
&.tk-owo-emotion {
max-height: 28px;
cursor: default;
}
}
a {
color: var(--vh-black-100);
}
code {
font-size: 0.68rem;
}
}
}

View File

@ -40,8 +40,19 @@ export default {
],
// 博客音乐组件解析接口
vhMusicApi: 'https://music.zhheo.com/meting-api/',
// 评论组件 Twikoo
Twikoo: { envId: '' },
// 评论组件(只允许同时开启一个)
Comment: {
// Twikoo 评论
Twikoo: {
enable: false,
envId: ''
},
// Waline 评论
Waline: {
enable: false,
serverURL: ''
}
},
// Han Analytics 统计https://github.com/uxiaohan/HanAnalytics
HanAnalytics: { enable: true, server: 'https://analytics.vvhan.com', siteId: 'Hello-HanHexoBlog' },
// Google 广告

View File

@ -46,7 +46,7 @@ top: true
- [x] LivePhoto
- [x] LaTex数学公式
- [x] 赞赏功能
- [x] Twikoo 评论
- [x] 评论 - 内置【Twikoo、Waline】
- [x] 本地搜索
- [x] 标签
- [x] 分类

View File

@ -2,12 +2,13 @@
const { frontmatter } = Astro.props;
// 页面 Info
import SITE_CONFIG from "@/config";
const { Description, Twikoo, Title } = SITE_CONFIG;
const { Description, Title } = SITE_CONFIG;
// Aside组件
import Aside from "@/components/Aside/Aside.astro";
// 公共 Layout
import Layout from "@/layouts/Layout/Layout.astro";
// 评论组件
import { checkComment } from "@/scripts/Comment";
import Comment from "@/components/Comment/Comment.astro";
// 关于样式
import "@/styles/About.less";
@ -23,7 +24,7 @@ import "@/styles/ArticleBase.less";
<p>{frontmatter.desc}</p>
</header>
<main><slot /></main>
{Twikoo.envId && frontmatter.comment != false && <Comment envId={Twikoo.envId} />}
{checkComment() && frontmatter.comment != false && <Comment />}
</section>
<Aside />
</section>

View File

@ -2,12 +2,13 @@
const { frontmatter } = Astro.props;
// 页面 Info
import SITE_CONFIG from "@/config";
const { Description, Twikoo, Title } = SITE_CONFIG;
const { Description, Title } = SITE_CONFIG;
// Aside组件
import Aside from "@/components/Aside/Aside.astro";
// 公共 Layout
import Layout from "@/layouts/Layout/Layout.astro";
// 评论组件
import { checkComment } from "@/scripts/Comment";
import Comment from "@/components/Comment/Comment.astro";
// ToolLayout 布局样式
import "./ToolLayout.less";
@ -24,7 +25,7 @@ import "@/styles/ArticleBase.less";
</header>
<main><slot /></main>
<main class={`${frontmatter.type}-main main`}><section class="vh-space-loading"><span></span><span></span><span></span></section></main>
{Twikoo.envId && frontmatter.comment != false && <Comment envId={Twikoo.envId} />}
{checkComment() && frontmatter.comment != false && <Comment />}
</section>
<Aside />
</section>

View File

@ -14,7 +14,7 @@ import getCover from "@/utils/getCover";
const ARTICLE_COVER: string = await getCover(post.data.cover);
// 页面 Info
import SITE_CONFIG from "@/config";
const { Site, Title, Author, Twikoo, GoogleAds } = SITE_CONFIG;
const { Site, Title, Author, GoogleAds } = SITE_CONFIG;
// 处理文章内容
const description = getDescription(post);
const { Content, remarkPluginFrontmatter } = await render(post);
@ -29,6 +29,7 @@ import Copyright from "@/components/Copyright/Copyright.astro";
// Reward 组件
import Reward from "@/components/Reward/Reward.astro";
// 评论组件
import { checkComment } from "@/scripts/Comment";
import Comment from "@/components/Comment/Comment.astro";
// Google 广告组件
import GoogleAd from "@/components/GoogleAd/GoogleAd.astro";
@ -70,7 +71,7 @@ import "@/styles/ArticleBase.less";
<!-- 底部谷歌广告 -->
{GoogleAds.ad_Client && GoogleAds.articleAD_Slot && <GoogleAd className="vh-article-ad" slotID={GoogleAds.articleAD_Slot} />}
</footer>
{Twikoo.envId && <Comment envId={Twikoo.envId} />}
{checkComment() && <Comment />}
</article>
<Aside />
</section>

View File

@ -1,22 +1,37 @@
import SITE_INFO from "@/config";
// 图片灯箱
import "@public/assets/js/view-image.min.js";
declare const ViewImage: any;
import { LoadScript } from "@/utils/index";
declare const twikoo: any;
// 处理评论区数据
const formateComment = () => {
// 图片灯箱
ViewImage && ViewImage.init(".vh-container>article.vh-article-main img.vh-article-img, main.talking-main>article>.main img, .vh-comment>.twikoo>.tk-comments img:not(.tk-avatar-img,.tk-owo-emotion,.OwO-item img)");
// 处理 URL
document.querySelectorAll('.vh-comment a[href="#"]').forEach(link => link.removeAttribute('href'));
}
// 初始化评论插件
export default async () => {
const commentDOM = '.vh-comment>section'
if (!document.querySelector(commentDOM) || !SITE_INFO.Twikoo.envId) return formateComment();
// Twikoo 评论
const TwikooFn = async (commentDOM: string) => {
document.querySelector(commentDOM)!.innerHTML = '<section class="vh-space-loading"><span></span><span></span><span></span></section>'
await LoadScript("https://registry.npmmirror.com/twikoo/1.6.41/files/dist/twikoo.all.min.js");
twikoo.init({ envId: SITE_INFO.Twikoo.envId, el: commentDOM, onCommentLoaded: () => setTimeout(formateComment) })
twikoo.init({ envId: SITE_INFO.Comment.Twikoo.envId, el: commentDOM, onCommentLoaded: () => setTimeout(() => document.querySelectorAll('.vh-comment a[href="#"]').forEach(link => link.removeAttribute('href'))) })
}
// Waline 评论
const WalineFn = async (commentDOM: string, walineInit: any) => {
import('@waline/client/waline.css');
import('@waline/client/waline-meta.css');
const { init } = await import('@waline/client');
walineInit = init({ el: commentDOM, serverURL: SITE_INFO.Comment.Waline.serverURL });
}
// 检查是否开启评论
const checkComment = () => {
const CommentARR: any = Object.keys(SITE_INFO.Comment);
const CommentItem = CommentARR.find((i: keyof typeof SITE_INFO.Comment) => SITE_INFO.Comment[i].enable);
return CommentItem;
}
// 初始化评论插件
const commentInit = async (key: string, walineInit: any) => {
// 评论列表
const CommentList: any = { TwikooFn, WalineFn };
// 评论 DOM
const commentDOM = '.vh-comment>section'
// 初始化评论
CommentList[`${key}Fn`](commentDOM, walineInit);
}
export { checkComment, commentInit }

View File

@ -2,6 +2,8 @@
import vh from 'vh-plugin'
import { fmtDate } from '@/utils/index'
import { $GET } from '@/utils/index'
// 图片懒加载
import vhLzImgInit from "@/scripts/vhLazyImg";
const FriendsInit = async (data: any) => {
const friendsDOM = document.querySelector('.vh-container>.vh-tools-main>main.friends-main')
@ -12,6 +14,8 @@ const FriendsInit = async (data: any) => {
res = await $GET(api);
}
friendsDOM.innerHTML = res.map((i: any) => `<article><a href="${i.link}" target="_blank" rel="noopener nofollow"><header><h2>${i.title}</h2></header><p>${i.content}</p><footer><span><img src="https://icon.bqb.cool/?url=${i.link.split('//')[1].split('/')[0]}" /><em>${i.auther}</em></span><time>${fmtDate(i.date, false)}前</time></footer></a></article>`).join('');
// 图片懒加载
vhLzImgInit();
} catch {
vh.Toast('获取数据失败')
}

View File

@ -13,6 +13,8 @@ import BackTopInitFn from "@/scripts/BackTop";
import { searchFn, vhSearchInit } from "@/scripts/Search";
// 图片懒加载
import vhLzImgInit from "@/scripts/vhLazyImg";
// 图片灯箱
import ViewImage from "@/scripts/ViewImage";
// 顶部导航 Current 状态
import initLinkCurrent from "@/scripts/Header";
// 底部网站运行时间
@ -24,7 +26,7 @@ import initFriends from "@/scripts/Friends";
// 动态说说初始化
import initTalking from "@/scripts/Talking";
// 文章评论初始化
import initComment from "@/scripts/Comment";
import { checkComment, commentInit } from "@/scripts/Comment";
// 移动端侧边栏初始化
import initMobileSidebar from "@/scripts/MobileSidebar";
// Google 广告
@ -39,6 +41,7 @@ import SmoothScroll from "@/scripts/Smoothscroll";
// 页面初始化 Only
const videoList: any[] = [];
const MusicList: any[] = [];
let commentLIst: any = { walineInit: null };
const indexInit = async (only: boolean = true) => {
// 预加载搜索数据
only && searchFn("");
@ -57,9 +60,11 @@ const indexInit = async (only: boolean = true) => {
// 初始化文章代码块
codeInit();
// 文章评论初始化
initComment();
checkComment() && commentInit(checkComment(), commentLIst)
// 图片懒加载初始化
vhLzImgInit();
// 图片灯箱
only && ViewImage();
// 友情链接初始化
initLinks();
// 朋友圈 RSS 初始化
@ -85,6 +90,9 @@ export default () => {
inRouter(() => indexInit(false));
// 离开当前页面时触发
outRouter(() => {
// 销毁评论
commentLIst.walineInit && commentLIst.walineInit.destroy();
commentLIst.walineInit = null;
// 销毁播放器
videoList.forEach((i: any) => i.destroy());
videoList.length = 0;

View File

@ -4,10 +4,6 @@ import { fmtDate } from '@/utils/index'
import { $GET } from '@/utils/index'
// 图片懒加载
import vhLzImgInit from "@/scripts/vhLazyImg";
// 灯箱JS初始化======
import "@public/assets/js/view-image.min.js";
declare const ViewImage: any;
// 灯箱JS初始化======
const TalkingInit = async (data: any) => {
const talkingDOM = document.querySelector('.vh-container>.vh-tools-main>main.talking-main')
@ -20,9 +16,6 @@ const TalkingInit = async (data: any) => {
talkingDOM.innerHTML = res.map((i: any) => `<article><header><img data-vh-lz-src="https://q1.qlogo.cn/g?b=qq&nk=1655466387&s=640" /><p class="info"><span>.𝙃𝙖𝙣</span><time>${fmtDate(i.date)}前</time></p></header><section class="main">${i.content}</section><footer>${i.tags.map((tag: any) => `<span>${tag}</span>`).join('')}</footer></article>`).join('');
// 图片懒加载
vhLzImgInit();
// 灯箱JS初始化======
setTimeout(() => (ViewImage && ViewImage.init("main.talking-main>article>.main img, .vh-comment>.twikoo>.tk-comments img:not(.tk-avatar-img,.tk-owo-emotion,.OwO-item img)")));
// 灯箱JS初始化======
} catch {
vh.Toast('获取数据失败')
}

23
src/scripts/ViewImage.ts Normal file
View File

@ -0,0 +1,23 @@
import { LoadScript } from "@/utils/index";
// 图片灯箱
declare const ViewImage: any;
const ViewImgList: string[] = [
// 文章内图片
".vh-container>article.vh-article-main img.vh-article-img",
// 动态页面图片
"main.talking-main>article>.main img",
// Twikoo 评论区图片
".vh-comment>.twikoo>.tk-comments img:not(.tk-avatar-img,.tk-owo-emotion,.OwO-item img)",
// Waline 评论区图片
".vh-comment div[data-waline] img:not(.wl-user-avatar,.wl-avatar img,.wl-reaction-list img)"
];
// 初始化
export default async () => {
try {
ViewImage.init(ViewImgList.join(","));
} catch (error) {
await LoadScript("/assets/js/view-image.min.js");
ViewImage.init(ViewImgList.join(","));
}
}

View File

@ -2,6 +2,7 @@
import LazyLoad from "vanilla-lazyload";
// 初始化图片懒加载
let lazyLoadStatus: any = null;
export default () => {
document.querySelectorAll("main>.vh-container img:not(.view-image-container)").forEach((i: any) => {
// 是否包含data-vh-lz-src
@ -10,9 +11,6 @@ export default () => {
i.setAttribute("src", '/assets/images/lazy-loading.webp');
}
});
new LazyLoad({
elements_selector: "img:not(.view-image-container)",
threshold: 0,
data_src: "vh-lz-src"
});
if (lazyLoadStatus) return lazyLoadStatus.update();
lazyLoadStatus = new LazyLoad({ elements_selector: "img:not(.view-image-container)", threshold: 0, data_src: "vh-lz-src" });
}

View File

@ -131,6 +131,15 @@
a {
color: #49B1F5;
transition: all .16s;
&:hover {
color: #1b99ee;
}
span {
color: currentColor;
}
}
th {
@ -165,7 +174,7 @@
}
&:hover {
background-color: #f2f2f2;
background-color: #f2f2f266;
}
}