#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""生成①专业名词词典：可编辑HTML + Word docx"""
import json, os

BASE = '/Users/liuxinyuan/Desktop/Hermes输出-工作类/刘子研上手包'
data = json.load(open(os.path.join(BASE, '数据/专业名词词典.json'), encoding='utf-8'))

# 分类顺序
CAT_ORDER = ['PCR基础','荧光定量PCR','逆转录','防污染','反应体系与酶','性能指标','样本处理','等温扩增与前沿','注册与质控','行业与法规']
CAT_ICON = {'PCR基础':'🧬','荧光定量PCR':'📈','逆转录':'🔄','防污染':'🛡️','反应体系与酶':'⚗️','性能指标':'🎯','样本处理':'🧪','等温扩增与前沿':'⚡','注册与质控':'📜','行业与法规':'📋'}

# ============ 1. 生成 HTML ============
def esc(s):
    return (s.replace('&','&amp;').replace('<','&lt;').replace('>','&gt;')
             .replace('"','&quot;').replace("'",'&#39;'))

html_data = json.dumps(data, ensure_ascii=False)
html = '''<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>分子诊断 IVD 专业名词词典 · 宝锐生物</title>
<style>
:root{--primary:#1a73e8;--deep:#1852B6;--bg:#f5f7fa;--card:#fff;--muted:#5f6368;--line:#e5e7eb;--green:#0d904f;--light:#e8f0fe;}
*{box-sizing:border-box;margin:0;padding:0;}
body{font-family:-apple-system,"PingFang SC","Microsoft YaHei",sans-serif;background:var(--bg);color:#202124;}
header{background:linear-gradient(135deg,#1852B6,#1a73e8);color:#fff;padding:22px 28px;}
header h1{font-size:22px;font-weight:700;letter-spacing:.5px;}
header .sub{font-size:13px;opacity:.9;margin-top:6px;}
header .brand{font-size:12px;opacity:.75;margin-top:4px;letter-spacing:1px;}
.toolbar{position:sticky;top:0;background:#fff;border-bottom:1px solid var(--line);padding:12px 28px;display:flex;gap:12px;align-items:center;flex-wrap:wrap;z-index:10;}
.toolbar input{flex:1;min-width:200px;padding:9px 14px;border:1px solid var(--line);border-radius:8px;font-size:14px;outline:none;}
.toolbar input:focus{border-color:var(--primary);}
.toolbar button{padding:9px 16px;border:1px solid var(--line);border-radius:8px;background:#fff;font-size:13px;cursor:pointer;color:#202124;}
.toolbar button.primary{background:var(--primary);color:#fff;border-color:var(--primary);}
.toolbar button:hover{opacity:.9;}
.cats{display:flex;gap:8px;flex-wrap:wrap;padding:0 28px;margin:18px 0 6px;}
.cat-chip{padding:7px 14px;border-radius:20px;background:#fff;border:1px solid var(--line);font-size:13px;cursor:pointer;color:var(--muted);}
.cat-chip.active{background:var(--primary);color:#fff;border-color:var(--primary);}
.wrap{padding:8px 28px 40px;}
.cat-section{margin-top:22px;}
.cat-title{font-size:17px;font-weight:700;color:var(--deep);margin-bottom:12px;display:flex;align-items:center;gap:8px;}
.cat-title .cnt{font-size:12px;color:var(--muted);font-weight:400;background:var(--light);padding:2px 10px;border-radius:12px;}
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(340px,1fr));gap:14px;}
.term-card{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:16px;transition:box-shadow .2s;}
.term-card:hover{box-shadow:0 4px 16px rgba(26,115,232,.12);}
.term-card .t{font-size:16px;font-weight:700;color:#202124;}
.term-card .en{font-size:12px;color:var(--primary);margin-top:2px;font-family:Menlo,Consolas,monospace;}
.term-card .explain{font-size:13.5px;color:#3c4043;line-height:1.65;margin-top:10px;}
.term-card .row{display:flex;gap:8px;margin-top:12px;font-size:12.5px;align-items:flex-start;}
.term-card .lab{flex-shrink:0;color:var(--muted);font-weight:600;padding-top:1px;}
.term-card .item{color:var(--green);font-weight:600;}
.term-card .ask{color:#b06000;background:#fff7e8;padding:6px 10px;border-radius:6px;flex:1;}
[contenteditable]{outline:none;border-radius:4px;transition:background .15s;}
[contenteditable]:hover{background:var(--light);}
[contenteditable]:focus{background:#fff3cd;}
.hint{font-size:12px;color:var(--muted);padding:0 28px;}
.toast{position:fixed;bottom:24px;right:24px;background:#202124;color:#fff;padding:12px 20px;border-radius:10px;font-size:14px;opacity:0;transition:opacity .3s;pointer-events:none;}
.toast.show{opacity:1;}
@media print{header,.toolbar,.cats,.hint{display:none;}body{background:#fff;}.term-card{break-inside:avoid;}}
</style>
</head>
<body>
<header>
  <h1>分子诊断 IVD 专业名词词典</h1>
  <div class="sub">100 条核心术语 · 大白话解释 + 对应宝锐货号 + 客户常问话术</div>
  <div class="brand">BIORI BIOTECH · 刘子研新手上手包</div>
</header>
<div class="toolbar">
  <input id="search" placeholder="🔍 搜索名词 / 英文 / 解释关键词…" oninput="render()">
  <button id="editBtn" onclick="toggleEdit()">🔒 锁定</button>
  <button onclick="exportHtml()">⬇ 导出 HTML</button>
  <button onclick="exportJson()">⬇ 导出 JSON</button>
  <button onclick="resetEdit()">⟲ 重置编辑</button>
  <button class="primary" onclick="window.print()">🖨 打印 / 存 PDF</button>
</div>
<div class="hint">💡 点击任意文字即可就地编辑，改动自动保存（本地缓存）。「货号」和「客户会怎么问」是销售实战重点。</div>
<div class="cats" id="cats"></div>
<div class="wrap" id="wrap"></div>
<div class="toast" id="toast"></div>
<script>
var DICT = __DATA__;
var CAT_ORDER = __CATS__;
var CAT_ICON = __ICONS__;
var edited = JSON.parse(localStorage.getItem('br_dict_edit') || '{}');
var curCat = '全部';

// 合并编辑缓存
DICT = DICT.map(function(d, i){
  var e = edited[i];
  if(e){ ['term','en','explain','item','ask'].forEach(function(k){ if(e[k]!==undefined) d[k]=e[k]; }); }
  return d;
});

function esc(s){return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');}

function render(){
  var q = (document.getElementById('search').value || '').trim().toLowerCase();
  // 分类 chips
  var catsHtml = '<div class="cat-chip'+(curCat==='全部'?' active':'')+'" data-cat="全部">全部 ('+DICT.length+')</div>';
  CAT_ORDER.forEach(function(c){
    var n = DICT.filter(function(d){return d.cat===c;}).length;
    catsHtml += '<div class="cat-chip'+(curCat===c?' active':'')+'" data-cat="'+c+'">'+CAT_ICON[c]+' '+c+' ('+n+')</div>';
  });
  document.getElementById('cats').innerHTML = catsHtml;
  document.getElementById('cats').addEventListener('click', function(e){
    var chip = e.target.closest('.cat-chip');
    if(chip){ setCat(chip.getAttribute('data-cat')); }
  });

  var html = '';
  CAT_ORDER.forEach(function(c){
    var items = DICT.filter(function(d){return d.cat===c;});
    var filtered = items.filter(function(d){
      if(curCat!=='全部' && d.cat!==curCat) return false;
      if(q){
        var hay = (d.term+d.en+d.explain+d.item+d.ask).toLowerCase();
        if(hay.indexOf(q)===-1) return false;
      }
      return true;
    });
    if(!filtered.length) return;
    html += '<div class="cat-section"><div class="cat-title">'+CAT_ICON[c]+' '+c+' <span class="cnt">'+filtered.length+' 条</span></div><div class="grid">';
    filtered.forEach(function(d){
      var i = DICT.indexOf(d);
      html += '<div class="term-card" data-i="'+i+'">'
        + '<div class="t" contenteditable="true" data-k="term" oninput="markEdit(this)">'+esc(d.term)+'</div>'
        + '<div class="en" contenteditable="true" data-k="en" oninput="markEdit(this)">'+esc(d.en)+'</div>'
        + '<div class="explain" contenteditable="true" data-k="explain" oninput="markEdit(this)">'+esc(d.explain)+'</div>'
        + '<div class="row"><span class="lab">客户问</span><span class="ask" contenteditable="true" data-k="ask" oninput="markEdit(this)">'+esc(d.ask)+'</span></div>'
        + '</div>';
    });
    html += '</div></div>';
  });
  document.getElementById('wrap').innerHTML = html || '<div style="text-align:center;color:#9e9e9e;padding:60px;">没有匹配的名词</div>';
}

function setCat(c){ curCat = c; render(); }

var saveTimer;
function markEdit(el){
  var v = el.innerText;
  var card = el.closest('.term-card');
  var i = card.getAttribute('data-i');
  var k = el.getAttribute('data-k');
  var e = JSON.parse(localStorage.getItem('br_dict_edit') || '{}');
  e[i] = e[i] || {};
  e[i][k] = v;
  localStorage.setItem('br_dict_edit', JSON.stringify(e));
  clearTimeout(saveTimer);
  saveTimer = setTimeout(function(){ showToast('已自动保存 ✓'); }, 800);
}

function exportJson(){
  var out = DICT.map(function(d){ return {cat:d.cat, term:d.term, en:d.en, explain:d.explain, item:d.item, ask:d.ask}; });
  var blob = new Blob([JSON.stringify(out, null, 2)], {type:'application/json'});
  var a = document.createElement('a');
  a.href = URL.createObjectURL(blob);
  a.download = '专业名词词典_编辑版.json';
  a.click();
  showToast('已导出 JSON');
}

function resetEdit(){
  localStorage.removeItem('br_dict_edit');
  location.reload();
}

function showToast(m){
  var t = document.getElementById('toast');
  t.textContent = m; t.classList.add('show');
  setTimeout(function(){ t.classList.remove('show'); }, 1500);
}
var editable = true;  // 默认编辑模式
function toggleEdit(){
  editable = !editable;
  var els = document.querySelectorAll('[contenteditable]');
  for(var i=0;i<els.length;i++){ els[i].contentEditable = editable ? 'true' : 'false'; }
  document.getElementById('editBtn').textContent = editable ? '🔒 锁定' : '✏️ 编辑';
  showToast(editable ? '编辑模式' : '已锁定');
}
function exportHtml(){
  var css = '';
  var st = document.querySelectorAll('style');
  for(var i=0;i<st.length;i++){ css += st[i].outerHTML; }
  var html = '<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><title>专业名词词典</title>' + css + '</head><body>' + document.body.innerHTML + '</body></html>';
  var blob = new Blob([html], {type:'text/html;charset=utf-8'});
  var a = document.createElement('a');
  a.href = URL.createObjectURL(blob);
  a.download = '专业名词词典_编辑版.html';
  document.body.appendChild(a); a.click(); a.remove();
  showToast('已导出 HTML');
}

render();
</script>
</body>
</html>'''
html = html.replace('__DATA__', html_data)
html = html.replace('__CATS__', json.dumps(CAT_ORDER, ensure_ascii=False))
html = html.replace('__ICONS__', json.dumps(CAT_ICON, ensure_ascii=False))

