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.
85 lines
5.5 KiB
85 lines
5.5 KiB
function edgeChip(e, cls) {
|
|
return '<span class="edge-chip ' + cls + '">' + esc(cname(e.parent)) + ' → ' + esc(cname(e.child)) + '</span>';
|
|
}
|
|
function renderGraphState() {
|
|
const delSet = new Set(G.pendingDeletes);
|
|
const delEdges = G.edges.filter(e => delSet.has(e.id));
|
|
$('graphStateBox').innerHTML =
|
|
'<div>committed 手动边(' + G.edges.length + '):' + (G.edges.length ? G.edges.map(e => edgeChip(e, 'manual')).join('') : '无') + '</div>' +
|
|
'<div>pending 新增(' + G.pendingAdds.length + '):' + (G.pendingAdds.length ? G.pendingAdds.map(a => edgeChip({ parent: a.parent, child: a.child }, 'pending')).join('') : '无') + '</div>' +
|
|
'<div>pending 删除(' + G.pendingDeletes.length + '):' + (delEdges.length ? delEdges.map(e => edgeChip(e, 'veto')).join('') : '无') + '</div>' +
|
|
'<div>公司派生边(等级10→公司,' + derivedCompanyEdges().length + '):' + (derivedCompanyEdges().length ? derivedCompanyEdges().map(e => edgeChip(e, 'company')).join('') : '无') + '</div>' +
|
|
'<div>图谱乐观锁版本:' + G.version + (G.editing ? '(编辑中)' : '') + '</div>';
|
|
}
|
|
function renderGraphLogs() {
|
|
const box = $('graphLogBox');
|
|
if (!box) return;
|
|
box.innerHTML = G.logs.length
|
|
? G.logs.slice().reverse().map(l => '<div style="border-left:2px solid #e5e7eb;padding:2px 0 2px 10px;margin-bottom:6px"><span class="hint">' + l.time + '</span> [' + l.type + '] ' + esc(l.text) + '</div>').join('')
|
|
: '暂无日志(保存关系 / 查看明文号码后出现在此)';
|
|
}
|
|
/* 列表视图渲染(数据源 = C.list 全字段;脱敏/reveal 与图谱共享 revealed) */
|
|
function filteredContacts() {
|
|
const kw = (C.query.keyword || '').trim(), internal = C.query.internal;
|
|
return C.list.filter(c => {
|
|
if (internal !== '' && String(Number(c.isInternal) || 0) !== internal) return false;
|
|
if (!kw) return true;
|
|
return (c.name || '').includes(kw) || (c.jobTitleName || '').includes(kw) || (c.phone || '').includes(kw);
|
|
});
|
|
}
|
|
function jobSelectHtml(c) {
|
|
const cur = c._jobCode || jobCodeOfName(c.jobTitleName);
|
|
return JOB_DICT.map(g =>
|
|
'<optgroup label="' + esc(g.cat) + '">' +
|
|
g.jobs.map(j => '<option value="' + esc(j.code) + '"' + (cur === j.code ? ' selected' : '') + '>' + esc(j.name) + '</option>').join('') +
|
|
'</optgroup>').join('');
|
|
}
|
|
function ynSel(c, f) {
|
|
const v = Number(c[f]);
|
|
return '<select data-f="' + f + '" data-id="' + c.id + '"><option value="0"' + (!v ? ' selected' : '') + '>否</option><option value="1"' + (v ? ' selected' : '') + '>是</option></select>';
|
|
}
|
|
function renderContactsList() {
|
|
const tbody = $('contactTbody');
|
|
const list = filteredContacts();
|
|
$('contactCount').textContent = '共 ' + C.list.length + ' 位联系人';
|
|
tbody.innerHTML = list.length ? list.map((c, i) => {
|
|
const cid = String(c.id);
|
|
const dirty = !!C.dirtyRows[cid];
|
|
const opBtns = C.editing
|
|
? (dirty ? '<span class="undo-link" data-op="undo" data-id="' + cid + '">撤销修改</span>' : '')
|
|
: '<span class="opt-link" data-op="edit" data-id="' + cid + '">编辑</span>' +
|
|
'<span class="opt-link" data-op="check" data-id="' + cid + '" data-phone="' + esc(c.phone || '') + '">查重</span>' +
|
|
'<span class="opt-link danger" data-op="del" data-id="' + cid + '">删除</span>';
|
|
const show = revealed[cid] || C.revealAll;
|
|
const phoneCell = show
|
|
? '<span class="phone-plain">' + esc(c.phone || '--') + '</span>'
|
|
: '<span class="phone-masked" data-op="reveal" data-id="' + cid + '">' + maskPhone(c.phone) + '</span>';
|
|
const cells = C.editing ? editRowCells(c) : [
|
|
esc(c.name || '--'), esc(c.jobTitleName || '--'), phoneCell,
|
|
Number(c.isKeyContact) ? '是' : '否', Number(c.isInternal) ? '是' : '否',
|
|
esc(sourceName(c.source)), esc(c.giftRemark || '--')
|
|
];
|
|
return '<tr class="' + (dirty ? 'dirty-row' : '') + '" data-id="' + cid + '">' +
|
|
'<td><input type="checkbox" class="row-chk" data-id="' + cid + '"' + (C.checked[cid] ? ' checked' : '') + '></td>' +
|
|
'<td>' + (i + 1) + '</td>' + cells.map(t => '<td>' + t + '</td>').join('') +
|
|
'<td>' + opBtns + '</td></tr>';
|
|
}).join('') : '<tr><td colspan="10" class="empty">无联系人</td></tr>';
|
|
const n = Object.keys(C.checked).filter(k => C.checked[k] && list.some(c => String(c.id) === k)).length;
|
|
$('selInfo').textContent = n ? '已选择 ' + n + ' 个联系人' : '';
|
|
$('chkAll').checked = list.length > 0 && list.every(c => C.checked[String(c.id)]);
|
|
$('btnEditList').textContent = C.editing ? '退出编辑' : '批量编辑';
|
|
$('btnSaveList').style.display = C.editing ? '' : 'none';
|
|
$('btnResetList').style.display = C.editing ? '' : 'none';
|
|
}
|
|
/* 编辑态:单元格 → 行内控件(姓名/职务二级/电话/关键/来源;内线/礼品 batchEdit 不提交 → 只读展示) */
|
|
function editRowCells(c) {
|
|
return [
|
|
'<input type="text" data-f="name" data-id="' + c.id + '" value="' + esc(c.name || '') + '">',
|
|
'<select data-f="jobFull" data-id="' + c.id + '">' + jobSelectHtml(c) + '</select>',
|
|
'<input type="text" data-f="phone" data-id="' + c.id + '" value="' + esc(c.phone || '') + '">',
|
|
ynSel(c, 'isKeyContact'),
|
|
'<span class="hint">' + (Number(c.isInternal) ? '是' : '否') + '(只读)</span>',
|
|
'<select data-f="source" data-id="' + c.id + '"><option value="">--</option>' + SOURCE_DICT.map(s => '<option value="' + esc(s.code) + '"' + (c.source === s.code ? ' selected' : '') + '>' + esc(s.name) + '</option>').join('') + '</select>',
|
|
'<span class="hint" style="white-space:normal">' + esc(c.giftRemark || '--') + '(只读)</span>'
|
|
];
|
|
}
|