# 科研测试反馈工作流

## 数据源

Excel文件路径：`数据/科研-测试反馈跟进/科研测试反馈-2026-7W2.xlsx`

Sheet名称：`测试反馈统计`

## 列结构

| 列 | 字段 | 说明 |
|----|------|------|
| A | 负责销售 | 销售员姓名 |
| B | 产品货号 | 如 BR3M121, BR1A401 |
| E | 二级分类 | 一步法制胶试剂盒、高保真酶 等 |
| F | 课题组 | 如 杨梦华课题组 |
| H | 问题接收日期 | 2026.7.2 格式 |
| J | 竞品信息 | 诺唯赞、赛默飞 等 |
| K | 正/负反馈 | "正" 或 "负" |
| L | 客户反馈情况 | 客户原话简述 |
| M | 具体信息收集 | 详细信息 |
| N | 问题解决情况 | 解决方案/闭环记录 |
| O | 对接技术 | 技术支持工程师 |
| P | 是否产品问题 | 是/否 |
| Q | 是否解决 | 是/否（负反馈闭环判定） |
| U | 问题处理进度 | 研发跟进状态 |

## 输出JSON

`数据/test_feedback.json`，结构：
{
  "total": 258,
  "positives": [{sales, product, category, group, date, competitor, ...}],
  "negatives": [...],
  "period": "2025-12 ~ 2026-07"
}

## HTML页面

文件：`工具/科研测试反馈.html`，两个Tab：

### 正反馈案例卡
- 网格布局，每卡：课题组 / 货号 / 分类 / 销售 / 技术 / 竞品 / 效果描述
- 每卡独立上传区（微信聊天记录截图）
- 持久化：图片存入 uploadedImages JS对象（key=课题组+货号），渲染时恢复
- 点击放大：打开 Modal 分享视图（A4风格，含打印按钮）
- 筛选：产品/销售员/关键词

### 负反馈追踪
- 表格：日期 / 课题组 / 货号 / 问题 / 解决方案 / 闭环状态
- 闭环筛选：已解决 / 未解决 / 跟进中

## 图片上传持久化模式 ⚠️ 重要

### 错误方案（已废弃）

```javascript
// ❌ 使用字符串键 + innerHTML 构建 → 中文引号转义断裂
var imgKey = f.group + '_' + f.product;
div.innerHTML = '<div onclick="uploadImg(\'' + imgKey + '\')">上传</div>';
```

问题：中文课题组名/货号含特殊字符时，inline onclick 中 `\'` 层层转义导致事件断裂，图片无法显示。

### 正确方案（三管齐下）

**1. 数字索引键 + DOM事件绑定**

```javascript
var key = String(i); // 用过滤后列表索引，不用字符串拼接
div.setAttribute('data-key', key);
div.onclick = function() { openShareCard(this.getAttribute('data-key')); };

// 上传区独立绑定（stopPropagation 防穿透）
document.getElementById('uz-' + key).onclick = function(e) {
  e.stopPropagation();
  uploadImg(this.id.replace('uz-', ''));
};
```

**2. localStorage 持久化 + 即时DOM更新**

```javascript
var uploadedImages = JSON.parse(localStorage.getItem('bior_fb_imgs') || '{}');

reader.onload = function(ev) {
  uploadedImages[key] = ev.target.result;
  localStorage.setItem('bior_fb_imgs', JSON.stringify(uploadedImages));
  // 只更新当前卡片，不重渲染整个网格
  var uz = document.getElementById('uz-' + key);
  uz.innerHTML = '<img src="' + ev.target.result + '">';
};
```

**3. 弹窗内上传（uploadToModal）**

弹窗分享卡中预留 `#sc-img-area` 可点击上传区。上传后同时更新：
- 弹窗内预览区（`sc-img-area`）
- 卡片网格对应的上传区（`uz-{key}`）
- uploadedImages 对象 + localStorage

```javascript
function uploadToModal(key) {
  // 选文件 → FileReader → 写入 uploadedImages[key]
  // → 更新 sc-img-area.innerHTML
  // → 更新 uz-{key}.innerHTML
  // → saveImages()
}
```

## 分享卡弹窗 — 社交媒体分享布局（最终版）

经过三轮迭代：700px横向 → 400px纵向堆叠 → 420px蓝渐变社交卡片。

### 三层DOM结构

```
modal-overlay (半透明黑底) ─ 点击空白处关闭
  modal-card (420px白底圆角, 20px padding)
    share-card (白底, overflow:hidden, border-radius:12px)
      sc-top (蓝渐变#1a73e8→#0d47a1, 白色文字)
        sc-title: "正面案例 · 客户真实反馈" (11px, letter-spacing:2px)
        sc-group: 课题组名 (18px bold)
        sc-meta: 日期 + 销售员 (11px, opacity:.8)
      sc-body (14px 18px padding)
        sc-info: 2列 grid (货号|分类, 技术|竞品)
        sc-quote: 蓝色左边框引用块 (background:#f0f7ff)
        sc-detail: 灰底详细信息
        sc-img-wrap: 点击上传截图区
      sc-footer: 白底品牌条 (BIORI BIOTECH)
```

### 关键CSS

```css
.sc-info { display:grid; grid-template-columns:1fr 1fr; gap:3px 16px; }
.sc-info .item { border-bottom:1px dotted #eee; }
.sc-quote { background:#f0f7ff; border-left:3px solid var(--pri); border-radius:0 6px 6px 0; }
```

### 迭代历程

| 版本 | 问题 | 修复 |
|------|------|------|
| V1 横向 | 700px太宽，信息散 | 缩至400px |
| V2 堆叠 | 卡片细长，排版凌乱 | 改为蓝渐变顶栏 + 2列网格 + 紧凑布局 |
| V3 社交卡 | 用户需要"发朋友圈"级别的视觉效果 | 品牌化设计，适配420px截图 |

## 分享卡导出图片 (html2canvas + Clipboard API)

### 加载

html2canvas 从 CDN 动态加载（避免页面初始加载慢）：
```javascript
var s=document.createElement('script');
s.src='https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js';
s.onload=function(){ /* ready */ };
document.head.appendChild(s);
```

### 渲染

```javascript
html2canvas(document.querySelector('.share-card'), {
  backgroundColor: '#ffffff',
  scale: 2,  // 2x 高清
  useCORS: true
}).then(function(canvas) {
  canvas.toBlob(function(blob) { /* ... */ }, 'image/png');
});
```

### 写入剪贴板

```javascript
navigator.clipboard.write([
  new ClipboardItem({'image/png': blob})
]);
```

### 降级策略

| 场景 | 处理 |
|------|------|
| Clipboard API 不支持 | 自动下载 PNG 文件 |
| 写入剪贴板失败 | 自动下载 PNG 文件 |
| html2canvas 渲染失败 | 按钮恢复可用（静默失败） |

### 按钮状态

```
📋 复制图片 → ⏳ 生成中… → ✅ 已复制(2s) → 📋 复制图片
```

打印时（`@media print`）隐藏按钮、工具栏，只保留分享卡片内容。
