【DV脚本】展示最近创建和修改的笔记文件(按天周月季年和世纪分组显示各天的文件树卡片)
- 这个脚本显示视图的方式:按最近一天,一周,一月,一季,一年,十年,一世纪分区显示,并且在每个分区
- 显示创建时间卡片分组
- 显示修改时间卡片分组
- 每个卡片分组包含若干个特定日期的卡片
- 每个卡片有两种类型:创建时间和修改时间
- 每个卡片含有一个文件树,显示着对应的文件
- 文件如果包含属性
categories
或 kws
, 则会在对应的列表项末尾添加显示这些属性的值
- 这些分区有如下的折叠状态
- 第一个分区显示今天创建和修改的笔记,且一个卡片默认展开,一个卡片默认折叠
- 从第二个分区到最后一个分区的分区都需要通过点击按钮来显示,且所有卡片默认折叠
预览
视图上半部分
视图下半部分
点击 最近一周
按钮的视图变化结果
脚本内容
const isEnableTimeSpan = false;
const allPages = dv.pages();
function main(){
dv.container.style.overflow = "visible"
initStyle();
show("1 days");
showWithButton("6 days","1 days", "Week");
showWithButton("23 days", "7 days", "Month");
showWithButton("60 days", "30 days", "Season");
showWithButton("275 days", "90 days", "Year");
showWithButton("3285 days", "365 days", "Decade");
showWithButton("32850 days", "3650 days", "Century");
}
function show(durationStr = "1 days") {
const div = document.createElement("div");
dv.container.appendChild(div)
const pagesCTime = getPagesInThePeriod(durationStr,null,"ctime");
const pagesMTime = getPagesInThePeriod(durationStr,null,"mtime");
dv.header(2, "🔎ctime (Day: "+durationStr+", "+pagesCTime.length+" created files in total)",{container:div});
showCardGroup(pagesCTime,"Day", "ctime",div);
dv.header(2, "🔎mtime (Day: "+durationStr+", "+pagesMTime.length+" modified files in total)",{container:div});
showCardGroup(pagesMTime,"Day", "mtime",div);
}
function showWithButton(durationStr = "7 days", skipedDurationStr = "1 days", periodTypeInfo = "Period") {
const br = document.createElement("br");
dv.container.appendChild(br);
const button = document.createElement("input");
button.type = "button";
const pagesCTime = getPagesInThePeriod(durationStr,skipedDurationStr,"ctime");
const pagesMTime = getPagesInThePeriod(durationStr,skipedDurationStr,"mtime");
button.value = periodTypeInfo+": Generate View where period equals "+durationStr+" more, skiped "+skipedDurationStr+"\n"+"file statics: "+pagesCTime.length+" created files, "+ pagesMTime.length+" modified files";
dv.container.appendChild(button);
const div = document.createElement("div");
dv.container.appendChild(div);
button.onclick = ()=>{
br.remove();
button.remove();
dv.header(2, "🔎ctime ("+periodTypeInfo+": "+durationStr+", skiped "+skipedDurationStr+", "+pagesCTime.length+" created files in total)",{container:div});
const con0201 = document.createElement("div");
div.appendChild(con0201);
showCardGroup(pagesCTime, periodTypeInfo, "ctime",con0201,false);
dv.header(2, "🔎mtime ("+periodTypeInfo+": "+durationStr+", skiped "+skipedDurationStr+", "+pagesMTime.length+" modified files in total)",{container:div});
const con0202 = document.createElement("div");
div.appendChild(con0202);
showCardGroup(pagesMTime,periodTypeInfo,"mtime",con0202,false);
}
}
function initStyle(){
const style = document.createElement("style");
style.id = "20250918114602-style";
const styleInDoc = document.getElementById(style.id);
style.appendChild(document.createTextNode(`
details.my-details[open]>summary::marker{
color: rgba(0,255,0,0);
}
details.my-details[open]:hover>summary::marker{
color: red;
}
div.file-item:hover ,details.my-details>summary:hover{
background-color: rgba(0,255,0,0.2)
}
`))
if (styleInDoc){
styleInDoc.replaceWith(style);
} else {
document.head.appendChild(style);
}
}
function getPagesInThePeriod(durationStr,skipedDurationStr,timeType){
const today = dv.date('today');
const period = dv.duration(durationStr);
const skipedPeriod = dv.duration(skipedDurationStr)
const startday = today - period + dv.duration("1 days") - skipedPeriod;
const stopday = (skipedDurationStr?(today-skipedPeriod):today) + dv.duration("1 days");;
const pages = allPages
.where(p => p.file[timeType] >= startday && p.file[timeType] < stopday)
.sort(p => p.file[timeType], 'desc');
return pages;
}
function showCardGroup(pagesInThePeriod,periodTypeInfo,timeType,container=dv.container,cardOpen=true){
if (pagesInThePeriod?.length === 0){
dv.paragraph("⚠️No Files",{container})
return container;
}
const groups = pagesInThePeriod.groupBy(p=>dv.func.dateformat(p.file[timeType],"yyyy-MM-dd")).sort(g=>Math.max(g.rows.file[timeType]),"desc")
groups.forEach((g,i)=>{
const groupDiv = document.createElement("div");
groupDiv.style.borderStyle = "solid";
groupDiv.style.borderColor = "rgba(0,0,0,0.3)";
groupDiv.style.borderWidth = "0.3em";
groupDiv.style.borderRadius = "1.2em";
groupDiv.style.marginTop = "0.2em";
groupDiv.style.marginBottom = "0.2em";
container.appendChild(groupDiv);
const details = document.createElement("details");
if(cardOpen||i < 1){
details.open = true;
}
details.classList.add("my-details");
const summary = document.createElement("summary");
groupDiv.ondblclick = (e)=>{
if ([groupDiv,details].includes(e.srcElement)){
summary.click();
}
}
details.appendChild(summary);
summary.innerText = "📅"+g.key+" ("+timeType+": "+g.rows.length+" files in total, "+periodTypeInfo+")";
summary.style.backgroundColor = "rgba(0,0,0,0.2)";
summary.style.borderStyle = "solid";
summary.style.borderWidth = "0.1em";
summary.style.borderRadius = "2em";
summary.style.borderColor = "rgba(0,0,0,0.2)";
async function dispCardContent(container){
const tree = getFileTree(g.rows);
const cardContent = renderNodes(tree.rootNode.children, timeType);
await dv.api.renderValue(cardContent, container, dv.component,dv.currentFilePath);
}
const cardContentDiv = document.createElement("div");
cardContentDiv.style.marginLeft = "0.1em";
cardContentDiv.style.marginRight = "2em";
details.appendChild(cardContentDiv);
if (g.rows.length < 50){
dispCardContent(cardContentDiv);
}else {
const loadingHint = document.createElement("div");
loadingHint.innerText = "Loading";
cardContentDiv.appendChild(loadingHint);
dispCardContent(cardContentDiv).then(d=>{
loadingHint.remove();
})
}
groupDiv.appendChild(details);
})
return container;
}
function getFileTree(pages){
const rootNode = {
name: "",
type: "folder",
displayName: "",
children: [],
isRoot: true
};
const folderNodes = [rootNode];
const fileNodes = [];
pages.forEach(p=>{
const folder = p.file.folder;
const fileNode = {
name: p.file.path,
type: "file",
displayName: p.file.link,
children: [],
data: p
}
fileNodes.push(fileNode);
if (folder.length === 0){
rootNode.children.push(fileNode);
return;
}
const folderParts = folder.split("/");
let curNode = fileNode;
let curFolderNode = null;
let i = folderParts.length;
for (; i > 0; --i){
const folderName = folderParts.slice(0,i).join("/");
const qFolderNode = folderNodes.find(n=>n.name === folderName);
if(qFolderNode){
qFolderNode.children.push(curNode)
break;
}
curFolderNode = {
name: folderName,
type: "folder",
displayName: folderParts[i-1],
children: [curNode]
};
folderNodes.push(curFolderNode)
curNode = curFolderNode;
}
if (i === 0){
rootNode.children.push(curNode);
}
})
return {
rootNode,
folderNodes,
fileNodes
}
}
const isEnableLogUp = false;
function renderNode(node, timeType){
if (node.type === "file"){
const div = document.createElement("div");
div.classList.add("file-item");
div.style.marginLeft = "1em";
let display = "📄"+node.displayName
if (node.data.categories && node.data.categories.length !== 0){
display += " "+dv.array(node.data.categories).distinct().map(k=>dv.value.isLink(k)?dv.fileLink(k.path,false,"📗"+k.path.replace(/\.md$/,"").replace(/^.*\//,"")):("📗"+k)).join(" ")
}
if (node.data.kws && node.data.kws.length !== 0){
display += " "+dv.array(node.data.kws).distinct().map(k=>dv.value.isLink(k)?dv.fileLink(k.path,false,"🔖"+k.path.replace(/\.md$/,"").replace(/^.*\//,"")):("🔖"+k)).join(" ")
}
if (isEnableLogUp){
let up = node.data.up;
if (dv.value.isArray(up)){
display+= "<br>  "+up.map(k=>dv.value.isLink(k)?dv.fileLink(k.path,false,"🔼"+k.path.replace(/\.md$/,"").replace(/^.*\//,"")):("🔼"+k)).join(" ")
}
}
dv.span(display, {container:div});
if (isEnableTimeSpan){
const timeSpan = document.createElement("span");
timeSpan.innerText = "⏲️"+dv.func.dateformat(node.data.file[timeType],"yyyy-MM-dd");
timeSpan.style.float = "right";
div.appendChild(timeSpan);
}
return div;
}
const details = document.createElement("details");
details.classList.add("my-details");
details.open = true;
if (node.children.length === 0){
return details;
}
const summary = document.createElement("summary");
details.appendChild(summary);
let curFolderNode = node;
let display = node.displayName;
while (curFolderNode.children.length === 1 && curFolderNode.children[0].type === "folder"){
curFolderNode = curFolderNode.children[0];
display += "/"+curFolderNode.displayName;
}
const childFolders = dv.array(curFolderNode.children)
.filter(c=>c.type === "folder")
.sort(c=>c.name);
const childFiles = dv.array(curFolderNode.children)
.filter(c=>c.type === "file")
.sort(c=>c.data.file[timeType],"desc");
function getFileCountInTotal(folderNode){
let folderNodes = [folderNode];
let i = 0;
for(; i < folderNodes.length; ++i){
folderNodes = folderNodes.concat(folderNodes[i].children.filter(c=>c.type === "folder"));
}
i = 0;
let sum = 0;
for(; i < folderNodes.length; ++i){
sum += folderNodes[i].children.filter(c=>c.type === "file").length;
}
return sum;
}
let infoArr = [];
if (childFolders.length !== 0){
infoArr.push(childFolders.length+" folders");
}
if (childFiles.length !== 0){
infoArr.push(childFiles.length+" files");
}
const fileCountInTotal = getFileCountInTotal(curFolderNode);
if (childFolders.length !== 0 && childFiles.length !== fileCountInTotal){
infoArr.push(fileCountInTotal+" files in total");
}
const info = " ("+infoArr.join(", ")+")";
dv.span("📁"+display+info, {container: summary});
const div = document.createElement("div");
div.style.marginLeft = "1em";
details.appendChild(div);
childFolders.forEach(c=>div.appendChild(renderNode(c, timeType)));
childFiles.forEach(c=>div.appendChild(renderNode(c, timeType)));
return details;
}
function renderNodes(nodes, timeType){
const div = document.createElement("div");
dv.array(nodes).sort(n=>n.name).forEach(n=>div.appendChild(renderNode(n, timeType)));
return div;
}
main();