/* ---- 删边(× 按钮;公司派生边无 × 不可删) ---- */ function edgeDeleteClick(e) { const del = e.target.closest('.edge-del'); if (!del || !G.editing) return; const g = del.closest('g.edge-g'); if (!g) return; const eid = g.dataset.eid, pair = g.dataset.pair.split('>'); if (eid.indexOf('p_') === 0) { G.pendingAdds = G.pendingAdds.filter(a => !(a.parent === pair[0] && a.child === pair[1])); toast('已撤销本次新增连线(未保存)'); renderGraph(); renderGraphState(); return; } if (!G.edges.find(x => x.id === eid)) return; G.pendingDeletes.push(eid); G.cycleHighlight = null; addLog('关系', '删除连线(未保存):' + cname(pair[0]) + ' → ' + cname(pair[1]) + '(手动边)'); renderGraph(); renderGraphState(); } /* ---- 图谱卡片电话点击明文 + 埋点(脱敏拍板:列表与图谱卡片均默认脱敏) ---- */ function cardPhoneClick(e) { const ph = e.target.closest('.gphone'); if (!ph) return; const card = ph.closest('.gcard'); if (!card || !card.dataset.id) return; revealOne(card.dataset.id).then(renderGraph); } /* ---- 悬停气泡(人脉关系/内线/礼品——节点 DTO 未含,诚实显 --) ---- */ function hideBubble() { $('bubble').style.display = 'none'; } function cardBubbleOver(e) { const card = e.target.closest('.gcard'); if (!card || card.classList.contains('company') || !card.dataset.id) { hideBubble(); return; } const c = byId(card.dataset.id); if (!c) return; const b = $('bubble'); b.innerHTML = '
' + esc(c.name) + ' · ' + esc(c.job) + '
' + '
人脉关系情况--
' + '
是否内线--
' + '
礼品备注--
'; const p = G.cardPos[c.id]; if (!p) return; b.style.left = (p.x * G.view.zoom + G.view.panX + CARD_W * G.view.zoom + 12) + 'px'; b.style.top = (p.y * G.view.zoom + G.view.panY) + 'px'; b.style.display = 'block'; } /* ---- 列表/图谱 pill 双视图切换(未保存拦截 → 三选;主 demo 按钮 on 类) ---- */ function switchContactView(v) { if (v === G.curView) return; const go = () => { G.curView = v; $('pillList').classList.toggle('on', v === 'list'); $('pillGraph').classList.toggle('on', v === 'graph'); $('cListView').style.display = v === 'list' ? '' : 'none'; $('gView').style.display = v === 'graph' ? '' : 'none'; if (v === 'graph') { layoutGraph(); if (!G.fitOnce) { fitView(); G.fitOnce = true; } renderGraph(); applyTransform(); } else renderContactsList(); }; if (G.editing) { exitEditAttempt(go); return; } if (C.editing) { exitListEditAttempt(go); return; } go(); }