https://github.com/frostime/sy-query-view/discussions/6
花了大概 7 分钟让 Deepseek 帮忙写的一个 QueryView 的倒计时组件。

效果如下:显示从今天到三月七日的倒计时。

用法:在 Query View 自定义视图文件(见参考文档说明)中添加组件代码 countdown


const custom = {
countdown: {
use: (dv) => {
return {
render: (container, startDateStr, targetDateStr) => {
// 创建容器
const countdownContainer = document.createElement('div');
Object.assign(countdownContainer.style, {
maxWidth: '800px',
margin: '5vh auto',
padding: '2rem',
//backgroundColor: '#f8f9fa',
backgroundColor: 'var(--b3-theme-surface-light)',
borderRadius: '15px',
//boxShadow: '0 10px 30px rgba(0,0,0,0.1)'
boxShadow: '0 10px 15px 3px var(--b3-theme-primary-lightest)'
});
// 解析日期参数
// const parseDate = (str) => {
// const isoRegex = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2})?$/;
// return new Date(isoRegex.test(str) ? str : `${str}T00:00:00`);
// };
// 计算时间差
const startDate = new Date(startDateStr);
const targetDate = new Date(targetDateStr);
const now = new Date();
const totalDuration = targetDate - startDate;
const timeDiff = targetDate - now;
// 时间单位计算
const days = Math.max(0, Math.floor(timeDiff / 864e5));
const hours = Math.max(0, Math.floor((timeDiff % 864e5) / 36e5));
const minutes = Math.max(0, Math.floor((timeDiff % 36e5) / 6e4));
const seconds = Math.max(0, Math.floor((timeDiff % 6e4) / 1e3));
// 百分比计算
const remainingPercent = totalDuration > 0
? Math.max(0, Math.min(100, (timeDiff / totalDuration) * 100)).toFixed(1)
: 0;
// 创建进度条
const progressContainer = document.createElement('div');
Object.assign(progressContainer.style, {
margin: '2rem 0',
padding: '0 1rem'
});
const progressBar = document.createElement('div');
Object.assign(progressBar.style, {
height: '12px',
backgroundColor: '#e9ecef',
borderRadius: '6px',
overflow: 'hidden'
});
const progressFill = document.createElement('div');
Object.assign(progressFill.style, {
height: '100%',
background: 'linear-gradient(90deg, #4facfe 0%, #00f2fe 100%)',
width: `${remainingPercent}%`
});
const progressText = document.createElement('div');
Object.assign(progressText.style, {
textAlign: 'center',
marginTop: '0.5rem',
color: '#6c757d',
fontSize: '0.9em',
fontWeight: '500'
});
progressText.textContent = `${remainingPercent}% 剩余 (${startDate.toLocaleDateString()} - ${targetDate.toLocaleDateString()})`;
// 构建进度条结构
progressBar.appendChild(progressFill);
progressContainer.appendChild(progressBar);
progressContainer.appendChild(progressText);
// 创建时间轴
const timeline = document.createElement('div');
Object.assign(timeline.style, {
display: 'flex',
justifyContent: 'space-between',
padding: '2rem 0'
});
// 时间单位定义
const timeUnits = [
{ name: 'days', value: days, color: '#3498db' },
{ name: 'hours', value: hours.toString().padStart(2, '0'), color: '#2ecc71' },
{ name: 'minutes', value: minutes.toString().padStart(2, '0'), color: '#f1c40f' },
{ name: 'seconds', value: seconds.toString().padStart(2, '0'), color: '#e74c3c' }
];
// 创建时间单位块
timeUnits.forEach(unit => {
const timeBlock = document.createElement('div');
Object.assign(timeBlock.style, {
flex: '1',
textAlign: 'center',
padding: '1.5rem',
//backgroundColor: 'white',
backgroundColor: 'var(--b3-theme-background)',
margin: '0 1rem',
borderRadius: '12px',
boxShadow: '0 4px 6px rgba(0,0,0,0.05)'
});
const timeNumber = document.createElement('div');
Object.assign(timeNumber.style, {
fontSize: '2.5em',
fontWeight: '700',
fontFamily: 'Arial, sans-serif',
color: unit.color
});
timeNumber.textContent = unit.value;
const timeUnitText = document.createElement('span');
Object.assign(timeUnitText.style, {
color: '#95a5a6',
display: 'block',
marginTop: '0.5rem',
textTransform: 'uppercase',
letterSpacing: '1px'
});
timeUnitText.textContent = unit.name;
timeBlock.appendChild(timeNumber);
timeBlock.appendChild(timeUnitText);
timeline.appendChild(timeBlock);
});
// 组装完整结构
countdownContainer.appendChild(progressContainer);
countdownContainer.appendChild(timeline);
container.appendChild(countdownContainer);
return countdownContainer;
}
};
},
alias: ['countdowntimer']
}
}
export default custom;
重载自定义组件

最后在嵌入块中输入如下的代码
//!js
const query = async () => {
let dv = Query.DataView(protyle, item, top);
dv.addcountdown('2025-02-26', '2025-03-07');
//实时倒计时, 但是不推荐
// let timer = setInterval(() => {
// dv.repaint();
// }, 1000);
// dv.addDisposer(() => {
// clearInterval(timer);
// });
dv.render();
}
return query();
处于 keep simple 考虑,没有实时倒计时,不过你每次点击一下嵌入块的刷新按钮就会自动刷新当前的倒计时状态。
如果想要实时倒计时,把代码中的注释给去掉。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于