feat: 新增知资札记工具及相关功能
1. 新增知资札记完整站点:包含首页、详情页、管理后台 2. 添加站点图标、鉴权配置与后端API鉴权逻辑 3. 在市场页面新增知资札记入口跳转 4. 新增经典乡村音乐播放列表文件 5. 临时添加养生谚语批量导入脚本
This commit is contained in:
@@ -0,0 +1,224 @@
|
||||
/**
|
||||
* 知资札记 - 文章详情页逻辑
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
const API_BASE = '/api/zhizi_notes';
|
||||
const STORAGE_KEY = 'zhizi_notes_theme';
|
||||
const $ = function (sel) { return document.querySelector(sel); };
|
||||
|
||||
let articleId = null;
|
||||
let isFavorited = false;
|
||||
|
||||
// ── 解析 URL 参数 ──
|
||||
function getParam(name) {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
return params.get(name);
|
||||
}
|
||||
|
||||
// ── Toast ──
|
||||
function showToast(msg, type) {
|
||||
type = type || 'info';
|
||||
const t = $('#zd-toast');
|
||||
if (!t) return;
|
||||
t.textContent = msg;
|
||||
t.className = 'zs-toast ' + type + ' show';
|
||||
clearTimeout(t._timer);
|
||||
t._timer = setTimeout(function () { t.classList.remove('show'); }, type === 'error' ? 4000 : 2000);
|
||||
}
|
||||
|
||||
// ── 暗黑模式 ──
|
||||
function initTheme() {
|
||||
const saved = localStorage.getItem(STORAGE_KEY);
|
||||
if (saved === 'dark') {
|
||||
document.documentElement.setAttribute('data-theme', 'dark');
|
||||
}
|
||||
const btn = $('#zs-theme-btn');
|
||||
if (btn) {
|
||||
btn.textContent = saved === 'dark' ? '☀️' : '🌙';
|
||||
btn.addEventListener('click', function () {
|
||||
const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
|
||||
if (isDark) {
|
||||
document.documentElement.removeAttribute('data-theme');
|
||||
localStorage.setItem(STORAGE_KEY, 'light');
|
||||
btn.textContent = '🌙';
|
||||
} else {
|
||||
document.documentElement.setAttribute('data-theme', 'dark');
|
||||
localStorage.setItem(STORAGE_KEY, 'dark');
|
||||
btn.textContent = '☀️';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ── API ──
|
||||
async function apiGet(path) {
|
||||
try {
|
||||
const res = await fetch(API_BASE + path, { credentials: 'same-origin' });
|
||||
return await res.json();
|
||||
} catch (e) { return { ok: false, error: e.message }; }
|
||||
}
|
||||
|
||||
async function apiPost(path, body) {
|
||||
try {
|
||||
const res = await fetch(API_BASE + path, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify(body)
|
||||
});
|
||||
return await res.json();
|
||||
} catch (e) { return { ok: false, error: e.message }; }
|
||||
}
|
||||
|
||||
// ── 加载详情 ──
|
||||
async function loadDetail() {
|
||||
articleId = getParam('id');
|
||||
if (!articleId) {
|
||||
showError('缺少文章ID');
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await apiGet('/articles/' + articleId);
|
||||
const loading = $('#zd-loading');
|
||||
if (loading) loading.style.display = 'none';
|
||||
|
||||
if (!data.ok) {
|
||||
showError(data.error || '文章不存在');
|
||||
return;
|
||||
}
|
||||
|
||||
renderArticle(data.data);
|
||||
}
|
||||
|
||||
function showError(msg) {
|
||||
const loading = $('#zd-loading');
|
||||
const error = $('#zd-error');
|
||||
if (loading) loading.style.display = 'none';
|
||||
if (error) {
|
||||
error.style.display = 'block';
|
||||
$('#zd-error-msg').textContent = msg;
|
||||
}
|
||||
}
|
||||
|
||||
// ── 渲染文章 ──
|
||||
function renderArticle(article) {
|
||||
const articleEl = $('#zd-article');
|
||||
if (!articleEl) return;
|
||||
articleEl.style.display = 'block';
|
||||
|
||||
// 标题
|
||||
$('#zd-title').textContent = article.title;
|
||||
document.title = article.title + ' · 知资札记';
|
||||
|
||||
// 元信息
|
||||
$('#zd-category').textContent = article.category || '未分类';
|
||||
$('#zd-source').textContent = article.source ? '来源:' + article.source : '';
|
||||
$('#zd-date').textContent = (article.created_at || '').slice(0, 10);
|
||||
$('#zd-views').textContent = (article.views || 0) + ' 阅读';
|
||||
|
||||
// 收藏按钮
|
||||
isFavorited = article.fav_count > 0;
|
||||
updateFavBtn();
|
||||
const favBtn = $('#zd-fav-btn');
|
||||
if (favBtn) {
|
||||
favBtn.addEventListener('click', function () { toggleFavorite(); });
|
||||
}
|
||||
|
||||
// 标签
|
||||
const tagsEl = $('#zd-tags');
|
||||
try {
|
||||
const tags = JSON.parse(article.tags_json || '[]');
|
||||
if (tags.length > 0 && tagsEl) {
|
||||
tagsEl.innerHTML = '';
|
||||
tags.forEach(function (t) {
|
||||
const span = document.createElement('span');
|
||||
span.className = 'zd-tag';
|
||||
span.textContent = t;
|
||||
tagsEl.appendChild(span);
|
||||
});
|
||||
} else if (tagsEl) {
|
||||
tagsEl.style.display = 'none';
|
||||
}
|
||||
} catch (e) { if (tagsEl) tagsEl.style.display = 'none'; }
|
||||
|
||||
// 封面图
|
||||
if (article.cover_url) {
|
||||
const cover = $('#zd-cover');
|
||||
const coverImg = $('#zd-cover-img');
|
||||
if (cover) cover.style.display = 'block';
|
||||
if (coverImg) coverImg.src = article.cover_url;
|
||||
}
|
||||
|
||||
// 正文
|
||||
const contentEl = $('#zd-content');
|
||||
if (contentEl) {
|
||||
contentEl.innerHTML = article.content || article.content_plain || '';
|
||||
}
|
||||
|
||||
// 图片集
|
||||
if (article.images && article.images.length > 0) {
|
||||
const gallery = $('#zd-gallery');
|
||||
const grid = $('#zd-gallery-grid');
|
||||
if (gallery) gallery.style.display = 'block';
|
||||
if (grid) {
|
||||
grid.innerHTML = '';
|
||||
article.images.forEach(function (img) {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'gallery-item';
|
||||
const imgEl = document.createElement('img');
|
||||
imgEl.src = '/uploads/zhizi_notes/' + img.filename;
|
||||
imgEl.alt = img.original_name || '';
|
||||
imgEl.loading = 'lazy';
|
||||
item.appendChild(imgEl);
|
||||
grid.appendChild(item);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── 收藏切换 ──
|
||||
async function toggleFavorite() {
|
||||
const data = await apiPost('/articles/' + articleId + '/toggle_favorite', {});
|
||||
if (data.ok) {
|
||||
isFavorited = data.data ? data.data.favorited : !isFavorited;
|
||||
updateFavBtn();
|
||||
showToast(isFavorited ? '已收藏' : '已取消收藏', isFavorited ? 'success' : 'info');
|
||||
} else {
|
||||
showToast('操作失败:' + (data.error || '未知错误'), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
function updateFavBtn() {
|
||||
const btn = $('#zd-fav-btn');
|
||||
if (!btn) return;
|
||||
if (isFavorited) {
|
||||
btn.classList.add('favorited');
|
||||
btn.textContent = '♥';
|
||||
} else {
|
||||
btn.classList.remove('favorited');
|
||||
btn.textContent = '♡';
|
||||
}
|
||||
}
|
||||
|
||||
// ── 初始化 ──
|
||||
function init() {
|
||||
initTheme();
|
||||
loadDetail();
|
||||
|
||||
// 返回按钮
|
||||
const backBtn = $('#zd-back');
|
||||
if (backBtn) {
|
||||
backBtn.addEventListener('click', function () {
|
||||
if (document.referrer && document.referrer.indexOf('/tools/zhizi_notes') !== -1) {
|
||||
history.back();
|
||||
} else {
|
||||
location.href = '/tools/zhizi_notes/index.html';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
})();
|
||||
Reference in New Issue
Block a user