优化文章排版
This commit is contained in:
@@ -160,6 +160,320 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* =========================================
|
||||||
|
文章页(简洁大气)
|
||||||
|
========================================= */
|
||||||
|
|
||||||
|
.single-article-page {
|
||||||
|
padding: 46px 0 72px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-article-shell {
|
||||||
|
max-width: 1320px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-article {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1280px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-article-header {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-article-kicker {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 24px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-article-kicker-link {
|
||||||
|
color: var(--text-primary);
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 1.08rem;
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: 0.01em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-article-kicker-link:hover {
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-article-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: clamp(2.3rem, 4.7vw, 5rem);
|
||||||
|
line-height: 1.1;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
color: var(--text-primary);
|
||||||
|
text-wrap: balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-article-meta {
|
||||||
|
margin-top: 28px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
align-items: baseline;
|
||||||
|
column-gap: 16px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
font-size: 0.94rem;
|
||||||
|
max-width: 980px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-article-meta-item {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: baseline;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 6px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-article-meta-label {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-article-meta-value {
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-weight: 600;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-article-body {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1080px;
|
||||||
|
margin: 52px auto 0;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: clamp(1.14rem, 1.2vw, 1.28rem);
|
||||||
|
line-height: 1.82;
|
||||||
|
font-family: "Times New Roman", Times, "Nimbus Roman No9 L", "Noto Sans SC", "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", var(--font-sans);
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-article-body > * {
|
||||||
|
margin: 0 0 1.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-article-body h2,
|
||||||
|
.single-article-body h3,
|
||||||
|
.single-article-body h4 {
|
||||||
|
margin-top: 1.7em;
|
||||||
|
line-height: 1.35;
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-article-body h2 {
|
||||||
|
font-size: clamp(1.45rem, 1.6vw, 2rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-article-body h3 {
|
||||||
|
font-size: clamp(1.2rem, 1.2vw, 1.55rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-article-body a {
|
||||||
|
color: var(--text-primary);
|
||||||
|
text-decoration-thickness: 1.5px;
|
||||||
|
text-decoration-color: color-mix(in srgb, var(--color-primary) 70%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-article-body a:hover {
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-article-body blockquote {
|
||||||
|
margin: 1.5em 0;
|
||||||
|
padding: 0.2em 0 0.2em 1.1em;
|
||||||
|
border-left: 3px solid var(--border-default);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-article-body img {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-article-body pre {
|
||||||
|
padding: 16px;
|
||||||
|
border: 1px solid var(--border-default);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
background: color-mix(in srgb, var(--bg-surface) 92%, transparent);
|
||||||
|
overflow: auto;
|
||||||
|
line-height: 1.45;
|
||||||
|
font-size: 0.92rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-article-body code {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.9em;
|
||||||
|
background: color-mix(in srgb, var(--bg-surface) 92%, transparent);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0.12em 0.35em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-article-tags {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1080px;
|
||||||
|
margin: 30px auto 0;
|
||||||
|
padding-top: 16px;
|
||||||
|
border-top: 1px solid var(--border-default);
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-article-tag {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 24px;
|
||||||
|
padding: 0 10px;
|
||||||
|
border: 1px solid var(--border-default);
|
||||||
|
border-radius: 999px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 0.78rem;
|
||||||
|
background: color-mix(in srgb, var(--bg-card) 88%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-article-tag:hover {
|
||||||
|
color: var(--color-primary);
|
||||||
|
border-color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-related {
|
||||||
|
max-width: 1180px;
|
||||||
|
margin: 62px auto 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-divider {
|
||||||
|
border-top: 1px solid var(--border-default);
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-related-title {
|
||||||
|
margin: 44px 0 24px;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: clamp(1.8rem, 2vw, 2.5rem);
|
||||||
|
letter-spacing: -0.01em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-related-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-related-card {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-related-card-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: clamp(1.2rem, 1.2vw, 1.75rem);
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-related-card-title a {
|
||||||
|
color: var(--text-primary);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-related-card-title a:hover {
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-related-card-excerpt {
|
||||||
|
margin: 12px 0 0;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
line-height: 1.66;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-family: "Times New Roman", Times, "Nimbus Roman No9 L", "Noto Sans SC", "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", var(--font-sans);
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-related-card-link {
|
||||||
|
margin-top: 16px;
|
||||||
|
display: inline-block;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 1.02rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-related-card-link:hover {
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-related-empty {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-article-comments {
|
||||||
|
max-width: 900px;
|
||||||
|
margin: 64px auto 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 980px) {
|
||||||
|
.single-article-page {
|
||||||
|
padding: 34px 0 56px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-article-body {
|
||||||
|
margin-top: 42px;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
line-height: 1.75;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-related-grid {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 760px) {
|
||||||
|
.single-article-kicker {
|
||||||
|
gap: 14px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-article-meta {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px 14px;
|
||||||
|
margin-top: 18px;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-article-body {
|
||||||
|
margin-top: 30px;
|
||||||
|
font-size: 1.02rem;
|
||||||
|
line-height: 1.72;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-related {
|
||||||
|
margin-top: 44px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-related-title {
|
||||||
|
margin-top: 34px;
|
||||||
|
margin-bottom: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-related-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-article-comments {
|
||||||
|
margin-top: 48px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* =========================================
|
/* =========================================
|
||||||
GitHub 风格博客
|
GitHub 风格博客
|
||||||
========================================= */
|
========================================= */
|
||||||
|
|||||||
@@ -0,0 +1,128 @@
|
|||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var TITLE_SELECTOR = ".single-article-title";
|
||||||
|
var MAX_LINES = 2;
|
||||||
|
var MIN_FONT_SIZE = 12;
|
||||||
|
var MAX_FONT_SIZE = 92;
|
||||||
|
var SEARCH_STEPS = 16;
|
||||||
|
|
||||||
|
function getLineHeightPx(element) {
|
||||||
|
var computed = window.getComputedStyle(element);
|
||||||
|
var lineHeight = computed.lineHeight;
|
||||||
|
var fontSize = parseFloat(computed.fontSize) || 16;
|
||||||
|
|
||||||
|
if (lineHeight && lineHeight.indexOf("px") > -1) {
|
||||||
|
return parseFloat(lineHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lineHeight === "normal") {
|
||||||
|
return fontSize * 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ratio = parseFloat(lineHeight);
|
||||||
|
if (Number.isFinite(ratio)) {
|
||||||
|
return ratio * fontSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fontSize * 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLineCount(element) {
|
||||||
|
var lineHeightPx = getLineHeightPx(element);
|
||||||
|
if (!lineHeightPx) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return element.getBoundingClientRect().height / lineHeightPx;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setFontSize(element, size) {
|
||||||
|
element.style.fontSize = size.toFixed(2) + "px";
|
||||||
|
}
|
||||||
|
|
||||||
|
function fitsWithinLines(element, size) {
|
||||||
|
setFontSize(element, size);
|
||||||
|
return getLineCount(element) <= MAX_LINES + 0.01;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fitTitle(title) {
|
||||||
|
if (!title) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var header = title.closest(".single-article-header") || title.parentElement || title;
|
||||||
|
if (!header || header.getBoundingClientRect().width <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
title.style.fontSize = "";
|
||||||
|
var computedSize = parseFloat(window.getComputedStyle(title).fontSize) || 48;
|
||||||
|
|
||||||
|
var low = MIN_FONT_SIZE;
|
||||||
|
var high = Math.min(MAX_FONT_SIZE, Math.max(computedSize * 1.8, computedSize + 8));
|
||||||
|
|
||||||
|
while (high < MAX_FONT_SIZE && fitsWithinLines(title, high)) {
|
||||||
|
high = Math.min(MAX_FONT_SIZE, high + 8);
|
||||||
|
if (high === MAX_FONT_SIZE) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var best = low;
|
||||||
|
for (var i = 0; i < SEARCH_STEPS; i += 1) {
|
||||||
|
var mid = (low + high) / 2;
|
||||||
|
if (fitsWithinLines(title, mid)) {
|
||||||
|
best = mid;
|
||||||
|
low = mid;
|
||||||
|
} else {
|
||||||
|
high = mid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fitsWithinLines(title, best)) {
|
||||||
|
while (best > MIN_FONT_SIZE && !fitsWithinLines(title, best)) {
|
||||||
|
best -= 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setFontSize(title, Math.max(MIN_FONT_SIZE, best - 0.05));
|
||||||
|
}
|
||||||
|
|
||||||
|
function initSingleTitleFitter() {
|
||||||
|
var title = document.querySelector(TITLE_SELECTOR);
|
||||||
|
if (!title) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var scheduled = false;
|
||||||
|
var scheduleFit = function () {
|
||||||
|
if (scheduled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
scheduled = true;
|
||||||
|
window.requestAnimationFrame(function () {
|
||||||
|
scheduled = false;
|
||||||
|
fitTitle(title);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
scheduleFit();
|
||||||
|
window.addEventListener("resize", scheduleFit, { passive: true });
|
||||||
|
window.addEventListener("pageshow", scheduleFit);
|
||||||
|
|
||||||
|
if ("ResizeObserver" in window) {
|
||||||
|
var resizeObserver = new ResizeObserver(scheduleFit);
|
||||||
|
resizeObserver.observe(title);
|
||||||
|
var header = title.closest(".single-article-header");
|
||||||
|
if (header) {
|
||||||
|
resizeObserver.observe(header);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.readyState === "loading") {
|
||||||
|
document.addEventListener("DOMContentLoaded", initSingleTitleFitter);
|
||||||
|
} else {
|
||||||
|
initSingleTitleFitter();
|
||||||
|
}
|
||||||
|
})();
|
||||||
+148
@@ -81,6 +81,10 @@ function itstudio_enqueue_scripts() {
|
|||||||
wp_enqueue_script('itstudio-theme-toggle', get_template_directory_uri() . '/assets/js/theme-toggle.js', array(), '1.0.0', true);
|
wp_enqueue_script('itstudio-theme-toggle', get_template_directory_uri() . '/assets/js/theme-toggle.js', array(), '1.0.0', true);
|
||||||
wp_enqueue_script('itstudio-lang-toggle', get_template_directory_uri() . '/assets/js/lang-toggle.js', array(), '1.0.0', true);
|
wp_enqueue_script('itstudio-lang-toggle', get_template_directory_uri() . '/assets/js/lang-toggle.js', array(), '1.0.0', true);
|
||||||
wp_enqueue_script('itstudio-main', get_template_directory_uri() . '/assets/js/main.js', array(), '1.0.0', true);
|
wp_enqueue_script('itstudio-main', get_template_directory_uri() . '/assets/js/main.js', array(), '1.0.0', true);
|
||||||
|
|
||||||
|
if (is_singular(array('post', 'announcement', 'news'))) {
|
||||||
|
wp_enqueue_script('itstudio-single-title-fit', get_template_directory_uri() . '/assets/js/single-title-fit.js', array(), '1.0.0', true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
add_action('wp_enqueue_scripts', 'itstudio_enqueue_scripts');
|
add_action('wp_enqueue_scripts', 'itstudio_enqueue_scripts');
|
||||||
|
|
||||||
@@ -484,6 +488,146 @@ function itstudio_get_post_excerpt_chars($post_id, $limit = 200) {
|
|||||||
return wp_html_excerpt($excerpt, $limit, '...');
|
return wp_html_excerpt($excerpt, $limit, '...');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function itstudio_is_probably_bot_request() {
|
||||||
|
$user_agent = strtolower(trim((string) ($_SERVER['HTTP_USER_AGENT'] ?? '')));
|
||||||
|
if ($user_agent === '') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$bot_signatures = array(
|
||||||
|
'bot',
|
||||||
|
'spider',
|
||||||
|
'crawl',
|
||||||
|
'slurp',
|
||||||
|
'headless',
|
||||||
|
'preview',
|
||||||
|
'facebookexternalhit',
|
||||||
|
'discordbot',
|
||||||
|
'telegrambot',
|
||||||
|
'linkedinbot',
|
||||||
|
'applebot',
|
||||||
|
'googlebot',
|
||||||
|
'bingbot',
|
||||||
|
'wget',
|
||||||
|
'curl',
|
||||||
|
'python-requests',
|
||||||
|
'postmanruntime',
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($bot_signatures as $signature) {
|
||||||
|
if (strpos($user_agent, $signature) !== false) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$purpose = strtolower((string) ($_SERVER['HTTP_PURPOSE'] ?? ''));
|
||||||
|
$sec_purpose = strtolower((string) ($_SERVER['HTTP_SEC_PURPOSE'] ?? ''));
|
||||||
|
$x_moz = strtolower((string) ($_SERVER['HTTP_X_MOZ'] ?? ''));
|
||||||
|
if (strpos($purpose, 'prefetch') !== false || strpos($sec_purpose, 'prefetch') !== false || $x_moz === 'prefetch') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function itstudio_get_view_cookie_name() {
|
||||||
|
return 'itstudio_viewed_posts';
|
||||||
|
}
|
||||||
|
|
||||||
|
function itstudio_read_view_cookie_map() {
|
||||||
|
$cookie_name = itstudio_get_view_cookie_name();
|
||||||
|
$raw_cookie = isset($_COOKIE[$cookie_name]) ? wp_unslash((string) $_COOKIE[$cookie_name]) : '';
|
||||||
|
if ($raw_cookie === '') {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$decoded = json_decode($raw_cookie, true);
|
||||||
|
if (!is_array($decoded)) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$clean = array();
|
||||||
|
foreach ($decoded as $post_id => $timestamp) {
|
||||||
|
$post_id = (int) $post_id;
|
||||||
|
$timestamp = (int) $timestamp;
|
||||||
|
if ($post_id > 0 && $timestamp > 0) {
|
||||||
|
$clean[(string) $post_id] = $timestamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $clean;
|
||||||
|
}
|
||||||
|
|
||||||
|
function itstudio_write_view_cookie_map($map, $window_seconds) {
|
||||||
|
if (!headers_sent()) {
|
||||||
|
$cookie_name = itstudio_get_view_cookie_name();
|
||||||
|
$expire_at = time() + max(DAY_IN_SECONDS, (int) $window_seconds * 2);
|
||||||
|
$path = defined('COOKIEPATH') && COOKIEPATH ? COOKIEPATH : '/';
|
||||||
|
$domain = defined('COOKIE_DOMAIN') ? COOKIE_DOMAIN : '';
|
||||||
|
$secure = is_ssl();
|
||||||
|
$http_only = true;
|
||||||
|
$encoded = wp_json_encode($map);
|
||||||
|
if (!is_string($encoded)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PHP_VERSION_ID >= 70300) {
|
||||||
|
setcookie($cookie_name, $encoded, array(
|
||||||
|
'expires' => $expire_at,
|
||||||
|
'path' => $path,
|
||||||
|
'domain' => $domain,
|
||||||
|
'secure' => $secure,
|
||||||
|
'httponly' => $http_only,
|
||||||
|
'samesite' => 'Lax',
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
setcookie($cookie_name, $encoded, $expire_at, $path, $domain, $secure, $http_only);
|
||||||
|
}
|
||||||
|
|
||||||
|
$_COOKIE[$cookie_name] = $encoded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function itstudio_should_count_post_view($post_id) {
|
||||||
|
$post_id = (int) $post_id;
|
||||||
|
if ($post_id <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itstudio_is_probably_bot_request()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$window_seconds = (int) apply_filters('itstudio_post_view_window_seconds', 12 * HOUR_IN_SECONDS);
|
||||||
|
$window_seconds = max(60, $window_seconds);
|
||||||
|
$max_entries = (int) apply_filters('itstudio_post_view_cookie_max_entries', 120);
|
||||||
|
$max_entries = max(10, $max_entries);
|
||||||
|
|
||||||
|
$now = time();
|
||||||
|
$key = (string) $post_id;
|
||||||
|
$map = itstudio_read_view_cookie_map();
|
||||||
|
|
||||||
|
foreach ($map as $id => $timestamp) {
|
||||||
|
if (($now - (int) $timestamp) > $window_seconds) {
|
||||||
|
unset($map[$id]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($map[$key]) && ($now - (int) $map[$key]) < $window_seconds) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$map[$key] = $now;
|
||||||
|
if (count($map) > $max_entries) {
|
||||||
|
asort($map, SORT_NUMERIC);
|
||||||
|
$map = array_slice($map, -$max_entries, null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
itstudio_write_view_cookie_map($map, $window_seconds);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
function itstudio_track_post_views() {
|
function itstudio_track_post_views() {
|
||||||
if (is_admin() || wp_doing_ajax() || wp_doing_cron()) {
|
if (is_admin() || wp_doing_ajax() || wp_doing_cron()) {
|
||||||
return;
|
return;
|
||||||
@@ -502,6 +646,10 @@ function itstudio_track_post_views() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!itstudio_should_count_post_view($post_id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$views = itstudio_get_post_views($post_id) + 1;
|
$views = itstudio_get_post_views($post_id) + 1;
|
||||||
update_post_meta($post_id, 'post_views_count', $views);
|
update_post_meta($post_id, 'post_views_count', $views);
|
||||||
update_post_meta($post_id, 'itstudio_views', $views);
|
update_post_meta($post_id, 'itstudio_views', $views);
|
||||||
|
|||||||
+113
-83
@@ -1,108 +1,138 @@
|
|||||||
<?php get_header(); ?>
|
<?php get_header(); ?>
|
||||||
|
|
||||||
<main class="site-main single-post-gh-pro">
|
<main class="site-main single-article-page">
|
||||||
<div class="container">
|
<div class="container single-article-shell">
|
||||||
|
<?php while (have_posts()) : the_post(); ?>
|
||||||
<?php
|
<?php
|
||||||
while (have_posts()) :
|
$post_id = get_the_ID();
|
||||||
the_post();
|
$post_type = get_post_type($post_id);
|
||||||
?>
|
$post_type_object = get_post_type_object($post_type);
|
||||||
<!-- 1. 文章头部:极简极客风,去掉 Issue 编号等干扰 -->
|
$post_type_archive_url = get_post_type_archive_link($post_type);
|
||||||
<header class="gh-pro-header">
|
$post_type_label_en = $post_type_object ? $post_type_object->labels->name : '';
|
||||||
<div class="gh-pro-meta-top">
|
$post_type_label_cn = $post_type_label_en;
|
||||||
<?php
|
if ($post_type === 'announcement') {
|
||||||
$categories = get_the_category();
|
$post_type_label_cn = '公告通知';
|
||||||
if ($categories) :
|
$post_type_label_en = 'Announcements';
|
||||||
$cat = $categories[0];
|
} elseif ($post_type === 'news') {
|
||||||
echo '<a href="' . get_category_link($cat->term_id) . '" class="gh-label-category">' . $cat->name . '</a>';
|
$post_type_label_cn = '社团新闻';
|
||||||
endif;
|
$post_type_label_en = 'News';
|
||||||
|
} elseif ($post_type === 'post') {
|
||||||
|
$post_type_label_cn = '技术博客';
|
||||||
|
$post_type_label_en = 'Blog';
|
||||||
|
}
|
||||||
|
$views = function_exists('itstudio_get_post_views') ? (int) itstudio_get_post_views($post_id) : 0;
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
||||||
|
<article id="post-<?php the_ID(); ?>" <?php post_class('single-article'); ?>>
|
||||||
|
<header class="single-article-header">
|
||||||
|
<div class="single-article-kicker">
|
||||||
|
<?php if (!empty($post_type_archive_url) && !empty($post_type_label_en)) : ?>
|
||||||
|
<a class="single-article-kicker-link" href="<?php echo esc_url($post_type_archive_url); ?>" data-cn="<?php echo esc_attr($post_type_label_cn); ?>" data-en="<?php echo esc_attr($post_type_label_en); ?>"><?php echo esc_html($post_type_label_en); ?></a>
|
||||||
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 class="entry-title"><?php the_title(); ?></h1>
|
<h1 class="single-article-title"><?php the_title(); ?></h1>
|
||||||
|
|
||||||
<div class="gh-pro-meta-bar">
|
<div class="single-article-meta">
|
||||||
<div class="gh-author-lockup">
|
<span class="single-article-meta-item">
|
||||||
<?php echo get_avatar(get_the_author_meta('ID'), 32); ?>
|
<span class="single-article-meta-label" data-cn="发布日期:" data-en="Published: ">Published: </span>
|
||||||
<span class="author-name"><?php the_author(); ?></span>
|
<time class="single-article-meta-value" datetime="<?php echo esc_attr(get_the_date('c')); ?>"><?php echo esc_html(get_the_date('Y年m月d日')); ?></time>
|
||||||
<span class="meta-divider">/</span>
|
</span>
|
||||||
<span class="publish-date"><?php echo get_the_date('Y.m.d'); ?></span>
|
<span class="single-article-meta-item">
|
||||||
</div>
|
<span class="single-article-meta-label" data-cn="作者:" data-en="Author: ">Author: </span>
|
||||||
|
<span class="single-article-meta-value"><?php echo esc_html(get_the_author()); ?></span>
|
||||||
<div class="gh-stats">
|
</span>
|
||||||
<span class="stat-item" title="<?php esc_attr_e('Comments', 'itstudio'); ?>">
|
<span class="single-article-meta-item">
|
||||||
<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-comment"><path d="M1 2.75C1 1.784 1.784 1 2.75 1h10.5c.966 0 1.75.784 1.75 1.75v7.5A1.75 1.75 0 0 1 13.25 12H9.06l-2.573 2.573A1.458 1.458 0 0 1 4 13.543V12H2.75A1.75 1.75 0 0 1 1 10.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h4.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path></svg>
|
<span class="single-article-meta-label" data-cn="浏览量:" data-en="Views: ">Views: </span>
|
||||||
<?php comments_number('0', '1', '%'); ?>
|
<span class="single-article-meta-value"><?php echo esc_html(number_format_i18n($views)); ?></span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<!-- 2. 正文容器:类似 GitHub File/Readme 的框体风格 -->
|
<div class="single-article-body">
|
||||||
<div class="gh-pro-body-container">
|
|
||||||
<div class="gh-file-box">
|
|
||||||
<div class="gh-file-header">
|
|
||||||
<div class="file-info">
|
|
||||||
<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-book"><path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path></svg>
|
|
||||||
<strong>readme.md</strong>
|
|
||||||
<span class="file-divider"></span>
|
|
||||||
<span class="file-size">
|
|
||||||
<?php
|
|
||||||
$read_time = round(str_word_count(strip_tags(get_the_content())) / 300, 1);
|
|
||||||
printf(
|
|
||||||
'<span data-cn="%s 分钟阅读" data-en="%s mins read"></span>',
|
|
||||||
$read_time,
|
|
||||||
$read_time
|
|
||||||
);
|
|
||||||
?>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="file-actions">
|
|
||||||
<a href="#comments" class="btn-sm" data-cn="发表评论" data-en="Post Comment"></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="gh-file-content markdown-body">
|
|
||||||
<?php if (has_post_thumbnail()) : ?>
|
|
||||||
<div class="entry-thumbnail-pro">
|
|
||||||
<?php the_post_thumbnail('large'); ?>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<?php the_content(); ?>
|
<?php the_content(); ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="gh-file-footer">
|
|
||||||
<?php
|
<?php
|
||||||
$tags = get_the_tags();
|
$tags = get_the_terms($post_id, 'post_tag');
|
||||||
if ($tags) :
|
if (!empty($tags) && !is_wp_error($tags)) :
|
||||||
foreach ($tags as $tag) {
|
|
||||||
echo '<span class="gh-tag tag-tag">#' . $tag->name . '</span> ';
|
|
||||||
}
|
|
||||||
endif;
|
|
||||||
?>
|
?>
|
||||||
</div>
|
<footer class="single-article-tags">
|
||||||
</div>
|
<?php foreach (array_slice($tags, 0, 8) as $tag) : ?>
|
||||||
</div>
|
<?php $tag_link = get_term_link($tag); ?>
|
||||||
</div>
|
<?php if (!is_wp_error($tag_link)) : ?>
|
||||||
|
<a class="single-article-tag" href="<?php echo esc_url($tag_link); ?>">#<?php echo esc_html($tag->name); ?></a>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</footer>
|
||||||
|
<?php endif; ?>
|
||||||
|
</article>
|
||||||
|
|
||||||
<!-- 3. 评论区:保持 GitHub Timeline 风格 -->
|
<section class="single-related">
|
||||||
<div class="gh-pro-comments">
|
<div class="single-divider" aria-hidden="true"></div>
|
||||||
|
<h2 class="single-related-title" data-cn="相关阅读" data-en="Related Content">Related Content</h2>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$related_args = array(
|
||||||
|
'post_type' => $post_type,
|
||||||
|
'post_status' => 'publish',
|
||||||
|
'posts_per_page' => 3,
|
||||||
|
'post__not_in' => array($post_id),
|
||||||
|
'ignore_sticky_posts' => true,
|
||||||
|
'orderby' => 'date',
|
||||||
|
'order' => 'DESC',
|
||||||
|
);
|
||||||
|
$tag_ids = wp_get_post_terms($post_id, 'post_tag', array('fields' => 'ids'));
|
||||||
|
if (!empty($tag_ids) && !is_wp_error($tag_ids)) {
|
||||||
|
$related_args['tax_query'] = array(
|
||||||
|
array(
|
||||||
|
'taxonomy' => 'post_tag',
|
||||||
|
'field' => 'term_id',
|
||||||
|
'terms' => $tag_ids,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$related_query = new WP_Query($related_args);
|
||||||
|
if (!$related_query->have_posts() && isset($related_args['tax_query'])) {
|
||||||
|
unset($related_args['tax_query']);
|
||||||
|
$related_query = new WP_Query($related_args);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="single-related-grid">
|
||||||
|
<?php if ($related_query->have_posts()) : ?>
|
||||||
|
<?php while ($related_query->have_posts()) : $related_query->the_post(); ?>
|
||||||
|
<?php
|
||||||
|
$related_excerpt = function_exists('itstudio_get_post_excerpt_chars')
|
||||||
|
? itstudio_get_post_excerpt_chars(get_the_ID(), 96)
|
||||||
|
: wp_html_excerpt(wp_strip_all_tags(get_the_excerpt() ?: get_the_content()), 96, '...');
|
||||||
|
?>
|
||||||
|
<article class="single-related-card">
|
||||||
|
<h3 class="single-related-card-title"><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h3>
|
||||||
|
<p class="single-related-card-excerpt"><?php echo esc_html($related_excerpt); ?></p>
|
||||||
|
<a class="single-related-card-link" href="<?php the_permalink(); ?>" data-cn="阅读更多 ->" data-en="Read more ->">Read more -></a>
|
||||||
|
</article>
|
||||||
|
<?php endwhile; ?>
|
||||||
|
<?php wp_reset_postdata(); ?>
|
||||||
|
<?php else : ?>
|
||||||
|
<p class="single-related-empty" data-cn="暂无相关文章" data-en="No related content">No related content</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<?php if (comments_open() || get_comments_number()) : ?>
|
||||||
|
<section class="single-article-comments gh-pro-comments">
|
||||||
<div class="timeline-header">
|
<div class="timeline-header">
|
||||||
<h3 data-cn="文章讨论" data-en="Discussion"></h3>
|
<h3 data-cn="文章讨论" data-en="Discussion"></h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="gh-timeline">
|
<div class="gh-timeline">
|
||||||
<!-- Vertical Line -->
|
<?php comments_template(); ?>
|
||||||
<div class="timeline-line"></div>
|
|
||||||
|
|
||||||
<?php
|
|
||||||
// 直接加载评论模板
|
|
||||||
if (comments_open() || get_comments_number()) :
|
|
||||||
comments_template();
|
|
||||||
endif;
|
|
||||||
?>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
|
<?php endif; ?>
|
||||||
<?php endwhile; ?>
|
<?php endwhile; ?>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
Reference in New Issue
Block a user