feat: 分享传播与经文纸水墨背景
- 分享按钮:系统分享 + 二维码卡片(qr.png),移动端移入播放行不再遮挡标题 - og 分享卡片元数据 + og.png 封面;beforeinstallprompt 安装提示 - robots.txt / sitemap.xml / poster.png 传播物料 - .sutra-paper 宣纸底纹:桌面 bg.jpg,移动端竖版 bg-m.jpg,夜间模式罩层适配 - 移动端设置区重排为双列标签网格 Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
@@ -1037,3 +1037,64 @@ function openAdmin() {
|
||||
loadStats();
|
||||
card.querySelector("#admin-refresh").addEventListener("click", loadStats);
|
||||
}
|
||||
|
||||
// ===== 分享与 PWA 安装提示 =====
|
||||
const btnShare = document.getElementById("btn-share");
|
||||
let installPromptEvt = null; // beforeinstallprompt 事件暂存,供分享卡片内"安装到主屏幕"使用
|
||||
window.addEventListener("beforeinstallprompt", (e) => {
|
||||
e.preventDefault();
|
||||
installPromptEvt = e;
|
||||
});
|
||||
|
||||
/** 分享本站:两端统一弹二维码卡片,手机端贴底升起;支持系统分享时另有"发送给朋友"。 */
|
||||
btnShare.addEventListener("click", () => {
|
||||
openShareCard(location.origin + "/");
|
||||
});
|
||||
|
||||
/** 分享卡片:二维码扫码即达 + 复制链接 + 系统分享(如可用)+ 可安装时显示安装按钮。 */
|
||||
function openShareCard(url) {
|
||||
const mask = document.createElement("div");
|
||||
mask.className = "admin-mask share";
|
||||
const card = document.createElement("div");
|
||||
card.className = "admin-card share-card";
|
||||
card.setAttribute("role", "dialog");
|
||||
card.setAttribute("aria-label", "分享本站");
|
||||
card.innerHTML = `
|
||||
<div class="admin-title">分享本站</div>
|
||||
<img class="share-qr" src="qr.png" alt="本站二维码">
|
||||
<span class="share-link">${url}</span>
|
||||
<div class="admin-actions">
|
||||
${navigator.share ? '<button type="button" class="admin-btn" id="share-native">发送给朋友</button>' : ""}
|
||||
<button type="button" class="admin-btn" id="share-copy">复制链接</button>
|
||||
${installPromptEvt ? '<button type="button" class="admin-btn" id="share-install">安装到主屏幕</button>' : ""}
|
||||
<button type="button" class="admin-btn" id="share-close">关闭</button>
|
||||
</div>`;
|
||||
mask.appendChild(card);
|
||||
document.body.appendChild(mask);
|
||||
const close = () => mask.remove();
|
||||
mask.addEventListener("click", (e) => { if (e.target === mask) close(); });
|
||||
card.querySelector("#share-close").addEventListener("click", close);
|
||||
card.querySelector("#share-native")?.addEventListener("click", async () => {
|
||||
try {
|
||||
await navigator.share({
|
||||
title: "经典诵读",
|
||||
text: "心经、金刚经、道德经等经典在线诵读:千问语音合成,逐字高亮跟读,山寺配乐。",
|
||||
url,
|
||||
});
|
||||
} catch { /* 用户取消分享 */ }
|
||||
});
|
||||
card.querySelector("#share-copy").addEventListener("click", async (e) => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(url);
|
||||
e.target.textContent = "已复制";
|
||||
} catch {
|
||||
e.target.textContent = "复制失败";
|
||||
}
|
||||
});
|
||||
card.querySelector("#share-install")?.addEventListener("click", async () => {
|
||||
installPromptEvt.prompt();
|
||||
await installPromptEvt.userChoice;
|
||||
installPromptEvt = null;
|
||||
close();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ HOST=154.8.162.18
|
||||
USER=root
|
||||
DEST=/opt/fj
|
||||
# 注意:不同步 .env —— 服务器端 PORT=8018 与本地开发配置不同,避免覆盖。
|
||||
FILES=(index.html style.css app.js server.py sw.js manifest.json icon.svg icon-180.png icon-192.png icon-512.png README.md run.md)
|
||||
FILES=(index.html style.css app.js server.py sw.js manifest.json icon.svg icon-180.png icon-192.png icon-512.png README.md run.md robots.txt sitemap.xml og.png qr.png poster.png bg.jpg bg-m.jpg)
|
||||
|
||||
SSH_OPTS="-o StrictHostKeyChecking=no -o ConnectTimeout=15"
|
||||
SSH_CMD="ssh $SSH_OPTS"
|
||||
|
||||
+10
-3
@@ -5,7 +5,13 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="theme-color" content="#f6f1e6">
|
||||
<meta name="description" content="心经、金刚经、阿弥陀经、普门品、道德经、清静经、太上感应篇诵读——千问语音合成,晨钟暮鼓配乐,逐段跟读高亮。">
|
||||
<title>心经</title>
|
||||
<title>经典诵读 · 心经</title>
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:title" content="经典诵读 · 佛经道家经典在线诵读">
|
||||
<meta property="og:description" content="心经、金刚经、道德经等七部经典:千问语音诵读,逐字高亮跟读,山寺晨钟配乐。">
|
||||
<meta property="og:url" content="https://fj.all8ai.top/">
|
||||
<meta property="og:image" content="https://fj.all8ai.top/og.png">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<link rel="icon" type="image/svg+xml" href="icon.svg">
|
||||
<link rel="apple-touch-icon" href="icon-180.png">
|
||||
<link rel="manifest" href="manifest.json">
|
||||
@@ -13,7 +19,7 @@
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="default">
|
||||
<meta name="apple-mobile-web-app-title" content="经典诵读">
|
||||
<link rel="stylesheet" href="style.css?v=11">
|
||||
<link rel="stylesheet" href="style.css?v=17">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -90,6 +96,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div id="status" class="status" role="status"></div>
|
||||
<button type="button" id="btn-share" class="share-btn" aria-label="分享本站">分享</button>
|
||||
<button type="button" id="btn-expand" class="expand-btn" aria-expanded="false" aria-label="展开设置" title="设置">
|
||||
<span class="expand-label">设置</span>
|
||||
<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09a1.65 1.65 0 0 0-1-1.51 1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09a1.65 1.65 0 0 0 1.51-1 1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||
@@ -116,6 +123,6 @@
|
||||
</footer>
|
||||
|
||||
<audio id="audio" preload="auto"></audio>
|
||||
<script src="app.js?v=13"></script>
|
||||
<script src="app.js?v=15"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
@@ -0,0 +1,5 @@
|
||||
User-agent: *
|
||||
Allow: /
|
||||
Disallow: /api/
|
||||
|
||||
Sitemap: https://fj.all8ai.top/sitemap.xml
|
||||
@@ -9,6 +9,8 @@
|
||||
| TTS | 千问 `qwen3-tts-flash`,走 DashScope 兼容接口 `/api/v1/services/aigc/multimodal-generation/generation` |
|
||||
| 经文数据 | `sutras/*.json` 外置数据文件,目录实时扫描,加经不改代码 |
|
||||
| 背景音乐 | `bgm/` 目录音频文件循环播放(前端淡入淡出),deploy.sh 随 `sutras/` 一并同步 |
|
||||
| 传播资源 | `og.png` 分享卡片图、`qr.png` 二维码、`poster.png` 海报、`bg.jpg` 经文底纹、`robots.txt`/`sitemap.xml` |
|
||||
| 后台面板 | 连点页脚提示 6 次唤出,`GET /api/stats` 返回运行统计(访客 IP 取 X-Forwarded-For 末位) |
|
||||
|
||||
## 首次准备
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url>
|
||||
<loc>https://fj.all8ai.top/</loc>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
</urlset>
|
||||
@@ -558,7 +558,18 @@ body.night .jump-now { background: rgba(43, 36, 27, 0.92); }
|
||||
.size-btn { width: 34px; height: 30px; font-size: 0.8rem; }
|
||||
.layout-btn, .sutra-btn, .pager-btn { padding: 5px 8px; font-size: 0.78rem; min-height: 30px; }
|
||||
|
||||
.sutra-paper { padding: 18px 16px; }
|
||||
.sutra-paper {
|
||||
padding: 18px 16px;
|
||||
/* 手机端竖版水墨底纹:底部山寺剪影,上部留白供文字 */
|
||||
background:
|
||||
linear-gradient(rgba(253, 250, 242, 0.85), rgba(253, 250, 242, 0.85)),
|
||||
url("bg-m.jpg") center bottom / cover no-repeat;
|
||||
}
|
||||
body.night .page .sutra-paper {
|
||||
background:
|
||||
linear-gradient(rgba(41, 34, 25, 0.88), rgba(41, 34, 25, 0.88)),
|
||||
url("bg-m.jpg") center bottom / cover no-repeat;
|
||||
}
|
||||
.sutra-paper::before { inset: 6px; }
|
||||
.sutra-paper::after { inset: 9px; }
|
||||
.sutra { column-width: auto; }
|
||||
@@ -775,3 +786,118 @@ body.night .seg.playing { background: rgba(201, 106, 92, 0.16); color: #f0c9c2;
|
||||
}
|
||||
.admin-btn:hover { border-color: var(--gold); color: var(--ink); }
|
||||
body.night .admin-card { background: #241d15; }
|
||||
|
||||
/* ===== 分享按钮(固定右上,与左上 logo 呼应) ===== */
|
||||
.share-btn {
|
||||
position: fixed;
|
||||
top: 18px;
|
||||
right: 26px;
|
||||
z-index: 30;
|
||||
padding: 6px 14px;
|
||||
font-family: inherit;
|
||||
font-size: 0.78rem;
|
||||
letter-spacing: 0.2em;
|
||||
color: var(--cinnabar);
|
||||
background: var(--paper);
|
||||
border: 1px solid var(--gold);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 3px 10px rgba(30, 22, 14, 0.12);
|
||||
transition: color 0.15s ease, border-color 0.15s ease;
|
||||
}
|
||||
.share-btn:hover { color: var(--ink); }
|
||||
.share-btn:focus-visible { outline: 3px solid var(--gold-soft); outline-offset: 1px; }
|
||||
body.night .share-btn { background: #241d15; }
|
||||
|
||||
/* ===== 分享弹层(二维码 + 复制链接 + 安装) ===== */
|
||||
.share-card .share-qr { display: block; width: 200px; margin: 8px auto; border: 1px solid var(--hairline); border-radius: 8px; }
|
||||
.share-card .share-link {
|
||||
display: block;
|
||||
text-align: center;
|
||||
font-size: 0.78rem;
|
||||
color: var(--ink-soft);
|
||||
word-break: break-all;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.share-card .admin-actions { flex-wrap: wrap; }
|
||||
.share-card .admin-btn { flex: 1 1 45%; }
|
||||
|
||||
@media (max-width: 720px) {
|
||||
/* 手机端分享按钮不再固定悬浮,进入播放行(收起态夹在"设置"前) */
|
||||
.share-btn {
|
||||
position: static;
|
||||
order: 3;
|
||||
flex: none;
|
||||
height: 34px;
|
||||
padding: 0 11px;
|
||||
font-size: 0.72rem;
|
||||
letter-spacing: 0.1em;
|
||||
color: var(--ink-soft);
|
||||
background: transparent;
|
||||
border-color: var(--hairline);
|
||||
box-shadow: none;
|
||||
}
|
||||
/* 展开态:与循环/夜间同行三等分 */
|
||||
.player-card.expanded .share-btn { order: 9; flex: 1 1 28%; height: 30px; padding: 5px 8px; font-size: 0.78rem; }
|
||||
.player-card.expanded .tool-group[aria-label="模式"] .layout-btn { flex: 1 1 28%; }
|
||||
/* 分享卡片贴底升起 */
|
||||
.admin-mask.share { align-items: flex-end; padding: 0; }
|
||||
.admin-mask.share .share-card {
|
||||
width: 100%;
|
||||
border-radius: 12px 12px 0 0;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
border-bottom: none;
|
||||
max-height: 70vh;
|
||||
padding-bottom: calc(16px + env(safe-area-inset-bottom));
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== 桌面端经文纸底纹:山寺水墨淡化垫底,仅宽屏启用 ===== */
|
||||
@media (min-width: 721px) {
|
||||
/* 水墨底纹衬在经文纸(.sutra-paper)区域,不铺出纸外 */
|
||||
.sutra-paper {
|
||||
background:
|
||||
linear-gradient(rgba(253, 250, 242, 0.84), rgba(253, 250, 242, 0.84)),
|
||||
url("bg.jpg") center / cover no-repeat;
|
||||
}
|
||||
body.night .sutra-paper {
|
||||
background:
|
||||
linear-gradient(rgba(41, 34, 25, 0.87), rgba(41, 34, 25, 0.87)),
|
||||
url("bg.jpg") center / cover no-repeat;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== 手机端展开态设置区重排:统一"小标签 + 控件"双列网格 ===== */
|
||||
@media (max-width: 720px) {
|
||||
.player-card.expanded .btn-play { order: 1; flex: 1 1 100%; }
|
||||
.player-card.expanded .sleep-remaining.show { order: 2; flex: 1 1 100%; justify-content: center; }
|
||||
/* 第一行:经典 | 音色 */
|
||||
.player-card.expanded .player-row > .field-pair { order: 3; flex: 1 1 46%; }
|
||||
.player-card.expanded .player-meta { order: 4; flex: 1 1 46%; gap: 8px; }
|
||||
/* 第二行:字号 | 语速 */
|
||||
.player-card.expanded .tool-group[aria-label="字号"] { order: 5; flex: 1 1 46%; justify-content: flex-start; gap: 8px; }
|
||||
.player-card.expanded .size-btn { flex: 1; }
|
||||
.player-card.expanded .tool-group[aria-label="模式"] { display: contents; }
|
||||
.player-card.expanded .tool-group[aria-label="模式"] .field-pair { flex: 1 1 46%; }
|
||||
.player-card.expanded .tool-group[aria-label="模式"] .field-pair:has(#rate-select) { order: 6; }
|
||||
/* 第三行:配乐 | 定时 */
|
||||
.player-card.expanded .tool-group[aria-label="模式"] .field-pair:has(#bgm-select) { order: 7; }
|
||||
.player-card.expanded .tool-group[aria-label="模式"] .field-pair:has(#timer-select) { order: 8; }
|
||||
/* 第四行:循环 | 夜间 */
|
||||
.player-card.expanded .tool-group[aria-label="模式"] .layout-btn { flex: 1 1 46%; order: 9; }
|
||||
.player-card.expanded .flex-break { display: none; }
|
||||
.player-card.expanded .expand-btn { order: 11; flex: 1 1 100%; }
|
||||
/* 每个单元格统一:金色小标签 + 控件 */
|
||||
.player-card.expanded .tool-name,
|
||||
.player-card.expanded .voice-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 0.68rem;
|
||||
color: var(--gold);
|
||||
letter-spacing: 0.06em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.player-card.expanded .field-pair .sheet-btn,
|
||||
.player-card.expanded .player-meta .sheet-btn { flex: 1; min-width: 0; }
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* - /api/tts 不拦截,直接透传网络
|
||||
*/
|
||||
const CACHE = "fj-shell-v1";
|
||||
const SHELL = ["/", "/index.html", "/style.css?v=11", "/app.js?v=13", "/icon.svg", "/icon-180.png", "/icon-192.png", "/icon-512.png", "/manifest.json"];
|
||||
const SHELL = ["/", "/index.html", "/style.css?v=17", "/app.js?v=15", "/icon.svg", "/icon-180.png", "/icon-192.png", "/icon-512.png", "/manifest.json"];
|
||||
|
||||
self.addEventListener("install", (e) => {
|
||||
e.waitUntil(caches.open(CACHE).then((c) => c.addAll(SHELL)).then(() => self.skipWaiting()));
|
||||
|
||||
Reference in New Issue
Block a user