html_path = os.path.join(BASE, '01_专业名词词典.html')
open(html_path, 'w', encoding='utf-8').write(html)
print('HTML 生成:', html_path, f'({len(html)/1024:.0f} KB)')

# ============ 2. 生成 Word docx ============
from docx import Document
from docx.shared import Pt, RGBColor, Inches
from docx.enum.text import WD_ALIGN_PARAGRAPH

doc = Document()
# 标题
h = doc.add_heading('分子诊断 IVD 专业名词词典', level=0)
sub = doc.add_paragraph('100 条核心术语 · 大白话解释 + 对应宝锐货号 + 客户常问话术')
sub.runs[0].font.color.rgb = RGBColor(0x5f, 0x63, 0x68)
doc.add_paragraph('BIORI BIOTECH · 刘子研新手上手包').runs[0].font.color.rgb = RGBColor(0x18, 0x52, 0xB6)

for cat in CAT_ORDER:
    items = [d for d in data if d['cat'] == cat]
    if not items:
        continue
    doc.add_heading(f"{CAT_ICON[cat]} {cat}（{len(items)} 条）", level=1)
    for d in items:
        p = doc.add_paragraph()
        r = p.add_run(d['term'])
        r.bold = True
        r.font.size = Pt(12)
        if d['en'] and d['en'] != '—':
            r2 = p.add_run('  ' + d['en'])
            r2.font.size = Pt(10)
            r2.font.color.rgb = RGBColor(0x1a, 0x73, 0xe8)
        # 解释
        pe = doc.add_paragraph()
        re = pe.add_run(d['explain'])
        re.font.size = Pt(10.5)
        # 客户问（货号字段已按要求移除）
        if d['ask'] and d['ask'] != '—':
            meta = doc.add_paragraph()
            ra = meta.add_run('客户问：' + d['ask'])
            ra.font.size = Pt(9)
            ra.font.color.rgb = RGBColor(0xb0, 0x60, 0x00)
        doc.add_paragraph()  # 空行分隔

docx_path = os.path.join(BASE, '01_专业名词词典.docx')
doc.save(docx_path)
print('Word 生成:', docx_path)
