优化细节
This commit is contained in:
@@ -1977,6 +1977,18 @@ h1.entry-title {
|
|||||||
color: #888888;
|
color: #888888;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.entry-census .bull {
|
||||||
|
margin: 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.twikoo-views[hidden] {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.twikoo-comments[hidden] {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
.entry-header hr {
|
.entry-header hr {
|
||||||
width: 30%;
|
width: 30%;
|
||||||
height: 1px;
|
height: 1px;
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
---
|
---
|
||||||
import type { PostListItem } from './PostListThumb.astro';
|
import type { PostListItem } from './PostListThumb.astro';
|
||||||
|
import TwikooViews from './TwikooViews.astro';
|
||||||
|
import config from '../../asky.config';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
posts: PostListItem[];
|
posts: PostListItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const { posts } = Astro.props;
|
const { posts } = Astro.props;
|
||||||
|
const showTwikooStats = Boolean((config.twikoo?.envId || '').trim());
|
||||||
|
|
||||||
function timeSince(date: string): string {
|
function timeSince(date: string): string {
|
||||||
const past = new Date(date).getTime();
|
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>
|
<a href={post.permalink}><i class="iconfont icon-more"></i></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-meta">
|
<div class="info-meta">
|
||||||
<div class="comnum">
|
{showTwikooStats && (
|
||||||
<span><i class="iconfont icon-mark"></i>{post.comments ?? 0} 条评论</span>
|
<div class="comnum">
|
||||||
</div>
|
<span class="twikoo-comments" data-twikoo-comments-url={post.permalink} hidden>
|
||||||
<div class="views">
|
<i class="iconfont icon-mark"></i><span data-twikoo-comments-count></span> 条评论
|
||||||
<span><i class="iconfont icon-eye"></i>{post.views ?? 0} 热度</span>
|
</span>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr />
|
||||||
</article>
|
</article>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
|
{showTwikooStats && <TwikooViews />}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
---
|
---
|
||||||
import { askyOption, bloginfo } from '../lib/options';
|
import { askyOption, bloginfo } from '../lib/options';
|
||||||
|
import TwikooViews from './TwikooViews.astro';
|
||||||
|
import config from '../../asky.config';
|
||||||
|
|
||||||
export interface PostListItem {
|
export interface PostListItem {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -23,6 +25,7 @@ const focusLogo = askyOption('focus_logo') || '/images/avatar.jpg';
|
|||||||
const showLike = askyOption('post_like') === 'yes';
|
const showLike = askyOption('post_like') === 'yes';
|
||||||
const blogName = bloginfo('name');
|
const blogName = bloginfo('name');
|
||||||
const blogUrl = bloginfo('url');
|
const blogUrl = bloginfo('url');
|
||||||
|
const showTwikooStats = Boolean((config.twikoo?.envId || '').trim());
|
||||||
|
|
||||||
function timeSince(date: string): string {
|
function timeSince(date: string): string {
|
||||||
const past = new Date(date).getTime();
|
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>
|
<p class="post-text" style="width:92%;float:right;">{post.excerpt}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="post-meta" style="margin-top:10px;width:92%;float:right;">
|
<div class="post-meta" style="margin-top:10px;width:92%;float:right;">
|
||||||
<span><i class="iconfont icon-eye"></i>{post.views ?? 0} </span>
|
{showTwikooStats && (
|
||||||
<span class="comments-number"><i class="iconfont icon-mark"></i>{post.comments ?? 0}</span>
|
<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 && (
|
{showLike && (
|
||||||
<span class="post-like">
|
<span class="post-like">
|
||||||
<a href="javascript:;" data-action="ding" data-id={post.id} class="specsZan" style="padding:0px">
|
<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>
|
</div>
|
||||||
</article>
|
</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;
|
patternAuthorUrl?: string;
|
||||||
patternAvatar?: string;
|
patternAvatar?: string;
|
||||||
patternMeta?: string;
|
patternMeta?: string;
|
||||||
|
patternViewsUrl?: string;
|
||||||
/** 自定义 body class */
|
/** 自定义 body class */
|
||||||
bodyClass?: string;
|
bodyClass?: string;
|
||||||
}
|
}
|
||||||
@@ -34,6 +35,7 @@ const {
|
|||||||
patternAuthorUrl = '/',
|
patternAuthorUrl = '/',
|
||||||
patternAvatar,
|
patternAvatar,
|
||||||
patternMeta,
|
patternMeta,
|
||||||
|
patternViewsUrl,
|
||||||
bodyClass = ''
|
bodyClass = ''
|
||||||
} = Astro.props;
|
} = Astro.props;
|
||||||
|
|
||||||
@@ -141,6 +143,14 @@ const patternHeaderClass = `pattern-header${patternSingle ? ' single-header' : '
|
|||||||
{patternMeta}
|
{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>
|
</p>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -7,8 +7,10 @@ import AuthorProfile from '../../components/AuthorProfile.astro';
|
|||||||
import PostNextPrev from '../../components/PostNextPrev.astro';
|
import PostNextPrev from '../../components/PostNextPrev.astro';
|
||||||
import ShareLike from '../../components/ShareLike.astro';
|
import ShareLike from '../../components/ShareLike.astro';
|
||||||
import Comments from '../../components/Comments.astro';
|
import Comments from '../../components/Comments.astro';
|
||||||
|
import TwikooViews from '../../components/TwikooViews.astro';
|
||||||
import { askyOption, isOptionOn } from '../../lib/options';
|
import { askyOption, isOptionOn } from '../../lib/options';
|
||||||
import { getCollection } from 'astro:content';
|
import { getCollection } from 'astro:content';
|
||||||
|
import config from '../../../asky.config';
|
||||||
|
|
||||||
export async function getStaticPaths() {
|
export async function getStaticPaths() {
|
||||||
const all = (await getCollection('posts')).sort(
|
const all = (await getCollection('posts')).sort(
|
||||||
@@ -35,9 +37,11 @@ const showPattern = isOptionOn('patternimg');
|
|||||||
const buildTime = post.data.date.toLocaleString('zh-CN');
|
const buildTime = post.data.date.toLocaleString('zh-CN');
|
||||||
const tags = post.data.tags ?? [];
|
const tags = post.data.tags ?? [];
|
||||||
const fullUrl = new URL(`/posts/${post.slug}`, Astro.site ?? 'https://example.com').toString();
|
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 patternImg = showPattern ? undefined : (post.data.thumbnail ?? '/images/hd.jpg');
|
||||||
const authorName = post.data.author ?? 'Admin';
|
const authorName = post.data.author ?? 'Admin';
|
||||||
const authorAvatar = askyOption('focus_logo') || '/images/avatar.jpg';
|
const authorAvatar = askyOption('focus_logo') || '/images/avatar.jpg';
|
||||||
|
const showTwikooViews = Boolean((config.twikoo?.envId || '').trim());
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout
|
<BaseLayout
|
||||||
@@ -50,6 +54,7 @@ const authorAvatar = askyOption('focus_logo') || '/images/avatar.jpg';
|
|||||||
patternAuthorUrl="/"
|
patternAuthorUrl="/"
|
||||||
patternAvatar={authorAvatar}
|
patternAvatar={authorAvatar}
|
||||||
patternMeta={buildTime}
|
patternMeta={buildTime}
|
||||||
|
patternViewsUrl={showTwikooViews ? postPath : undefined}
|
||||||
>
|
>
|
||||||
<Imgbox slot="imgbox" />
|
<Imgbox slot="imgbox" />
|
||||||
<Header slot="header" />
|
<Header slot="header" />
|
||||||
@@ -63,6 +68,14 @@ const authorAvatar = askyOption('focus_logo') || '/images/avatar.jpg';
|
|||||||
<h1 class="entry-title">{post.data.title}</h1>
|
<h1 class="entry-title">{post.data.title}</h1>
|
||||||
<p class="entry-census">
|
<p class="entry-census">
|
||||||
<i class="iconfont icon-clock"></i>{buildTime}
|
<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>
|
</p>
|
||||||
<hr />
|
<hr />
|
||||||
</header>
|
</header>
|
||||||
@@ -89,6 +102,7 @@ const authorAvatar = askyOption('focus_logo') || '/images/avatar.jpg';
|
|||||||
|
|
||||||
<PostNextPrev prev={prev} next={next} />
|
<PostNextPrev prev={prev} next={next} />
|
||||||
<AuthorProfile authorName={authorName} authorUrl="/" />
|
<AuthorProfile authorName={authorName} authorUrl="/" />
|
||||||
|
{showTwikooViews && <TwikooViews incrementUrl={postPath} title={post.data.title} />}
|
||||||
<Comments />
|
<Comments />
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user