diff --git a/index.html b/index.html
index 64e0b2e..4809d1d 100755
--- a/index.html
+++ b/index.html
@@ -3,17 +3,23 @@
Click Gap 数据看板
Click Gap 数据看板
-按住 Command(macOS)或 Ctrl 可多选包名;当前选择会显示在下方状态中。
+选择包名后点击“确定”应用筛选;搜索只过滤候选项。
加载中…
@@ -26,10 +32,15 @@ function text(value){return value===null||value===undefined?"—":String(value)}
function validDate(value){return /^\d{4}-\d{2}-\d{2}$/.test(value)&&!Number.isNaN(Date.parse(value))}
function date8(value){return value.replaceAll("-","")}
function localDate(value){return `${value.getFullYear()}-${String(value.getMonth()+1).padStart(2,"0")}-${String(value.getDate()).padStart(2,"0")}`}
-function selectedPackages(){return [...$("pkg").selectedOptions].map(option=>option.value)}
+let appliedPackages=new Set(),draftPackages=new Set();
+function selectedPackages(){return [...appliedPackages]}
+function packageLabel(){return appliedPackages.size?`App(${appliedPackages.size} 个已选)`:"App(全部)"}
+function renderPackageOptions(){const query=$("pkgSearch").value.trim().toLowerCase(),container=$("pkgOptions");container.replaceChildren();[...new Set(rows.map(row=>row.pkg_name).filter(Boolean))].sort().filter(pkg=>pkg.toLowerCase().includes(query)).forEach(pkg=>{const label=document.createElement("label");label.className="pkg-option";const checkbox=document.createElement("input");checkbox.type="checkbox";checkbox.value=pkg;checkbox.checked=draftPackages.has(pkg);checkbox.addEventListener("change",()=>{if(checkbox.checked)draftPackages.add(pkg);else draftPackages.delete(pkg)});label.append(checkbox,pkg);container.append(label)})}
+function openPackages(){draftPackages=new Set(appliedPackages);renderPackageOptions();$("pkgPanel").hidden=false;$("pkgTrigger").setAttribute("aria-expanded","true");$("pkgSearch").focus()}
+function closePackages(){$("pkgPanel").hidden=true;$("pkgTrigger").setAttribute("aria-expanded","false")}
+function filteredRows(){const start=$("start").value,end=$("end").value,packages=selectedPackages();return rows.filter(row=>row.dt>=date8(start)&&row.dt<=date8(end)&&(!packages.length||packages.includes(row.pkg_name)))}
function rateValue(value){if(typeof value!=="number"||!Number.isFinite(value))return null;return Math.abs(value)<=1?value*100:value}
function formatRate(value){const rate=rateValue(value);return rate===null?"—":`${rate.toFixed(2)}%`}
-function filteredRows(){const start=$("start").value,end=$("end").value,packages=selectedPackages();return rows.filter(row=>row.dt>=date8(start)&&row.dt<=date8(end)&&(!packages.length||packages.includes(row.pkg_name)))}
function compare(a,b){const av=a[sort.key],bv=b[sort.key];if(av===bv)return 0;if(av===null||av===undefined)return 1;if(bv===null||bv===undefined)return -1;const an=sort.key==="clicks_diff_rate"?rateValue(av):av,bn=sort.key==="clicks_diff_rate"?rateValue(bv):bv;const result=typeof an==="number"&&typeof bn==="number"?an-bn:String(an).localeCompare(String(bn));return result*sort.direction}
function show(){const start=$("start").value,end=$("end").value;if(!validDate(start)||!validDate(end)||start>end){status.textContent="请输入有效日期范围,且开始日期不得晚于结束日期。";status.className="status error";$("body").replaceChildren();updatePagination(0);return}const filtered=filteredRows().sort(compare),totalPages=Math.max(1,Math.ceil(filtered.length/pageSize));page=Math.min(page,totalPages);const visible=filtered.slice((page-1)*pageSize,page*pageSize);$("body").replaceChildren();visible.forEach(row=>{const tr=document.createElement("tr");columns.forEach(column=>{const td=document.createElement("td");td.textContent=column==="clicks_diff_rate"?formatRate(row[column]):text(row[column]);if(column==="clicks_diff_rate"&&rateValue(row[column])!==null&&rateValue(row[column])>10)td.className="rate-high";tr.append(td)});$("body").append(tr)});updatePagination(filtered.length);const selected=selectedPackages();status.textContent=(failed.length?`数据不完整:跳过 ${failed.length} 个文件。 `:"")+`显示 ${filtered.length} 行,第 ${page} / ${totalPages} 页(每页 ${pageSize} 行)。`+(selected.length?`已选择 ${selected.length} 个包名。`:"未筛选包名。");status.className="status"+(failed.length?" error":"")}
function updatePagination(total){const totalPages=Math.max(1,Math.ceil(total/pageSize));$("pageInfo").textContent=`第 ${total?`${page} / ${totalPages}`:"0 / 0"} 页`;$('previous').disabled=!total||page<=1;$('next').disabled=!total||page>=totalPages}
@@ -37,7 +48,7 @@ function setupHeaders(){const header=$("head");header.replaceChildren();columns.
function resetPage(){page=1;show()}
function setDefaultDates(){const dates=rows.map(row=>row.dt).sort();if(!dates.length){$("start").value=$("end").value="";return}const end=dates.at(-1),endDate=new Date(`${end.slice(0,4)}-${end.slice(4,6)}-${end.slice(6)}T00:00:00`);endDate.setDate(endDate.getDate()-29);$("end").value=`${end.slice(0,4)}-${end.slice(4,6)}-${end.slice(6)}`;$("start").value=localDate(endDate)}
function downloadRaw(){const blob=new Blob([JSON.stringify(rows,null,2)],{type:"application/json;charset=utf-8"}),url=URL.createObjectURL(blob),link=document.createElement("a"),stamp=new Date().toISOString().replaceAll(/[:.]/g,"-");link.href=url;link.download=`af-click-gap-raw-${stamp}.json`;link.click();URL.revokeObjectURL(url)}
-async function load(){setupHeaders();try{const response=await fetch("data/index.json",{cache:"no-store"});if(!response.ok)throw Error("index.json 加载失败");const index=await response.json();if(!Array.isArray(index.files))throw Error("数据清单格式错误");const safe=index.files.filter(file=>/^\d{8}$/.test(file.date)&&file.path===`data/${file.date}.json`);failed=safe.length===index.files.length?[]:["清单路径"];const loaded=await Promise.all(safe.map(async file=>{try{const response=await fetch(file.path,{cache:"no-store"});if(!response.ok)throw Error("HTTP");const data=await response.json();if(!Array.isArray(data)||data.some(row=>!row||typeof row!=="object"||row.dt!==file.date))throw Error("格式");return data}catch(error){failed.push(file.date);return []}}));rows=loaded.flat();setDefaultDates();[...new Set(rows.map(row=>row.pkg_name).filter(Boolean))].sort().forEach(pkg=>{const option=document.createElement("option");option.value=pkg;option.textContent=pkg;$("pkg").append(option)});if(rows.length)show();else{status.textContent="暂无数据。";updatePagination(0)}}catch(error){status.textContent=`无法加载数据:${error.message}`;status.className="status error"}}
-$("apply").addEventListener("click",resetPage);$("start").addEventListener("change",resetPage);$("end").addEventListener("change",resetPage);$("clearPkg").addEventListener("click",()=>{$("pkg").selectedIndex=-1;resetPage()});$("reset").addEventListener("click",()=>{if(rows.length){$("pkg").selectedIndex=-1;setDefaultDates();resetPage()}});$("previous").addEventListener("click",()=>{if(page>1){page--;show()}});$("next").addEventListener("click",()=>{if(page/^\d{8}$/.test(file.date)&&file.path===`data/${file.date}.json`);failed=safe.length===index.files.length?[]:["清单路径"];const loaded=await Promise.all(safe.map(async file=>{try{const response=await fetch(file.path,{cache:"no-store"});if(!response.ok)throw Error("HTTP");const data=await response.json();if(!Array.isArray(data)||data.some(row=>!row||typeof row!=="object"||row.dt!==file.date))throw Error("格式");return data}catch(error){failed.push(file.date);return []}}));rows=loaded.flat();setDefaultDates();renderPackageOptions();$("pkgTrigger").textContent=packageLabel();if(rows.length)show();else{status.textContent="暂无数据。";updatePagination(0)}}catch(error){status.textContent=`无法加载数据:${error.message}`;status.className="status error"}}
+$("pkgTrigger").addEventListener("click",()=>$("pkgPanel").hidden?openPackages():closePackages());$("pkgSearch").addEventListener("input",renderPackageOptions);$("selectAllPkg").addEventListener("click",()=>{[...new Set(rows.map(row=>row.pkg_name).filter(Boolean))].forEach(pkg=>draftPackages.add(pkg));renderPackageOptions()});$("clearPkg").addEventListener("click",()=>{draftPackages.clear();renderPackageOptions()});$("confirmPkg").addEventListener("click",()=>{appliedPackages=new Set(draftPackages);$("pkgTrigger").textContent=packageLabel();closePackages();resetPage()});document.addEventListener("click",event=>{if(!$("pkgFilter").contains(event.target)&&!$("pkgPanel").hidden)closePackages()});document.addEventListener("keydown",event=>{if(event.key==="Escape"&&!$("pkgPanel").hidden)closePackages()});$("apply").addEventListener("click",resetPage);$("start").addEventListener("change",resetPage);$("end").addEventListener("change",resetPage);$("reset").addEventListener("click",()=>{if(rows.length){appliedPackages.clear();$("pkgTrigger").textContent=packageLabel();setDefaultDates();resetPage()}});$("previous").addEventListener("click",()=>{if(page>1){page--;show()}});$("next").addEventListener("click",()=>{if(page