You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
111 lines
5.1 KiB
111 lines
5.1 KiB
/* ---- 图谱编辑模式(进入=快照基准;保存成功后基准刷新为已保存状态) ---- */
|
|
function snapshotGraph() {
|
|
return { edges: JSON.parse(JSON.stringify(G.edges)), pos: JSON.parse(JSON.stringify(G.cardPos)),
|
|
version: G.version, levels: Object.fromEntries(G.contacts.map(c => [c.id, c.level])) };
|
|
}
|
|
function paintGraphEditButtons() {
|
|
$('btnGraphEdit').textContent = G.editing ? '退出编辑' : '编辑图谱';
|
|
$('btnGraphSave').style.display = G.editing ? '' : 'none';
|
|
$('btnGraphReset').style.display = G.editing ? '' : 'none';
|
|
$('hintBar').classList.toggle('show', G.editing);
|
|
wrap.classList.toggle('editing', G.editing);
|
|
}
|
|
function enterEdit() {
|
|
G.editing = true;
|
|
G.pendingAdds = []; G.pendingDeletes = []; G.cycleHighlight = null;
|
|
G.editSnapshot = snapshotGraph();
|
|
document.body.classList.add('graph-edit');
|
|
paintGraphEditButtons();
|
|
renderGraph(); renderGraphState();
|
|
toast('已进入图谱编辑:拖卡片定级/改级,拖圆点连线,悬停连线删边');
|
|
}
|
|
function doExitEdit() {
|
|
G.editing = false;
|
|
G.pendingAdds = []; G.pendingDeletes = []; G.cycleHighlight = null;
|
|
document.body.classList.remove('graph-edit');
|
|
paintGraphEditButtons();
|
|
hideBubble();
|
|
renderGraph(); renderGraphState();
|
|
}
|
|
function restoreLevels() {
|
|
const lv = (G.editSnapshot && G.editSnapshot.levels) || {};
|
|
G.contacts.forEach(c => { if (c.id in lv) c.level = lv[c.id]; });
|
|
}
|
|
function discardGraphEdit() {
|
|
if (G.editSnapshot) {
|
|
G.edges = JSON.parse(JSON.stringify(G.editSnapshot.edges));
|
|
G.cardPos = JSON.parse(JSON.stringify(G.editSnapshot.pos));
|
|
G.version = G.editSnapshot.version;
|
|
restoreLevels();
|
|
}
|
|
doExitEdit();
|
|
toast('已放弃本次修改,恢复到进入编辑时的状态');
|
|
}
|
|
function graphEditToggle() { if (G.editing) exitEditAttempt(); else enterEdit(); }
|
|
function exitEditAttempt(after) {
|
|
const dirty = G.pendingAdds.length || G.pendingDeletes.length;
|
|
if (!dirty) { doExitEdit(); if (after) after(); return; }
|
|
gUnsavedDlg({
|
|
discard: () => { discardGraphEdit(); if (after) after(); },
|
|
save: () => { saveGraph().then(ok => { if (ok) { doExitEdit(); if (after) after(); } }); }
|
|
});
|
|
}
|
|
/* 全量快照 POST graph/save(版本 CAS):nodes 全员等级终态(含未定级空值=撤级)+ 手动边终态集;
|
|
公司边派生不入快照(D8)。67017 冲突 → 退出编辑态并重拉图谱(比原型更稳,原型保留编辑态易叠脏改)。 */
|
|
async function saveGraph() {
|
|
const list = effectiveEdges();
|
|
const cyc = findCycle(list);
|
|
if (cyc) {
|
|
G.cycleHighlight = cyc;
|
|
renderGraph();
|
|
toast('检测到循环关系(' + cyc.slice(0, -1).map(cname).join(' → ') + '),已阻止保存,请修改环边(红色高亮)', 'err');
|
|
return false;
|
|
}
|
|
const adds = G.pendingAdds.slice();
|
|
const delEdges = G.edges.filter(e => G.pendingDeletes.includes(e.id));
|
|
const nextEdges = G.edges.filter(e => !G.pendingDeletes.includes(e.id))
|
|
.concat(adds.map(a => ({ id: a.parent + '>' + a.child, parent: a.parent, child: a.child })));
|
|
const form = new URLSearchParams();
|
|
form.set('customerId', D.id);
|
|
form.set('version', String(G.version));
|
|
G.contacts.forEach((c, i) => {
|
|
form.set('nodes[' + i + '].contactId', c.id);
|
|
form.set('nodes[' + i + '].level', c.level == null ? '' : String(c.level));
|
|
});
|
|
nextEdges.forEach((e, i) => {
|
|
form.set('edges[' + i + '].parentId', e.parent);
|
|
form.set('edges[' + i + '].childId', e.child);
|
|
});
|
|
try {
|
|
const newVer = await api('/api/customer/contact/graph/save', { method: 'POST', body: form.toString() });
|
|
G.version = Number(newVer) || G.version + 1;
|
|
G.edges = nextEdges;
|
|
adds.forEach(a => addLog('关系', '新增手动连线:' + cname(a.parent) + ' → ' + cname(a.child)));
|
|
delEdges.forEach(e => addLog('关系', '删除连线:' + cname(e.parent) + ' → ' + cname(e.child) + '(手动边)'));
|
|
G.pendingAdds = []; G.pendingDeletes = []; G.cycleHighlight = null;
|
|
G.editSnapshot = snapshotGraph();
|
|
toast('保存成功(版本 ' + G.version + '),已记录关系变更日志', 'ok');
|
|
renderContactsList(); renderGraph(); renderGraphState();
|
|
return true;
|
|
} catch (err) {
|
|
toast('保存失败(' + err.code + '):' + err.message + (err.code === 67017 ? ',版本已被他人推进,已退出编辑并重拉图谱' : ''), 'err');
|
|
if (err.code === 67017) { doExitEdit(); loadGraph().then(() => { layoutGraph(); renderContactsList(); renderGraph(); renderGraphState(); }); }
|
|
return false;
|
|
}
|
|
}
|
|
function resetGraphEdit() {
|
|
const dirty = G.pendingAdds.length || G.pendingDeletes.length;
|
|
const doReset = () => {
|
|
if (G.editSnapshot) {
|
|
G.edges = JSON.parse(JSON.stringify(G.editSnapshot.edges));
|
|
G.cardPos = JSON.parse(JSON.stringify(G.editSnapshot.pos));
|
|
G.version = G.editSnapshot.version;
|
|
restoreLevels();
|
|
}
|
|
G.pendingAdds = []; G.pendingDeletes = []; G.cycleHighlight = null;
|
|
toast('已重置:恢复到进入编辑时的初始状态');
|
|
renderGraph(); renderGraphState();
|
|
};
|
|
if (dirty) gConfirm('重置', '确定重置图谱布局吗?所有未保存的调整将恢复到初始状态。', doReset);
|
|
else doReset();
|
|
}
|