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.
 
 
 
 
 

76 lines
3.6 KiB

/* 定级/撤级统一入口(修订⑦+⑧):有上级边禁调级;有下级边可调但须高于最高下级(公司边不计入);取消等级仍须无任何边 */
function tryLevelDrop(id, target) {
const c = byId(id);
if (!c) return;
const newLv = target.kind === 'unrec' ? null : target.lv;
if (newLv === c.level) return;
const eff = effectiveEdges();
const parentEdge = eff.find(e => e.child === id);
const childEdges = eff.filter(e => e.parent === id && e.child !== COMPANY_ID);
if (parentEdge) {
toast('「' + c.name + '」有上级连线(' + cname(parentEdge.parent) + '),请先取消上级连线后再调整等级', 'err');
layoutGraph(); renderEdgesOnly(); return;
}
if (newLv == null) {
if (childEdges.length) {
toast('「' + c.name + '」存在连线,请先取消所有连线后再取消等级', 'err');
layoutGraph(); renderEdgesOnly(); return;
}
} else if (childEdges.length) {
const maxChild = Math.max.apply(null, childEdges.map(e => byId(e.child).level));
const childNames = childEdges.filter(e => byId(e.child).level === maxChild).map(e => cname(e.child)).join('、');
if (newLv <= maxChild) {
toast('「' + c.name + '」最高下级为等级 ' + maxChild + '(' + childNames + '),新等级须高于它', 'err');
layoutGraph(); renderEdgesOnly(); return;
}
}
const from = c.level == null ? '未设置' : c.level;
c.level = newLv;
delete G.cardPos[id];
sanitizeManualEdges();
layoutGraph();
if (newLv == null) {
addLog('数据', '「' + c.name + '」等级已取消(拖回未设置栏,原等级:' + from + ')');
toast('已取消「' + c.name + '」的等级,移入未设置栏');
} else {
addLog('数据', '为「' + c.name + '」设定等级 ' + newLv + '(拖入等级格子,原等级:' + from + ')');
toast('已为「' + c.name + '」设定等级 ' + newLv);
}
renderGraph(); renderGraphState();
}
/* 强制按等级归行:等级 10 在公司上方,等级 9..1 在公司下方(9 邻近公司),未定级固定右侧栏 */
function layoutGraph() {
canvasInner.style.width = '2400px'; canvasInner.style.height = '1400px';
renderLevelRows();
const top = [], byLv = {}, unrec = [];
G.contacts.forEach(c => {
if (c.level == null) unrec.push(c);
else if (c.level === 10) top.push(c);
else (byLv[c.level] = byLv[c.level] || []).push(c);
});
const placeRow = (arr, y) => {
const L = ROW_X + 100, R = ROW_X + ROW_W;
const total = arr.length * CARD_W + Math.max(0, arr.length - 1) * 44;
let x = Math.max(L, (L + R - total) / 2);
arr.forEach(c => { G.cardPos[c.id] = { x: x, y: y }; x += CARD_W + 44; });
};
placeRow(top, LV10_Y);
const lvs = Object.keys(byLv).map(Number).sort((a, b) => b - a);
lvs.forEach(lv => placeRow(byLv[lv], lvRowY(lv)));
const zone = $('unrecZone');
zone.style.left = UNREC_X + 'px'; zone.style.top = UNREC_Y + 'px'; zone.style.width = UNREC_W + 'px'; zone.style.height = UNREC_H + 'px';
unrec.forEach((c, i) => {
G.cardPos[c.id] = { x: UNREC_X + 16 + (i % 2) * 200, y: UNREC_Y + 52 + Math.floor(i / 2) * 130 };
});
}
/* 10 个等级格子横条 + 标签(公司行不画格子) */
function renderLevelRows() {
canvasInner.querySelectorAll('.lv-row').forEach(el => el.remove());
ROWS.forEach(r => {
const el = document.createElement('div');
el.className = 'lv-row'; el.dataset.lv = r.lv;
el.style.left = ROW_X + 'px'; el.style.top = r.y + 'px'; el.style.width = ROW_W + 'px'; el.style.height = CARD_H + 'px';
el.innerHTML = '<span class="row-label">等级 ' + r.lv + (r.lv === 10 ? ' · 顶层' : '') + '</span>';
canvasInner.insertBefore(el, $('unrecZone'));
});
}