# baoyu-workbench-patterns — frontend ui

### 三Tab面板现代UX升级（2026-07-20）

对MCP三Tab面板（如 `飞书实时数据面板.html`）升级时，以下模式可显著提升UI/UX：

**① Tab内容淡入动画** — 每次 `renderTab()` 时创建 `.tab-content` 包装div，利用CSS动画：
```css
.tab-content{animation:fadeSlideIn .35s cubic-bezier(.4,0,.2,1)}
@keyframes fadeSlideIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}
```
```javascript
function renderTab(tab){
  var wrapper=document.createElement('div');
  wrapper.className='tab-content';
  document.getElementById('app').innerHTML='';
  document.getElementById('app').appendChild(wrapper);
  // render into wrapper, not app
}
```
> ⚠️ 注意：必须 `replaceChildren` 或用 `innerHTML='';appendChild()`，不能用 `innerHTML='<div class="tab-content">...'` 因为浏览器不会对新 innerHTML 重播 CSS animation。

**② 搜索防抖** — 筛选输入框 `oninput` 后加 200ms debounce，避免每次按键重渲染：
```javascript
function debounce(fn,delay){
  var timer;return function(){var ctx=this,args=arguments;clearTimeout(timer);
  timer=setTimeout(function(){fn.apply(ctx,args);},delay);};
}
var applyFilterDebounced=debounce(applyFilter,200);
// HTML: <input oninput="applyFilterDebounced()">
```

**③ 排序方向箭头** — 替代模糊的 `↕`，用 `↑`/`↓` 明确指示当前排序方向：
```css
.sortable::after{content:' ↕';opacity:.25}
.sortable.sorted-asc::after{content:' ↑';opacity:1;color:var(--pri)}
.sortable.sorted-desc::after{content:' ↓';opacity:1;color:var(--pri)}
```
```javascript
function toggleSortEl(th,col,sortState){
  var ths=document.querySelectorAll('.sortable[data-col="'+col+'"]');
  for(var i=0;i<ths.length;i++){
    ths[i].classList.remove('sorted-asc','sorted-desc');
    if(sortState.dir===1) ths[i].classList.add('sorted-asc');
    else if(sortState.dir===-1) ths[i].classList.add('sorted-desc');
  }
}
```

**④ KPI卡片hover lift** — 悬浮时微上浮+阴影增强+装饰光圈：
```css
.kpi-card{transition:transform .2s,box-shadow .2s;overflow:hidden}
.kpi-card:hover{transform:translateY(-2px);box-shadow:0 10px 15px rgba(0,0,0,.06)}
.kpi-card::before{content:'';position:absolute;top:0;right:0;width:80px;height:80px;
  border-radius:50%;transform:translate(30px,-30px);opacity:.04}
.kpi-card.blue::before{background:var(--pri)}
```

**⑤ HSL柱状图色相旋转** — 月度趋势柱状图用HSL色相渐变替代单一蓝色：
```javascript
months.forEach(function(m,i){
  var hue=(i/months.length*120+200); // 蓝(200°)→紫(320°)
  html+='<div class="bar" style="height:'+pct+'%;background:linear-gradient(to top,'
    +'hsl('+hue+',50%,40%),hsl('+hue+',80%,60%))">';
});
```

**⑥ 移动端汉堡菜单** — 768px以下，顶栏Tab折叠为 ☰ 按钮：
```html
<button class="menu-toggle" onclick="toggleMenu()">☰</button>
```
```css
@media(max-width:768px){
  .topbar .menu-toggle{display:block}
  .topbar .nav-links{display:none;flex-direction:column;position:fixed;top:50px;...}
  .topbar .nav-links.open{display:flex}
}
```

**⑦ 清除筛选按钮** — 一键重置所有筛选条件：
```html
<button class="btn-clear" onclick="clearFilter()">✕ 清除</button>
```
```javascript
function clearFilter(){
  document.getElementById('deptSel').value='all';
  document.getElementById('searchInput').value='';
  applyFilter();
}
```

**⑧ 聚焦发光** — 输入框focus时蓝色光晕：
```css
input:focus,select:focus{border-color:var(--pri);box-shadow:0 0 0 3px rgba(59,130,246,.1)}
```

## Tmux 编码子Agent模式（图灵 — 2026-07-19 上线）

使用 tmux 持久会话运行独立的 Hermes + Kimi k3，专门处理 HTML/编程任务。主 Agent 通过 `tmux send-keys` 委派任务，子Agent 独立工作不阻塞主会话上下文。


### SVG 绘制要点

```javascript
// 每组两根柱子：目标(浅蓝 #e8f0fe) / 实际(宝锐蓝 #1852B6)
// 实际 > 目标 → 柱子变绿 #0d904f
var over = actuals[i] > targets[i];
var color = over ? '#0d904f' : '#1852B6';
// 数值标签在柱顶
```


### 渲染函数要点

```javascript
function renderOppSandbox() {
  if (!DATA.oppSandbox) return;
  var d = DATA.oppSandbox;
  var totalStock = 0, totalIncr = 0;
  for (var s in d.by_sales) { totalStock += d.by_sales[s]['存量']||0; totalIncr += d.by_sales[s]['增量']||0; }
  // 统计卡: 总商机 / 2026目标 / 存量 / 增量
  // 汇总表: 销售员行可点击展开商机明细
}
```


### 状态标签颜色规则

- 已关闭/完成 → `tag-green`
- 审核中/处理中 → `tag-orange`
- 其他 → `tag-red`


### 渲染

左彩色边框（按分类颜色）+ 分类标签 + 客户名+档案链接 + 截止日期标签（过期红色/今天橙色）+ 删除按钮。


### 面板排序建议
价格(跨2列) | 联系人 | 集团决策链 → 商机 | 项目 | 客诉 → 飞书活动(全宽) → 待办

## 决策链iframe嵌入编辑模式（2026-07-11）

飞书Wiki画板可直接嵌入客户档案页面，用户可在档案页内编辑决策链（前提：用户浏览器已登录飞书）。

```html
<iframe src="{wiki_url}" style="width:100%;height:1200px;border:none"
  sandbox="allow-same-origin allow-scripts allow-forms allow-popups allow-fullscreen"></iframe>
```

**全屏按钮**：
```javascript
function toggleFullscreen(){
  var el=document.getElementById('dcFrame');
  if(document.fullscreenElement){document.exitFullscreen();}
  else{el.requestFullscreen();}
}
```

**推荐布局**：全宽面板内 `grid-template-columns:4fr 1fr`（画板:集团列表），1200-2400px高。画板右上角加 `⛶ 全屏` 按钮。

**⚠️ 关键限制**：仅用户本地浏览器可用（需飞书登录态），远程/headless浏览器显示登录页。

## 逐年销售数据聚合（防重复计算铁律）

每年的销售额必须从原始 ERP 订单 Excel 逐行聚合，**禁止**使用 pricing JSON 的 `years` 字段累加。


### 到货弹窗（localStorage去重）

```javascript
var prevNotified = JSON.parse(localStorage.getItem('bior_delivery_notified') || '{}');
orders.forEach(function(o) {
  if (o.shipment.status === 'delivered' && !prevNotified[o.trackingNo]) {
    // 弹窗通知 + 记录已通知
    prevNotified[o.trackingNo] = true;
  }
});
localStorage.setItem('bior_delivery_notified', JSON.stringify(prevNotified));
```
