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 条线上探针全过。
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* 全局附件预览(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(); }
|
||||
});
|
||||
})();
|
||||
Reference in New Issue
Block a user