优化细节
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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 />}
|
||||
|
||||
@@ -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 />}
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
@@ -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>
|
||||
</>
|
||||
) : (
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user