/* ---- 通用确认 / 未保存三选(主 demo 版;「关闭」按钮由 dlgOpen 追加,等同继续编辑) ---- */ function gConfirm(title, text, onOk) { dlgOpen(title, '
' + text + '
', [ { text: '取消', fn: () => dlgClose() }, { text: '确定', ok: true, fn: () => { dlgClose(); onOk(); } } ]); } function gUnsavedDlg(handlers) { dlgOpen('未保存提醒', '
您有未保存的更改,请选择后续操作:
', [ { text: '继续编辑', fn: () => dlgClose() }, { text: '放弃修改', fn: () => { dlgClose(); handlers.discard(); } }, { text: '保存修改', ok: true, fn: () => { dlgClose(); handlers.save(); } } ]); } /* ---- 画布变换(平移 / 缩放 / 适应) ---- */ function applyTransform() { canvasInner.style.transform = 'translate(' + G.view.panX + 'px,' + G.view.panY + 'px) scale(' + G.view.zoom + ')'; $('zoomLabel').textContent = Math.round(G.view.zoom * 100) + '%'; } function toCanvas(cx, cy) { const r = wrap.getBoundingClientRect(); return { x: (cx - r.left - G.view.panX) / G.view.zoom, y: (cy - r.top - G.view.panY) / G.view.zoom }; } function zoomAt(factor, anchorClientX, anchorClientY) { const old = G.view.zoom; const nz = Math.max(0.3, Math.min(2, old * factor)); if (nz === old) return; const r = wrap.getBoundingClientRect(); const ax = (anchorClientX == null ? r.width / 2 : anchorClientX - r.left); const ay = (anchorClientY == null ? r.height / 2 : anchorClientY - r.top); G.view.panX = ax - (ax - G.view.panX) * (nz / old); G.view.panY = ay - (ay - G.view.panY) * (nz / old); G.view.zoom = nz; applyTransform(); } function fitView() { if (!G.contacts.length) { applyTransform(); return; } const xs = G.contacts.map(c => (G.cardPos[c.id] || { x: 0 }).x).concat([COMPANY_POS.x]); const ys = G.contacts.map(c => (G.cardPos[c.id] || { y: 0 }).y).concat([COMPANY_POS.y]); const minX = Math.min.apply(null, xs), maxX = Math.max.apply(null, xs) + CARD_W; const minY = Math.min.apply(null, ys), maxY = Math.max.apply(null, ys) + CARD_H; const zone = $('unrecZone'); const zoneBottom = zone.offsetTop + zone.offsetHeight; const wr = wrap.getBoundingClientRect(); const bw = maxX - minX, bh = Math.max(maxY, zoneBottom) - minY; G.view.zoom = Math.max(0.3, Math.min(2, Math.min((wr.width - 50) / bw, (wr.height - 50) / bh))); G.view.panX = (wr.width - bw * G.view.zoom) / 2 - minX * G.view.zoom; G.view.panY = (wr.height - bh * G.view.zoom) / 2 - minY * G.view.zoom; applyTransform(); }