Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+160
-16
@@ -377,6 +377,43 @@
|
||||
background: var(--orange); color: #0d1117; font-size: 10px; font-weight: 700;
|
||||
}
|
||||
|
||||
/* ════ 文件类型多选下拉 ════ */
|
||||
.fb-field-ext { flex-shrink: 0; border-right: 1px solid var(--border); padding: 0 12px; }
|
||||
.ext-pop {
|
||||
display: none;
|
||||
position: absolute; top: calc(100% - 4px); left: 8px; z-index: 320;
|
||||
width: 320px; max-height: 360px; overflow: auto;
|
||||
background: var(--surface); border: 1px solid var(--border);
|
||||
border-radius: 8px; box-shadow: 0 6px 18px rgba(0,0,0,.45);
|
||||
padding: 10px 12px;
|
||||
}
|
||||
.ext-pop.on { display: block; }
|
||||
.ext-grp-title {
|
||||
font-size: 11px; color: var(--muted); font-weight: 600;
|
||||
margin: 8px 0 5px; letter-spacing: .5px;
|
||||
}
|
||||
.ext-grp-title:first-child { margin-top: 0; }
|
||||
.ext-grp { display: flex; flex-wrap: wrap; gap: 5px; }
|
||||
.ext-chip {
|
||||
display: inline-flex; align-items: center;
|
||||
padding: 3px 9px; border-radius: 12px;
|
||||
border: 1px solid var(--border); background: transparent;
|
||||
color: var(--muted); font-size: 11px; cursor: pointer;
|
||||
transition: all .12s; user-select: none;
|
||||
}
|
||||
.ext-chip:hover { border-color: var(--accent); color: var(--accent); }
|
||||
.ext-chip.on { border-color: var(--orange); color: var(--orange); background: rgba(210,153,34,.12); }
|
||||
.ext-pop-foot {
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
margin-top: 10px; padding-top: 8px; border-top: 1px solid var(--border);
|
||||
font-size: 11px;
|
||||
}
|
||||
.ext-pop-foot a {
|
||||
color: var(--muted); cursor: pointer; text-decoration: none;
|
||||
}
|
||||
.ext-pop-foot a:hover { color: var(--accent); }
|
||||
.ext-pop-foot a.danger:hover { color: var(--red); }
|
||||
|
||||
/* ════ 过滤器弹窗 ════ */
|
||||
.filter-mask {
|
||||
display: none; position: fixed; inset: 0;
|
||||
@@ -968,6 +1005,15 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 文件类型 -->
|
||||
<div class="fb-field fb-field-ext">
|
||||
<div class="fb-label">文件类型</div>
|
||||
<button class="btn-filter" id="ext-open-btn" onclick="toggleExtPop(event)">
|
||||
🗂 全部 <span class="filter-badge" id="ext-badge" style="display:none">0</span>
|
||||
</button>
|
||||
<div class="ext-pop" id="ext-pop" onclick="event.stopPropagation()"></div>
|
||||
</div>
|
||||
|
||||
<!-- 保存路径 -->
|
||||
<div class="fb-field fb-field-path">
|
||||
<div class="fb-label">
|
||||
@@ -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 `<span class="ext-chip${on}" onclick="toggleExt('${ext}')">${ext}</span>`;
|
||||
}).join('');
|
||||
return `<div class="ext-grp-title">${g.name}</div><div class="ext-grp">${chips}</div>`;
|
||||
}).join('');
|
||||
pop.innerHTML = groups + `
|
||||
<div class="ext-pop-foot">
|
||||
<a onclick="clearExtensions()" class="danger">清空</a>
|
||||
<a onclick="closeExtPop()">完成</a>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
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() {
|
||||
|
||||
Reference in New Issue
Block a user