Files
FamilyBook/assets/js/preview.js
T
laoyang 8f80f3e835 v24 第 2 轮:19 条清单收尾(附件预览/CalDAV/任务分类看板/全局选人/家谱/纪念日/财务)
用户 2026-10-02 提的 19 条需求,本轮完成剩下的 8 条 + CalDAV 服务端:

- ③ 附件:图片/视频给缩略图,PDF/Word/Excel 给**同尺寸**图标;可预览的在本页
     弹层里看(不再新开标签页),渲染不了的点了直接下载;预览层右上角恒有下载按钮。
- ⑦b CalDAV:把「我的日历本」按 RFC 4791 只读子集分享出去(OPTIONS/PROPFIND/
     REPORT/GET,写方法一律 403)。用**专用口令**(库里只存 sha256),不是登录口令。
- ⑨⑩ 任务:分类与看板都由用户自建;已完成的任务从看板消失,改在「已完成」页
     按分类查看/搜索;设置入口在「新建任务」旁的 ☰。
- ⑪ 全局人员选择器:一个片段铺到观影/旅程/资源/日历共享/任务共享/财务共享;
     修掉了老组件「名字和拼音都搜不动」的 bug —— .att-item 自己声明了 display:flex,
     压过了浏览器 [hidden]{display:none},过滤根本没生效。
- ⑫ 家谱:头像改方形不再裁切;姻亲不在图上的卡片给「⇄ 姻亲」入口,
     以 TA 为中心重画 + 「返回我的家谱」。
- ⑬ 纪念日:阴历/阳历都能设且按对应历法重复;名册里每个人的生日默认可见
     (实时派生,不复制一份);全部作为全天日程进日历。
- ⑮ 财务:账本可共享,双方都能记账、都能看全部账目,资产−负债=净资产;
     金额一律以「分」存,不用浮点。

验证:端到端 724 项通过 / 0 失败;几何 368、交互 97、移动端 516、家谱 24、
升级 123 全绿;SQL 列名静态校验 0 处可疑;部署 11 条线上探针全过。
2026-10-03 08:18:05 +08:00

102 lines
3.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 全局附件预览(v24 用户第 3 条)
*
* 点缩略图 → **在本页**弹一层把内容放出来,不再新开标签页。
* · 图片 → <img>
* · 视频 → <video controls autoplay>(media.php 支持 Range,进度条能拖)
* · PDF → <iframe>(用浏览器自带的阅读器)
* · 其它(Word / Excel / …)→ 根本不进这里:那段链接指向的是下载地址,点了就下。
*
* 右上角恒有一个「下载」按钮(用户原话:所有的预览页面,右上角都有一个下载按钮)。
*
* 触发方式是数据属性,不用给每个地方写一遍初始化:
* <a href="…" data-prev="image|video|pdf" data-src="…" data-dl="…" data-name="…">
*/
(function () {
'use strict';
var box = document.getElementById('pv');
if (!box) { return; }
var stage = box.querySelector('.pv-stage');
var titleEl = box.querySelector('.pv-name');
var metaEl = box.querySelector('.pv-meta');
var dlEl = box.querySelector('.pv-dl');
var closeEl = box.querySelector('.pv-close');
var opener = null;
function clear() {
// ⚠️ 换内容要**整个重建**节点,不能只改 src:
// <video> 改 src 不会自动卸载上一支(声音会叠着放),<iframe> 同理。
stage.innerHTML = '';
}
function open(trigger) {
var kind = trigger.getAttribute('data-prev') || 'image';
var src = trigger.getAttribute('data-src') || trigger.getAttribute('href') || '';
var dl = trigger.getAttribute('data-dl') || src;
if (!src) { return; }
opener = trigger;
clear();
var el;
if (kind === 'video') {
el = document.createElement('video');
el.className = 'pv-video';
el.src = src;
el.controls = true;
el.autoplay = true;
el.playsInline = true;
} else if (kind === 'pdf') {
el = document.createElement('iframe');
el.className = 'pv-pdf';
el.src = src;
// PDF 是"另一份文档",别让它拿到本页的 window
el.setAttribute('sandbox', 'allow-same-origin allow-scripts');
} else {
el = document.createElement('img');
el.className = 'pv-img';
el.src = src;
el.alt = '';
}
stage.appendChild(el);
titleEl.textContent = trigger.getAttribute('data-name') || '';
var sz = trigger.getAttribute('data-size') || '';
metaEl.textContent = sz;
dlEl.setAttribute('href', dl);
// 下载要带上真实文件名:download 属性 + 服务端 Content-Disposition 双保险
var nm = trigger.getAttribute('data-name') || '';
if (nm) { dlEl.setAttribute('download', nm); } else { dlEl.removeAttribute('download'); }
box.hidden = false;
box.setAttribute('aria-hidden', 'false');
closeEl.focus();
}
function close() {
box.hidden = true;
box.setAttribute('aria-hidden', 'true');
clear(); // 停掉视频 / 卸掉 PDF
if (opener && opener.focus) { opener.focus(); }
opener = null;
}
document.addEventListener('click', function (e) {
var t = e.target && e.target.closest ? e.target.closest('[data-prev]') : null;
if (t) {
// ⚠️ 只拦"可预览"的:Word/Excel 那类链接没有 data-prev,
// 浏览器照常走 href(就是下载地址),不会被这里挡住。
e.preventDefault();
open(t);
return;
}
// 点遮罩(stage 本身或弹层空白处)关闭;点内容不关
if (!box.hidden && (e.target === box || e.target === stage)) { close(); }
});
closeEl.addEventListener('click', close);
document.addEventListener('keydown', function (e) {
if (!box.hidden && e.key === 'Escape') { close(); }
});
})();