优化细节

This commit is contained in:
浪子
2026-05-17 17:43:48 +08:00
parent ee4dd9973e
commit bfec198c18
6 changed files with 236 additions and 8 deletions
+12
View File
@@ -1977,6 +1977,18 @@ h1.entry-title {
color: #888888;
}
.entry-census .bull {
margin: 0 5px;
}
.twikoo-views[hidden] {
display: none !important;
}
.twikoo-comments[hidden] {
display: none !important;
}
.entry-header hr {
width: 30%;
height: 1px;
+19 -6
View File
@@ -1,11 +1,14 @@
---
import type { PostListItem } from './PostListThumb.astro';
import TwikooViews from './TwikooViews.astro';
import config from '../../asky.config';
export interface Props {
posts: PostListItem[];
}
const { posts } = Astro.props;
const showTwikooStats = Boolean((config.twikoo?.envId || '').trim());
function timeSince(date: string): string {
const past = new Date(date).getTime();
@@ -45,15 +48,25 @@ function timeSince(date: string): string {
<a href={post.permalink}><i class="iconfont icon-more"></i></a>
</div>
<div class="info-meta">
<div class="comnum">
<span><i class="iconfont icon-mark"></i>{post.comments ?? 0} 条评论</span>
</div>
<div class="views">
<span><i class="iconfont icon-eye"></i>{post.views ?? 0} 热度</span>
</div>
{showTwikooStats && (
<div class="comnum">
<span class="twikoo-comments" data-twikoo-comments-url={post.permalink} hidden>
<i class="iconfont icon-mark"></i><span data-twikoo-comments-count></span> 条评论
</span>
</div>
)}
{showTwikooStats && (
<div class="views">
<span class="twikoo-views" data-twikoo-views-url={post.permalink} hidden>
<i class="iconfont icon-eye"></i><span data-twikoo-views-count></span> 热度
</span>
</div>
)}
</div>
</footer>
</div>
<hr />
</article>
))}
{showTwikooStats && <TwikooViews />}
+15 -2
View File
@@ -1,5 +1,7 @@
---
import { askyOption, bloginfo } from '../lib/options';
import TwikooViews from './TwikooViews.astro';
import config from '../../asky.config';
export interface PostListItem {
id: string;
@@ -23,6 +25,7 @@ const focusLogo = askyOption('focus_logo') || '/images/avatar.jpg';
const showLike = askyOption('post_like') === 'yes';
const blogName = bloginfo('name');
const blogUrl = bloginfo('url');
const showTwikooStats = Boolean((config.twikoo?.envId || '').trim());
function timeSince(date: string): string {
const past = new Date(date).getTime();
@@ -69,8 +72,16 @@ function timeSince(date: string): string {
<p class="post-text" style="width:92%;float:right;">{post.excerpt}</p>
</div>
<div class="post-meta" style="margin-top:10px;width:92%;float:right;">
<span><i class="iconfont icon-eye"></i>{post.views ?? 0} </span>
<span class="comments-number"><i class="iconfont icon-mark"></i>{post.comments ?? 0}</span>
{showTwikooStats && (
<span class="twikoo-views" data-twikoo-views-url={post.permalink} hidden>
<i class="iconfont icon-eye"></i><span data-twikoo-views-count></span>
</span>
)}
{showTwikooStats && (
<span class="comments-number twikoo-comments" data-twikoo-comments-url={post.permalink} hidden>
<i class="iconfont icon-mark"></i><span data-twikoo-comments-count></span>
</span>
)}
{showLike && (
<span class="post-like">
<a href="javascript:;" data-action="ding" data-id={post.id} class="specsZan" style="padding:0px">
@@ -84,3 +95,5 @@ function timeSince(date: string): string {
</div>
</article>
))}
{showTwikooStats && <TwikooViews />}
+166
View File
@@ -0,0 +1,166 @@
---
import config from '../../asky.config';
export interface Props {
incrementUrl?: string;
title?: string;
}
const { incrementUrl = '', title = '' } = Astro.props;
const envId = (config.twikoo?.envId || '').replace(/\/$/, '');
---
{envId && (
<script is:inline define:vars={{ envId, incrementUrl, title }}>
(function () {
function normalizePath(value) {
if (!value) return '';
var path = '';
try {
path = new URL(value, location.origin).pathname;
} catch (e) {
path = String(value || '');
}
if (path.length > 1 && path.charAt(path.length - 1) !== '/') path += '/';
return path;
}
function callApi(event, data) {
return new Promise(function (resolve, reject) {
var accessToken = null;
try { accessToken = localStorage.getItem('twikoo-access-token'); } catch (e) {}
var body = Object.assign({ event: event, accessToken: accessToken }, data || {});
try {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState !== 4) return;
if (xhr.status === 200) {
try {
var result = JSON.parse(xhr.responseText);
if (result && result.accessToken) {
try { localStorage.setItem('twikoo-access-token', result.accessToken); } catch (e) {}
}
resolve(result);
} catch (e) { reject(e); }
} else {
reject(new Error('HTTP ' + xhr.status));
}
};
xhr.open('POST', envId);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify(body));
} catch (e) { reject(e); }
});
}
function storageKey(path) {
return 'twikoo-counter:' + path;
}
function readCachedCount(path) {
try {
var raw = localStorage.getItem(storageKey(path));
if (raw == null || raw === '') return null;
var count = parseInt(raw, 10);
return Number.isFinite(count) ? count : null;
} catch (e) {
return null;
}
}
function writeCachedCount(path, count) {
try { localStorage.setItem(storageKey(path), String(count)); } catch (e) {}
}
function renderCount(path, count) {
document.querySelectorAll('[data-twikoo-views-url]').forEach(function (el) {
if (normalizePath(el.getAttribute('data-twikoo-views-url')) !== path) return;
var target = el.querySelector('[data-twikoo-views-count]');
if (target) target.textContent = String(count);
el.hidden = false;
});
}
function renderCachedCounts() {
document.querySelectorAll('[data-twikoo-views-url]').forEach(function (el) {
var path = normalizePath(el.getAttribute('data-twikoo-views-url'));
var count = readCachedCount(path);
if (count != null) renderCount(path, count);
});
}
function uniqueItems(items) {
return items.filter(function (item, index) {
return item && items.indexOf(item) === index;
});
}
function normalizeCommentPath(value) {
var path = normalizePath(value);
if (path.length > 1) path = path.replace(/\/+$/, '');
return path;
}
function commentPathVariants(value) {
var path = normalizeCommentPath(value);
if (!path) return [];
if (path === '/') return ['/'];
return [path, path + '/'];
}
function collectCommentUrls() {
var urls = [];
document.querySelectorAll('[data-twikoo-comments-url]').forEach(function (el) {
urls = urls.concat(commentPathVariants(el.getAttribute('data-twikoo-comments-url')));
});
return uniqueItems(urls);
}
function renderCommentCount(path, count) {
var normalizedPath = normalizeCommentPath(path);
document.querySelectorAll('[data-twikoo-comments-url]').forEach(function (el) {
if (normalizeCommentPath(el.getAttribute('data-twikoo-comments-url')) !== normalizedPath) return;
var target = el.querySelector('[data-twikoo-comments-count]');
if (target) target.textContent = String(count);
el.hidden = false;
});
}
function loadCommentCounts() {
var urls = collectCommentUrls();
if (!urls.length) return;
callApi('GET_COMMENTS_COUNT', { urls: urls }).then(function (res) {
if (!res || (res.code && res.code !== 0) || !Array.isArray(res.data)) return;
var totals = {};
res.data.forEach(function (item) {
var path = normalizeCommentPath(item.url);
var count = parseInt(item.count, 10);
totals[path] = (totals[path] || 0) + (Number.isFinite(count) ? count : 0);
});
Object.keys(totals).forEach(function (path) {
renderCommentCount(path, totals[path]);
});
}).catch(function () {});
}
renderCachedCounts();
loadCommentCounts();
var currentPath = normalizePath(incrementUrl);
if (!currentPath) return;
callApi('COUNTER_GET', {
url: currentPath,
href: new URL(currentPath, location.origin).href,
title: title || document.title
}).then(function (res) {
if (!res || (res.code && res.code !== 0)) return;
var current = parseInt(res.time, 10);
if (!Number.isFinite(current)) current = 0;
var next = current + 1;
writeCachedCount(currentPath, next);
renderCount(currentPath, next);
}).catch(function () {});
})();
</script>
)}
+10
View File
@@ -18,6 +18,7 @@ export interface Props {
patternAuthorUrl?: string;
patternAvatar?: string;
patternMeta?: string;
patternViewsUrl?: string;
/** 自定义 body class */
bodyClass?: string;
}
@@ -34,6 +35,7 @@ const {
patternAuthorUrl = '/',
patternAvatar,
patternMeta,
patternViewsUrl,
bodyClass = ''
} = Astro.props;
@@ -141,6 +143,14 @@ const patternHeaderClass = `pattern-header${patternSingle ? ' single-header' : '
{patternMeta}
</>
)}
{patternViewsUrl && (
<>
{(patternAuthor || patternMeta) && <span class="bull">·</span>}
<span class="twikoo-views" data-twikoo-views-url={patternViewsUrl} hidden>
<i class="iconfont icon-eye"></i><span data-twikoo-views-count></span> 热度
</span>
</>
)}
</p>
</>
) : (
+14
View File
@@ -7,8 +7,10 @@ import AuthorProfile from '../../components/AuthorProfile.astro';
import PostNextPrev from '../../components/PostNextPrev.astro';
import ShareLike from '../../components/ShareLike.astro';
import Comments from '../../components/Comments.astro';
import TwikooViews from '../../components/TwikooViews.astro';
import { askyOption, isOptionOn } from '../../lib/options';
import { getCollection } from 'astro:content';
import config from '../../../asky.config';
export async function getStaticPaths() {
const all = (await getCollection('posts')).sort(
@@ -35,9 +37,11 @@ const showPattern = isOptionOn('patternimg');
const buildTime = post.data.date.toLocaleString('zh-CN');
const tags = post.data.tags ?? [];
const fullUrl = new URL(`/posts/${post.slug}`, Astro.site ?? 'https://example.com').toString();
const postPath = `/posts/${post.slug}/`;
const patternImg = showPattern ? undefined : (post.data.thumbnail ?? '/images/hd.jpg');
const authorName = post.data.author ?? 'Admin';
const authorAvatar = askyOption('focus_logo') || '/images/avatar.jpg';
const showTwikooViews = Boolean((config.twikoo?.envId || '').trim());
---
<BaseLayout
@@ -50,6 +54,7 @@ const authorAvatar = askyOption('focus_logo') || '/images/avatar.jpg';
patternAuthorUrl="/"
patternAvatar={authorAvatar}
patternMeta={buildTime}
patternViewsUrl={showTwikooViews ? postPath : undefined}
>
<Imgbox slot="imgbox" />
<Header slot="header" />
@@ -63,6 +68,14 @@ const authorAvatar = askyOption('focus_logo') || '/images/avatar.jpg';
<h1 class="entry-title">{post.data.title}</h1>
<p class="entry-census">
<i class="iconfont icon-clock"></i>{buildTime}
{showTwikooViews && (
<>
<span class="bull">·</span>
<span class="twikoo-views" data-twikoo-views-url={postPath} hidden>
<i class="iconfont icon-eye"></i><span data-twikoo-views-count></span> 热度
</span>
</>
)}
</p>
<hr />
</header>
@@ -89,6 +102,7 @@ const authorAvatar = askyOption('focus_logo') || '/images/avatar.jpg';
<PostNextPrev prev={prev} next={next} />
<AuthorProfile authorName={authorName} authorUrl="/" />
{showTwikooViews && <TwikooViews incrementUrl={postPath} title={post.data.title} />}
<Comments />
</main>
</div>