@@ -1126,25 +1172,117 @@
return d.toLocaleDateString('zh-CN');
}
+ // ── 文件类型多选 ──
+ // 分组定义;扩展名要全小写(底层 file_extension 字段保证小写)
+ const EXT_GROUPS = [
+ { name: '🎬 视频', items: ['mp4','mov','mkv','avi','webm','flv','wmv'] },
+ { name: '🎵 音频', items: ['mp3','flac','wav','m4a','aac','ogg','opus'] },
+ { name: '🖼 图片', items: ['jpg','png','gif','webp','heic','bmp'] },
+ { name: '📄 文档', items: ['pdf','zip','rar','7z','txt','doc','docx','xlsx','pptx','epub'] },
+ ];
+ let selectedExtensions = []; // 当前主表单选中的扩展名列表
+
+ function renderExtPop() {
+ const pop = document.getElementById('ext-pop');
+ if (!pop) return;
+ const groups = EXT_GROUPS.map(g => {
+ const chips = g.items.map(ext => {
+ const on = selectedExtensions.includes(ext) ? ' on' : '';
+ return `
${ext}`;
+ }).join('');
+ return `
${g.name}
${chips}
`;
+ }).join('');
+ pop.innerHTML = groups + `
+ `;
+ }
+
+ function toggleExtPop(ev) {
+ if (ev) ev.stopPropagation();
+ const pop = document.getElementById('ext-pop');
+ const open = !pop.classList.contains('on');
+ if (open) {
+ renderExtPop();
+ pop.classList.add('on');
+ // 点击外部关闭:注册一次性 listener
+ setTimeout(() => document.addEventListener('click', closeExtPopOnOutside), 0);
+ } else {
+ closeExtPop();
+ }
+ }
+ function closeExtPop() {
+ document.getElementById('ext-pop').classList.remove('on');
+ document.removeEventListener('click', closeExtPopOnOutside);
+ }
+ function closeExtPopOnOutside() { closeExtPop(); }
+
+ function toggleExt(ext) {
+ const i = selectedExtensions.indexOf(ext);
+ if (i >= 0) selectedExtensions.splice(i, 1);
+ else selectedExtensions.push(ext);
+ renderExtPop();
+ updateExtBtn();
+ }
+ function clearExtensions() {
+ selectedExtensions = [];
+ renderExtPop();
+ updateExtBtn();
+ }
+ function updateExtBtn() {
+ const btn = document.getElementById('ext-open-btn');
+ const badge = document.getElementById('ext-badge');
+ if (!btn) return;
+ const n = selectedExtensions.length;
+ if (n > 0) {
+ btn.className = 'btn-filter active';
+ btn.firstChild.textContent = '🗂 已选 ';
+ badge.style.display = '';
+ badge.textContent = n;
+ } else {
+ btn.className = 'btn-filter';
+ btn.firstChild.textContent = '🗂 全部 ';
+ badge.style.display = 'none';
+ }
+ }
+ function buildExtensionFilter(exts) {
+ if (!exts || !exts.length) return '';
+ if (exts.length === 1) return `file_extension == '${exts[0]}'`;
+ return `file_extension == r'(${exts.join('|')})'`;
+ }
+ // 从表达式里提取已经写在 file_extension == r'(...)' / 'xxx' 里的扩展名列表
+ function parseExtensionsFromFilter(filter) {
+ if (!filter) return [];
+ const reg = filter.match(/file_extension\s*==\s*r'\(([^)]+)\)'/i);
+ if (reg) return reg[1].split('|').map(s => s.trim().toLowerCase()).filter(Boolean);
+ const single = filter.match(/file_extension\s*==\s*'([^']+)'/i);
+ if (single) return [single[1].trim().toLowerCase()];
+ return [];
+ }
+
// ── 加载配置 ──
function parseFilterDisplay(filter) {
if (!filter) return '';
const startM = filter.match(/message_date\s*>=\s*(\d{4}-\d{2}-\d{2})/);
const endM = filter.match(/message_date\s*<=\s*(\d{4}-\d{2}-\d{2})/);
+ const exts = parseExtensionsFromFilter(filter);
const parts = [];
if (startM || endM) {
if (startM && endM) parts.push('📅 ' + startM[1] + ' 至 ' + endM[1]);
else if (startM) parts.push('📅 ' + startM[1] + ' 起');
else parts.push('📅 至 ' + endM[1]);
- // 检查是否还有日期之外的自定义条件
- const rest = filter
- .replace(/message_date\s*>=\s*\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}/gi, '')
- .replace(/message_date\s*<=\s*\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}/gi, '')
- .replace(/\band\b/gi, '').trim();
- if (rest) parts.push('🔍 ' + rest);
- } else {
- parts.push('🔍 ' + filter);
}
+ if (exts.length) parts.push('🗂 ' + exts.join(', '));
+ // 检查是否还有日期/扩展名之外的自定义条件
+ const rest = filter
+ .replace(/message_date\s*>=\s*\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}/gi, '')
+ .replace(/message_date\s*<=\s*\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}/gi, '')
+ .replace(/file_extension\s*==\s*r'\([^)]+\)'/gi, '')
+ .replace(/file_extension\s*==\s*'[^']+'/gi, '')
+ .replace(/\band\b/gi, '').trim();
+ if (rest) parts.push('🔍 ' + rest);
+ if (!parts.length) parts.push('🔍 ' + filter);
return parts.join(' · ');
}
@@ -1310,19 +1448,25 @@
}
function buildFilterFromForm(startDate, endDate) {
+ // 高级过滤器表达式存在时优先用它,文件类型选择被忽略(与日期同样的策略)
const custom = filterExpression || '';
if (custom) return custom;
- if (!startDate) return '';
- return 'message_date >= ' + startDate + ' 00:00:00' +
- (endDate ? ' and message_date <= ' + endDate + ' 23:59:59' : '');
+ const segs = [];
+ if (startDate) {
+ segs.push('message_date >= ' + startDate + ' 00:00:00');
+ if (endDate) segs.push('message_date <= ' + endDate + ' 23:59:59');
+ }
+ const extSeg = buildExtensionFilter(selectedExtensions);
+ if (extSeg) segs.push(extSeg);
+ return segs.join(' and ');
}
- // 把队列里的起止日期格式化成 "📅 2025-11-20 至 2026-04-23";无日期则 fallback 到原 filter
+ // 把队列里的起止日期/扩展名格式化展示;优先用解析 filter(能覆盖日期+文件类型)
function formatQueueDateRange(startDate, endDate, fallbackFilter) {
- if (startDate) {
- return '📅 ' + startDate + ' 至 ' + (endDate || startDate);
- }
- return fallbackFilter || '无过滤条件';
+ const parsed = parseFilterDisplay(fallbackFilter || '');
+ if (parsed) return parsed;
+ if (startDate) return '📅 ' + startDate + ' 至 ' + (endDate || startDate);
+ return '无过滤条件';
}
function renderQueue() {