cleanup: remove preset builder, restore simple preset fields
- Revert preset panel to original cnrs/isrs/cnam/snam simple fields - Remove applyPresetFilter, addPresetGroup, addPresetCondition, resetPreset, populatePresetBuilder, applyFilterPreset functions - Remove mode='preset' backend code - Remove root-format support from upload preset (no longer needed) - Remove preset template buttons from filter action bar - Keep: value dropdown with top-50, filter groups, tree-format advanced filter Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -184,6 +184,11 @@
|
|||||||
.sa-group-actions { display: flex; gap: 0.3rem; margin: 0.3rem 0; }
|
.sa-group-actions { display: flex; gap: 0.3rem; margin: 0.3rem 0; }
|
||||||
.sa-group-actions button { font-size: 0.75rem; padding: 0.15rem 0.5rem; }
|
.sa-group-actions button { font-size: 0.75rem; padding: 0.15rem 0.5rem; }
|
||||||
.sa-filter-actions { display: flex; gap: 0.5rem; margin: 0.5rem 0; flex-wrap: wrap; }
|
.sa-filter-actions { display: flex; gap: 0.5rem; margin: 0.5rem 0; flex-wrap: wrap; }
|
||||||
|
.sa-preset-row { display: flex; gap: 0.5rem; margin: 0.35rem 0; align-items: center; flex-wrap: wrap; }
|
||||||
|
.sa-preset-row label { font-size: 0.8rem; min-width: 60px; color: #555; }
|
||||||
|
.sa-preset-row select, .sa-preset-row input {
|
||||||
|
padding: 0.25rem 0.5rem; border: 1px solid #ccc; border-radius: 4px; font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Debug panel ───────────────────────────── */
|
/* ── Debug panel ───────────────────────────── */
|
||||||
.sa-debug-panel {
|
.sa-debug-panel {
|
||||||
@@ -274,18 +279,29 @@
|
|||||||
<span style="color:#999;font-size:0.75rem;">展开 ▼</span>
|
<span style="color:#999;font-size:0.75rem;">展开 ▼</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="sa-preset-body" id="saPresetBody">
|
<div class="sa-preset-body" id="saPresetBody">
|
||||||
<div style="font-size:0.85rem;margin-bottom:0.5rem;">上传后预筛选(与高级筛选格式一致,上传后生效)</div>
|
<div class="sa-preset-row">
|
||||||
<div id="saPresetConditions">
|
<label>cnrs:</label>
|
||||||
<div class="empty-state" style="padding:0.5rem;color:#999;font-size:0.85rem;text-align:center;">上传 CSV 后可用条件构建器设置预筛选</div>
|
<select id="saPresetCnrs">
|
||||||
|
<option value="">不限</option>
|
||||||
|
<option value="+">等于 +</option>
|
||||||
|
<option value="-">等于 -</option>
|
||||||
|
</select>
|
||||||
|
<label>isrs:</label>
|
||||||
|
<select id="saPresetIsrs">
|
||||||
|
<option value="">不限</option>
|
||||||
|
<option value="+">等于 +</option>
|
||||||
|
<option value="-">等于 -</option>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="sa-group-actions" style="margin-top:0.5rem;">
|
<div class="sa-preset-row">
|
||||||
<button class="btn" style="font-size:0.78rem;padding:0.2rem 0.5rem;" id="saBtnPresetAddCond" onclick="addPresetCondition()" disabled>+ 条件</button>
|
<label>cnam:</label>
|
||||||
<button class="btn" style="font-size:0.78rem;padding:0.2rem 0.5rem;" id="saBtnPresetAddGroup" onclick="addPresetGroup()" disabled>( ) 组</button>
|
<input type="text" id="saPresetCnam" placeholder="留空不限" style="width:160px;">
|
||||||
<button class="btn btn-primary" style="font-size:0.78rem;padding:0.2rem 0.6rem;" id="saBtnApplyPreset" onclick="applyPresetFilter()" disabled>应用预设筛选</button>
|
<label>snam:</label>
|
||||||
|
<input type="text" id="saPresetSnam" placeholder="留空不限" style="width:160px;">
|
||||||
</div>
|
</div>
|
||||||
<div style="font-size:0.75rem;color:#999;margin-top:0.35rem;">
|
<div style="font-size:0.75rem;color:#999;margin-top:0.35rem;">
|
||||||
条件上传后生效,可缩小分析范围。
|
条件上传时即生效,未选中的行直接丢弃。
|
||||||
<span style="color:#4361ee;cursor:pointer;margin-left:0.5rem;" onclick="resetPreset()">清除预设</span>
|
<span style="color:#4361ee;cursor:pointer;margin-left:0.5rem;" onclick="localStorage.removeItem('sa_preset');showSuccess('已清除记忆')">清除记忆</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -339,9 +355,6 @@
|
|||||||
<div class="sa-filter-actions">
|
<div class="sa-filter-actions">
|
||||||
<button class="btn" style="font-size:0.78rem;padding:0.3rem 0.6rem;" onclick="addFilterCondition()">+ 条件</button>
|
<button class="btn" style="font-size:0.78rem;padding:0.3rem 0.6rem;" onclick="addFilterCondition()">+ 条件</button>
|
||||||
<button class="btn" style="font-size:0.78rem;padding:0.3rem 0.6rem;" onclick="addFilterGroup()">( ) 组</button>
|
<button class="btn" style="font-size:0.78rem;padding:0.3rem 0.6rem;" onclick="addFilterGroup()">( ) 组</button>
|
||||||
<span style="font-size:0.75rem;color:#999;">预设:</span>
|
|
||||||
<button class="btn" style="font-size:0.72rem;padding:0.2rem 0.4rem;" onclick="applyFilterPreset('cnrs_isrs')">cnrs=+ OR isrs=+</button>
|
|
||||||
<button class="btn" style="font-size:0.72rem;padding:0.2rem 0.4rem;" onclick="applyFilterPreset('cnam_empty_cnrs_isrs')">cnam=(空) AND (cnrs=+ OR isrs=+)</button>
|
|
||||||
<select id="saFilterGlobalLogic" style="padding:0.25rem;border:1px solid #ccc;border-radius:4px;font-size:0.8rem;">
|
<select id="saFilterGlobalLogic" style="padding:0.25rem;border:1px solid #ccc;border-radius:4px;font-size:0.8rem;">
|
||||||
<option value="AND">根逻辑:AND</option>
|
<option value="AND">根逻辑:AND</option>
|
||||||
<option value="OR">根逻辑:OR</option>
|
<option value="OR">根逻辑:OR</option>
|
||||||
@@ -620,10 +633,10 @@ function restorePreset() {
|
|||||||
const saved = localStorage.getItem('sa_preset');
|
const saved = localStorage.getItem('sa_preset');
|
||||||
if (saved) {
|
if (saved) {
|
||||||
const p = JSON.parse(saved);
|
const p = JSON.parse(saved);
|
||||||
const rootEl = document.getElementById('saPresetRoot');
|
if (p.cnrs) document.getElementById('saPresetCnrs').value = p.cnrs;
|
||||||
if (rootEl && p.root) {
|
if (p.isrs) document.getElementById('saPresetIsrs').value = p.isrs;
|
||||||
rootEl.value = JSON.stringify(p.root, null, 2);
|
if (p.cnam) document.getElementById('saPresetCnam').value = p.cnam;
|
||||||
}
|
if (p.snam) document.getElementById('saPresetSnam').value = p.snam;
|
||||||
}
|
}
|
||||||
} catch(e) {}
|
} catch(e) {}
|
||||||
}
|
}
|
||||||
@@ -746,51 +759,6 @@ function updateFilterLogicTags() {
|
|||||||
}
|
}
|
||||||
document.getElementById('saFilterGlobalLogic')?.addEventListener('change', updateFilterLogicTags);
|
document.getElementById('saFilterGlobalLogic')?.addEventListener('change', updateFilterLogicTags);
|
||||||
|
|
||||||
function applyFilterPreset(val) {
|
|
||||||
if (!val) return;
|
|
||||||
const container = document.getElementById('saFilterConditions');
|
|
||||||
if (val === 'cnrs_isrs') {
|
|
||||||
// cnrs=+ OR isrs=+
|
|
||||||
const g = document.createElement('div'); g.className = 'sa-filter-group';
|
|
||||||
g.innerHTML = '<div class="sa-group-header"><span class="paren">(</span><select class="sa-group-logic" style="padding:0.2rem;border:1px solid #ccc;border-radius:3px;font-size:0.78rem;"><option value="AND">AND</option><option value="OR" selected>OR</option></select></div><div class="sa-group-inner"></div><div class="sa-group-actions"><button class="btn" onclick="addFilterCondition(this.closest(\'.sa-filter-group\').querySelector(\'.sa-group-inner\'))">+ 条件</button></div><div class="sa-group-footer"><span class="paren">)</span></div>';
|
|
||||||
container.appendChild(g);
|
|
||||||
const inner = g.querySelector('.sa-group-inner');
|
|
||||||
addFilterCondition(inner);
|
|
||||||
const r1 = inner.lastElementChild;
|
|
||||||
r1.querySelector('.sa-filter-col-select').value = 'cnrs';
|
|
||||||
r1.querySelector('.sa-filter-op').value = 'eq';
|
|
||||||
r1.querySelector('.sa-filter-val').value = '+';
|
|
||||||
addFilterCondition(inner);
|
|
||||||
const r2 = inner.lastElementChild;
|
|
||||||
r2.querySelector('.sa-filter-col-select').value = 'isrs';
|
|
||||||
r2.querySelector('.sa-filter-op').value = 'eq';
|
|
||||||
r2.querySelector('.sa-filter-val').value = '+';
|
|
||||||
} else if (val === 'cnam_empty_cnrs_isrs') {
|
|
||||||
// cnam=(空) AND (cnrs=+ OR isrs=+)
|
|
||||||
addFilterCondition(container);
|
|
||||||
const r0 = container.lastElementChild;
|
|
||||||
r0.querySelector('.sa-filter-col-select').value = 'cnam';
|
|
||||||
r0.querySelector('.sa-filter-op').value = 'is_empty';
|
|
||||||
r0.querySelector('.sa-filter-val').value = '';
|
|
||||||
r0.querySelector('.sa-filter-val').placeholder = '(空)';
|
|
||||||
const g = document.createElement('div'); g.className = 'sa-filter-group';
|
|
||||||
g.innerHTML = '<div class="sa-group-header"><span class="paren">(</span><select class="sa-group-logic" style="padding:0.2rem;border:1px solid #ccc;border-radius:3px;font-size:0.78rem;"><option value="AND">AND</option><option value="OR" selected>OR</option></select></div><div class="sa-group-inner"></div><div class="sa-group-actions"><button class="btn" onclick="addFilterCondition(this.closest(\'.sa-filter-group\').querySelector(\'.sa-group-inner\'))">+ 条件</button></div><div class="sa-group-footer"><span class="paren">)</span></div>';
|
|
||||||
container.appendChild(g);
|
|
||||||
const inner = g.querySelector('.sa-group-inner');
|
|
||||||
addFilterCondition(inner);
|
|
||||||
const r1 = inner.lastElementChild;
|
|
||||||
r1.querySelector('.sa-filter-col-select').value = 'cnrs';
|
|
||||||
r1.querySelector('.sa-filter-op').value = 'eq';
|
|
||||||
r1.querySelector('.sa-filter-val').value = '+';
|
|
||||||
addFilterCondition(inner);
|
|
||||||
const r2 = inner.lastElementChild;
|
|
||||||
r2.querySelector('.sa-filter-col-select').value = 'isrs';
|
|
||||||
r2.querySelector('.sa-filter-op').value = 'eq';
|
|
||||||
r2.querySelector('.sa-filter-val').value = '+';
|
|
||||||
}
|
|
||||||
updateFilterLogicTags();
|
|
||||||
}
|
|
||||||
|
|
||||||
function wrapRowInGroup(rowEl) {
|
function wrapRowInGroup(rowEl) {
|
||||||
if (!rowEl || !rowEl.parentElement) return;
|
if (!rowEl || !rowEl.parentElement) return;
|
||||||
const parent = rowEl.parentElement;
|
const parent = rowEl.parentElement;
|
||||||
@@ -909,8 +877,22 @@ function uploadFiles(files) {
|
|||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
Array.from(files).forEach(f => formData.append('files', f));
|
Array.from(files).forEach(f => formData.append('files', f));
|
||||||
|
|
||||||
// Preset filter is now applied post-upload via the condition builder
|
// Collect preset filters (optional - null-safe)
|
||||||
// (no longer sent during upload)
|
try {
|
||||||
|
const preset = {};
|
||||||
|
const cnrsEl = document.getElementById('saPresetCnrs');
|
||||||
|
const isrsEl = document.getElementById('saPresetIsrs');
|
||||||
|
const cnamEl = document.getElementById('saPresetCnam');
|
||||||
|
const snamEl = document.getElementById('saPresetSnam');
|
||||||
|
if (cnrsEl) { const v = cnrsEl.value; if (v) preset.cnrs = v; }
|
||||||
|
if (isrsEl) { const v = isrsEl.value; if (v) preset.isrs = v; }
|
||||||
|
if (cnamEl) { const v = cnamEl.value.trim(); if (v) preset.cnam = v; }
|
||||||
|
if (snamEl) { const v = snamEl.value.trim(); if (v) preset.snam = v; }
|
||||||
|
if (Object.keys(preset).length) {
|
||||||
|
formData.append('preset_filters', JSON.stringify(preset));
|
||||||
|
localStorage.setItem('sa_preset', JSON.stringify(preset));
|
||||||
|
}
|
||||||
|
} catch(e) { /* preset filters are optional */ }
|
||||||
|
|
||||||
setBusy(true, '正在上传和解析CSV...');
|
setBusy(true, '正在上传和解析CSV...');
|
||||||
fetch('/simple/upload/', {
|
fetch('/simple/upload/', {
|
||||||
@@ -959,63 +941,11 @@ function uploadFiles(files) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('saUploadResult').style.display = 'block';
|
document.getElementById('saUploadResult').style.display = 'block';
|
||||||
populatePresetBuilder();
|
|
||||||
showSuccess(`上传成功!共 ${data.total_rows.toLocaleString()} 条记录`);
|
showSuccess(`上传成功!共 ${data.total_rows.toLocaleString()} 条记录`);
|
||||||
})
|
})
|
||||||
.catch(err => { hideLoading(); showError('上传失败: ' + err.message); });
|
.catch(err => { hideLoading(); showError('上传失败: ' + err.message); });
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Preset filter (upload step) ──────────────────────── */
|
|
||||||
function addPresetCondition() { addFilterCondition(document.getElementById('saPresetConditions')); }
|
|
||||||
function addPresetGroup() { addFilterGroup(document.getElementById('saPresetConditions')); }
|
|
||||||
|
|
||||||
function applyPresetFilter() {
|
|
||||||
const container = document.getElementById('saPresetConditions');
|
|
||||||
const items = serializeFilterTree(container);
|
|
||||||
if (!items.length) { showError('请至少添加一个筛选条件'); return; }
|
|
||||||
if (SA._loading || !SA.sessionId) return;
|
|
||||||
setBusy(true, '正在应用预设筛选...');
|
|
||||||
fetch('/simple/filter/advanced/', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {'Content-Type': 'application/json', 'X-CSRFToken': getCSRF()},
|
|
||||||
body: JSON.stringify({
|
|
||||||
session_id: SA.sessionId,
|
|
||||||
mode: 'preset',
|
|
||||||
root: {type: 'group', logic: 'AND', items: items},
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
.then(r => r.json())
|
|
||||||
.then(data => {
|
|
||||||
setBusy(false);
|
|
||||||
if (data.error) { showError(data.error); return; }
|
|
||||||
setText('saTotalRows', (data.filtered_rows || 0).toLocaleString());
|
|
||||||
updateDebug({status: '已上传+预设', preset: data.filtered_rows, total: data.total_before_filter});
|
|
||||||
showSuccess('预设筛选完成,' + data.filtered_rows + ' 条记录');
|
|
||||||
const anyCol = container.querySelector('.sa-filter-col-select');
|
|
||||||
if (anyCol && anyCol.value) onFilterColChange(anyCol);
|
|
||||||
})
|
|
||||||
.catch(err => { setBusy(false); showError('预设筛选失败: ' + err.message); });
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetPreset() {
|
|
||||||
const container = document.getElementById('saPresetConditions');
|
|
||||||
container.innerHTML = '<div class="empty-state" style="padding:0.5rem;color:#999;font-size:0.85rem;text-align:center;">上传 CSV 后可用条件构建器设置预筛选</div>';
|
|
||||||
document.getElementById('saBtnPresetAddCond').disabled = true;
|
|
||||||
document.getElementById('saBtnPresetAddGroup').disabled = true;
|
|
||||||
document.getElementById('saBtnApplyPreset').disabled = true;
|
|
||||||
localStorage.removeItem('sa_preset');
|
|
||||||
showSuccess('预设已清除');
|
|
||||||
}
|
|
||||||
|
|
||||||
function populatePresetBuilder() {
|
|
||||||
const container = document.getElementById('saPresetConditions');
|
|
||||||
container.innerHTML = '';
|
|
||||||
document.getElementById('saBtnPresetAddCond').disabled = false;
|
|
||||||
document.getElementById('saBtnPresetAddGroup').disabled = false;
|
|
||||||
document.getElementById('saBtnApplyPreset').disabled = false;
|
|
||||||
addFilterCondition(container);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Step 2: Filter ───────────────────────────────────── */
|
/* ── Step 2: Filter ───────────────────────────────────── */
|
||||||
|
|
||||||
function renderPreview(cols, rows) {
|
function renderPreview(cols, rows) {
|
||||||
|
|||||||
+19
-33
@@ -287,34 +287,25 @@ def upload_csv(request):
|
|||||||
# ── 预设筛选 ────────────────────────────────────────────────────────────
|
# ── 预设筛选 ────────────────────────────────────────────────────────────
|
||||||
preset_filtered = total_rows
|
preset_filtered = total_rows
|
||||||
try:
|
try:
|
||||||
# 新格式:树状条件 root(与高级筛选格式一致)
|
cond = None
|
||||||
root_filter = preset_filters.get('root')
|
if preset_filters.get('cnrs') in ('+', '-'):
|
||||||
if root_filter:
|
if cnrs_col:
|
||||||
combined_cond = _build_tree_condition(root_filter, df)
|
c = (pl.col(cnrs_col).cast(pl.Utf8).str.strip_chars() == '+')
|
||||||
if combined_cond is not None:
|
if preset_filters['cnrs'] == '-':
|
||||||
df = df.filter(combined_cond)
|
c = ~c
|
||||||
preset_filtered = len(df)
|
cond = c if cond is None else (cond & c)
|
||||||
else:
|
if preset_filters.get('isrs') in ('+', '-'):
|
||||||
# 旧格式:简单字段 AND
|
if isrs_col:
|
||||||
cond = None
|
c = (pl.col(isrs_col).cast(pl.Utf8).str.strip_chars() == '+')
|
||||||
if preset_filters.get('cnrs') in ('+', '-'):
|
if preset_filters['isrs'] == '-':
|
||||||
if cnrs_col:
|
c = ~c
|
||||||
c = (pl.col(cnrs_col).cast(pl.Utf8).str.strip_chars() == '+')
|
cond = c if cond is None else (cond & c)
|
||||||
if preset_filters['cnrs'] == '-':
|
if preset_filters.get('cnam'):
|
||||||
c = ~c
|
if cnam_col:
|
||||||
cond = c if cond is None else (cond & c)
|
c = (pl.col(cnam_col).cast(pl.Utf8) == preset_filters['cnam'])
|
||||||
if preset_filters.get('isrs') in ('+', '-'):
|
cond = c if cond is None else (cond & c)
|
||||||
if isrs_col:
|
if preset_filters.get('snam'):
|
||||||
c = (pl.col(isrs_col).cast(pl.Utf8).str.strip_chars() == '+')
|
if snam_col:
|
||||||
if preset_filters['isrs'] == '-':
|
|
||||||
c = ~c
|
|
||||||
cond = c if cond is None else (cond & c)
|
|
||||||
if preset_filters.get('cnam'):
|
|
||||||
if cnam_col:
|
|
||||||
c = (pl.col(cnam_col).cast(pl.Utf8) == preset_filters['cnam'])
|
|
||||||
cond = c if cond is None else (cond & c)
|
|
||||||
if preset_filters.get('snam'):
|
|
||||||
if snam_col:
|
|
||||||
c = (pl.col(snam_col).cast(pl.Utf8) == preset_filters['snam'])
|
c = (pl.col(snam_col).cast(pl.Utf8) == preset_filters['snam'])
|
||||||
cond = c if cond is None else (cond & c)
|
cond = c if cond is None else (cond & c)
|
||||||
if cond is not None:
|
if cond is not None:
|
||||||
@@ -551,11 +542,6 @@ def advanced_filter(request):
|
|||||||
return JsonResponse({'error': f'筛选构建失败: {e}'}, status=400)
|
return JsonResponse({'error': f'筛选构建失败: {e}'}, status=400)
|
||||||
|
|
||||||
filtered_rows = len(filtered)
|
filtered_rows = len(filtered)
|
||||||
mode = body.get('mode', '')
|
|
||||||
if mode == 'preset':
|
|
||||||
# 预设模式:覆写原始 df,后续步骤基于预设筛选后的数据
|
|
||||||
entry['df'] = filtered
|
|
||||||
entry['meta']['preset_filtered'] = filtered_rows
|
|
||||||
entry['meta']['filtered_rows'] = filtered_rows
|
entry['meta']['filtered_rows'] = filtered_rows
|
||||||
entry['filtered_df'] = filtered
|
entry['filtered_df'] = filtered
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user