feat(client): 库存规格/系列筛选改可搜索单选下拉、仓库单选,修复下拉过宽过高溢出

原型与代码同提交(design-first):下拉宽度锚定触发器宽 max(宽,220)、列表 264 封顶超出滚动、底部计数常驻。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bupi8Kdqkfx2N5acFsHTx5
This commit is contained in:
wangjia
2026-08-28 17:30:55 +08:00
parent a1a3118c88
commit 801b8528ec
4 changed files with 283 additions and 52 deletions
+13 -8
View File
@@ -126,17 +126,17 @@ const ITEMS=[
const STATUS=['全部','在售','库存','已售'];
// 成本价/总价列 = 成本口径,仅管理员可见(非管理员两列隐藏、列设置菜单不出现,2026-07-06);原型=管理员形态
const COLS=[{key:'product',label:'商品',fixed:true},{key:'spec',label:'规格'},{key:'series',label:'系列'},{key:'prodDate',label:'生产日期',sort:true},{key:'qty',label:'库存',num:true,sort:true},{key:'cost',label:'成本价',num:true,sort:true},{key:'price',label:'总价',num:true,sort:true},{key:'batch',label:'批次号'},{key:'inDate',label:'入库时间',sort:true},{key:'supplier',label:'供应商'},{key:'status',label:'状态'},{key:'act',label:'操作',fixed:true}];
const state={q:'',code:'',status:'全部',page:1,perPage:10,hidden:new Set(),sortBy:null,sortAsc:true,spec:new Set(),series:new Set(),wh:new Set()};
const state={q:'',code:'',status:'全部',page:1,perPage:10,hidden:new Set(),sortBy:null,sortAsc:true,spec:'',series:'',wh:''};
function setActive(k){ document.querySelectorAll('.nav[data-k]').forEach(e=>e.classList.toggle('active',e.dataset.k===k)); }
function navTo(k){ setActive(k); const inv=document.querySelector('[data-view="inventory"]'), ph=document.querySelector('[data-view="placeholder"]'); if(k==='inventory'){inv.classList.add('active');ph.classList.remove('active');return;} inv.classList.remove('active');ph.classList.add('active'); const nav=NAVS.find(n=>n.k===k); document.getElementById('phTitle').textContent=nav.label; document.getElementById('phIcon').innerHTML=navSvg(nav.d); }
function visibleCols(){ return COLS.filter(c=>!state.hidden.has(c.key)); }
function filtered(){ const rows=ITEMS.filter(it=>{ if(state.status!=='全部'&&it.status!==state.status)return false; if(state.spec.size&&!state.spec.has(it.spec))return false; if(state.series.size&&!state.series.has(it.series))return false; if(state.wh.size&&!state.wh.has(it.supplier))return false; if(state.q){const q=state.q.toLowerCase(); if(!it.name.toLowerCase().includes(q)&&!it.code.toLowerCase().includes(q))return false;} return true; }); if(state.sortBy&&SORT_VAL[state.sortBy]){const v=SORT_VAL[state.sortBy];rows.sort((a,b)=>{const x=v(a),y=v(b);return (x<y?-1:x>y?1:0)*(state.sortAsc?1:-1);});} return rows; }
function filtered(){ const rows=ITEMS.filter(it=>{ if(state.status!=='全部'&&it.status!==state.status)return false; if(state.spec&&it.spec!==state.spec)return false; if(state.series&&it.series!==state.series)return false; if(state.wh&&it.supplier!==state.wh)return false; if(state.q){const q=state.q.toLowerCase(); if(!it.name.toLowerCase().includes(q)&&!it.code.toLowerCase().includes(q))return false;} return true; }); if(state.sortBy&&SORT_VAL[state.sortBy]){const v=SORT_VAL[state.sortBy];rows.sort((a,b)=>{const x=v(a),y=v(b);return (x<y?-1:x>y?1:0)*(state.sortAsc?1:-1);});} return rows; }
function render(){
document.getElementById('statusVal').textContent=state.status==='全部'?'':state.status;
// 选摘要(镜像 DsChip value):1 项=值本身,多项=「N 项」
[['spec','chipSpec','specVal'],['series','chipSeries','seriesVal'],['wh','chipWh','whVal']].forEach(([k,cid,vid])=>{ const s=state[k]; document.getElementById(vid).textContent=!s.size?'':(s.size===1?[...s][0]:`${s.size}`); document.getElementById(cid).classList.toggle('on',!!s.size); });
// 选摘要(镜像 DsChip value):选中值本身,未选为空
[['spec','chipSpec','specVal'],['series','chipSeries','seriesVal'],['wh','chipWh','whVal']].forEach(([k,cid,vid])=>{ const s=state[k]; document.getElementById(vid).textContent=s||''; document.getElementById(cid).classList.toggle('on',!!s); });
document.getElementById('chipStatus').classList.toggle('on',state.status!=='全部');
const cols=visibleCols();
document.getElementById('thead').innerHTML='<tr>'+cols.map(c=>{ const cls=(c.num?'num ':'')+(c.key==='act'?'act ':''); if(c.filter){const on=state.status!=='全部'; return `<th class="${cls}"><span class="fh ${on?'filtered':''}" onclick="openFilter('${c.filter}',this)">${c.label} <svg class="funnel" viewBox="0 0 24 24"><use href="#i-ic22"/></svg></span></th>`;} if(c.sort){const on=state.sortBy===c.key; const ic=!on?'i-chevron-down':(state.sortAsc?'i-ic51':'i-chevron-down'); return `<th class="${cls}"><span class="sh ${on?'on':''}" onclick="toggleSort('${c.key}')">${c.label} <svg class="sort-ic" viewBox="0 0 24 24"><use href="#${ic}"/></svg></span></th>`;} return `<th class="${cls}">${c.label}</th>`; }).join('')+'</tr>';
@@ -155,14 +155,19 @@ function filterStatus(s){ state.status=s; state.page=1; navTo('inventory'); rend
// 列头排序:无→升→降→无 循环(真实实现为服务端全局排序)
function toggleSort(k){ if(state.sortBy!==k){state.sortBy=k;state.sortAsc=true;} else if(state.sortAsc){state.sortAsc=false;} else {state.sortBy=null;state.sortAsc=true;} state.page=1; render(); }
const SORT_VAL={qty:it=>it.qty,cost:it=>parseFloat(String(it.cost).replace(/[¥,]/g,''))||0,price:it=>parseFloat(String(it.price).replace(/[¥,]/g,''))||0,prodDate:it=>it.prodDate||'',inDate:it=>it.inDate||''};
function clearFilters(){ state.q='';state.status='全部';state.spec.clear();state.series.clear();state.wh.clear();state.page=1; document.getElementById('searchInput').value=''; render(); }
function clearFilters(){ state.q='';state.status='全部';state.spec='';state.series='';state.wh='';state.page=1; document.getElementById('searchInput').value=''; render(); }
function openFilter(kind,t){ openMenu(t,STATUS.map(v=>({v,label:v,sel:v===state.status})),v=>{ state.status=v; state.page=1; closeMenus(); render(); }); }
// 维度筛选 chip(规格/系列/仓库,2026-07-07 从列头漏斗上移):多选,点选即生效、菜单保持打开(镜像 showDsMultiMenu
// 维度筛选 chip(规格/系列/仓库,2026-07-07 从列头漏斗上移):均为单选(2026-08-28 用户口径)。
// 规格/系列名录长 → 可搜索单选下拉(openSearchMenu,照 stock-out 客户筛选写法);
// 仓库名录短 → 普通单选下拉(openMenu,无搜索框)。
const DIM_KEY={spec:'spec',series:'series',wh:'supplier'};
function openDimFilter(dim,t){ const key=DIM_KEY[dim]; const opts=[...new Set(ITEMS.map(it=>it[key]).filter(Boolean))]; openMenu(t,opts.map(v=>({v,label:v,sel:state[dim].has(v)})),v=>{ state[dim].has(v)?state[dim].delete(v):state[dim].add(v); state.page=1; render(); openDimFilter(dim,t); }); }
const DIM_PH={spec:'搜索规格…',series:'搜索系列…'};
function openDimFilter(dim,t){ const key=DIM_KEY[dim]; const opts=[...new Set(ITEMS.map(it=>it[key]).filter(Boolean))];
if(dim==='wh'){ openMenu(t,opts.map(v=>({v,label:v,sel:v===state.wh})),v=>{ state.wh=v; state.page=1; closeMenus(); render(); }); return; }
openSearchMenu(t,opts.map(v=>({v,label:v,sel:v===state[dim]})),v=>{ state[dim]=v; state.page=1; closeMenus(); render(); },{placeholder:DIM_PH[dim]}); }
// chip 尾部 × 单清(镜像 DsChip onClear:只清本维度,不展开菜单)
function clearDim(dim,e){ e.stopPropagation(); state[dim].clear(); state.page=1; render(); }
function clearDim(dim,e){ e.stopPropagation(); state[dim]=''; state.page=1; render(); }
function clearStatus(e){ e.stopPropagation(); state.status='全部'; state.page=1; render(); }
document.getElementById('chipStatus').onclick=e=>openFilter('status',e.currentTarget);
const COL_KEY='jiu-inv-hidden';
+1 -1
View File
@@ -56,7 +56,7 @@
// 可搜索菜单:视觉承袭 .combo-pop(顶部 .cp-search 显式搜索框 + 边打边过滤 + 结果统计),
// 供列表工具栏「供应商 / 客户」等长名录筛选复用(复用 atoms.css .combo-pop 族,零新增样式)。
// items: [{ v, label, sel, small? }]opts: { placeholder, minW, cls }。
// items: [{ v, label, sel, small? }]opts: { placeholder, minW, cls }。单选:点选后由 onPick 内自行 closeMenus。
function openSearchMenu(t, items, onPick, opts) {
closeMenus();
opts = opts || {};